From 5b1ed1ef2076b3a89ba28836eee6072cb16124dc Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Thu, 27 Nov 2025 23:31:32 +0000 Subject: [PATCH 001/367] [DOC] Remove unneeded filename from rdoc-ref links (#15339) --- doc/language/calendars.rdoc | 2 +- doc/language/format_specifications.rdoc | 26 ++++----- doc/language/options.md | 70 ++++++++++++------------- doc/language/strftime_formatting.rdoc | 6 +-- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/doc/language/calendars.rdoc b/doc/language/calendars.rdoc index eb9638f55231ca..a2540f1c433b5a 100644 --- a/doc/language/calendars.rdoc +++ b/doc/language/calendars.rdoc @@ -31,7 +31,7 @@ See also {a concrete example here}[rdoc-ref:DateTime@When+should+you+use+DateTim === Argument +start+ Certain methods in class \Date handle differences in the -{Julian and Gregorian calendars}[rdoc-ref:language/calendars.rdoc@Julian+and+Gregorian+Calendars] +{Julian and Gregorian calendars}[rdoc-ref:@Julian+and+Gregorian+Calendars] by accepting an optional argument +start+, whose value may be: - Date::ITALY (the default): the created date is Julian diff --git a/doc/language/format_specifications.rdoc b/doc/language/format_specifications.rdoc index 61eaefec4af179..a59f2103775ec8 100644 --- a/doc/language/format_specifications.rdoc +++ b/doc/language/format_specifications.rdoc @@ -46,42 +46,42 @@ The links lead to the details and examples. === \Integer Type Specifiers - +b+ or +B+: Format +argument+ as a binary integer. - See {Specifiers b and B}[rdoc-ref:language/format_specifications.rdoc@Specifiers+b+and+B]. + See {Specifiers b and B}[rdoc-ref:@Specifiers+b+and+B]. - +d+, +i+, or +u+ (all are identical): Format +argument+ as a decimal integer. - See {Specifier d}[rdoc-ref:language/format_specifications.rdoc@Specifier+d]. + See {Specifier d}[rdoc-ref:@Specifier+d]. - +o+: Format +argument+ as an octal integer. - See {Specifier o}[rdoc-ref:language/format_specifications.rdoc@Specifier+o]. + See {Specifier o}[rdoc-ref:@Specifier+o]. - +x+ or +X+: Format +argument+ as a hexadecimal integer. - See {Specifiers x and X}[rdoc-ref:language/format_specifications.rdoc@Specifiers+x+and+X]. + See {Specifiers x and X}[rdoc-ref:@Specifiers+x+and+X]. === Floating-Point Type Specifiers - +a+ or +A+: Format +argument+ as hexadecimal floating-point number. - See {Specifiers a and A}[rdoc-ref:language/format_specifications.rdoc@Specifiers+a+and+A]. + See {Specifiers a and A}[rdoc-ref:@Specifiers+a+and+A]. - +e+ or +E+: Format +argument+ in scientific notation. - See {Specifiers e and E}[rdoc-ref:language/format_specifications.rdoc@Specifiers+e+and+E]. + See {Specifiers e and E}[rdoc-ref:@Specifiers+e+and+E]. - +f+: Format +argument+ as a decimal floating-point number. - See {Specifier f}[rdoc-ref:language/format_specifications.rdoc@Specifier+f]. + See {Specifier f}[rdoc-ref:@Specifier+f]. - +g+ or +G+: Format +argument+ in a "general" format. - See {Specifiers g and G}[rdoc-ref:language/format_specifications.rdoc@Specifiers+g+and+G]. + See {Specifiers g and G}[rdoc-ref:@Specifiers+g+and+G]. === Other Type Specifiers - +c+: Format +argument+ as a character. - See {Specifier c}[rdoc-ref:language/format_specifications.rdoc@Specifier+c]. + See {Specifier c}[rdoc-ref:@Specifier+c]. - +p+: Format +argument+ as a string via argument.inspect. - See {Specifier p}[rdoc-ref:language/format_specifications.rdoc@Specifier+p]. + See {Specifier p}[rdoc-ref:@Specifier+p]. - +s+: Format +argument+ as a string via argument.to_s. - See {Specifier s}[rdoc-ref:language/format_specifications.rdoc@Specifier+s]. + See {Specifier s}[rdoc-ref:@Specifier+s]. - %: Format +argument+ ('%') as a single percent character. - See {Specifier %}[rdoc-ref:language/format_specifications.rdoc@Specifier+-25]. + See {Specifier %}[rdoc-ref:@Specifier+-25]. == Flags The effect of a flag may vary greatly among type specifiers. These remarks are general in nature. -See {type-specific details}[rdoc-ref:language/format_specifications.rdoc@Type+Specifier+Details+and+Examples]. +See {type-specific details}[rdoc-ref:@Type+Specifier+Details+and+Examples]. Multiple flags may be given with single type specifier; order does not matter. diff --git a/doc/language/options.md b/doc/language/options.md index ededb67f8d8dbf..c805c7dd65c2c0 100644 --- a/doc/language/options.md +++ b/doc/language/options.md @@ -68,15 +68,15 @@ nil See also: -- {Option -a}[rdoc-ref:language/options.md@a-3A+Split+Input+Lines+into+Fields]: +- {Option -a}[rdoc-ref:@a-3A+Split+Input+Lines+into+Fields]: Split input lines into fields. -- {Option -F}[rdoc-ref:language/options.md@F-3A+Set+Input+Field+Separator]: +- {Option -F}[rdoc-ref:@F-3A+Set+Input+Field+Separator]: Set input field separator. -- {Option -l}[rdoc-ref:language/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: +- {Option -l}[rdoc-ref:@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: Set output record separator; chop lines. -- {Option -n}[rdoc-ref:language/options.md@n-3A+Run+Program+in+gets+Loop]: +- {Option -n}[rdoc-ref:@n-3A+Run+Program+in+gets+Loop]: Run program in `gets` loop. -- {Option -p}[rdoc-ref:language/options.md@p-3A+-n-2C+with+Printing]: +- {Option -p}[rdoc-ref:@p-3A+-n-2C+with+Printing]: `-n`, with printing. ### `-a`: Split Input Lines into Fields @@ -98,15 +98,15 @@ and the default field separator is `$;`. See also: -- {Option -0}[rdoc-ref:language/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: +- {Option -0}[rdoc-ref:@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: Set `$/` (input record separator). -- {Option -F}[rdoc-ref:language/options.md@F-3A+Set+Input+Field+Separator]: +- {Option -F}[rdoc-ref:@F-3A+Set+Input+Field+Separator]: Set input field separator. -- {Option -l}[rdoc-ref:language/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: +- {Option -l}[rdoc-ref:@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: Set output record separator; chop lines. -- {Option -n}[rdoc-ref:language/options.md@n-3A+Run+Program+in+gets+Loop]: +- {Option -n}[rdoc-ref:@n-3A+Run+Program+in+gets+Loop]: Run program in `gets` loop. -- {Option -p}[rdoc-ref:language/options.md@p-3A+-n-2C+with+Printing]: +- {Option -p}[rdoc-ref:@p-3A+-n-2C+with+Printing]: `-n`, with printing. ### `-c`: Check Syntax @@ -228,15 +228,15 @@ The argument must immediately follow the option See also: -- {Option -0}[rdoc-ref:language/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: +- {Option -0}[rdoc-ref:@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: Set `$/` (input record separator). -- {Option -a}[rdoc-ref:language/options.md@a-3A+Split+Input+Lines+into+Fields]: +- {Option -a}[rdoc-ref:@a-3A+Split+Input+Lines+into+Fields]: Split input lines into fields. -- {Option -l}[rdoc-ref:language/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: +- {Option -l}[rdoc-ref:@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: Set output record separator; chop lines. -- {Option -n}[rdoc-ref:language/options.md@n-3A+Run+Program+in+gets+Loop]: +- {Option -n}[rdoc-ref:@n-3A+Run+Program+in+gets+Loop]: Run program in `gets` loop. -- {Option -p}[rdoc-ref:language/options.md@p-3A+-n-2C+with+Printing]: +- {Option -p}[rdoc-ref:@p-3A+-n-2C+with+Printing]: `-n`, with printing. ### `-h`: Print Short Help Message @@ -314,15 +314,15 @@ $ ruby -ln -e 'p $_' desiderata.txt See also: -- {Option -0}[rdoc-ref:language/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: +- {Option -0}[rdoc-ref:@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: Set `$/` (input record separator). -- {Option -a}[rdoc-ref:language/options.md@a-3A+Split+Input+Lines+into+Fields]: +- {Option -a}[rdoc-ref:@a-3A+Split+Input+Lines+into+Fields]: Split input lines into fields. -- {Option -F}[rdoc-ref:language/options.md@F-3A+Set+Input+Field+Separator]: +- {Option -F}[rdoc-ref:@F-3A+Set+Input+Field+Separator]: Set input field separator. -- {Option -n}[rdoc-ref:language/options.md@n-3A+Run+Program+in+gets+Loop]: +- {Option -n}[rdoc-ref:@n-3A+Run+Program+in+gets+Loop]: Run program in `gets` loop. -- {Option -p}[rdoc-ref:language/options.md@p-3A+-n-2C+with+Printing]: +- {Option -p}[rdoc-ref:@p-3A+-n-2C+with+Printing]: `-n`, with printing. ### `-n`: Run Program in `gets` Loop @@ -348,15 +348,15 @@ be on good terms with all persons. See also: -- {Option -0}[rdoc-ref:language/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: +- {Option -0}[rdoc-ref:@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: Set `$/` (input record separator). -- {Option -a}[rdoc-ref:language/options.md@a-3A+Split+Input+Lines+into+Fields]: +- {Option -a}[rdoc-ref:@a-3A+Split+Input+Lines+into+Fields]: Split input lines into fields. -- {Option -F}[rdoc-ref:language/options.md@F-3A+Set+Input+Field+Separator]: +- {Option -F}[rdoc-ref:@F-3A+Set+Input+Field+Separator]: Set input field separator. -- {Option -l}[rdoc-ref:language/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: +- {Option -l}[rdoc-ref:@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: Set output record separator; chop lines. -- {Option -p}[rdoc-ref:language/options.md@p-3A+-n-2C+with+Printing]: +- {Option -p}[rdoc-ref:@p-3A+-n-2C+with+Printing]: `-n`, with printing. ### `-p`: `-n`, with Printing @@ -377,15 +377,15 @@ be on good terms with all persons. See also: -- {Option -0}[rdoc-ref:language/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: +- {Option -0}[rdoc-ref:@0-3A+Set+-24-2F+-28Input+Record+Separator-29]: Set `$/` (input record separator). -- {Option -a}[rdoc-ref:language/options.md@a-3A+Split+Input+Lines+into+Fields]: +- {Option -a}[rdoc-ref:@a-3A+Split+Input+Lines+into+Fields]: Split input lines into fields. -- {Option -F}[rdoc-ref:language/options.md@F-3A+Set+Input+Field+Separator]: +- {Option -F}[rdoc-ref:@F-3A+Set+Input+Field+Separator]: Set input field separator. -- {Option -l}[rdoc-ref:language/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: +- {Option -l}[rdoc-ref:@l-3A+Set+Output+Record+Separator-3B+Chop+Lines]: Set output record separator; chop lines. -- {Option -n}[rdoc-ref:language/options.md@n-3A+Run+Program+in+gets+Loop]: +- {Option -n}[rdoc-ref:@n-3A+Run+Program+in+gets+Loop]: Run program in `gets` loop. ### `-r`: Require Library @@ -434,7 +434,7 @@ $ ruby -s t.rb -foo=baz -bar=bat ``` The option may not be used with -{option -e}[rdoc-ref:language/options.md@e-3A+Execute+Given+Ruby+Code] +{option -e}[rdoc-ref:@e-3A+Execute+Given+Ruby+Code] ### `-S`: Search Directories in `ENV['PATH']` @@ -583,7 +583,7 @@ ruby - Copyright (C) 1993-2024 Yukihiro Matsumoto ### `--debug`: Alias for `-d` Option `--debug` is an alias for -{option -d}[rdoc-ref:language/options.md@d-3A+Set+-24DEBUG+to+true]. +{option -d}[rdoc-ref:@d-3A+Set+-24DEBUG+to+true]. ### `--disable`: Disable Features @@ -617,9 +617,9 @@ option was given: - `--dump=help`: Same as {option \-\-help}[options_md.html#label--help-3A+Print+Help+Message]. - `--dump=syntax`: - Same as {option -c}[rdoc-ref:language/options.md@c-3A+Check+Syntax]. + Same as {option -c}[rdoc-ref:@c-3A+Check+Syntax]. - `--dump=usage`: - Same as {option -h}[rdoc-ref:language/options.md@h-3A+Print+Short+Help+Message]. + Same as {option -h}[rdoc-ref:@h-3A+Print+Short+Help+Message]. - `--dump=version`: Same as {option \-\-version}[options_md.html#label--version-3A+Print+Ruby+Version]. @@ -641,7 +641,7 @@ see {option --disable}[options_md.html#label--disable-3A+Disable+Features]. ### `--encoding`: Alias for `-E`. Option `--encoding` is an alias for -{option -E}[rdoc-ref:language/options.md@E-3A+Set+Default+Encodings]. +{option -E}[rdoc-ref:@E-3A+Set+Default+Encodings]. ### `--external-encoding`: Set Default External \Encoding diff --git a/doc/language/strftime_formatting.rdoc b/doc/language/strftime_formatting.rdoc index 991a708fa4cb3c..2bfa6b975e3153 100644 --- a/doc/language/strftime_formatting.rdoc +++ b/doc/language/strftime_formatting.rdoc @@ -136,7 +136,7 @@ the only required part is the conversion specifier, so we begin with that. t = Time.now # => 2022-06-29 07:10:20.3230914 -0500 t.strftime('%N') # => "323091400" # Default. - Use {width specifiers}[rdoc-ref:language/strftime_formatting.rdoc@Width+Specifiers] + Use {width specifiers}[rdoc-ref:@Width+Specifiers] to adjust units: t.strftime('%3N') # => "323" # Milliseconds. @@ -522,6 +522,4 @@ An ISO 8601 combined date and time representation may be any ISO 8601 date and any ISO 8601 time, separated by the letter +T+. -For the relevant +strftime+ formats, see -{Dates}[rdoc-ref:language/strftime_formatting.rdoc@Dates] -and {Times}[rdoc-ref:language/strftime_formatting.rdoc@Times] above. +For the relevant +strftime+ formats, see {Dates}[rdoc-ref:@Dates] and {Times}[rdoc-ref:@Times] above. From 9929dc4440a76b57f362ad676a9b8b64c309a6d8 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 20 Nov 2025 02:42:42 -0800 Subject: [PATCH 002/367] Mask off unused VWA bits --- gc/default/default.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gc/default/default.c b/gc/default/default.c index 0858d94b89e79c..7f568aaccc1815 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -426,6 +426,7 @@ typedef int (*gc_compact_compare_func)(const void *l, const void *r, void *d); typedef struct rb_heap_struct { short slot_size; + bits_t slot_bits_mask; /* Basic statistics */ size_t total_allocated_pages; @@ -3570,9 +3571,12 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context GC_ASSERT(bitmap_plane_count == HEAP_PAGE_BITMAP_LIMIT - 1 || bitmap_plane_count == HEAP_PAGE_BITMAP_LIMIT); + bits_t slot_mask = heap->slot_bits_mask; + // Skip out of range slots at the head of the page bitset = ~bits[0]; bitset >>= NUM_IN_PAGE(p); + bitset &= slot_mask; if (bitset) { gc_sweep_plane(objspace, heap, p, bitset, ctx); } @@ -3580,6 +3584,7 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context for (int i = 1; i < bitmap_plane_count; i++) { bitset = ~bits[i]; + bitset &= slot_mask; if (bitset) { gc_sweep_plane(objspace, heap, p, bitset, ctx); } @@ -9435,6 +9440,17 @@ rb_gc_impl_objspace_init(void *objspace_ptr) heap->slot_size = (1 << i) * BASE_SLOT_SIZE; + // Bitmask with every (1 << i)th bit set, representing aligned slot positions + static const bits_t slot_bits_masks[] = { + ~(bits_t)0, // i=0: every 1st bit + (bits_t)0x5555555555555555ULL, // i=1: every 2nd bit + (bits_t)0x1111111111111111ULL, // i=2: every 4th bit + (bits_t)0x0101010101010101ULL, // i=3: every 8th bit + (bits_t)0x0001000100010001ULL, // i=4: every 16th bit + }; + GC_ASSERT(i < numberof(slot_bits_masks)); + heap->slot_bits_mask = slot_bits_masks[i]; + ccan_list_head_init(&heap->pages); } From 5e2e45fc242b79e844754691ad106c463ebb6251 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 27 Nov 2025 00:49:28 -0800 Subject: [PATCH 003/367] Fix for modgc --- gc/default/default.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gc/default/default.c b/gc/default/default.c index 7f568aaccc1815..530fc4df891acb 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -9448,7 +9448,7 @@ rb_gc_impl_objspace_init(void *objspace_ptr) (bits_t)0x0101010101010101ULL, // i=3: every 8th bit (bits_t)0x0001000100010001ULL, // i=4: every 16th bit }; - GC_ASSERT(i < numberof(slot_bits_masks)); + GC_ASSERT(i < sizeof(slot_bits_masks) / sizeof(slot_bits_masks[0])); heap->slot_bits_mask = slot_bits_masks[i]; ccan_list_head_init(&heap->pages); From 404e6aa9b5e56b094b50f54c00487e5a7bfdf142 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 27 Nov 2025 21:14:07 -0500 Subject: [PATCH 004/367] [DOC] Fix typo in rb_debug_inspector_current_depth --- include/ruby/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ruby/debug.h b/include/ruby/debug.h index 0a9f693951dc1f..547d5d94c45f5b 100644 --- a/include/ruby/debug.h +++ b/include/ruby/debug.h @@ -297,7 +297,7 @@ VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index) #define RB_DEBUG_INSPECTOR_FRAME_DEPTH(dc, index) rb_debug_inspector_frame_depth(dc, index) /** - * Return current frmae depth. + * Return current frame depth. * * @retval The depth of the current frame in Integer. */ From 5a82880ea98617ab6894cd771ea3c3899f9521bc Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 28 Nov 2025 10:48:53 +0000 Subject: [PATCH 005/367] Bump RDoc version to 6.16.1 (#15344) --- gems/bundled_gems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index e1e6b01bfdda10..180e1dffcd4dae 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -39,7 +39,7 @@ ostruct 0.6.3 https://github.com/ruby/ostruct pstore 0.2.0 https://github.com/ruby/pstore benchmark 0.5.0 https://github.com/ruby/benchmark logger 1.7.0 https://github.com/ruby/logger -rdoc 6.16.0 https://github.com/ruby/rdoc +rdoc 6.16.1 https://github.com/ruby/rdoc win32ole 1.9.2 https://github.com/ruby/win32ole irb 1.15.3 https://github.com/ruby/irb reline 0.6.3 https://github.com/ruby/reline From dcb9e17f467683b32f429736cf7598b1ce89cdc5 Mon Sep 17 00:00:00 2001 From: git Date: Fri, 28 Nov 2025 10:49:46 +0000 Subject: [PATCH 006/367] [DOC] Update bundled gems list at 5a82880ea98617ab6894cd771ea3c3 --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6fea49923f1a96..18265f7c7cada5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -167,7 +167,7 @@ The following bundled gems are promoted from default gems. * pstore 0.2.0 * benchmark 0.5.0 * logger 1.7.0 -* rdoc 6.16.0 +* rdoc 6.16.1 * win32ole 1.9.2 * irb 1.15.3 * reline 0.6.3 From 8eaefd93951143661b1f515a8c9cda12263d2b56 Mon Sep 17 00:00:00 2001 From: qraqras Date: Fri, 28 Nov 2025 09:58:38 +0900 Subject: [PATCH 007/367] [ruby/prism] Fix invalid Ruby code example in ClassNode comment https://github.com/ruby/prism/commit/5b7456c8f6 --- prism/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/prism/config.yml b/prism/config.yml index 69a46de628e63c..3acab9f58d5d44 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -1860,7 +1860,7 @@ nodes: comment: | Represents the location of the `class` keyword. - class Foo end + class Foo; end ^^^^^ - name: constant_path type: node @@ -1899,19 +1899,19 @@ nodes: comment: | Represents the location of the `end` keyword. - class Foo end - ^^^ + class Foo; end + ^^^ - name: name type: constant comment: | The name of the class. - class Foo end # name `:Foo` + class Foo; end # name `:Foo` comment: | Represents a class declaration involving the `class` keyword. - class Foo end - ^^^^^^^^^^^^^ + class Foo; end + ^^^^^^^^^^^^^^ - name: ClassVariableAndWriteNode fields: - name: name From 191bfcb9c505ba3f5771f7ac67d6131aeb6b6837 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Fri, 28 Nov 2025 16:17:06 +0100 Subject: [PATCH 008/367] Define Kernel#instance_variables_to_inspect [Bug #21718] Otherwise objects that don't define it, but define a fairly liberal `method_missing` method will run into errors that are hard to understand: ```ruby class Foo def method_missing(name, ...) name end end p Foo.new.inspect ``` ``` 'Kernel#inspect': wrong argument type Symbol (expected Array) (TypeError) from ../test.rb:7:in '
' ``` --- object.c | 21 ++++++++++++++++--- spec/ruby/core/kernel/inspect_spec.rb | 29 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/object.c b/object.c index 097199479e241a..4b73fcefa8a646 100644 --- a/object.c +++ b/object.c @@ -854,14 +854,21 @@ rb_obj_inspect(VALUE obj) { VALUE ivars = rb_check_funcall(obj, id_instance_variables_to_inspect, 0, 0); st_index_t n = 0; - if (UNDEF_P(ivars)) { + if (UNDEF_P(ivars) || NIL_P(ivars)) { n = rb_ivar_count(obj); ivars = Qnil; } - else if (!NIL_P(ivars)) { - Check_Type(ivars, T_ARRAY); + else if (RB_TYPE_P(ivars, T_ARRAY)) { n = RARRAY_LEN(ivars); } + else { + rb_raise( + rb_eTypeError, + "Expected #instance_variables_to_inspect to return an Array or nil, but it returned %"PRIsVALUE, + rb_obj_class(ivars) + ); + } + if (n > 0) { VALUE c = rb_class_name(CLASS_OF(obj)); VALUE args[2] = { @@ -875,6 +882,13 @@ rb_obj_inspect(VALUE obj) } } +/* :nodoc: */ +static VALUE +rb_obj_instance_variables_to_inspect(VALUE obj) +{ + return Qnil; +} + static VALUE class_or_module_required(VALUE c) { @@ -4535,6 +4549,7 @@ InitVM_Object(void) rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0); rb_define_method(rb_mKernel, "inspect", rb_obj_inspect, 0); + rb_define_private_method(rb_mKernel, "instance_variables_to_inspect", rb_obj_instance_variables_to_inspect, 0); rb_define_method(rb_mKernel, "methods", rb_obj_methods, -1); /* in class.c */ rb_define_method(rb_mKernel, "singleton_methods", rb_obj_singleton_methods, -1); /* in class.c */ rb_define_method(rb_mKernel, "protected_methods", rb_obj_protected_methods, -1); /* in class.c */ diff --git a/spec/ruby/core/kernel/inspect_spec.rb b/spec/ruby/core/kernel/inspect_spec.rb index 6ecf1e1c8c86d1..1fa66cab98a979 100644 --- a/spec/ruby/core/kernel/inspect_spec.rb +++ b/spec/ruby/core/kernel/inspect_spec.rb @@ -57,5 +57,34 @@ class << obj inspected = obj.inspect.sub(/^#" end + + it "displays all instance variables if #instance_variables_to_inspect returns nil" do + obj = Object.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + obj.singleton_class.class_eval do + private def instance_variables_to_inspect = nil + end + + inspected = obj.inspect.sub(/^#} + end + + it "raises an error if #instance_variables_to_inspect returns an invalid value" do + obj = Object.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + obj.singleton_class.class_eval do + private def instance_variables_to_inspect = {} + end + + ->{ obj.inspect }.should raise_error(TypeError, "Expected #instance_variables_to_inspect to return an Array or nil, but it returned Hash") + end end end From 8c70def42c4596a02d45464d0d694d0c234fb1d7 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 27 Nov 2025 22:57:48 -0800 Subject: [PATCH 009/367] Use ALWAYS_INLINE for vm_getinstancevariable Recently rb_vm_getinstancevariable was introduced exposing this method to ZJIT. On clang specifically this ended up causing the compiler not to inline into vm_exec_core and cause a significant performance regression in optcarrot for the interpreter. Co-authored-by: Luke Gruber --- vm_insnhelper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index c9913a92bbd748..60e1b647ae5d8d 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1680,6 +1680,7 @@ rb_vm_setclassvariable(const rb_iseq_t *iseq, const rb_control_frame_t *cfp, ID vm_setclassvariable(iseq, cfp, id, val, ic); } +ALWAYS_INLINE(static VALUE vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic)); static inline VALUE vm_getinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, IVC ic) { From 413d15a3440abf570fc4bff24fe4b5ee8b7880a5 Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Fri, 28 Nov 2025 02:08:21 +0100 Subject: [PATCH 010/367] [ruby/rubygems] Add `MAKEFLAGS=-j` by default before compiling: - Depending on the native extension, it can greatly reduce compilation time when executing recipes simultaneously. For example on Prism: ``` # Before time gem install prism Building native extensions. This could take a while... Successfully installed prism-1.6.0 1 gem installed gem install prism 3.61s user 0.80s system 95% cpu 4.595 total ``` ``` # After time MAKEFLAGS="-j" gem install prism Building native extensions. This could take a while... Successfully installed prism-1.6.0 1 gem installed MAKEFLAGS="-j" gem install prism 4.47s user 1.27s system 246% cpu 2.330 total ``` I don't think adding `-j` as a default is harmful, but I'm admitedly not very knowledgable when it comes to compiler. https://github.com/ruby/rubygems/commit/61340081c6 Co-authored-by: Aaron Patterson --- lib/rubygems/ext/ext_conf_builder.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index ec2fa59412df64..021273ef12e3f0 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -40,6 +40,7 @@ def self.build(extension, dest_path, results, args = [], lib_dir = nil, extensio end ENV["DESTDIR"] = nil + ENV["MAKEFLAGS"] ||= "-j#{Etc.nprocessors + 1}" make dest_path, results, extension_dir, tmp_dest_relative, target_rbconfig: target_rbconfig From 7bd80e7e3e3dbb724616c038e6e0944128d6f905 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 29 Nov 2025 07:06:24 +0900 Subject: [PATCH 011/367] nmake didn't support -j flag --- lib/rubygems/ext/ext_conf_builder.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index 021273ef12e3f0..745cf08b6c8b45 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -40,7 +40,9 @@ def self.build(extension, dest_path, results, args = [], lib_dir = nil, extensio end ENV["DESTDIR"] = nil - ENV["MAKEFLAGS"] ||= "-j#{Etc.nprocessors + 1}" + unless RbConfig::CONFIG["MAKE"] =~ /nmake/ + ENV["MAKEFLAGS"] ||= "-j#{Etc.nprocessors + 1}" + end make dest_path, results, extension_dir, tmp_dest_relative, target_rbconfig: target_rbconfig From bb2e4d58cce135d3609dba1ee17ed614522a88bf Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 28 Nov 2025 16:57:15 +0900 Subject: [PATCH 012/367] [ruby/rubygems] Fixed checksums generation issue when no source is specified https://github.com/ruby/rubygems/commit/bb4d791cb4 --- lib/bundler/definition.rb | 2 ++ spec/bundler/commands/lock_spec.rb | 50 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 437390f3ec448b..2fa7d0d277943b 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -539,6 +539,8 @@ def unlocking? end def add_checksums + require "rubygems/package" + @locked_checksums = true setup_domain!(add_checksums: true) diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index ab1926734c1e78..c8af9c8dd404fb 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -2035,6 +2035,56 @@ L end + it "adds checksums when source is not specified" do + system_gems(%w[myrack-1.0.0], path: default_bundle_path) + + gemfile <<-G + gem "myrack" + G + + lockfile <<~L + GEM + specs: + myrack (1.0.0) + + PLATFORMS + ruby + x86_64-linux + + DEPENDENCIES + myrack + + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "x86_64-linux" do + bundle "lock --add-checksums" + end + + # myrack is coming from gem_repo1 + # but it's simulated to install in the system gems path + checksums = checksums_section do |c| + c.checksum gem_repo1, "myrack", "1.0.0" + end + + expect(lockfile).to eq <<~L + GEM + specs: + myrack (1.0.0) + + PLATFORMS + ruby + x86_64-linux + + DEPENDENCIES + myrack + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + it "adds checksums to an existing lockfile, when gems are already installed" do build_repo4 do build_gem "nokogiri", "1.14.2" From f18bedaf96d950a69ae18df9a7eeea6754224c20 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 29 Nov 2025 07:18:43 +0900 Subject: [PATCH 013/367] [ruby/rubygems] Use String#include? with suggested by Performance/StringInclude cop https://github.com/ruby/rubygems/commit/fdd3419144 --- lib/rubygems/ext/ext_conf_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index 745cf08b6c8b45..bc70ed67fb8604 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -40,7 +40,7 @@ def self.build(extension, dest_path, results, args = [], lib_dir = nil, extensio end ENV["DESTDIR"] = nil - unless RbConfig::CONFIG["MAKE"] =~ /nmake/ + unless RbConfig::CONFIG["MAKE"]&.include?("nmake") ENV["MAKEFLAGS"] ||= "-j#{Etc.nprocessors + 1}" end From 7dae2a1f488afc4133dfbd6ea243aaeb71107f71 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 26 Nov 2025 18:58:42 +0100 Subject: [PATCH 014/367] [ruby/rubygems] Restore `install` as default command Fix: https://github.com/ruby/rubygems/issues/9124 This behavior is a deeply entrenched convention and changing it will annoy lots of developers with unclear gains. https://github.com/ruby/rubygems/commit/628e0ede46 --- lib/bundler/cli.rb | 12 +----------- spec/bundler/bundler/cli_spec.rb | 3 +-- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 55f656e3f3afd0..b7829f2a0db248 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -123,17 +123,7 @@ def cli_help def self.default_command(meth = nil) return super if meth - default_cli_command = Bundler.settings[:default_cli_command] - return default_cli_command if default_cli_command - - Bundler.ui.warn(<<~MSG) - In the next version of Bundler, running `bundle` without argument will no longer run `bundle install`. - Instead, the `help` command will be displayed. - - If you'd like to keep the previous behaviour please run `bundle config set default_cli_command install --global`. - MSG - - "install" + Bundler.settings[:default_cli_command] || "install" end class_option "no-color", type: :boolean, desc: "Disable colorization in output" diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index ee3c0d0fd50ff5..611c1985e277b3 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -87,9 +87,8 @@ def out_with_macos_man_workaround end context "with no arguments" do - it "installs and log a warning by default" do + it "installs by default" do bundle "", raise_on_error: false - expect(err).to include("running `bundle` without argument will no longer run `bundle install`.") expect(err).to include("Could not locate Gemfile") end From 69293f52550032cbda8d92188407b8700f4c3bb1 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 26 Nov 2025 20:03:13 +0100 Subject: [PATCH 015/367] [ruby/rubygems] Print help summary when the default command fail As mentioned in https://github.com/ruby/rubygems/issues/9124, the intent for changing the default command was to be more welcoming. I think we can acheive that by attempting to install, but to print that same help message if there is no Gemfile. That should address both concerns. https://github.com/ruby/rubygems/commit/f3f505c02a --- lib/bundler/cli.rb | 23 ++++++++++++++++++++++- lib/bundler/vendor/thor/lib/thor.rb | 2 +- spec/bundler/bundler/cli_spec.rb | 13 +------------ spec/bundler/other/cli_dispatch_spec.rb | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index b7829f2a0db248..d3c948c7ce22ce 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -120,10 +120,18 @@ def cli_help self.class.send(:class_options_help, shell) end + desc "install_or_cli_help", "Tries to run bundle install but prints a summary of bundler commands if there is no Gemfile", hide: true + def install_or_cli_help + invoke_other_command("install") + rescue GemfileNotFound => error + Bundler.ui.error error.message, wrap: true + invoke_other_command("cli_help") + end + def self.default_command(meth = nil) return super if meth - Bundler.settings[:default_cli_command] || "install" + Bundler.settings[:default_cli_command] || "install_or_cli_help" end class_option "no-color", type: :boolean, desc: "Disable colorization in output" @@ -713,6 +721,19 @@ def current_command config[:current_command] end + def invoke_other_command(name) + _, _, config = @_initializer + original_command = config[:current_command] + command = self.class.all_commands[name] + config[:current_command] = command + send(name) + ensure + config[:current_command] = original_command + end + + def current_command=(command) + end + def print_command return unless Bundler.ui.debug? cmd = current_command diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb index bfd9f5c91412ab..945bdbd5515fe5 100644 --- a/lib/bundler/vendor/thor/lib/thor.rb +++ b/lib/bundler/vendor/thor/lib/thor.rb @@ -625,7 +625,7 @@ def normalize_command_name(meth) #:nodoc: # alias name. def find_command_possibilities(meth) len = meth.to_s.length - possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort + possibilities = all_commands.reject { |_k, c| c.hidden? }.merge(map).keys.select { |n| meth == n[0, len] }.sort unique_possibilities = possibilities.map { |k| map[k] || k }.uniq if possibilities.include?(meth) diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index 611c1985e277b3..33a23a75def180 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -87,27 +87,16 @@ def out_with_macos_man_workaround end context "with no arguments" do - it "installs by default" do + it "tries to installs by default but print help on missing Gemfile" do bundle "", raise_on_error: false expect(err).to include("Could not locate Gemfile") - end - it "prints a concise help message when default_cli_command set to cli_help" do - bundle "config set default_cli_command cli_help" - bundle "" - expect(err).to be_empty expect(out).to include("Bundler version #{Bundler::VERSION}"). and include("\n\nBundler commands:\n\n"). and include("\n\n Primary commands:\n"). and include("\n\n Utilities:\n"). and include("\n\nOptions:\n") end - - it "runs bundle install when default_cli_command set to install" do - bundle "config set default_cli_command install" - bundle "", raise_on_error: false - expect(err).to include("Could not locate Gemfile") - end end context "when ENV['BUNDLE_GEMFILE'] is set to an empty string" do diff --git a/spec/bundler/other/cli_dispatch_spec.rb b/spec/bundler/other/cli_dispatch_spec.rb index 1039737b993983..a2c745b0703d24 100644 --- a/spec/bundler/other/cli_dispatch_spec.rb +++ b/spec/bundler/other/cli_dispatch_spec.rb @@ -15,6 +15,6 @@ it "print a friendly error when ambiguous" do bundle "in", raise_on_error: false - expect(err).to eq("Ambiguous command in matches [info, init, inject, install]") + expect(err).to eq("Ambiguous command in matches [info, init, install]") end end From a9d2a46d64ead4dae8491c2f93b9e86416006fd4 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 28 Nov 2025 19:23:50 +0900 Subject: [PATCH 016/367] [ruby/rubygems] Add informational message when default_cli_command is unset. https://github.com/ruby/rubygems/commit/9e44b5ebc4 --- lib/bundler/cli.rb | 10 ++++++++++ spec/bundler/bundler/cli_spec.rb | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index d3c948c7ce22ce..f5977863ddcc26 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -131,6 +131,16 @@ def install_or_cli_help def self.default_command(meth = nil) return super if meth + unless Bundler.settings[:default_cli_command] + Bundler.ui.info <<-MSG + In the feature version of Bundler, running `bundle` without argument will no longer run `bundle install`. + Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. + If you wish to use feature behavior now with `bundle config set default_cli_command cli_help --global` + or you can continue to use the old behavior with `bundle config set default_cli_command install_or_cli_help --global`. + This message will be removed after a default_cli_command value is set. + MSG + end + Bundler.settings[:default_cli_command] || "install_or_cli_help" end diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index 33a23a75def180..ac6b77ad7483bf 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -90,6 +90,7 @@ def out_with_macos_man_workaround it "tries to installs by default but print help on missing Gemfile" do bundle "", raise_on_error: false expect(err).to include("Could not locate Gemfile") + expect(out).to include("In the feature version of Bundler") expect(out).to include("Bundler version #{Bundler::VERSION}"). and include("\n\nBundler commands:\n\n"). @@ -97,6 +98,13 @@ def out_with_macos_man_workaround and include("\n\n Utilities:\n"). and include("\n\nOptions:\n") end + + it "runs bundle install when default_cli_command set to install" do + bundle "config set default_cli_command install_or_cli_help" + bundle "", raise_on_error: false + expect(out).to_not include("In the feature version of Bundler") + expect(err).to include("Could not locate Gemfile") + end end context "when ENV['BUNDLE_GEMFILE'] is set to an empty string" do From 34290048be4a480437bbfff3dc5d8bf93e495d9b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 29 Nov 2025 09:05:14 +0900 Subject: [PATCH 017/367] Fixup with mswin and nmake build for -j flag --- lib/rubygems/ext/ext_conf_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index bc70ed67fb8604..5bc8b01f3be9f8 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -40,7 +40,7 @@ def self.build(extension, dest_path, results, args = [], lib_dir = nil, extensio end ENV["DESTDIR"] = nil - unless RbConfig::CONFIG["MAKE"]&.include?("nmake") + unless RUBY_PLATFORM =~ /mswin/ && RbConfig::CONFIG["configure_args"]&.include?("nmake") ENV["MAKEFLAGS"] ||= "-j#{Etc.nprocessors + 1}" end From 688350dacc4bab436043bd2435fc64633a1408ce Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Tue, 25 Nov 2025 05:11:52 +0000 Subject: [PATCH 018/367] [DOC] Avoid term 'derived'; use 'subclass' --- numeric.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/numeric.c b/numeric.c index a2c91d87a57db3..54c9649a87fa55 100644 --- a/numeric.c +++ b/numeric.c @@ -595,8 +595,8 @@ num_uminus(VALUE num) * fdiv(other) -> float * * Returns the quotient self/other as a float, - * using method +/+ in the derived class of +self+. - * (\Numeric itself does not define method +/+.) + * using method +/+ as defined in the subclass of \Numeric. + * (\Numeric itself does not define +/+.) * * Of the Core and Standard Library classes, * only BigDecimal uses this implementation. @@ -614,8 +614,8 @@ num_fdiv(VALUE x, VALUE y) * div(other) -> integer * * Returns the quotient self/other as an integer (via +floor+), - * using method +/+ in the derived class of +self+. - * (\Numeric itself does not define method +/+.) + * using method +/+ as defined in the subclass of \Numeric. + * (\Numeric itself does not define +/+.) * * Of the Core and Standard Library classes, * Only Float and Rational use this implementation. @@ -847,7 +847,8 @@ num_nonzero_p(VALUE num) * to_int -> integer * * Returns +self+ as an integer; - * converts using method +to_i+ in the derived class. + * converts using method +to_i+ in the subclass of \Numeric. + * (\Numeric itself does not define +to_i+.) * * Of the Core and Standard Library classes, * only Rational and Complex use this implementation. From 005fba0713947a8241b7560ec4a6f052f49067c0 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Fri, 28 Nov 2025 19:27:35 -0600 Subject: [PATCH 019/367] [DOC] Tweaks for Module#<=> --- object.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/object.c b/object.c index 4b73fcefa8a646..e960f1855b0c80 100644 --- a/object.c +++ b/object.c @@ -2012,14 +2012,26 @@ rb_mod_gt(VALUE mod, VALUE arg) /* * call-seq: - * module <=> other_module -> -1, 0, +1, or nil + * self <=> object -> -1, 0, +1, or nil * - * Comparison---Returns -1, 0, +1 or nil depending on whether +module+ - * includes +other_module+, they are the same, or if +module+ is included by - * +other_module+. + * Returns: + * + * - +-1+, if +self+ includes +object+, if or +self+ is a subclass of +object+. + * - +0+, if +self+ and +object+ are the same. + * - +1+, if +object+ includes +self+, or if +object+ is a subclass of +self+. + * - +nil+, if none of the above is true. + * + * Examples: + * + * # Class Array includes module Enumerable. + * Array <=> Enumerable # => -1 + * Enumerable <=> Enumerable # => 0 + * Enumerable <=> Array # => 1 + * # Class File is a subclass of class IO. + * File <=> IO # => -1 + * IO <=> File # => 1 + * File <=> File # => 0 * - * Returns +nil+ if +module+ has no relationship with +other_module+, if - * +other_module+ is not a module, or if the two values are incomparable. */ static VALUE From 0f80d0ee05a2cb9daad426a7aa9aad7c4f2c3401 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 28 Nov 2025 20:39:24 -0500 Subject: [PATCH 020/367] [DOC] Fix backticks in InstructionSequence docs --- iseq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iseq.c b/iseq.c index 9bf56ad6557545..b53362b2444709 100644 --- a/iseq.c +++ b/iseq.c @@ -1652,7 +1652,7 @@ iseqw_s_compile_parser(int argc, VALUE *argv, VALUE self, bool prism) * real path and first line number of the ruby code in +source+ which are * metadata attached to the returned +iseq+. * - * +file+ is used for `__FILE__` and exception backtrace. +path+ is used for + * +file+ is used for +__FILE__+ and exception backtrace. +path+ is used for * +require_relative+ base. It is recommended these should be the same full * path. * @@ -1694,7 +1694,7 @@ iseqw_s_compile(int argc, VALUE *argv, VALUE self) * real path and first line number of the ruby code in +source+ which are * metadata attached to the returned +iseq+. * - * +file+ is used for `__FILE__` and exception backtrace. +path+ is used for + * +file+ is used for +__FILE__+ and exception backtrace. +path+ is used for * +require_relative+ base. It is recommended these should be the same full * path. * @@ -1736,7 +1736,7 @@ iseqw_s_compile_parsey(int argc, VALUE *argv, VALUE self) * real path and first line number of the ruby code in +source+ which are * metadata attached to the returned +iseq+. * - * +file+ is used for `__FILE__` and exception backtrace. +path+ is used for + * +file+ is used for +__FILE__+ and exception backtrace. +path+ is used for * +require_relative+ base. It is recommended these should be the same full * path. * From 82b91ec7e55cb5ef4acd61213843614542bea3b3 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 29 Nov 2025 10:06:03 +0900 Subject: [PATCH 021/367] [ruby/rubygems] Also use String#include? for RUBY_PLATFORM https://github.com/ruby/rubygems/commit/5fd95f38d3 --- lib/rubygems/ext/ext_conf_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index 5bc8b01f3be9f8..81491eac79abbc 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -40,7 +40,7 @@ def self.build(extension, dest_path, results, args = [], lib_dir = nil, extensio end ENV["DESTDIR"] = nil - unless RUBY_PLATFORM =~ /mswin/ && RbConfig::CONFIG["configure_args"]&.include?("nmake") + unless RUBY_PLATFORM.include?("mswin") && RbConfig::CONFIG["configure_args"]&.include?("nmake") ENV["MAKEFLAGS"] ||= "-j#{Etc.nprocessors + 1}" end From e13ad22274ff2432f705536832f03f360a5b4af5 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 28 Nov 2025 20:28:42 -0500 Subject: [PATCH 022/367] Re-enable clang-18 The issue might have been fixed in 8bf333a. --- .github/workflows/compilers.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index f8ff22fe10fa0a..3ecc749c0456a0 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -111,8 +111,7 @@ jobs: - { uses: './.github/actions/compilers', name: 'clang 21', with: { tag: 'clang-21' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 20', with: { tag: 'clang-20' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 19', with: { tag: 'clang-19' }, timeout-minutes: 5 } - # clang-18 has a bug causing ruby_current_ec to sometimes be null - # - { uses: './.github/actions/compilers', name: 'clang 18', with: { tag: 'clang-18' }, timeout-minutes: 5 } + - { uses: './.github/actions/compilers', name: 'clang 18', with: { tag: 'clang-18' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 17', with: { tag: 'clang-17' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 16', with: { tag: 'clang-16' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 15', with: { tag: 'clang-15' }, timeout-minutes: 5 } From c8bfbd5700bc00ab06e715235818c099a0063436 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 30 Nov 2025 01:29:01 +0900 Subject: [PATCH 023/367] [ruby/openssl] ts: fix docs for attrs on OpenSSL::Timestamp::Factory Move attribute documentation out of the class-level section and into the appropriate sections so that they attach correctly. https://github.com/ruby/openssl/commit/61410acc50 --- ext/openssl/ossl_ts.c | 76 ++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 51 deletions(-) diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index 575418ccc6ce46..9dd9c5c765baea 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -1511,65 +1511,39 @@ Init_ossl_ts(void) * fac.default_policy_id = '1.2.3.4.5' * fac.additional_certificates = [ inter1, inter2 ] * timestamp = fac.create_timestamp(p12.key, p12.certificate, req) - * - * ==Attributes - * - * ===default_policy_id + */ + cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject); + /* + * The list of digest algorithms that the factory is allowed + * create timestamps for. Known vulnerable or weak algorithms should not be + * allowed where possible. Must be an Array of String or OpenSSL::Digest + * subclass instances. + */ + rb_attr(cTimestampFactory, rb_intern_const("allowed_digests"), 1, 1, 0); + /* + * A String representing the default policy object identifier, or +nil+. * * Request#policy_id will always be preferred over this if present in the - * Request, only if Request#policy_id is nil default_policy will be used. + * Request, only if Request#policy_id is +nil+ default_policy will be used. * If none of both is present, a TimestampError will be raised when trying * to create a Response. - * - * call-seq: - * factory.default_policy_id = "string" -> string - * factory.default_policy_id -> string or nil - * - * ===serial_number - * - * Sets or retrieves the serial number to be used for timestamp creation. - * Must be present for timestamp creation. - * - * call-seq: - * factory.serial_number = number -> number - * factory.serial_number -> number or nil - * - * ===gen_time - * - * Sets or retrieves the Time value to be used in the Response. Must be - * present for timestamp creation. - * - * call-seq: - * factory.gen_time = Time -> Time - * factory.gen_time -> Time or nil - * - * ===additional_certs - * - * Sets or retrieves additional certificates apart from the timestamp - * certificate (e.g. intermediate certificates) to be added to the Response. - * Must be an Array of OpenSSL::X509::Certificate. - * - * call-seq: - * factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ] - * factory.additional_certs -> array or nil - * - * ===allowed_digests - * - * Sets or retrieves the digest algorithms that the factory is allowed - * create timestamps for. Known vulnerable or weak algorithms should not be - * allowed where possible. - * Must be an Array of String or OpenSSL::Digest subclass instances. - * - * call-seq: - * factory.allowed_digests = ["sha1", OpenSSL::Digest.new('SHA256').new] -> [ "sha1", OpenSSL::Digest) ] - * factory.allowed_digests -> array or nil - * */ - cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject); - rb_attr(cTimestampFactory, rb_intern_const("allowed_digests"), 1, 1, 0); rb_attr(cTimestampFactory, rb_intern_const("default_policy_id"), 1, 1, 0); + /* + * The serial number to be used for timestamp creation. Must be present for + * timestamp creation. Must be an instance of OpenSSL::BN or Integer. + */ rb_attr(cTimestampFactory, rb_intern_const("serial_number"), 1, 1, 0); + /* + * The Time value to be used in the Response. Must be present for timestamp + * creation. + */ rb_attr(cTimestampFactory, rb_intern_const("gen_time"), 1, 1, 0); + /* + * Additional certificates apart from the timestamp certificate (e.g. + * intermediate certificates) to be added to the Response. + * Must be an Array of OpenSSL::X509::Certificate, or +nil+. + */ rb_attr(cTimestampFactory, rb_intern_const("additional_certs"), 1, 1, 0); rb_define_method(cTimestampFactory, "create_timestamp", ossl_tsfac_create_ts, 3); } From bae06ce22c5ab6a4a3085300274f258d55858e90 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 30 Nov 2025 01:47:26 +0900 Subject: [PATCH 024/367] [ruby/openssl] Remove dummy declarations for mOSSL and eOSSLError These declarations were added to every source file because older versions of RDoc did not resolve ancestor tree across files. Since RDoc 6.9.0 supports this, this workaround is no longer needed. https://redirect.github.com/ruby/rdoc/pull/1217 https://github.com/ruby/openssl/commit/6491ce63be --- ext/openssl/ossl_asn1.c | 5 ----- ext/openssl/ossl_bn.c | 5 ----- ext/openssl/ossl_cipher.c | 5 ----- ext/openssl/ossl_config.c | 5 ----- ext/openssl/ossl_digest.c | 5 ----- ext/openssl/ossl_engine.c | 5 ----- ext/openssl/ossl_hmac.c | 5 ----- ext/openssl/ossl_kdf.c | 5 ----- ext/openssl/ossl_ns_spki.c | 5 ----- ext/openssl/ossl_ocsp.c | 5 ----- ext/openssl/ossl_pkcs12.c | 5 ----- ext/openssl/ossl_pkcs7.c | 5 ----- ext/openssl/ossl_pkey.c | 5 ----- ext/openssl/ossl_pkey_dh.c | 6 ------ ext/openssl/ossl_pkey_dsa.c | 6 ------ ext/openssl/ossl_pkey_ec.c | 7 ------- ext/openssl/ossl_pkey_rsa.c | 6 ------ ext/openssl/ossl_provider.c | 5 ----- ext/openssl/ossl_rand.c | 5 ----- ext/openssl/ossl_ssl.c | 2 -- ext/openssl/ossl_ssl_session.c | 5 ----- ext/openssl/ossl_ts.c | 4 ---- ext/openssl/ossl_x509.c | 4 ---- ext/openssl/ossl_x509attr.c | 6 ------ ext/openssl/ossl_x509cert.c | 6 ------ ext/openssl/ossl_x509crl.c | 6 ------ ext/openssl/ossl_x509ext.c | 6 ------ ext/openssl/ossl_x509name.c | 6 ------ ext/openssl/ossl_x509req.c | 6 ------ ext/openssl/ossl_x509revoked.c | 6 ------ ext/openssl/ossl_x509store.c | 6 ------ 31 files changed, 163 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 1309811c742e31..38397ebda87c86 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -1288,11 +1288,6 @@ Init_ossl_asn1(void) VALUE ary; int i; -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL")); sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC")); sym_APPLICATION = ID2SYM(rb_intern_const("APPLICATION")); diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index a6fa2c1daba3e9..0ac8ae248ef9e4 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -1202,11 +1202,6 @@ ossl_bn_set_flags(VALUE self, VALUE arg) void Init_ossl_bn(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - #ifdef HAVE_RB_EXT_RACTOR_SAFE ossl_bn_ctx_key = rb_ractor_local_storage_ptr_newkey(&ossl_bn_ctx_key_type); #else diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index a52518291124b3..f7b2c01a36d933 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -901,11 +901,6 @@ ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len) void Init_ossl_cipher(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-class: OpenSSL::Cipher * * Provides symmetric algorithms for encryption and decryption. The diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c index 9be5fe6f6320dc..274875a97830d0 100644 --- a/ext/openssl/ossl_config.c +++ b/ext/openssl/ossl_config.c @@ -413,11 +413,6 @@ Init_ossl_config(void) char *path; VALUE path_str; -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-class: OpenSSL::Config * * Configuration for the openssl library. diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index e2f1af7e61aa8f..8c1b825cf3909c 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -365,11 +365,6 @@ ossl_digest_block_length(VALUE self) void Init_ossl_digest(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-class: OpenSSL::Digest * * OpenSSL::Digest allows you to compute message digests (sometimes diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index 1565068743802e..e40f0ff68de37f 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -466,11 +466,6 @@ ossl_engine_inspect(VALUE self) void Init_ossl_engine(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - cEngine = rb_define_class_under(mOSSL, "Engine", rb_cObject); eEngineError = rb_define_class_under(cEngine, "EngineError", eOSSLError); diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index 250b427bdcc8f6..8153f64fc57d65 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -256,11 +256,6 @@ ossl_hmac_reset(VALUE self) void Init_ossl_hmac(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * Document-class: OpenSSL::HMAC * diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c index e7429a76881c7d..9450612ee2d375 100644 --- a/ext/openssl/ossl_kdf.c +++ b/ext/openssl/ossl_kdf.c @@ -239,11 +239,6 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self) void Init_ossl_kdf(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * Document-module: OpenSSL::KDF * diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index 51ec8532c3ee8c..e1589fea0e0b24 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -378,11 +378,6 @@ ossl_spki_verify(VALUE self, VALUE key) void Init_ossl_ns_spki(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - mNetscape = rb_define_module_under(mOSSL, "Netscape"); eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError); diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index 390215a8e3d67c..b19b51a4ffbb85 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -1626,11 +1626,6 @@ ossl_ocspcid_to_der(VALUE self) void Init_ossl_ocsp(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * OpenSSL::OCSP implements Online Certificate Status Protocol requests * and responses. diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c index f76e1625f596a2..8aff77a6dad934 100644 --- a/ext/openssl/ossl_pkcs12.c +++ b/ext/openssl/ossl_pkcs12.c @@ -300,11 +300,6 @@ void Init_ossl_pkcs12(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* * Defines a file format commonly used to store private keys with * accompanying public key certificates, protected with a password-based diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 0fcae1971cfa4b..7bd6c18f92acbb 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -1099,11 +1099,6 @@ void Init_ossl_pkcs7(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - cPKCS7 = rb_define_class_under(mOSSL, "PKCS7", rb_cObject); ePKCS7Error = rb_define_class_under(cPKCS7, "PKCS7Error", eOSSLError); rb_define_singleton_method(cPKCS7, "read_smime", ossl_pkcs7_s_read_smime, 1); diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 2d66c6ce625d34..61ff472aadd1b5 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -1658,11 +1658,6 @@ void Init_ossl_pkey(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - /* Document-module: OpenSSL::PKey * * == Asymmetric Public Key Algorithms diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 79509bef3d2f82..60cd20a857fc73 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -357,12 +357,6 @@ OSSL_PKEY_BN_DEF2(dh, DH, key, pub_key, priv_key) void Init_ossl_dh(void) { -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - /* Document-class: OpenSSL::PKey::DH * * An implementation of the Diffie-Hellman key exchange protocol based on diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 34e1c7052165be..8b141afab775b1 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -334,12 +334,6 @@ OSSL_PKEY_BN_DEF2(dsa, DSA, key, pub_key, priv_key) void Init_ossl_dsa(void) { -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - /* Document-class: OpenSSL::PKey::DSA * * DSA, the Digital Signature Algorithm, is specified in NIST's diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index a9133062081c05..bf77e0fa0556d3 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -1526,13 +1526,6 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) void Init_ossl_ec(void) { #undef rb_intern -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - /* * Document-class: OpenSSL::PKey::EC * diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index ed65121acc8849..9cc0bfa37933f9 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -536,12 +536,6 @@ OSSL_PKEY_BN_DEF3(rsa, RSA, crt_params, dmp1, dmq1, iqmp) void Init_ossl_rsa(void) { -#if 0 - mPKey = rb_define_module_under(mOSSL, "PKey"); - cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); - ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); -#endif - /* Document-class: OpenSSL::PKey::RSA * * RSA is an asymmetric public key algorithm that has been formalized in diff --git a/ext/openssl/ossl_provider.c b/ext/openssl/ossl_provider.c index 529a5e1c72ab0a..ea5abb8e4830f4 100644 --- a/ext/openssl/ossl_provider.c +++ b/ext/openssl/ossl_provider.c @@ -185,11 +185,6 @@ ossl_provider_inspect(VALUE self) void Init_ossl_provider(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - cProvider = rb_define_class_under(mOSSL, "Provider", rb_cObject); eProviderError = rb_define_class_under(cProvider, "ProviderError", eOSSLError); diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index 764900dfc656ba..97d01903e276ae 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -175,11 +175,6 @@ ossl_rand_status(VALUE self) void Init_ossl_rand(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - mRandom = rb_define_module_under(mOSSL, "Random"); eRandomError = rb_define_class_under(mRandom, "RandomError", eOSSLError); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 583396ebfc6f3b..454f3de4a4b9ed 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -2704,8 +2704,6 @@ void Init_ossl_ssl(void) { #if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable"); rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); #endif diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index 55b9d6c6d57e88..010adf20ac9618 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -305,11 +305,6 @@ static VALUE ossl_ssl_session_to_text(VALUE self) void Init_ossl_ssl_session(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - mSSL = rb_define_module_under(mOSSL, "SSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif #ifndef OPENSSL_NO_SOCK cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index 9dd9c5c765baea..e071ee84811c75 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -1298,10 +1298,6 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) void Init_ossl_ts(void) { - #if 0 - mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ - #endif - /* * Possible return value for +Response#failure_info+. Indicates that the * timestamp server rejects the message imprint algorithm used in the diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c index 2d552d78478b5b..04adfab960799a 100644 --- a/ext/openssl/ossl_x509.c +++ b/ext/openssl/ossl_x509.c @@ -29,10 +29,6 @@ ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) void Init_ossl_x509(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); -#endif - mX509 = rb_define_module_under(mOSSL, "X509"); Init_ossl_x509attr(); diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index 9beb15f4a0ca71..998f87b0e8649b 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -288,12 +288,6 @@ ossl_x509attr_to_der(VALUE self) void Init_ossl_x509attr(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509AttrError = rb_define_class_under(mX509, "AttributeError", eOSSLError); cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject); diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index cf00eb78bf9c03..dca28395935a12 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -886,12 +886,6 @@ ossl_x509_load(VALUE klass, VALUE buffer) void Init_ossl_x509cert(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509CertError = rb_define_class_under(mX509, "CertificateError", eOSSLError); /* Document-class: OpenSSL::X509::Certificate diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index af4803f47d3cc9..19f08053e15c82 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -506,12 +506,6 @@ ossl_x509crl_add_extension(VALUE self, VALUE extension) void Init_ossl_x509crl(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509CRLError = rb_define_class_under(mX509, "CRLError", eOSSLError); cX509CRL = rb_define_class_under(mX509, "CRL", rb_cObject); diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c index 01b342b5be7a32..a982efd798b939 100644 --- a/ext/openssl/ossl_x509ext.c +++ b/ext/openssl/ossl_x509ext.c @@ -441,12 +441,6 @@ void Init_ossl_x509ext(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509ExtError = rb_define_class_under(mX509, "ExtensionError", eOSSLError); cX509ExtFactory = rb_define_class_under(mX509, "ExtensionFactory", rb_cObject); diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c index 5483450aa7606b..3c0c6aca2905be 100644 --- a/ext/openssl/ossl_x509name.c +++ b/ext/openssl/ossl_x509name.c @@ -496,12 +496,6 @@ Init_ossl_x509name(void) #undef rb_intern VALUE utf8str, ptrstr, ia5str, hash; -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - id_aref = rb_intern("[]"); eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError); cX509Name = rb_define_class_under(mX509, "Name", rb_cObject); diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index 1ae0fd56a6e612..d7252379cdda4d 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -414,12 +414,6 @@ ossl_x509req_add_attribute(VALUE self, VALUE attr) void Init_ossl_x509req(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509ReqError = rb_define_class_under(mX509, "RequestError", eOSSLError); cX509Req = rb_define_class_under(mX509, "Request", rb_cObject); diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c index 9496c4bf1b49fc..e6bd1d8e1b1c16 100644 --- a/ext/openssl/ossl_x509revoked.c +++ b/ext/openssl/ossl_x509revoked.c @@ -267,12 +267,6 @@ ossl_x509revoked_to_der(VALUE self) void Init_ossl_x509revoked(void) { -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - eX509RevError = rb_define_class_under(mX509, "RevokedError", eOSSLError); cX509Rev = rb_define_class_under(mX509, "Revoked", rb_cObject); diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index c18596cbf5be73..3c7da66a2efc62 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -859,12 +859,6 @@ void Init_ossl_x509store(void) { #undef rb_intern -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); - mX509 = rb_define_module_under(mOSSL, "X509"); -#endif - /* Register ext_data slot for verify callback Proc */ stctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"stctx_ex_verify_cb_idx", 0, 0, 0); if (stctx_ex_verify_cb_idx < 0) From d9093eab625c7a7029888e538c5474430fb1c110 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sat, 29 Nov 2025 13:40:05 -0500 Subject: [PATCH 025/367] [ruby/tempfile] [DOC] Fix monofont for Tempfile.create https://github.com/ruby/tempfile/commit/96361e9e42 --- lib/tempfile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tempfile.rb b/lib/tempfile.rb index 27ebb96439c7f3..eeed2534a917f7 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -550,7 +550,7 @@ def open(*args, **kw) # # Implementation note: # -# The keyword argument +anonymous=true+ is implemented using FILE_SHARE_DELETE on Windows. +# The keyword argument anonymous=true is implemented using FILE_SHARE_DELETE on Windows. # O_TMPFILE is used on Linux. # # Related: Tempfile.new. From 8a0ae3a71a8b12a5e5929a565ba98fdf7c16233b Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sat, 29 Nov 2025 13:42:16 -0500 Subject: [PATCH 026/367] [ruby/tempfile] [DOC] Monofont some text in Tempfile.create https://github.com/ruby/tempfile/commit/7fa7436baa --- lib/tempfile.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tempfile.rb b/lib/tempfile.rb index eeed2534a917f7..cd512bb1c5224b 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -550,8 +550,8 @@ def open(*args, **kw) # # Implementation note: # -# The keyword argument anonymous=true is implemented using FILE_SHARE_DELETE on Windows. -# O_TMPFILE is used on Linux. +# The keyword argument anonymous=true is implemented using +FILE_SHARE_DELETE+ on Windows. +# +O_TMPFILE+ is used on Linux. # # Related: Tempfile.new. # From 48a73303e45b1dbaa3422e14e35c7834db98be4d Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:13:04 +0100 Subject: [PATCH 027/367] [ruby/prism] Optimize `Prism::Source#find_line` This is more concise and ruby does a better job performance-wise. This used to be `bsearch_index` already but https://github.com/ruby/prism/commit/6d8358c08395438d5924777c1fc3001a5ebf0aa3 changed it. https://github.com/ruby/prism/pull/1733#discussion_r1373702087 said: > Yeah the edge case was that the value matched an element exactly But surely there would be a test to show this behaviour? Gets called as part of pretty-printing nodes. Further reduces the time for `SnapshotsTest` by ~16% for me. https://github.com/ruby/prism/commit/f448e2b995 --- lib/prism/parse_result.rb | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 05c14e33f5c99e..3570af136a4fb7 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -155,21 +155,8 @@ def deep_freeze # Binary search through the offsets to find the line number for the given # byte offset. def find_line(byte_offset) - left = 0 - right = offsets.length - 1 - - while left <= right - mid = left + (right - left) / 2 - return mid if (offset = offsets[mid]) == byte_offset - - if offset < byte_offset - left = mid + 1 - else - right = mid - 1 - end - end - - left - 1 + index = offsets.bsearch_index { |offset| offset > byte_offset } || offsets.length + index - 1 end end From f2a6cb2dc88ca12938b45f35bc6e64d72060a959 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 14:14:48 -0500 Subject: [PATCH 028/367] [ruby/prism] Handle invalid string pattern key When a pattern match is using a string as a hash pattern key and is using it incorrectly, we were previously assuming it was a symbol. In the case of an error, that's not the case. So we need to add a missing node in this case. https://github.com/ruby/prism/commit/f0b06d6269 --- prism/prism.c | 6 +++++- test/prism/errors/pattern_string_key.txt | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 test/prism/errors/pattern_string_key.txt diff --git a/prism/prism.c b/prism/prism.c index 186cdd354c9843..3485a58c28ee90 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -17336,7 +17336,11 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_node_t *value = NULL; if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { - value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); + if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) { + value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); + } else { + value = (pm_node_t *) pm_missing_node_create(parser, key->location.end, key->location.end); + } } else { value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); } diff --git a/test/prism/errors/pattern_string_key.txt b/test/prism/errors/pattern_string_key.txt new file mode 100644 index 00000000000000..9f28feddb96dea --- /dev/null +++ b/test/prism/errors/pattern_string_key.txt @@ -0,0 +1,8 @@ +case:a +in b:"","#{}" + ^~~~~ expected a label after the `,` in the hash pattern + ^ expected a pattern expression after the key + ^ expected a delimiter after the patterns of an `in` clause + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected an `end` to close the `case` statement + From f0f2a45f2d46fa4a0323ba5f29fba895b9e78f4b Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 14:20:06 -0500 Subject: [PATCH 029/367] [ruby/prism] Fix out-of-bounds read after utf-8 BOM https://github.com/ruby/prism/commit/198080c106 Co-authored-by: Steven Johnstone --- prism/prism.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 3485a58c28ee90..7d7072be79d76d 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -22865,8 +22865,8 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm // If the shebang does not include "ruby" and this is the main script being // parsed, then we will start searching the file for a shebang that does // contain "ruby" as if -x were passed on the command line. - const uint8_t *newline = next_newline(parser->start, parser->end - parser->start); - size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->start); + const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); + size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end); if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') { const char *engine; From 7c2eb946e04940901ee1957c3fb43897c661568e Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 14:28:36 -0500 Subject: [PATCH 030/367] [ruby/prism] Fix label interpolated string https://github.com/ruby/prism/commit/e3e2b1ed04 --- prism/config.yml | 3 +++ prism/prism.c | 6 ++++-- test/prism/errors/label_in_interpolated_string.txt | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test/prism/errors/label_in_interpolated_string.txt diff --git a/prism/config.yml b/prism/config.yml index 3acab9f58d5d44..36becff28de508 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -3306,6 +3306,9 @@ nodes: - EmbeddedVariableNode - InterpolatedStringNode # `"a" "#{b}"` - on error: XStringNode # `<<`FOO` "bar" + - on error: InterpolatedXStringNode + - on error: SymbolNode + - on error: InterpolatedSymbolNode - name: closing_loc type: location? newline: parts diff --git a/prism/prism.c b/prism/prism.c index 7d7072be79d76d..6d10fcb1e46522 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -5344,8 +5344,10 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ break; case PM_X_STRING_NODE: case PM_INTERPOLATED_X_STRING_NODE: - // If this is an x string, then this is a syntax error. But we want - // to handle it here so that we don't fail the assertion. + case PM_SYMBOL_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + // These will only happen in error cases. But we want to handle it + // here so that we don't fail the assertion. CLEAR_FLAGS(node); break; default: diff --git a/test/prism/errors/label_in_interpolated_string.txt b/test/prism/errors/label_in_interpolated_string.txt new file mode 100644 index 00000000000000..e8f40dd2a8aa69 --- /dev/null +++ b/test/prism/errors/label_in_interpolated_string.txt @@ -0,0 +1,14 @@ +case in el""Q +^~~~ expected a predicate for a case matching statement + ^ expected a delimiter after the patterns of an `in` clause + ^ unexpected constant, expecting end-of-input + !"""#{in el"":Q + ^~ unexpected 'in', assuming it is closing the parent 'in' clause + ^ expected a `}` to close the embedded expression + ^~ cannot parse the string part + ^~ cannot parse the string part + ^ cannot parse the string part + ^~~~~~~~~~~ unexpected label + ^~~~~~~~~~~ expected a string for concatenation + ^ expected an `end` to close the `case` statement + From 748dc9ac8d68b7979936f171179c475c7f174953 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 14:31:34 -0500 Subject: [PATCH 031/367] [ruby/prism] Fix out-of-bounds read in parser_lex_magic_comment https://github.com/ruby/prism/commit/e24e701f3a Co-authored-by: Steven Johnstone --- prism/prism.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index 6d10fcb1e46522..3d60e008c48a0a 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -8445,7 +8445,7 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { if (*cursor == '\\' && (cursor + 1 < end)) cursor++; } value_end = cursor; - if (*cursor == '"') cursor++; + if (cursor < end && *cursor == '"') cursor++; } else { value_start = cursor; while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++; From e3bc1852b3a7bcacf3e18d1644fbea7839abe019 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 15:51:06 -0500 Subject: [PATCH 032/367] [ruby/prism] Revert "Fix invalid Ruby code example in ClassNode comment" https://github.com/ruby/prism/commit/b960079559 --- prism/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/prism/config.yml b/prism/config.yml index 36becff28de508..5e29d6fa181c34 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -1860,7 +1860,7 @@ nodes: comment: | Represents the location of the `class` keyword. - class Foo; end + class Foo end ^^^^^ - name: constant_path type: node @@ -1899,19 +1899,19 @@ nodes: comment: | Represents the location of the `end` keyword. - class Foo; end - ^^^ + class Foo end + ^^^ - name: name type: constant comment: | The name of the class. - class Foo; end # name `:Foo` + class Foo end # name `:Foo` comment: | Represents a class declaration involving the `class` keyword. - class Foo; end - ^^^^^^^^^^^^^^ + class Foo end + ^^^^^^^^^^^^^ - name: ClassVariableAndWriteNode fields: - name: name From b1314f159ec501f4fdc1c055293799b33c9ad134 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 15:36:53 -0500 Subject: [PATCH 033/367] [ruby/prism] Fully destroy call operator write arguments If we are about to delete a call operator write argument, it needs to be removed from the list of block exits as well. https://github.com/ruby/prism/commit/ebc91c2e39 --- prism/prism.c | 38 +++++++++++++++++++ .../destroy_call_operator_write_arguments.txt | 11 ++++++ 2 files changed, 49 insertions(+) create mode 100644 test/prism/errors/destroy_call_operator_write_arguments.txt diff --git a/prism/prism.c b/prism/prism.c index 3d60e008c48a0a..60d45cbcd23ae6 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -21062,6 +21062,42 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding return value; } +static bool +parse_call_operator_write_block_exits_each(const pm_node_t *node, void *data) { + pm_parser_t *parser = (pm_parser_t *) data; + size_t index = 0; + + while (index < parser->current_block_exits->size) { + pm_node_t *block_exit = parser->current_block_exits->nodes[index]; + + if (block_exit == node) { + if (index + 1 < parser->current_block_exits->size) { + memmove( + &parser->current_block_exits->nodes[index], + &parser->current_block_exits->nodes[index + 1], + (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *) + ); + } + parser->current_block_exits->size--; + return false; + } + + index++; + } + + return true; +} + +/** + * When we are about to destroy a set of nodes that could potentially contain + * block exits for the current scope, we need to check if they are contained in + * the list of block exits and remove them if they are. + */ +static void +parse_call_operator_write_block_exits(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, parse_call_operator_write_block_exits_each, parser); +} + /** * Ensure a call node that is about to become a call operator node does not * have arguments or a block attached. If it does, then we'll need to add an @@ -21073,12 +21109,14 @@ static void parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) { if (call_node->arguments != NULL) { pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS); + parse_call_operator_write_block_exits(parser, (pm_node_t *) call_node->arguments); pm_node_destroy(parser, (pm_node_t *) call_node->arguments); call_node->arguments = NULL; } if (call_node->block != NULL) { pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK); + parse_call_operator_write_block_exits(parser, (pm_node_t *) call_node->block); pm_node_destroy(parser, (pm_node_t *) call_node->block); call_node->block = NULL; } diff --git a/test/prism/errors/destroy_call_operator_write_arguments.txt b/test/prism/errors/destroy_call_operator_write_arguments.txt new file mode 100644 index 00000000000000..c3c72f92260d02 --- /dev/null +++ b/test/prism/errors/destroy_call_operator_write_arguments.txt @@ -0,0 +1,11 @@ +t next&&do end&= + ^~ unexpected 'do'; expected an expression after the operator + ^~~~ unexpected void value expression + ^~~~ unexpected void value expression +^~~~~~~~~~~~~~ unexpected write target + ^~ unexpected operator after a call with arguments + ^~ unexpected operator after a call with a block +''while= + ^~~~~ expected a predicate expression for the `while` statement + ^ unexpected '='; target cannot be written + From c4b5fa419ee266c973be9b676caa2cfad0400d58 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 15:25:35 -0500 Subject: [PATCH 034/367] [ruby/prism] Ensure implicit parameter nodes are destroyed. When we are about to destroy a node because of a syntax error, we need to check if it is potentially containing an implicit parameter in its subtree. https://github.com/ruby/prism/commit/1531433e02 --- prism/prism.c | 65 +++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 60d45cbcd23ae6..8c886bb050b06a 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -13454,28 +13454,43 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { return (pm_node_t *) result; } +static bool +parse_target_implicit_parameter_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + case PM_LOCAL_VARIABLE_READ_NODE: + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; + + for (size_t index = 0; index < implicit_parameters->size; index++) { + if (implicit_parameters->nodes[index] == node) { + // If the node is not the last one in the list, we need to + // shift the remaining nodes down to fill the gap. This is + // extremely unlikely to happen. + if (index != implicit_parameters->size - 1) { + memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); + } + + implicit_parameters->size--; + break; + } + } + + return false; + } + default: + return true; + } +} + /** * When an implicit local variable is written to or targeted, it becomes a * regular, named local variable. This function removes it from the list of * implicit parameters when that happens. */ static void -parse_target_implicit_parameter(pm_parser_t *parser, pm_node_t *node) { - pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; - - for (size_t index = 0; index < implicit_parameters->size; index++) { - if (implicit_parameters->nodes[index] == node) { - // If the node is not the last one in the list, we need to shift the - // remaining nodes down to fill the gap. This is extremely unlikely - // to happen. - if (index != implicit_parameters->size - 1) { - memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); - } - - implicit_parameters->size--; - break; - } - } +parse_target_implicit_parameter(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, parse_target_implicit_parameter_each, parser); } /** @@ -13851,18 +13866,12 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod // syntax error. In this case we'll fall through to our default // handling. We need to free the value that we parsed because there // is no way for us to attach it to the tree at this point. - switch (PM_NODE_TYPE(value)) { - case PM_LOCAL_VARIABLE_READ_NODE: - case PM_IT_LOCAL_VARIABLE_READ_NODE: - // Since it is possible for the value to be an implicit - // parameter, we need to remove it from the list of implicit - // parameters. - parse_target_implicit_parameter(parser, value); - break; - default: - break; - } - + // + // Since it is possible for the value to contain an implicit + // parameter somewhere in its subtree, we need to walk it and remove + // any implicit parameters from the list of implicit parameters for + // the current scope. + parse_target_implicit_parameter(parser, value); pm_node_destroy(parser, value); } PRISM_FALLTHROUGH From 3bd9583a52e9c3f55adb2097a84c4adb09cf74a3 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sat, 29 Nov 2025 15:58:10 -0500 Subject: [PATCH 035/367] [ruby/prism] Update unicode tables to match that of CRuby The unicode version has been updated upstream, which means new codepoints mapped to alpha/alnum/isupper flags. We need to update our tables to match. I'm purposefully not adding a version check here, since that is such a large amount of code. It's possible that we could include different tables depending on a macro (like UNICODE_VERSION) or something to that effect, but it's such a minimal impact on the running of the actual parser that I don't think it's necessary. https://github.com/ruby/prism/commit/78925fe5b6 --- prism/encoding.c | 155 +++++++++++++++++++------- test/prism/encoding/encodings_test.rb | 18 +-- 2 files changed, 118 insertions(+), 55 deletions(-) diff --git a/prism/encoding.c b/prism/encoding.c index a4aeed104f89b9..424f149b8f833f 100644 --- a/prism/encoding.c +++ b/prism/encoding.c @@ -2,7 +2,7 @@ typedef uint32_t pm_unicode_codepoint_t; -#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1450 +#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1508 static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEPOINTS_LENGTH] = { 0x100, 0x2C1, 0x2C6, 0x2D1, @@ -10,7 +10,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x2EC, 0x2EC, 0x2EE, 0x2EE, 0x345, 0x345, - 0x370, 0x374, + 0x363, 0x374, 0x376, 0x377, 0x37A, 0x37D, 0x37F, 0x37F, @@ -50,7 +50,8 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x840, 0x858, 0x860, 0x86A, 0x870, 0x887, - 0x889, 0x88E, + 0x889, 0x88F, + 0x897, 0x897, 0x8A0, 0x8C9, 0x8D4, 0x8DF, 0x8E3, 0x8E9, @@ -140,7 +141,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0xC4A, 0xC4C, 0xC55, 0xC56, 0xC58, 0xC5A, - 0xC5D, 0xC5D, + 0xC5C, 0xC5D, 0xC60, 0xC63, 0xC80, 0xC83, 0xC85, 0xC8C, @@ -152,7 +153,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0xCC6, 0xCC8, 0xCCA, 0xCCC, 0xCD5, 0xCD6, - 0xCDD, 0xCDE, + 0xCDC, 0xCDE, 0xCE0, 0xCE3, 0xCF1, 0xCF3, 0xD00, 0xD0C, @@ -264,7 +265,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1C00, 0x1C36, 0x1C4D, 0x1C4F, 0x1C5A, 0x1C7D, - 0x1C80, 0x1C88, + 0x1C80, 0x1C8A, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, 0x1CE9, 0x1CEC, @@ -272,7 +273,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1CF5, 0x1CF6, 0x1CFA, 0x1CFA, 0x1D00, 0x1DBF, - 0x1DE7, 0x1DF4, + 0x1DD3, 0x1DF4, 0x1E00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, @@ -352,11 +353,8 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0xA67F, 0xA6EF, 0xA717, 0xA71F, 0xA722, 0xA788, - 0xA78B, 0xA7CA, - 0xA7D0, 0xA7D1, - 0xA7D3, 0xA7D3, - 0xA7D5, 0xA7D9, - 0xA7F2, 0xA805, + 0xA78B, 0xA7DC, + 0xA7F1, 0xA805, 0xA807, 0xA827, 0xA840, 0xA873, 0xA880, 0xA8C3, @@ -446,6 +444,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, + 0x105C0, 0x105F3, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, @@ -464,6 +463,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x108F4, 0x108F5, 0x10900, 0x10915, 0x10920, 0x10939, + 0x10940, 0x10959, 0x10980, 0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A03, @@ -483,9 +483,14 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x10C80, 0x10CB2, 0x10CC0, 0x10CF2, 0x10D00, 0x10D27, + 0x10D4A, 0x10D65, + 0x10D69, 0x10D69, + 0x10D6F, 0x10D85, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAC, 0x10EB0, 0x10EB1, + 0x10EC2, 0x10EC7, + 0x10EFA, 0x10EFC, 0x10F00, 0x10F1C, 0x10F27, 0x10F27, 0x10F30, 0x10F45, @@ -529,6 +534,17 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, + 0x11380, 0x11389, + 0x1138B, 0x1138B, + 0x1138E, 0x1138E, + 0x11390, 0x113B5, + 0x113B7, 0x113C0, + 0x113C2, 0x113C2, + 0x113C5, 0x113C5, + 0x113C7, 0x113CA, + 0x113CC, 0x113CD, + 0x113D1, 0x113D1, + 0x113D3, 0x113D3, 0x11400, 0x11441, 0x11443, 0x11445, 0x11447, 0x1144A, @@ -567,6 +583,8 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x11A50, 0x11A97, 0x11A9D, 0x11A9D, 0x11AB0, 0x11AF8, + 0x11B60, 0x11B67, + 0x11BC0, 0x11BE0, 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C3E, @@ -588,6 +606,7 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x11D90, 0x11D91, 0x11D93, 0x11D96, 0x11D98, 0x11D98, + 0x11DB0, 0x11DDB, 0x11EE0, 0x11EF6, 0x11F00, 0x11F10, 0x11F12, 0x11F3A, @@ -599,7 +618,9 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x12F90, 0x12FF0, 0x13000, 0x1342F, 0x13441, 0x13446, + 0x13460, 0x143FA, 0x14400, 0x14646, + 0x16100, 0x1612E, 0x16800, 0x16A38, 0x16A40, 0x16A5E, 0x16A70, 0x16ABE, @@ -608,16 +629,19 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x16B40, 0x16B43, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, + 0x16D40, 0x16D6C, 0x16E40, 0x16E7F, + 0x16EA0, 0x16EB8, + 0x16EBB, 0x16ED3, 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, 0x16FE0, 0x16FE1, 0x16FE3, 0x16FE3, - 0x16FF0, 0x16FF1, - 0x17000, 0x187F7, - 0x18800, 0x18CD5, - 0x18D00, 0x18D08, + 0x16FF0, 0x16FF6, + 0x17000, 0x18CD5, + 0x18CFF, 0x18D1E, + 0x18D80, 0x18DF2, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, @@ -677,6 +701,11 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1E290, 0x1E2AD, 0x1E2C0, 0x1E2EB, 0x1E4D0, 0x1E4EB, + 0x1E5D0, 0x1E5ED, + 0x1E5F0, 0x1E5F0, + 0x1E6C0, 0x1E6DE, + 0x1E6E0, 0x1E6F5, + 0x1E6FE, 0x1E6FF, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, @@ -722,16 +751,16 @@ static const pm_unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEP 0x1F150, 0x1F169, 0x1F170, 0x1F189, 0x20000, 0x2A6DF, - 0x2A700, 0x2B739, - 0x2B740, 0x2B81D, - 0x2B820, 0x2CEA1, + 0x2A700, 0x2B81D, + 0x2B820, 0x2CEAD, 0x2CEB0, 0x2EBE0, + 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, - 0x31350, 0x323AF, + 0x31350, 0x33479, }; -#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1528 +#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1598 static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEPOINTS_LENGTH] = { 0x100, 0x2C1, 0x2C6, 0x2D1, @@ -739,7 +768,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x2EC, 0x2EC, 0x2EE, 0x2EE, 0x345, 0x345, - 0x370, 0x374, + 0x363, 0x374, 0x376, 0x377, 0x37A, 0x37D, 0x37F, 0x37F, @@ -778,7 +807,8 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x840, 0x858, 0x860, 0x86A, 0x870, 0x887, - 0x889, 0x88E, + 0x889, 0x88F, + 0x897, 0x897, 0x8A0, 0x8C9, 0x8D4, 0x8DF, 0x8E3, 0x8E9, @@ -872,7 +902,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0xC4A, 0xC4C, 0xC55, 0xC56, 0xC58, 0xC5A, - 0xC5D, 0xC5D, + 0xC5C, 0xC5D, 0xC60, 0xC63, 0xC66, 0xC6F, 0xC80, 0xC83, @@ -885,7 +915,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0xCC6, 0xCC8, 0xCCA, 0xCCC, 0xCD5, 0xCD6, - 0xCDD, 0xCDE, + 0xCDC, 0xCDE, 0xCE0, 0xCE3, 0xCE6, 0xCEF, 0xCF1, 0xCF3, @@ -1007,7 +1037,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1C00, 0x1C36, 0x1C40, 0x1C49, 0x1C4D, 0x1C7D, - 0x1C80, 0x1C88, + 0x1C80, 0x1C8A, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, 0x1CE9, 0x1CEC, @@ -1015,7 +1045,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1CF5, 0x1CF6, 0x1CFA, 0x1CFA, 0x1D00, 0x1DBF, - 0x1DE7, 0x1DF4, + 0x1DD3, 0x1DF4, 0x1E00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, @@ -1094,11 +1124,8 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0xA67F, 0xA6EF, 0xA717, 0xA71F, 0xA722, 0xA788, - 0xA78B, 0xA7CA, - 0xA7D0, 0xA7D1, - 0xA7D3, 0xA7D3, - 0xA7D5, 0xA7D9, - 0xA7F2, 0xA805, + 0xA78B, 0xA7DC, + 0xA7F1, 0xA805, 0xA807, 0xA827, 0xA840, 0xA873, 0xA880, 0xA8C3, @@ -1191,6 +1218,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, + 0x105C0, 0x105F3, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, @@ -1209,6 +1237,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x108F4, 0x108F5, 0x10900, 0x10915, 0x10920, 0x10939, + 0x10940, 0x10959, 0x10980, 0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A03, @@ -1229,9 +1258,14 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x10CC0, 0x10CF2, 0x10D00, 0x10D27, 0x10D30, 0x10D39, + 0x10D40, 0x10D65, + 0x10D69, 0x10D69, + 0x10D6F, 0x10D85, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAC, 0x10EB0, 0x10EB1, + 0x10EC2, 0x10EC7, + 0x10EFA, 0x10EFC, 0x10F00, 0x10F1C, 0x10F27, 0x10F27, 0x10F30, 0x10F45, @@ -1278,6 +1312,17 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, + 0x11380, 0x11389, + 0x1138B, 0x1138B, + 0x1138E, 0x1138E, + 0x11390, 0x113B5, + 0x113B7, 0x113C0, + 0x113C2, 0x113C2, + 0x113C5, 0x113C5, + 0x113C7, 0x113CA, + 0x113CC, 0x113CD, + 0x113D1, 0x113D1, + 0x113D3, 0x113D3, 0x11400, 0x11441, 0x11443, 0x11445, 0x11447, 0x1144A, @@ -1297,6 +1342,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11680, 0x116B5, 0x116B8, 0x116B8, 0x116C0, 0x116C9, + 0x116D0, 0x116E3, 0x11700, 0x1171A, 0x1171D, 0x1172A, 0x11730, 0x11739, @@ -1322,6 +1368,9 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11A50, 0x11A97, 0x11A9D, 0x11A9D, 0x11AB0, 0x11AF8, + 0x11B60, 0x11B67, + 0x11BC0, 0x11BE0, + 0x11BF0, 0x11BF9, 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C3E, @@ -1346,6 +1395,8 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x11D93, 0x11D96, 0x11D98, 0x11D98, 0x11DA0, 0x11DA9, + 0x11DB0, 0x11DDB, + 0x11DE0, 0x11DE9, 0x11EE0, 0x11EF6, 0x11F00, 0x11F10, 0x11F12, 0x11F3A, @@ -1358,7 +1409,10 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x12F90, 0x12FF0, 0x13000, 0x1342F, 0x13441, 0x13446, + 0x13460, 0x143FA, 0x14400, 0x14646, + 0x16100, 0x1612E, + 0x16130, 0x16139, 0x16800, 0x16A38, 0x16A40, 0x16A5E, 0x16A60, 0x16A69, @@ -1370,16 +1424,20 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x16B50, 0x16B59, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, + 0x16D40, 0x16D6C, + 0x16D70, 0x16D79, 0x16E40, 0x16E7F, + 0x16EA0, 0x16EB8, + 0x16EBB, 0x16ED3, 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, 0x16FE0, 0x16FE1, 0x16FE3, 0x16FE3, - 0x16FF0, 0x16FF1, - 0x17000, 0x187F7, - 0x18800, 0x18CD5, - 0x18D00, 0x18D08, + 0x16FF0, 0x16FF6, + 0x17000, 0x18CD5, + 0x18CFF, 0x18D1E, + 0x18D80, 0x18DF2, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, @@ -1394,6 +1452,7 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1BC80, 0x1BC88, 0x1BC90, 0x1BC99, 0x1BC9E, 0x1BC9E, + 0x1CCF0, 0x1CCF9, 0x1D400, 0x1D454, 0x1D456, 0x1D49C, 0x1D49E, 0x1D49F, @@ -1443,6 +1502,11 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1E2F0, 0x1E2F9, 0x1E4D0, 0x1E4EB, 0x1E4F0, 0x1E4F9, + 0x1E5D0, 0x1E5ED, + 0x1E5F0, 0x1E5FA, + 0x1E6C0, 0x1E6DE, + 0x1E6E0, 0x1E6F5, + 0x1E6FE, 0x1E6FF, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, @@ -1490,16 +1554,16 @@ static const pm_unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEP 0x1F170, 0x1F189, 0x1FBF0, 0x1FBF9, 0x20000, 0x2A6DF, - 0x2A700, 0x2B739, - 0x2B740, 0x2B81D, - 0x2B820, 0x2CEA1, + 0x2A700, 0x2B81D, + 0x2B820, 0x2CEAD, 0x2CEB0, 0x2EBE0, + 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, - 0x31350, 0x323AF, + 0x31350, 0x33479, }; -#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1302 +#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1320 static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_CODEPOINTS_LENGTH] = { 0x100, 0x100, 0x102, 0x102, @@ -1774,6 +1838,7 @@ static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_C 0x10C7, 0x10C7, 0x10CD, 0x10CD, 0x13A0, 0x13F5, + 0x1C89, 0x1C89, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, 0x1E00, 0x1E00, @@ -2103,9 +2168,15 @@ static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_C 0xA7C2, 0xA7C2, 0xA7C4, 0xA7C7, 0xA7C9, 0xA7C9, + 0xA7CB, 0xA7CC, + 0xA7CE, 0xA7CE, 0xA7D0, 0xA7D0, + 0xA7D2, 0xA7D2, + 0xA7D4, 0xA7D4, 0xA7D6, 0xA7D6, 0xA7D8, 0xA7D8, + 0xA7DA, 0xA7DA, + 0xA7DC, 0xA7DC, 0xA7F5, 0xA7F5, 0xFF21, 0xFF3A, 0x10400, 0x10427, @@ -2115,8 +2186,10 @@ static const pm_unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_C 0x1058C, 0x10592, 0x10594, 0x10595, 0x10C80, 0x10CB2, + 0x10D50, 0x10D65, 0x118A0, 0x118BF, 0x16E40, 0x16E5F, + 0x16EA0, 0x16EB8, 0x1D400, 0x1D419, 0x1D434, 0x1D44D, 0x1D468, 0x1D481, diff --git a/test/prism/encoding/encodings_test.rb b/test/prism/encoding/encodings_test.rb index 4ad2b465cc1842..b008fc3fa10238 100644 --- a/test/prism/encoding/encodings_test.rb +++ b/test/prism/encoding/encodings_test.rb @@ -56,21 +56,11 @@ def assert_encoding_identifier(name, character) # Check that we can properly parse every codepoint in the given encoding. def assert_encoding(encoding, name, range) - # I'm not entirely sure, but I believe these codepoints are incorrect in - # their parsing in CRuby. They all report as matching `[[:lower:]]` but - # then they are parsed as constants. This is because CRuby determines if - # an identifier is a constant or not by case folding it down to lowercase - # and checking if there is a difference. And even though they report - # themselves as lowercase, their case fold is different. I have reported - # this bug upstream. + unicode = false + case encoding when Encoding::UTF_8, Encoding::UTF_8_MAC, Encoding::UTF8_DoCoMo, Encoding::UTF8_KDDI, Encoding::UTF8_SoftBank, Encoding::CESU_8 - range = range.to_a - [ - 0x01c5, 0x01c8, 0x01cb, 0x01f2, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, - 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, - 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, - 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fcc, 0x1ffc, - ] + unicode = true when Encoding::Windows_1253 range = range.to_a - [0xb5] end @@ -79,7 +69,7 @@ def assert_encoding(encoding, name, range) character = codepoint.chr(encoding) if character.match?(/[[:alpha:]]/) - if character.match?(/[[:upper:]]/) + if character.match?(/[[:upper:]]/) || (unicode && character.match?(Regexp.new("\\p{Lt}".encode(encoding)))) assert_encoding_constant(name, character) else assert_encoding_identifier(name, character) From 806e554cc027b71cf0970fd4eb8f9ee5f1ccf128 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 30 Nov 2025 14:14:03 +0900 Subject: [PATCH 036/367] Compare with the upper bound of the loop variable Fix sign-compare warning --- gc/default/default.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gc/default/default.c b/gc/default/default.c index 530fc4df891acb..2d862b0b212489 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -9448,7 +9448,7 @@ rb_gc_impl_objspace_init(void *objspace_ptr) (bits_t)0x0101010101010101ULL, // i=3: every 8th bit (bits_t)0x0001000100010001ULL, // i=4: every 16th bit }; - GC_ASSERT(i < sizeof(slot_bits_masks) / sizeof(slot_bits_masks[0])); + GC_ASSERT(HEAP_COUNT == sizeof(slot_bits_masks) / sizeof(slot_bits_masks[0])); heap->slot_bits_mask = slot_bits_masks[i]; ccan_list_head_init(&heap->pages); From d7cfd275f881bbd29d95dfdba585f055dd89ba44 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 30 Nov 2025 14:31:34 +0900 Subject: [PATCH 037/367] Set DESTDIR if relative loading When relative loading, `prefix` makes no sense actually. Use the given (or default) path as `DESTDIR` instead. This change affects only when the relative loading is enabled and the destdir is not given, and does not change the final installation path, but makes the configuration options simpler a little. --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index 22baeabb32ab46..21f2e5a466e382 100644 --- a/configure.ac +++ b/configure.ac @@ -4753,6 +4753,11 @@ AC_ARG_WITH(destdir, [DESTDIR="$withval"]) AC_SUBST(DESTDIR) +AS_IF([test "x$load_relative:$DESTDIR" = xyes:], [ + AS_IF([test "x$prefix" = xNONE], [DESTDIR="$ac_default_prefix"], [DESTDIR="$prefix"]) + prefix=/. +]) + AC_OUTPUT } From 9aa9cf8ea02f9860d735103b0e3ccfde62c68f95 Mon Sep 17 00:00:00 2001 From: "Daisuke Fujimura (fd0)" Date: Sun, 30 Nov 2025 12:01:44 +0900 Subject: [PATCH 038/367] Fix switch fall-through in copy_ext_file_error --- box.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/box.c b/box.c index 42ee967c509c84..81f285b3212ccd 100644 --- a/box.c +++ b/box.c @@ -514,16 +514,22 @@ copy_ext_file_error(char *message, size_t size, int copy_retvalue, const char *s switch (copy_retvalue) { case 1: snprintf(message, size, "can't open the extension path: %s", src_path); + break; case 2: snprintf(message, size, "can't open the file to write: %s", dst_path); + break; case 3: snprintf(message, size, "failed to read the extension path: %s", src_path); + break; case 4: snprintf(message, size, "failed to write the extension path: %s", dst_path); + break; case 5: snprintf(message, size, "failed to stat the extension path to copy permissions: %s", src_path); + break; case 6: snprintf(message, size, "failed to set permissions to the copied extension path: %s", dst_path); + break; default: rb_bug("unknown return value of copy_ext_file: %d", copy_retvalue); } From a0cd81e005d185be37b3a0323e4ee61b7b4360a6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 30 Nov 2025 21:07:33 +0900 Subject: [PATCH 039/367] Remove stale check Any objects with `call` method can be accepted as trap handlers, and the check for `Proc` type is useless since ruby/ruby@29f5911cf545. --- signal.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/signal.c b/signal.c index 1e2b0f613248c2..5971e12ee9b00f 100644 --- a/signal.c +++ b/signal.c @@ -1252,11 +1252,6 @@ trap_handler(VALUE *cmd, int sig) break; } } - else { - rb_proc_t *proc; - GetProcPtr(*cmd, proc); - (void)proc; - } } return func; From f92ff9a86b4de5cd6e368be62077741aa40abc64 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 1 Dec 2025 07:15:36 +0900 Subject: [PATCH 040/367] Remove an excess semicolon in a macro --- array.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/array.c b/array.c index 2ea6b55a7f69a3..2acaafee03d833 100644 --- a/array.c +++ b/array.c @@ -109,11 +109,11 @@ should_be_T_ARRAY(VALUE ary) #define FL_UNSET_SHARED(ary) FL_UNSET((ary), RARRAY_SHARED_FLAG) #define ARY_SET_PTR_FORCE(ary, p) \ - RARRAY(ary)->as.heap.ptr = (p); + (RARRAY(ary)->as.heap.ptr = (p)) #define ARY_SET_PTR(ary, p) do { \ RUBY_ASSERT(!ARY_EMBED_P(ary)); \ RUBY_ASSERT(!OBJ_FROZEN(ary)); \ - ARY_SET_PTR_FORCE(ary, p); \ + ARY_SET_PTR_FORCE(ary, p); \ } while (0) #define ARY_SET_EMBED_LEN(ary, n) do { \ long tmp_n = (n); \ From bcdad7f2867c5955ce06cd6fabd3b3c06361eb47 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sun, 30 Nov 2025 20:25:55 -0500 Subject: [PATCH 041/367] [ruby/prism] Properly remove references https://github.com/ruby/prism/commit/17b246fd6a --- prism/prism.c | 183 +++++++++++++++++++++++++------------------------- 1 file changed, 90 insertions(+), 93 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 8c886bb050b06a..58a6a9987db237 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -13404,6 +13404,78 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, return parse_value_expression(parser, binding_power, accepts_command_call, false, diag_id, depth); } +static bool +pm_node_unreference_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + /* When we are about to destroy a set of nodes that could potentially + * contain block exits for the current scope, we need to check if they + * are contained in the list of block exits and remove them if they are. + */ + case PM_BREAK_NODE: + case PM_NEXT_NODE: + case PM_REDO_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + size_t index = 0; + + while (index < parser->current_block_exits->size) { + pm_node_t *block_exit = parser->current_block_exits->nodes[index]; + + if (block_exit == node) { + if (index + 1 < parser->current_block_exits->size) { + memmove( + &parser->current_block_exits->nodes[index], + &parser->current_block_exits->nodes[index + 1], + (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *) + ); + } + parser->current_block_exits->size--; + return false; + } + + index++; + } + + return true; + } + /* When an implicit local variable is written to or targeted, it becomes + * a regular, named local variable. This branch removes it from the list + * of implicit parameters when that happens. */ + case PM_LOCAL_VARIABLE_READ_NODE: + case PM_IT_LOCAL_VARIABLE_READ_NODE: { + pm_parser_t *parser = (pm_parser_t *) data; + pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; + + for (size_t index = 0; index < implicit_parameters->size; index++) { + if (implicit_parameters->nodes[index] == node) { + /* If the node is not the last one in the list, we need to + * shift the remaining nodes down to fill the gap. This is + * extremely unlikely to happen. */ + if (index != implicit_parameters->size - 1) { + memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); + } + + implicit_parameters->size--; + break; + } + } + + return false; + } + default: + return true; + } +} + +/** + * When we are about to destroy a set of nodes that could potentially be + * referenced by one or more lists on the parser, then remove them from those + * lists so we don't get a use-after-free. + */ +static void +pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, pm_node_unreference_each, parser); +} + /** * Convert the name of a method into the corresponding write method name. For * example, foo would be turned into foo=. @@ -13454,45 +13526,6 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { return (pm_node_t *) result; } -static bool -parse_target_implicit_parameter_each(const pm_node_t *node, void *data) { - switch (PM_NODE_TYPE(node)) { - case PM_LOCAL_VARIABLE_READ_NODE: - case PM_IT_LOCAL_VARIABLE_READ_NODE: { - pm_parser_t *parser = (pm_parser_t *) data; - pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters; - - for (size_t index = 0; index < implicit_parameters->size; index++) { - if (implicit_parameters->nodes[index] == node) { - // If the node is not the last one in the list, we need to - // shift the remaining nodes down to fill the gap. This is - // extremely unlikely to happen. - if (index != implicit_parameters->size - 1) { - memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *)); - } - - implicit_parameters->size--; - break; - } - } - - return false; - } - default: - return true; - } -} - -/** - * When an implicit local variable is written to or targeted, it becomes a - * regular, named local variable. This function removes it from the list of - * implicit parameters when that happens. - */ -static void -parse_target_implicit_parameter(pm_parser_t *parser, const pm_node_t *node) { - pm_visit_node(node, parse_target_implicit_parameter_each, parser); -} - /** * Convert the given node into a valid target node. * @@ -13550,7 +13583,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p case PM_LOCAL_VARIABLE_READ_NODE: { if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) { PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, target->location.start); - parse_target_implicit_parameter(parser, target); + pm_node_unreference(parser, target); } const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target; @@ -13567,7 +13600,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); pm_node_t *node = (pm_node_t *) pm_local_variable_target_node_create(parser, &target->location, name, 0); - parse_target_implicit_parameter(parser, target); + pm_node_unreference(parser, target); pm_node_destroy(parser, target); return node; @@ -13742,7 +13775,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) { pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED; PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, diag_id, target->location.start); - parse_target_implicit_parameter(parser, target); + pm_node_unreference(parser, target); } pm_locals_unread(&scope->locals, name); @@ -13754,7 +13787,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); pm_node_t *node = (pm_node_t *) pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator); - parse_target_implicit_parameter(parser, target); + pm_node_unreference(parser, target); pm_node_destroy(parser, target); return node; @@ -13861,9 +13894,9 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod return target; } - // If there are arguments on the call node, then it can't be a method - // call ending with = or a local variable write, so it must be a - // syntax error. In this case we'll fall through to our default + // If there are arguments on the call node, then it can't be a + // method call ending with = or a local variable write, so it must + // be a syntax error. In this case we'll fall through to our default // handling. We need to free the value that we parsed because there // is no way for us to attach it to the tree at this point. // @@ -13871,7 +13904,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod // parameter somewhere in its subtree, we need to walk it and remove // any implicit parameters from the list of implicit parameters for // the current scope. - parse_target_implicit_parameter(parser, value); + pm_node_unreference(parser, value); pm_node_destroy(parser, value); } PRISM_FALLTHROUGH @@ -18772,7 +18805,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we're about to convert an 'it' implicit local // variable read into a method call, we need to remove // it from the list of implicit local variables. - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); } else { // Otherwise, we're about to convert a regular local // variable read into a method call, in which case we @@ -18781,7 +18814,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)); if (pm_token_is_numbered_parameter(identifier.start, identifier.end)) { - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); } else { pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name); @@ -21071,42 +21104,6 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding return value; } -static bool -parse_call_operator_write_block_exits_each(const pm_node_t *node, void *data) { - pm_parser_t *parser = (pm_parser_t *) data; - size_t index = 0; - - while (index < parser->current_block_exits->size) { - pm_node_t *block_exit = parser->current_block_exits->nodes[index]; - - if (block_exit == node) { - if (index + 1 < parser->current_block_exits->size) { - memmove( - &parser->current_block_exits->nodes[index], - &parser->current_block_exits->nodes[index + 1], - (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *) - ); - } - parser->current_block_exits->size--; - return false; - } - - index++; - } - - return true; -} - -/** - * When we are about to destroy a set of nodes that could potentially contain - * block exits for the current scope, we need to check if they are contained in - * the list of block exits and remove them if they are. - */ -static void -parse_call_operator_write_block_exits(pm_parser_t *parser, const pm_node_t *node) { - pm_visit_node(node, parse_call_operator_write_block_exits_each, parser); -} - /** * Ensure a call node that is about to become a call operator node does not * have arguments or a block attached. If it does, then we'll need to add an @@ -21118,14 +21115,14 @@ static void parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) { if (call_node->arguments != NULL) { pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS); - parse_call_operator_write_block_exits(parser, (pm_node_t *) call_node->arguments); + pm_node_unreference(parser, (pm_node_t *) call_node->arguments); pm_node_destroy(parser, (pm_node_t *) call_node->arguments); call_node->arguments = NULL; } if (call_node->block != NULL) { pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK); - parse_call_operator_write_block_exits(parser, (pm_node_t *) call_node->block); + pm_node_unreference(parser, (pm_node_t *) call_node->block); pm_node_destroy(parser, (pm_node_t *) call_node->block); call_node->block = NULL; } @@ -21517,14 +21514,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); pm_node_destroy(parser, node); return result; } case PM_LOCAL_VARIABLE_READ_NODE: { if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); } pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; @@ -21651,14 +21648,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); pm_node_destroy(parser, node); return result; } case PM_LOCAL_VARIABLE_READ_NODE: { if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); } pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; @@ -21795,14 +21792,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); pm_node_destroy(parser, node); return result; } case PM_LOCAL_VARIABLE_READ_NODE: { if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) { PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start); - parse_target_implicit_parameter(parser, node); + pm_node_unreference(parser, node); } pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; From 4c56001d27c4b2431fee65e6e9426319c475c45d Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sun, 30 Nov 2025 20:45:44 -0500 Subject: [PATCH 042/367] [ruby/prism] Fix up newlines in newline-delimited-literals When you have a %-literal that is delimited by newlines, and you are also interpolating a heredoc into that literal, then both concepts will attempt to add the same newline to the newline list. https://github.com/ruby/prism/commit/c831abb888 --- prism/prism.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 58a6a9987db237..793346f855ea4d 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12249,7 +12249,13 @@ parser_lex(pm_parser_t *parser) { size_t eol_length = match_eol_at(parser, breakpoint); if (eol_length) { parser->current.end = breakpoint + eol_length; - pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + + // Track the newline if we're not in a heredoc that + // would have already have added the newline to the + // list. + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } } else { parser->current.end = breakpoint + 1; } @@ -12503,7 +12509,13 @@ parser_lex(pm_parser_t *parser) { size_t eol_length = match_eol_at(parser, breakpoint); if (eol_length) { parser->current.end = breakpoint + eol_length; - pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + + // Track the newline if we're not in a heredoc that + // would have already have added the newline to the + // list. + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, parser->current.end - 1); + } } else { parser->current.end = breakpoint + 1; } From 8eea9a502031e866f210accc7d02347fc55f65c9 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 1 Dec 2025 17:19:42 +1300 Subject: [PATCH 043/367] Nullify scheduler during `terminate_atfork_i`. (#15354) --- thread.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/thread.c b/thread.c index f545a5377d8ed6..6e5c8e89ff075a 100644 --- a/thread.c +++ b/thread.c @@ -4979,6 +4979,9 @@ static void terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th) { if (th != current_th) { + // Clear the scheduler as it is no longer operational: + th->scheduler = Qnil; + rb_native_mutex_initialize(&th->interrupt_lock); rb_mutex_abandon_keeping_mutexes(th); rb_mutex_abandon_locking_mutex(th); From bc9ea585bee075480b4f12f84c8ab99315766595 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 1 Dec 2025 17:49:31 +1300 Subject: [PATCH 044/367] Add `rb_ec_close` function to manage execution context cleanup. (#15253) --- cont.c | 1 + vm.c | 7 +++++++ vm_core.h | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/cont.c b/cont.c index c49256c977a5b3..50e8ffb3498ec4 100644 --- a/cont.c +++ b/cont.c @@ -2907,6 +2907,7 @@ void rb_fiber_close(rb_fiber_t *fiber) { fiber_status_set(fiber, FIBER_TERMINATED); + rb_ec_close(&fiber->cont.saved_ec); } static void diff --git a/vm.c b/vm.c index f9c615c3e23c30..1a8a6d059b569b 100644 --- a/vm.c +++ b/vm.c @@ -3890,6 +3890,13 @@ rb_ec_clear_vm_stack(rb_execution_context_t *ec) rb_ec_set_vm_stack(ec, NULL, 0); } +void +rb_ec_close(rb_execution_context_t *ec) +{ + // Fiber storage is not accessible from outside the running fiber, so it is safe to clear it here. + ec->storage = Qnil; +} + static void th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm) { diff --git a/vm_core.h b/vm_core.h index 28d585deb2ab01..9d11457966ed71 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1104,6 +1104,10 @@ void rb_ec_initialize_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t // @param ec the execution context to update. void rb_ec_clear_vm_stack(rb_execution_context_t *ec); +// Close an execution context and free related resources that are no longer needed. +// @param ec the execution context to close. +void rb_ec_close(rb_execution_context_t *ec); + struct rb_ext_config { bool ractor_safe; }; From 8dc5822b007937f75eb0b156c9d9dcc7b16f9de8 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 1 Dec 2025 09:22:51 -0500 Subject: [PATCH 045/367] [ruby/prism] PM_NODE_INIT Hide the initialization of the base node inside the node initializer lists by a macro. As such, consistently enforce flags are set properly. https://github.com/ruby/prism/commit/c7b3d66d84 --- prism/prism.c | 1404 ++++++++----------------------------------------- 1 file changed, 214 insertions(+), 1190 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 793346f855ea4d..ac6fbd9f6b461a 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -1928,8 +1928,13 @@ pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { return memory; } -#define PM_NODE_ALLOC(parser, type) (type *) pm_node_alloc(parser, sizeof(type)) -#define PM_NODE_IDENTIFY(parser) (++parser->node_id) +#define PM_NODE_ALLOC(parser_, type_) (type_ *) pm_node_alloc(parser_, sizeof(type_)) +#define PM_NODE_INIT(parser_, type_, flags_, start_, end_) (pm_node_t) { \ + .type = (type_), \ + .flags = (flags_), \ + .node_id = ++(parser_)->node_id, \ + .location = { .start = (start_), .end = (end_) } \ +} /** * Allocate a new MissingNode node. @@ -1938,11 +1943,9 @@ static pm_missing_node_t * pm_missing_node_create(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { pm_missing_node_t *node = PM_NODE_ALLOC(parser, pm_missing_node_t); - *node = (pm_missing_node_t) {{ - .type = PM_MISSING_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { .start = start, .end = end } - }}; + *node = (pm_missing_node_t) { + .base = PM_NODE_INIT(parser, PM_MISSING_NODE, 0, start, end) + }; return node; } @@ -1956,14 +1959,7 @@ pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyw pm_alias_global_variable_node_t *node = PM_NODE_ALLOC(parser, pm_alias_global_variable_node_t); *node = (pm_alias_global_variable_node_t) { - { - .type = PM_ALIAS_GLOBAL_VARIABLE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = old_name->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, keyword->start, old_name->location.end), .new_name = new_name, .old_name = old_name, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) @@ -1981,14 +1977,7 @@ pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_n pm_alias_method_node_t *node = PM_NODE_ALLOC(parser, pm_alias_method_node_t); *node = (pm_alias_method_node_t) { - { - .type = PM_ALIAS_METHOD_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = old_name->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_ALIAS_METHOD_NODE, 0, keyword->start, old_name->location.end), .new_name = new_name, .old_name = old_name, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) @@ -2005,14 +1994,7 @@ pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node pm_alternation_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_alternation_pattern_node_t); *node = (pm_alternation_pattern_node_t) { - { - .type = PM_ALTERNATION_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = left->location.start, - .end = right->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_ALTERNATION_PATTERN_NODE, 0, left->location.start, right->location.end), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -2031,14 +2013,7 @@ pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *opera pm_and_node_t *node = PM_NODE_ALLOC(parser, pm_and_node_t); *node = (pm_and_node_t) { - { - .type = PM_AND_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = left->location.start, - .end = right->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_AND_NODE, 0, left->location.start, right->location.end), .left = left, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .right = right @@ -2055,11 +2030,7 @@ pm_arguments_node_create(pm_parser_t *parser) { pm_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_arguments_node_t); *node = (pm_arguments_node_t) { - { - .type = PM_ARGUMENTS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser) - }, + .base = PM_NODE_INIT(parser, PM_ARGUMENTS_NODE, 0, parser->start, parser->start), .arguments = { 0 } }; @@ -2103,12 +2074,7 @@ pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) { pm_array_node_t *node = PM_NODE_ALLOC(parser, pm_array_node_t); *node = (pm_array_node_t) { - { - .type = PM_ARRAY_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(opening) - }, + .base = PM_NODE_INIT(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, opening->end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .elements = { 0 } @@ -2159,14 +2125,7 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - { - .type = PM_ARRAY_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = nodes->nodes[0]->location.start, - .end = nodes->nodes[nodes->size - 1]->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, nodes->nodes[0]->location.start, nodes->nodes[nodes->size - 1]->location.end), .constant = NULL, .rest = NULL, .requireds = { 0 }, @@ -2202,11 +2161,7 @@ pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - { - .type = PM_ARRAY_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = rest->location, - }, + .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, rest->location.start, rest->location.end), .constant = NULL, .rest = rest, .requireds = { 0 }, @@ -2227,14 +2182,7 @@ pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - { - .type = PM_ARRAY_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = constant->location.start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, constant->location.start, closing->end), .constant = constant, .rest = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -2255,14 +2203,7 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - { - .type = PM_ARRAY_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, opening->start, closing->end), .constant = NULL, .rest = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -2313,15 +2254,7 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper } *node = (pm_assoc_node_t) { - { - .type = PM_ASSOC_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = key->location.start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_ASSOC_NODE, flags, key->location.start, end), .key = key, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value @@ -2339,14 +2272,7 @@ pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token pm_assoc_splat_node_t *node = PM_NODE_ALLOC(parser, pm_assoc_splat_node_t); *node = (pm_assoc_splat_node_t) { - { - .type = PM_ASSOC_SPLAT_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = value == NULL ? operator->end : value->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_ASSOC_SPLAT_NODE, 0, operator->start, value == NULL ? operator->end : value->location.end), .value = value, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -2363,11 +2289,7 @@ pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) pm_back_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_back_reference_read_node_t); *node = (pm_back_reference_read_node_t) { - { - .type = PM_BACK_REFERENCE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name), - }, + .base = PM_NODE_INIT(parser, PM_BACK_REFERENCE_READ_NODE, 0, name->start, name->end), .name = pm_parser_constant_id_token(parser, name) }; @@ -2382,14 +2304,7 @@ pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_st pm_begin_node_t *node = PM_NODE_ALLOC(parser, pm_begin_node_t); *node = (pm_begin_node_t) { - { - .type = PM_BEGIN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = begin_keyword->start, - .end = statements == NULL ? begin_keyword->end : statements->base.location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_BEGIN_NODE, 0, begin_keyword->start, statements == NULL ? begin_keyword->end : statements->base.location.end), .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), .statements = statements, .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE @@ -2448,14 +2363,7 @@ pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, p pm_block_argument_node_t *node = PM_NODE_ALLOC(parser, pm_block_argument_node_t); *node = (pm_block_argument_node_t) { - { - .type = PM_BLOCK_ARGUMENT_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = expression == NULL ? operator->end : expression->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator->start, expression == NULL ? operator->end : expression->location.end), .expression = expression, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -2471,11 +2379,7 @@ pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p pm_block_node_t *node = PM_NODE_ALLOC(parser, pm_block_node_t); *node = (pm_block_node_t) { - { - .type = PM_BLOCK_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { .start = opening->start, .end = closing->end }, - }, + .base = PM_NODE_INIT(parser, PM_BLOCK_NODE, 0, opening->start, closing->end), .locals = *locals, .parameters = parameters, .body = body, @@ -2495,14 +2399,7 @@ pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, cons pm_block_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_block_parameter_node_t); *node = (pm_block_parameter_node_t) { - { - .type = PM_BLOCK_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) - }, - }, + .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETER_NODE, 0, operator->start, name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -2537,14 +2434,7 @@ pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *param } *node = (pm_block_parameters_node_t) { - { - .type = PM_BLOCK_PARAMETERS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = start, - .end = end - } - }, + .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETERS_NODE, 0, start, end), .parameters = parameters, .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -2573,11 +2463,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) pm_block_local_variable_node_t *node = PM_NODE_ALLOC(parser, pm_block_local_variable_node_t); *node = (pm_block_local_variable_node_t) { - { - .type = PM_BLOCK_LOCAL_VARIABLE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name), - }, + .base = PM_NODE_INIT(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, name->start, name->end), .name = pm_parser_constant_id_token(parser, name) }; @@ -2604,14 +2490,7 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument pm_break_node_t *node = PM_NODE_ALLOC(parser, pm_break_node_t); *node = (pm_break_node_t) { - { - .type = PM_BREAK_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = (arguments == NULL ? keyword->end : arguments->base.location.end) - }, - }, + .base = PM_NODE_INIT(parser, PM_BREAK_NODE, 0, keyword->start, arguments == NULL ? keyword->end : arguments->base.location.end), .arguments = arguments, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; @@ -2638,12 +2517,7 @@ pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) { pm_call_node_t *node = PM_NODE_ALLOC(parser, pm_call_node_t); *node = (pm_call_node_t) { - { - .type = PM_CALL_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser), - }, + .base = PM_NODE_INIT(parser, PM_CALL_NODE, flags, parser->start, parser->start), .receiver = NULL, .call_operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .message_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -2950,15 +2824,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_and_write_node_t); *node = (pm_call_and_write_node_t) { - { - .type = PM_CALL_AND_WRITE_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CALL_AND_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3013,15 +2879,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_and_write_node_t) { - { - .type = PM_INDEX_AND_WRITE_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INDEX_AND_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3049,15 +2907,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, pm_call_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_operator_write_node_t); *node = (pm_call_operator_write_node_t) { - { - .type = PM_CALL_OPERATOR_WRITE_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CALL_OPERATOR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3089,15 +2939,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_operator_write_node_t) { - { - .type = PM_INDEX_OPERATOR_WRITE_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INDEX_OPERATOR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3127,15 +2969,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_or_write_node_t); *node = (pm_call_or_write_node_t) { - { - .type = PM_CALL_OR_WRITE_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CALL_OR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3167,15 +3001,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_or_write_node_t) { - { - .type = PM_INDEX_OR_WRITE_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INDEX_OR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3203,12 +3029,7 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { pm_call_target_node_t *node = PM_NODE_ALLOC(parser, pm_call_target_node_t); *node = (pm_call_target_node_t) { - { - .type = PM_CALL_TARGET_NODE, - .flags = target->base.flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = target->base.location - }, + .base = PM_NODE_INIT(parser, PM_CALL_TARGET_NODE, target->base.flags, target->base.location.start, target->base.location.end), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .name = target->name, @@ -3236,12 +3057,7 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_target_node_t) { - { - .type = PM_INDEX_TARGET_NODE, - .flags = flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = target->base.location - }, + .base = PM_NODE_INIT(parser, PM_INDEX_TARGET_NODE, flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target->base.location.start, target->base.location.end), .receiver = target->receiver, .opening_loc = target->opening_loc, .arguments = target->arguments, @@ -3265,14 +3081,7 @@ pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_v pm_capture_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_capture_pattern_node_t); *node = (pm_capture_pattern_node_t) { - { - .type = PM_CAPTURE_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = value->location.start, - .end = target->base.location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_CAPTURE_PATTERN_NODE, 0, value->location.start, target->base.location.end), .value = value, .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -3289,14 +3098,7 @@ pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node pm_case_node_t *node = PM_NODE_ALLOC(parser, pm_case_node_t); *node = (pm_case_node_t) { - { - .type = PM_CASE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = case_keyword->start, - .end = end_keyword->end - }, - }, + .base = PM_NODE_INIT(parser, PM_CASE_NODE, 0, case_keyword->start, end_keyword->end), .predicate = predicate, .else_clause = NULL, .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), @@ -3344,14 +3146,7 @@ pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, p pm_case_match_node_t *node = PM_NODE_ALLOC(parser, pm_case_match_node_t); *node = (pm_case_match_node_t) { - { - .type = PM_CASE_MATCH_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = case_keyword->start, - .end = end_keyword->end - }, - }, + .base = PM_NODE_INIT(parser, PM_CASE_MATCH_NODE, 0, case_keyword->start, end_keyword->end), .predicate = predicate, .else_clause = NULL, .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), @@ -3399,11 +3194,7 @@ pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p pm_class_node_t *node = PM_NODE_ALLOC(parser, pm_class_node_t); *node = (pm_class_node_t) { - { - .type = PM_CLASS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { .start = class_keyword->start, .end = end_keyword->end }, - }, + .base = PM_NODE_INIT(parser, PM_CLASS_NODE, 0, class_keyword->start, end_keyword->end), .locals = *locals, .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), .constant_path = constant_path, @@ -3426,14 +3217,7 @@ pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_r pm_class_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_and_write_node_t); *node = (pm_class_variable_and_write_node_t) { - { - .type = PM_CLASS_VARIABLE_AND_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3451,14 +3235,7 @@ pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_varia pm_class_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_operator_write_node_t); *node = (pm_class_variable_operator_write_node_t) { - { - .type = PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3478,14 +3255,7 @@ pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_re pm_class_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_or_write_node_t); *node = (pm_class_variable_or_write_node_t) { - { - .type = PM_CLASS_VARIABLE_OR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3504,11 +3274,7 @@ pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) pm_class_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_read_node_t); *node = (pm_class_variable_read_node_t) { - { - .type = PM_CLASS_VARIABLE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_READ_NODE, 0, token->start, token->end), .name = pm_parser_constant_id_token(parser, token) }; @@ -3535,17 +3301,10 @@ pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) { static pm_class_variable_write_node_t * pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) { pm_class_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_class_variable_write_node_t) { - { - .type = PM_CLASS_VARIABLE_WRITE_NODE, - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = read_node->base.location.start, - .end = value->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node->base.location.start, value->location.end), .name = read_node->name, .name_loc = PM_LOCATION_NODE_VALUE((pm_node_t *) read_node), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3564,14 +3323,7 @@ pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_nod pm_constant_path_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_and_write_node_t); *node = (pm_constant_path_and_write_node_t) { - { - .type = PM_CONSTANT_PATH_AND_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3588,14 +3340,7 @@ pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_pat pm_constant_path_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_operator_write_node_t); *node = (pm_constant_path_operator_write_node_t) { - { - .type = PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), .target = target, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -3614,14 +3359,7 @@ pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node pm_constant_path_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_or_write_node_t); *node = (pm_constant_path_or_write_node_t) { - { - .type = PM_CONSTANT_PATH_OR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3644,14 +3382,7 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to } *node = (pm_constant_path_node_t) { - { - .type = PM_CONSTANT_PATH_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = parent == NULL ? delimiter->start : parent->location.start, - .end = name_token->end - }, - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_NODE, 0, parent == NULL ? delimiter->start : parent->location.start, name_token->end), .parent = parent, .name = name, .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), @@ -3667,17 +3398,10 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to static pm_constant_path_write_node_t * pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) { pm_constant_path_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_constant_path_write_node_t) { - { - .type = PM_CONSTANT_PATH_WRITE_NODE, - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, target->base.location.start, value->location.end), .target = target, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3695,14 +3419,7 @@ pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t * pm_constant_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_and_write_node_t); *node = (pm_constant_and_write_node_t) { - { - .type = PM_CONSTANT_AND_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3720,14 +3437,7 @@ pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_nod pm_constant_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_operator_write_node_t); *node = (pm_constant_operator_write_node_t) { - { - .type = PM_CONSTANT_OPERATOR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3747,14 +3457,7 @@ pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *t pm_constant_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_or_write_node_t); *node = (pm_constant_or_write_node_t) { - { - .type = PM_CONSTANT_OR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3773,11 +3476,7 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { pm_constant_read_node_t *node = PM_NODE_ALLOC(parser, pm_constant_read_node_t); *node = (pm_constant_read_node_t) { - { - .type = PM_CONSTANT_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name) - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_READ_NODE, 0, name->start, name->end), .name = pm_parser_constant_id_token(parser, name) }; @@ -3790,17 +3489,10 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { static pm_constant_write_node_t * pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) { pm_constant_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_constant_write_node_t) { - { - .type = PM_CONSTANT_WRITE_NODE, - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_CONSTANT_WRITE_NODE, flags, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -3887,11 +3579,7 @@ pm_def_node_create( } *node = (pm_def_node_t) { - { - .type = PM_DEF_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { .start = def_keyword->start, .end = end }, - }, + .base = PM_NODE_INIT(parser, PM_DEF_NODE, 0, def_keyword->start, end), .name = name, .name_loc = PM_LOCATION_TOKEN_VALUE(name_loc), .receiver = receiver, @@ -3915,16 +3603,10 @@ pm_def_node_create( static pm_defined_node_t * pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_location_t *keyword_loc) { pm_defined_node_t *node = PM_NODE_ALLOC(parser, pm_defined_node_t); + const uint8_t *end = rparen->type == PM_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end; *node = (pm_defined_node_t) { - { - .type = PM_DEFINED_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword_loc->start, - .end = (rparen->type == PM_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end) - }, - }, + .base = PM_NODE_INIT(parser, PM_DEFINED_NODE, 0, keyword_loc->start, end), .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), .value = value, .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), @@ -3948,14 +3630,7 @@ pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_stat } *node = (pm_else_node_t) { - { - .type = PM_ELSE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = else_keyword->start, - .end = end, - }, - }, + .base = PM_NODE_INIT(parser, PM_ELSE_NODE, 0, else_keyword->start, end), .else_keyword_loc = PM_LOCATION_TOKEN_VALUE(else_keyword), .statements = statements, .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) @@ -3972,14 +3647,7 @@ pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *openin pm_embedded_statements_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_statements_node_t); *node = (pm_embedded_statements_node_t) { - { - .type = PM_EMBEDDED_STATEMENTS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - } - }, + .base = PM_NODE_INIT(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, opening->start, closing->end), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .statements = statements, .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) @@ -3996,14 +3664,7 @@ pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator pm_embedded_variable_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_variable_node_t); *node = (pm_embedded_variable_node_t) { - { - .type = PM_EMBEDDED_VARIABLE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = variable->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_EMBEDDED_VARIABLE_NODE, 0, operator->start, variable->location.end), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .variable = variable }; @@ -4019,14 +3680,7 @@ pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_ pm_ensure_node_t *node = PM_NODE_ALLOC(parser, pm_ensure_node_t); *node = (pm_ensure_node_t) { - { - .type = PM_ENSURE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = ensure_keyword->start, - .end = end_keyword->end - }, - }, + .base = PM_NODE_INIT(parser, PM_ENSURE_NODE, 0, ensure_keyword->start, end_keyword->end), .ensure_keyword_loc = PM_LOCATION_TOKEN_VALUE(ensure_keyword), .statements = statements, .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword) @@ -4043,12 +3697,9 @@ pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_FALSE); pm_false_node_t *node = PM_NODE_ALLOC(parser, pm_false_node_t); - *node = (pm_false_node_t) {{ - .type = PM_FALSE_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_false_node_t) { + .base = PM_NODE_INIT(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + }; return node; } @@ -4082,14 +3733,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { pm_node_t *right_splat_node = right; #endif *node = (pm_find_pattern_node_t) { - { - .type = PM_FIND_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = left->location.start, - .end = right->location.end, - }, - }, + .base = PM_NODE_INIT(parser, PM_FIND_PATTERN_NODE, 0, left->location.start, right->location.end), .constant = NULL, .left = left_splat_node, .right = right_splat_node, @@ -4190,12 +3834,7 @@ pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_float_node_t *node = PM_NODE_ALLOC(parser, pm_float_node_t); *node = (pm_float_node_t) { - { - .type = PM_FLOAT_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .value = pm_double_parse(parser, token) }; @@ -4211,12 +3850,7 @@ pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - { - .type = PM_IMAGINARY_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT, .start = token->start, @@ -4236,12 +3870,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t); *node = (pm_rational_node_t) { - { - .type = PM_RATIONAL_NODE, - .flags = PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .numerator = { 0 }, .denominator = { 0 } }; @@ -4290,12 +3919,7 @@ pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *t pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - { - .type = PM_IMAGINARY_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .numeric = (pm_node_t *) pm_float_node_rational_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT_RATIONAL, .start = token->start, @@ -4323,14 +3947,7 @@ pm_for_node_create( pm_for_node_t *node = PM_NODE_ALLOC(parser, pm_for_node_t); *node = (pm_for_node_t) { - { - .type = PM_FOR_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = for_keyword->start, - .end = end_keyword->end - }, - }, + .base = PM_NODE_INIT(parser, PM_FOR_NODE, 0, for_keyword->start, end_keyword->end), .index = index, .collection = collection, .statements = statements, @@ -4351,11 +3968,9 @@ pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token assert(token->type == PM_TOKEN_UDOT_DOT_DOT); pm_forwarding_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_arguments_node_t); - *node = (pm_forwarding_arguments_node_t) {{ - .type = PM_FORWARDING_ARGUMENTS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_forwarding_arguments_node_t) { + .base = PM_NODE_INIT(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, token->start, token->end) + }; return node; } @@ -4368,11 +3983,9 @@ pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token assert(token->type == PM_TOKEN_UDOT_DOT_DOT); pm_forwarding_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_parameter_node_t); - *node = (pm_forwarding_parameter_node_t) {{ - .type = PM_FORWARDING_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_forwarding_parameter_node_t) { + .base = PM_NODE_INIT(parser, PM_FORWARDING_PARAMETER_NODE, 0, token->start, token->end) + }; return node; } @@ -4391,15 +4004,9 @@ pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm block = (pm_block_node_t *) arguments->block; } + const uint8_t *end = block != NULL ? block->base.location.end : token->end; *node = (pm_forwarding_super_node_t) { - { - .type = PM_FORWARDING_SUPER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = token->start, - .end = block != NULL ? block->base.location.end : token->end - }, - }, + .base = PM_NODE_INIT(parser, PM_FORWARDING_SUPER_NODE, 0, token->start, end), .block = block }; @@ -4415,14 +4022,7 @@ pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening pm_hash_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_hash_pattern_node_t); *node = (pm_hash_pattern_node_t) { - { - .type = PM_HASH_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, opening->start, closing->end), .constant = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -4458,14 +4058,7 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme } *node = (pm_hash_pattern_node_t) { - { - .type = PM_HASH_PATTERN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, start, end), .constant = NULL, .elements = { 0 }, .rest = rest, @@ -4510,14 +4103,7 @@ pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, pm_global_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_and_write_node_t); *node = (pm_global_variable_and_write_node_t) { - { - .type = PM_GLOBAL_VARIABLE_AND_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, target->location.start, value->location.end), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4535,14 +4121,7 @@ pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *ta pm_global_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_operator_write_node_t); *node = (pm_global_variable_operator_write_node_t) { - { - .type = PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target->location.start, value->location.end), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4562,14 +4141,7 @@ pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, pm_global_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_or_write_node_t); *node = (pm_global_variable_or_write_node_t) { - { - .type = PM_GLOBAL_VARIABLE_OR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, target->location.start, value->location.end), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4587,11 +4159,7 @@ pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { - { - .type = PM_GLOBAL_VARIABLE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name), - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, name->start, name->end), .name = pm_parser_constant_id_token(parser, name) }; @@ -4606,11 +4174,7 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { - { - .type = PM_GLOBAL_VARIABLE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser) - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, parser->start, parser->start), .name = name }; @@ -4623,17 +4187,10 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant static pm_global_variable_write_node_t * pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) { pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_global_variable_write_node_t) { - { - .type = PM_GLOBAL_VARIABLE_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .location = { - .start = target->location.start, - .end = value->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, target->location.start, value->location.end), .name = pm_global_variable_write_name(parser, target), .name_loc = PM_LOCATION_NODE_VALUE(target), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -4651,11 +4208,7 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); *node = (pm_global_variable_write_node_t) { - { - .type = PM_GLOBAL_VARIABLE_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser) - }, + .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0, parser->start, parser->start), .name = name, .name_loc = PM_LOCATION_NULL_VALUE(parser), .operator_loc = PM_LOCATION_NULL_VALUE(parser), @@ -4674,12 +4227,7 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) { pm_hash_node_t *node = PM_NODE_ALLOC(parser, pm_hash_node_t); *node = (pm_hash_node_t) { - { - .type = PM_HASH_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(opening) - }, + .base = PM_NODE_INIT(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, opening->end), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_NULL_VALUE(parser), .elements = { 0 } @@ -4741,15 +4289,7 @@ pm_if_node_create(pm_parser_t *parser, } *node = (pm_if_node_t) { - { - .type = PM_IF_NODE, - .flags = PM_NODE_FLAG_NEWLINE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = if_keyword->start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, if_keyword->start, end), .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), @@ -4773,15 +4313,7 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t pm_statements_node_body_append(parser, statements, statement, true); *node = (pm_if_node_t) { - { - .type = PM_IF_NODE, - .flags = PM_NODE_FLAG_NEWLINE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = statement->location.start, - .end = predicate->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement->location.start, predicate->location.end), .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -4813,15 +4345,7 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); *node = (pm_if_node_t) { - { - .type = PM_IF_NODE, - .flags = PM_NODE_FLAG_NEWLINE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = predicate->location.start, - .end = false_expression->location.end, - }, - }, + .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate->location.start, false_expression->location.end), .if_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .predicate = predicate, .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark), @@ -4854,11 +4378,7 @@ pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) { pm_implicit_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_node_t); *node = (pm_implicit_node_t) { - { - .type = PM_IMPLICIT_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = value->location - }, + .base = PM_NODE_INIT(parser, PM_IMPLICIT_NODE, 0, value->location.start, value->location.end), .value = value }; @@ -4875,11 +4395,7 @@ pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_implicit_rest_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_rest_node_t); *node = (pm_implicit_rest_node_t) { - { - .type = PM_IMPLICIT_REST_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - } + .base = PM_NODE_INIT(parser, PM_IMPLICIT_REST_NODE, 0, token->start, token->end) }; return node; @@ -4894,12 +4410,7 @@ pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token pm_integer_node_t *node = PM_NODE_ALLOC(parser, pm_integer_node_t); *node = (pm_integer_node_t) { - { - .type = PM_INTEGER_NODE, - .flags = base | PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .value = { 0 } }; @@ -4926,12 +4437,7 @@ pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, cons pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - { - .type = PM_IMAGINARY_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER, .start = token->start, @@ -4952,12 +4458,7 @@ pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t); *node = (pm_rational_node_t) { - { - .type = PM_RATIONAL_NODE, - .flags = base | PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .numerator = { 0 }, .denominator = { .value = 1, 0 } }; @@ -4986,12 +4487,7 @@ pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t b pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - { - .type = PM_IMAGINARY_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), .numeric = (pm_node_t *) pm_integer_node_rational_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER_RATIONAL, .start = token->start, @@ -5019,14 +4515,7 @@ pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t } *node = (pm_in_node_t) { - { - .type = PM_IN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = in_keyword->start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_IN_NODE, 0, in_keyword->start, end), .pattern = pattern, .statements = statements, .in_loc = PM_LOCATION_TOKEN_VALUE(in_keyword), @@ -5045,14 +4534,7 @@ pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_vari pm_instance_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_and_write_node_t); *node = (pm_instance_variable_and_write_node_t) { - { - .type = PM_INSTANCE_VARIABLE_AND_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5070,14 +4552,7 @@ pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance pm_instance_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_operator_write_node_t); *node = (pm_instance_variable_operator_write_node_t) { - { - .type = PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5097,14 +4572,7 @@ pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_varia pm_instance_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_or_write_node_t); *node = (pm_instance_variable_or_write_node_t) { - { - .type = PM_INSTANCE_VARIABLE_OR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5123,11 +4591,7 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok pm_instance_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_read_node_t); *node = (pm_instance_variable_read_node_t) { - { - .type = PM_INSTANCE_VARIABLE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, token->start, token->end), .name = pm_parser_constant_id_token(parser, token) }; @@ -5141,16 +4605,10 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok static pm_instance_variable_write_node_t * pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) { pm_instance_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); + *node = (pm_instance_variable_write_node_t) { - { - .type = PM_INSTANCE_VARIABLE_WRITE_NODE, - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = read_node->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node->base.location.start, value->location.end), .name = read_node->name, .name_loc = PM_LOCATION_NODE_BASE_VALUE(read_node), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -5212,15 +4670,7 @@ pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_tok pm_interpolated_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_regular_expression_node_t); *node = (pm_interpolated_regular_expression_node_t) { - { - .type = PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = NULL, - }, - }, + .base = PM_NODE_INIT(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, NULL), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(opening), .parts = { 0 } @@ -5379,15 +4829,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin } *node = (pm_interpolated_string_node_t) { - { - .type = PM_INTERPOLATED_STRING_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end, - }, - }, + .base = PM_NODE_INIT(parser, PM_INTERPOLATED_STRING_NODE, flags, opening->start, closing->end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -5436,15 +4878,7 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin pm_interpolated_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_symbol_node_t); *node = (pm_interpolated_symbol_node_t) { - { - .type = PM_INTERPOLATED_SYMBOL_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end, - }, - }, + .base = PM_NODE_INIT(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, closing->end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -5468,14 +4902,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi pm_interpolated_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_x_string_node_t); *node = (pm_interpolated_x_string_node_t) { - { - .type = PM_INTERPOLATED_X_STRING_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_INTERPOLATED_X_STRING_NODE, 0, opening->start, closing->end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -5504,11 +4931,7 @@ pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *nam pm_it_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_it_local_variable_read_node_t); *node = (pm_it_local_variable_read_node_t) { - { - .type = PM_IT_LOCAL_VARIABLE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name) - } + .base = PM_NODE_INIT(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, name->start, name->end), }; return node; @@ -5522,14 +4945,7 @@ pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, con pm_it_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_it_parameters_node_t); *node = (pm_it_parameters_node_t) { - { - .type = PM_IT_PARAMETERS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - } - } + .base = PM_NODE_INIT(parser, PM_IT_PARAMETERS_NODE, 0, opening->start, closing->end), }; return node; @@ -5543,12 +4959,7 @@ pm_keyword_hash_node_create(pm_parser_t *parser) { pm_keyword_hash_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_hash_node_t); *node = (pm_keyword_hash_node_t) { - .base = { - .type = PM_KEYWORD_HASH_NODE, - .flags = PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE - }, + .base = PM_NODE_INIT(parser, PM_KEYWORD_HASH_NODE, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, NULL, NULL), .elements = { 0 } }; @@ -5581,14 +4992,7 @@ pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t pm_required_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_keyword_parameter_node_t); *node = (pm_required_keyword_parameter_node_t) { - { - .type = PM_REQUIRED_KEYWORD_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = name->start, - .end = name->end - }, - }, + .base = PM_NODE_INIT(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, name->start, name->end), .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), .name_loc = PM_LOCATION_TOKEN_VALUE(name), }; @@ -5604,14 +5008,7 @@ pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t pm_optional_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_keyword_parameter_node_t); *node = (pm_optional_keyword_parameter_node_t) { - { - .type = PM_OPTIONAL_KEYWORD_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = name->start, - .end = value->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, name->start, value->location.end), .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), .name_loc = PM_LOCATION_TOKEN_VALUE(name), .value = value @@ -5628,14 +5025,7 @@ pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *ope pm_keyword_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_rest_parameter_node_t); *node = (pm_keyword_rest_parameter_node_t) { - { - .type = PM_KEYWORD_REST_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) - }, - }, + .base = PM_NODE_INIT(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator->start, (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5660,14 +5050,7 @@ pm_lambda_node_create( pm_lambda_node_t *node = PM_NODE_ALLOC(parser, pm_lambda_node_t); *node = (pm_lambda_node_t) { - { - .type = PM_LAMBDA_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_LAMBDA_NODE, 0, operator->start, closing->end), .locals = *locals, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -5689,14 +5072,7 @@ pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, pm_local_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_and_write_node_t); *node = (pm_local_variable_and_write_node_t) { - { - .type = PM_LOCAL_VARIABLE_AND_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, target->location.start, value->location.end), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5715,14 +5091,7 @@ pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *tar pm_local_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_operator_write_node_t); *node = (pm_local_variable_operator_write_node_t) { - { - .type = PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target->location.start, value->location.end), .name_loc = target->location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5744,14 +5113,7 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c pm_local_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_or_write_node_t); *node = (pm_local_variable_or_write_node_t) { - { - .type = PM_LOCAL_VARIABLE_OR_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, target->location.start, value->location.end), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5772,11 +5134,7 @@ pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_tok pm_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_read_node_t); *node = (pm_local_variable_read_node_t) { - { - .type = PM_LOCAL_VARIABLE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name) - }, + .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, name->start, name->end), .name = name_id, .depth = depth }; @@ -5809,17 +5167,10 @@ pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t static pm_local_variable_write_node_t * pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name, uint32_t depth, pm_node_t *value, const pm_location_t *name_loc, const pm_token_t *operator) { pm_local_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_local_variable_write_node_t) { - { - .type = PM_LOCAL_VARIABLE_WRITE_NODE, - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = name_loc->start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, name_loc->start, value->location.end), .name = name, .depth = depth, .value = value, @@ -5868,11 +5219,7 @@ pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *l pm_local_variable_target_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_target_node_t); *node = (pm_local_variable_target_node_t) { - { - .type = PM_LOCAL_VARIABLE_TARGET_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = *location - }, + .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, location->start, location->end), .name = name, .depth = depth }; @@ -5890,14 +5237,7 @@ pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t pm_match_predicate_node_t *node = PM_NODE_ALLOC(parser, pm_match_predicate_node_t); *node = (pm_match_predicate_node_t) { - { - .type = PM_MATCH_PREDICATE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = value->location.start, - .end = pattern->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_MATCH_PREDICATE_NODE, 0, value->location.start, pattern->location.end), .value = value, .pattern = pattern, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5916,14 +5256,7 @@ pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t * pm_match_required_node_t *node = PM_NODE_ALLOC(parser, pm_match_required_node_t); *node = (pm_match_required_node_t) { - { - .type = PM_MATCH_REQUIRED_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = value->location.start, - .end = pattern->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_MATCH_REQUIRED_NODE, 0, value->location.start, pattern->location.end), .value = value, .pattern = pattern, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5940,11 +5273,7 @@ pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) { pm_match_write_node_t *node = PM_NODE_ALLOC(parser, pm_match_write_node_t); *node = (pm_match_write_node_t) { - { - .type = PM_MATCH_WRITE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = call->base.location - }, + .base = PM_NODE_INIT(parser, PM_MATCH_WRITE_NODE, 0, call->base.location.start, call->base.location.end), .call = call, .targets = { 0 } }; @@ -5960,14 +5289,7 @@ pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_module_node_t *node = PM_NODE_ALLOC(parser, pm_module_node_t); *node = (pm_module_node_t) { - { - .type = PM_MODULE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = module_keyword->start, - .end = end_keyword->end - } - }, + .base = PM_NODE_INIT(parser, PM_MODULE_NODE, 0, module_keyword->start, end_keyword->end), .locals = (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals), .module_keyword_loc = PM_LOCATION_TOKEN_VALUE(module_keyword), .constant_path = constant_path, @@ -5987,11 +5309,7 @@ pm_multi_target_node_create(pm_parser_t *parser) { pm_multi_target_node_t *node = PM_NODE_ALLOC(parser, pm_multi_target_node_t); *node = (pm_multi_target_node_t) { - { - .type = PM_MULTI_TARGET_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { .start = NULL, .end = NULL } - }, + .base = PM_NODE_INIT(parser, PM_MULTI_TARGET_NODE, 0, NULL, NULL), .lefts = { 0 }, .rest = NULL, .rights = { 0 }, @@ -6060,17 +5378,10 @@ pm_multi_target_node_closing_set(pm_multi_target_node_t *node, const pm_token_t static pm_multi_write_node_t * pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) { pm_multi_write_node_t *node = PM_NODE_ALLOC(parser, pm_multi_write_node_t); + pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_multi_write_node_t) { - { - .type = PM_MULTI_WRITE_NODE, - .flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = target->base.location.start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_MULTI_WRITE_NODE, flags, target->base.location.start, value->location.end), .lefts = target->lefts, .rest = target->rest, .rights = target->rights, @@ -6096,14 +5407,7 @@ pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments pm_next_node_t *node = PM_NODE_ALLOC(parser, pm_next_node_t); *node = (pm_next_node_t) { - { - .type = PM_NEXT_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = (arguments == NULL ? keyword->end : arguments->base.location.end) - } - }, + .base = PM_NODE_INIT(parser, PM_NEXT_NODE, 0, keyword->start, (arguments == NULL ? keyword->end : arguments->base.location.end)), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .arguments = arguments }; @@ -6119,12 +5423,9 @@ pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_NIL); pm_nil_node_t *node = PM_NODE_ALLOC(parser, pm_nil_node_t); - *node = (pm_nil_node_t) {{ - .type = PM_NIL_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_nil_node_t) { + .base = PM_NODE_INIT(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + }; return node; } @@ -6139,14 +5440,7 @@ pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *oper pm_no_keywords_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_no_keywords_parameter_node_t); *node = (pm_no_keywords_parameter_node_t) { - { - .type = PM_NO_KEYWORDS_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = keyword->end - } - }, + .base = PM_NODE_INIT(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, operator->start, keyword->end), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; @@ -6162,11 +5456,7 @@ pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *loc pm_numbered_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_parameters_node_t); *node = (pm_numbered_parameters_node_t) { - { - .type = PM_NUMBERED_PARAMETERS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = *location - }, + .base = PM_NODE_INIT(parser, PM_NUMBERED_PARAMETERS_NODE, 0, location->start, location->end), .maximum = maximum }; @@ -6231,11 +5521,7 @@ pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *na pm_numbered_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_reference_read_node_t); *node = (pm_numbered_reference_read_node_t) { - { - .type = PM_NUMBERED_REFERENCE_READ_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(name), - }, + .base = PM_NODE_INIT(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, name->start, name->end), .number = pm_numbered_reference_read_node_number(parser, name) }; @@ -6250,14 +5536,7 @@ pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, c pm_optional_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_parameter_node_t); *node = (pm_optional_parameter_node_t) { - { - .type = PM_OPTIONAL_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = name->start, - .end = value->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_OPTIONAL_PARAMETER_NODE, 0, name->start, value->location.end), .name = pm_parser_constant_id_token(parser, name), .name_loc = PM_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -6277,14 +5556,7 @@ pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operat pm_or_node_t *node = PM_NODE_ALLOC(parser, pm_or_node_t); *node = (pm_or_node_t) { - { - .type = PM_OR_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = left->location.start, - .end = right->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_OR_NODE, 0, left->location.start, right->location.end), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6301,11 +5573,7 @@ pm_parameters_node_create(pm_parser_t *parser) { pm_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_parameters_node_t); *node = (pm_parameters_node_t) { - { - .type = PM_PARAMETERS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(&parser->current) - }, + .base = PM_NODE_INIT(parser, PM_PARAMETERS_NODE, 0, NULL, NULL), .rest = NULL, .keyword_rest = NULL, .block = NULL, @@ -6408,15 +5676,11 @@ static pm_program_node_t * pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) { pm_program_node_t *node = PM_NODE_ALLOC(parser, pm_program_node_t); + const uint8_t *start = statements == NULL ? parser->start : statements->base.location.start; + const uint8_t *end = statements == NULL ? parser->end : statements->base.location.end; + *node = (pm_program_node_t) { - { - .type = PM_PROGRAM_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = statements == NULL ? parser->start : statements->base.location.start, - .end = statements == NULL ? parser->end : statements->base.location.end - } - }, + .base = PM_NODE_INIT(parser, PM_PROGRAM_NODE, 0, start, end), .locals = *locals, .statements = statements }; @@ -6432,15 +5696,7 @@ pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_no pm_parentheses_node_t *node = PM_NODE_ALLOC(parser, pm_parentheses_node_t); *node = (pm_parentheses_node_t) { - { - .type = PM_PARENTHESES_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - } - }, + .base = PM_NODE_INIT(parser, PM_PARENTHESES_NODE, flags, opening->start, closing->end), .body = body, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) @@ -6457,14 +5713,7 @@ pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, con pm_pinned_expression_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_expression_node_t); *node = (pm_pinned_expression_node_t) { - { - .type = PM_PINNED_EXPRESSION_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = rparen->end - } - }, + .base = PM_NODE_INIT(parser, PM_PINNED_EXPRESSION_NODE, 0, operator->start, rparen->end), .expression = expression, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen), @@ -6482,14 +5731,7 @@ pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_pinned_variable_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_variable_node_t); *node = (pm_pinned_variable_node_t) { - { - .type = PM_PINNED_VARIABLE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = variable->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_PINNED_VARIABLE_NODE, 0, operator->start, variable->location.end), .variable = variable, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -6505,14 +5747,7 @@ pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, co pm_post_execution_node_t *node = PM_NODE_ALLOC(parser, pm_post_execution_node_t); *node = (pm_post_execution_node_t) { - { - .type = PM_POST_EXECUTION_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = closing->end - } - }, + .base = PM_NODE_INIT(parser, PM_POST_EXECUTION_NODE, 0, keyword->start, closing->end), .statements = statements, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -6530,14 +5765,7 @@ pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, con pm_pre_execution_node_t *node = PM_NODE_ALLOC(parser, pm_pre_execution_node_t); *node = (pm_pre_execution_node_t) { - { - .type = PM_PRE_EXECUTION_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = closing->end - } - }, + .base = PM_NODE_INIT(parser, PM_PRE_EXECUTION_NODE, 0, keyword->start, closing->end), .statements = statements, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -6574,15 +5802,7 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope } *node = (pm_range_node_t) { - { - .type = PM_RANGE_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = (left == NULL ? operator->start : left->location.start), - .end = (right == NULL ? operator->end : right->location.end) - } - }, + .base = PM_NODE_INIT(parser, PM_RANGE_NODE, flags, (left == NULL ? operator->start : left->location.start), (right == NULL ? operator->end : right->location.end)), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6599,11 +5819,9 @@ pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_REDO); pm_redo_node_t *node = PM_NODE_ALLOC(parser, pm_redo_node_t); - *node = (pm_redo_node_t) {{ - .type = PM_REDO_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_redo_node_t) { + .base = PM_NODE_INIT(parser, PM_REDO_NODE, 0, token->start, token->end) + }; return node; } @@ -6615,17 +5833,10 @@ pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { static pm_regular_expression_node_t * pm_regular_expression_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) { pm_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_regular_expression_node_t); + pm_node_flags_t flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL; *node = (pm_regular_expression_node_t) { - { - .type = PM_REGULAR_EXPRESSION_NODE, - .flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = MIN(opening->start, closing->start), - .end = MAX(opening->end, closing->end) - } - }, + .base = PM_NODE_INIT(parser, PM_REGULAR_EXPRESSION_NODE, flags, MIN(opening->start, closing->start), MAX(opening->end, closing->end)), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -6651,11 +5862,7 @@ pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) pm_required_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_parameter_node_t); *node = (pm_required_parameter_node_t) { - { - .type = PM_REQUIRED_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }, + .base = PM_NODE_INIT(parser, PM_REQUIRED_PARAMETER_NODE, 0, token->start, token->end), .name = pm_parser_constant_id_token(parser, token) }; @@ -6670,14 +5877,7 @@ pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_rescue_modifier_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_modifier_node_t); *node = (pm_rescue_modifier_node_t) { - { - .type = PM_RESCUE_MODIFIER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = expression->location.start, - .end = rescue_expression->location.end - } - }, + .base = PM_NODE_INIT(parser, PM_RESCUE_MODIFIER_NODE, 0, expression->location.start, rescue_expression->location.end), .expression = expression, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .rescue_expression = rescue_expression @@ -6694,11 +5894,7 @@ pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) { pm_rescue_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_node_t); *node = (pm_rescue_node_t) { - { - .type = PM_RESCUE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(keyword) - }, + .base = PM_NODE_INIT(parser, PM_RESCUE_NODE, 0, keyword->start, keyword->end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -6762,14 +5958,7 @@ pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, c pm_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_rest_parameter_node_t); *node = (pm_rest_parameter_node_t) { - { - .type = PM_REST_PARAMETER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end) - } - }, + .base = PM_NODE_INIT(parser, PM_REST_PARAMETER_NODE, 0, operator->start, (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6786,11 +5975,9 @@ pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_RETRY); pm_retry_node_t *node = PM_NODE_ALLOC(parser, pm_retry_node_t); - *node = (pm_retry_node_t) {{ - .type = PM_RETRY_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_retry_node_t) { + .base = PM_NODE_INIT(parser, PM_RETRY_NODE, 0, token->start, token->end) + }; return node; } @@ -6803,14 +5990,7 @@ pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argumen pm_return_node_t *node = PM_NODE_ALLOC(parser, pm_return_node_t); *node = (pm_return_node_t) { - { - .type = PM_RETURN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = (arguments == NULL ? keyword->end : arguments->base.location.end) - } - }, + .base = PM_NODE_INIT(parser, PM_RETURN_NODE, 0, keyword->start, (arguments == NULL ? keyword->end : arguments->base.location.end)), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .arguments = arguments }; @@ -6826,11 +6006,9 @@ pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_SELF); pm_self_node_t *node = PM_NODE_ALLOC(parser, pm_self_node_t); - *node = (pm_self_node_t) {{ - .type = PM_SELF_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_self_node_t) { + .base = PM_NODE_INIT(parser, PM_SELF_NODE, 0, token->start, token->end) + }; return node; } @@ -6843,12 +6021,7 @@ pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shar pm_shareable_constant_node_t *node = PM_NODE_ALLOC(parser, pm_shareable_constant_node_t); *node = (pm_shareable_constant_node_t) { - { - .type = PM_SHAREABLE_CONSTANT_NODE, - .flags = (pm_node_flags_t) value, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NODE_VALUE(write) - }, + .base = PM_NODE_INIT(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) value, write->location.start, write->location.end), .write = write }; @@ -6863,14 +6036,7 @@ pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *local pm_singleton_class_node_t *node = PM_NODE_ALLOC(parser, pm_singleton_class_node_t); *node = (pm_singleton_class_node_t) { - { - .type = PM_SINGLETON_CLASS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = class_keyword->start, - .end = end_keyword->end - } - }, + .base = PM_NODE_INIT(parser, PM_SINGLETON_CLASS_NODE, 0, class_keyword->start, end_keyword->end), .locals = *locals, .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -6890,12 +6056,9 @@ pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD___ENCODING__); pm_source_encoding_node_t *node = PM_NODE_ALLOC(parser, pm_source_encoding_node_t); - *node = (pm_source_encoding_node_t) {{ - .type = PM_SOURCE_ENCODING_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_source_encoding_node_t) { + .base = PM_NODE_INIT(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + }; return node; } @@ -6920,12 +6083,7 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) } *node = (pm_source_file_node_t) { - { - .type = PM_SOURCE_FILE_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(file_keyword), - }, + .base = PM_NODE_INIT(parser, PM_SOURCE_FILE_NODE, flags, file_keyword->start, file_keyword->end), .filepath = parser->filepath }; @@ -6940,12 +6098,9 @@ pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD___LINE__); pm_source_line_node_t *node = PM_NODE_ALLOC(parser, pm_source_line_node_t); - *node = (pm_source_line_node_t) {{ - .type = PM_SOURCE_LINE_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_source_line_node_t) { + .base = PM_NODE_INIT(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + }; return node; } @@ -6958,14 +6113,7 @@ pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t pm_splat_node_t *node = PM_NODE_ALLOC(parser, pm_splat_node_t); *node = (pm_splat_node_t) { - { - .type = PM_SPLAT_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = operator->start, - .end = (expression == NULL ? operator->end : expression->location.end) - } - }, + .base = PM_NODE_INIT(parser, PM_SPLAT_NODE, 0, operator->start, (expression == NULL ? operator->end : expression->location.end)), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .expression = expression }; @@ -6981,11 +6129,7 @@ pm_statements_node_create(pm_parser_t *parser) { pm_statements_node_t *node = PM_NODE_ALLOC(parser, pm_statements_node_t); *node = (pm_statements_node_t) { - { - .type = PM_STATEMENTS_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser) - }, + .base = PM_NODE_INIT(parser, PM_STATEMENTS_NODE, 0, parser->start, parser->start), .body = { 0 } }; @@ -7077,16 +6221,11 @@ pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, break; } + const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start); + const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end); + *node = (pm_string_node_t) { - { - .type = PM_STRING_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start), - .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end) - } - }, + .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, start, end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7129,14 +6268,7 @@ pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument } *node = (pm_super_node_t) { - { - .type = PM_SUPER_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = end, - } - }, + .base = PM_NODE_INIT(parser, PM_SUPER_NODE, 0, keyword->start, end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .lparen_loc = arguments->opening_loc, .arguments = arguments->arguments, @@ -7365,16 +6497,11 @@ static pm_symbol_node_t * pm_symbol_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing, const pm_string_t *unescaped, pm_node_flags_t flags) { pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); + const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start); + const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end); + *node = (pm_symbol_node_t) { - { - .type = PM_SYMBOL_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL | flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start), - .end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end) - } - }, + .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | flags, start, end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .value_loc = PM_LOCATION_TOKEN_VALUE(value), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7448,12 +6575,7 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *node = (pm_symbol_node_t) { - { - .type = PM_SYMBOL_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser) - }, + .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, parser->start, parser->start), .value_loc = PM_LOCATION_NULL_VALUE(parser), .unescaped = { 0 } }; @@ -7491,15 +6613,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_symbol_node_t *new_node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *new_node = (pm_symbol_node_t) { - { - .type = PM_SYMBOL_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - } - }, + .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, closing->end), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .value_loc = node->content_loc, .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7535,12 +6649,7 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { } *new_node = (pm_string_node_t) { - { - .type = PM_STRING_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = node->base.location - }, + .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, node->base.location.start, node->base.location.end), .opening_loc = node->opening_loc, .content_loc = node->value_loc, .closing_loc = node->closing_loc, @@ -7563,12 +6672,9 @@ pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) { assert(token->type == PM_TOKEN_KEYWORD_TRUE); pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); - *node = (pm_true_node_t) {{ - .type = PM_TRUE_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token) - }}; + *node = (pm_true_node_t) { + .base = PM_NODE_INIT(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + }; return node; } @@ -7580,12 +6686,9 @@ static pm_true_node_t * pm_true_node_synthesized_create(pm_parser_t *parser) { pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); - *node = (pm_true_node_t) {{ - .type = PM_TRUE_NODE, - .flags = PM_NODE_FLAG_STATIC_LITERAL, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { .start = parser->start, .end = parser->end } - }}; + *node = (pm_true_node_t) { + .base = PM_NODE_INIT(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, parser->start, parser->end) + }; return node; } @@ -7599,11 +6702,7 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_undef_node_t *node = PM_NODE_ALLOC(parser, pm_undef_node_t); *node = (pm_undef_node_t) { - { - .type = PM_UNDEF_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_TOKEN_VALUE(token), - }, + .base = PM_NODE_INIT(parser, PM_UNDEF_NODE, 0, token->start, token->end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(token), .names = { 0 } }; @@ -7636,15 +6735,7 @@ pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t } *node = (pm_unless_node_t) { - { - .type = PM_UNLESS_NODE, - .flags = PM_NODE_FLAG_NEWLINE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, keyword->start, end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), @@ -7668,15 +6759,7 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_statements_node_body_append(parser, statements, statement, true); *node = (pm_unless_node_t) { - { - .type = PM_UNLESS_NODE, - .flags = PM_NODE_FLAG_NEWLINE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = statement->location.start, - .end = predicate->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement->location.start, predicate->location.end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -7726,15 +6809,7 @@ pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); *node = (pm_until_node_t) { - { - .type = PM_UNTIL_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = closing->end, - }, - }, + .base = PM_NODE_INIT(parser, PM_UNTIL_NODE, flags, keyword->start, closing->end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7755,15 +6830,7 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm pm_loop_modifier_block_exits(parser, statements); *node = (pm_until_node_t) { - { - .type = PM_UNTIL_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = statements->base.location.start, - .end = predicate->location.end, - }, - }, + .base = PM_NODE_INIT(parser, PM_UNTIL_NODE, flags, statements->base.location.start, predicate->location.end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -7782,14 +6849,7 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) { pm_when_node_t *node = PM_NODE_ALLOC(parser, pm_when_node_t); *node = (pm_when_node_t) { - { - .type = PM_WHEN_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = NULL - } - }, + .base = PM_NODE_INIT(parser, PM_WHEN_NODE, 0, keyword->start, NULL), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .statements = NULL, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -7838,15 +6898,7 @@ pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); *node = (pm_while_node_t) { - { - .type = PM_WHILE_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_WHILE_NODE, flags, keyword->start, closing->end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -7867,15 +6919,7 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm pm_loop_modifier_block_exits(parser, statements); *node = (pm_while_node_t) { - { - .type = PM_WHILE_NODE, - .flags = flags, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = statements->base.location.start, - .end = predicate->location.end - }, - }, + .base = PM_NODE_INIT(parser, PM_WHILE_NODE, flags, statements->base.location.start, predicate->location.end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -7894,11 +6938,7 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); *node = (pm_while_node_t) { - { - .type = PM_WHILE_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = PM_LOCATION_NULL_VALUE(parser) - }, + .base = PM_NODE_INIT(parser, PM_WHILE_NODE, 0, parser->start, parser->start), .keyword_loc = PM_LOCATION_NULL_VALUE(parser), .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser), .closing_loc = PM_LOCATION_NULL_VALUE(parser), @@ -7918,15 +6958,7 @@ pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, pm_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_x_string_node_t); *node = (pm_x_string_node_t) { - { - .type = PM_X_STRING_NODE, - .flags = PM_STRING_FLAGS_FROZEN, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = opening->start, - .end = closing->end - }, - }, + .base = PM_NODE_INIT(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, opening->start, closing->end), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -7963,14 +6995,7 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo } *node = (pm_yield_node_t) { - { - .type = PM_YIELD_NODE, - .node_id = PM_NODE_IDENTIFY(parser), - .location = { - .start = keyword->start, - .end = end - }, - }, + .base = PM_NODE_INIT(parser, PM_YIELD_NODE, 0, keyword->start, end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .lparen_loc = *lparen_loc, .arguments = arguments, @@ -7981,7 +7006,6 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo } #undef PM_NODE_ALLOC -#undef PM_NODE_IDENTIFY /** * Check if any of the currently visible scopes contain a local variable From c58970b57a91a10eb75f933258643d0393ce0ba8 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Mon, 1 Dec 2025 17:14:08 +0000 Subject: [PATCH 046/367] ZJIT: Optimize variadic cfunc `Send` calls into `CCallVariadic` (#14898) ZJIT: Optimize variadic cfunc Send calls into CCallVariadic --- test/ruby/test_zjit.rb | 15 +++++++++ zjit/src/codegen.rs | 37 ++++++++++++++++----- zjit/src/hir.rs | 69 ++++++++++++++++++++++++++++----------- zjit/src/hir/opt_tests.rs | 42 +++++++++++++++++------- 4 files changed, 125 insertions(+), 38 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 7472ff77156b95..d821d8ad5cca69 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -525,6 +525,21 @@ def test = [1, 2].map(&:to_s) } end + def test_send_variadic_with_block + assert_compiles '[[1, "a"], [2, "b"], [3, "c"]]', %q{ + A = [1, 2, 3] + B = ["a", "b", "c"] + + def test + result = [] + A.zip(B) { |x, y| result << [x, y] } + result + end + + test; test + }, call_threshold: 2 + end + def test_send_splat assert_runs '[1, 2]', %q{ def test(a, b) = [a, b] diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 66436b2374b4ff..5200894f878d4f 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -431,8 +431,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::CCallWithFrameTooManyArgs), Insn::CCallWithFrame { cfunc, name, args, cme, state, blockiseq, .. } => gen_ccall_with_frame(jit, asm, *cfunc, *name, opnds!(args), *cme, *blockiseq, &function.frame_state(*state)), - Insn::CCallVariadic { cfunc, recv, args, name, cme, state, return_type: _, elidable: _ } => { - gen_ccall_variadic(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state)) + Insn::CCallVariadic { cfunc, recv, args, name, cme, state, blockiseq, .. } => { + gen_ccall_variadic(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *blockiseq, &function.frame_state(*state)) } Insn::GetIvar { self_val, id, ic, state: _ } => gen_getivar(jit, asm, opnd!(self_val), *id, *ic), Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))), @@ -845,26 +845,47 @@ fn gen_ccall_variadic( recv: Opnd, args: Vec, cme: *const rb_callable_method_entry_t, + blockiseq: Option, state: &FrameState, ) -> lir::Opnd { gen_incr_counter(asm, Counter::variadic_cfunc_optimized_send_count); + gen_stack_overflow_check(jit, asm, state, state.stack_size()); - gen_prepare_non_leaf_call(jit, asm, state); + let args_with_recv_len = args.len() + 1; - let stack_growth = state.stack_size(); - gen_stack_overflow_check(jit, asm, state, stack_growth); + // Compute the caller's stack size after consuming recv and args. + // state.stack() includes recv + args, so subtract both. + let caller_stack_size = state.stack_size() - args_with_recv_len; - gen_push_frame(asm, args.len(), state, ControlFrame { + // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP + // to account for the receiver and arguments (like gen_ccall_with_frame does) + gen_prepare_call_with_gc(asm, state, false); + gen_save_sp(asm, caller_stack_size); + gen_spill_stack(jit, asm, state); + gen_spill_locals(jit, asm, state); + + let block_handler_specval = if let Some(block_iseq) = blockiseq { + // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block(). + // VM_CFP_TO_CAPTURED_BLOCK then turns &cfp->self into a block handler. + // rb_captured_block->code.iseq aliases with cfp->block_code. + asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE), VALUE::from(block_iseq).into()); + let cfp_self_addr = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)); + asm.or(cfp_self_addr, Opnd::Imm(1)) + } else { + VM_BLOCK_HANDLER_NONE.into() + }; + + gen_push_frame(asm, args_with_recv_len, state, ControlFrame { recv, iseq: None, cme, frame_type: VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL, - specval: VM_BLOCK_HANDLER_NONE.into(), + specval: block_handler_specval, pc: PC_POISON, }); asm_comment!(asm, "switch to new SP register"); - let sp_offset = (state.stack().len() - args.len() + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE; + let sp_offset = (caller_stack_size + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE; let new_sp = asm.add(SP, sp_offset.into()); asm.mov(SP, new_sp); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index fbc9d80700dcaf..8c1ec664d03a5f 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -804,6 +804,7 @@ pub enum Insn { state: InsnId, return_type: Type, elidable: bool, + blockiseq: Option, }, /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions @@ -1920,8 +1921,8 @@ impl Function { elidable, blockiseq, }, - &CCallVariadic { cfunc, recv, ref args, cme, name, state, return_type, elidable } => CCallVariadic { - cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state, return_type, elidable + &CCallVariadic { cfunc, recv, ref args, cme, name, state, return_type, elidable, blockiseq } => CCallVariadic { + cfunc, recv: find!(recv), args: find_vec!(args), cme, name, state, return_type, elidable, blockiseq }, &Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) }, &DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state }, @@ -2989,9 +2990,23 @@ impl Function { return Err(()); } - // Find the `argc` (arity) of the C method, which describes the parameters it expects + let ci_flags = unsafe { vm_ci_flag(call_info) }; + + // When seeing &block argument, fall back to dynamic dispatch for now + // TODO: Support block forwarding + if unspecializable_call_type(ci_flags) { + fun.count_complex_call_features(block, ci_flags); + fun.set_dynamic_send_reason(send_insn_id, ComplexArgPass); + return Err(()); + } + + let blockiseq = if blockiseq.is_null() { None } else { Some(blockiseq) }; + let cfunc = unsafe { get_cme_def_body_cfunc(cme) }; + // Find the `argc` (arity) of the C method, which describes the parameters it expects let cfunc_argc = unsafe { get_mct_argc(cfunc) }; + let cfunc_ptr = unsafe { get_mct_func(cfunc) }.cast(); + match cfunc_argc { 0.. => { // (self, arg0, arg1, ..., argc) form @@ -3001,16 +3016,6 @@ impl Function { return Err(()); } - let ci_flags = unsafe { vm_ci_flag(call_info) }; - - // When seeing &block argument, fall back to dynamic dispatch for now - // TODO: Support block forwarding - if unspecializable_call_type(ci_flags) { - fun.count_complex_call_features(block, ci_flags); - fun.set_dynamic_send_reason(send_insn_id, ComplexArgPass); - return Err(()); - } - // Commit to the replacement. Put PatchPoint. fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); if recv_class.instance_can_have_singleton_class() { @@ -3023,17 +3028,14 @@ impl Function { fun.insn_types[recv.0] = fun.infer_type(recv); } - let blockiseq = if blockiseq.is_null() { None } else { Some(blockiseq) }; - // Emit a call - let cfunc = unsafe { get_mct_func(cfunc) }.cast(); let mut cfunc_args = vec![recv]; cfunc_args.append(&mut args); let name = rust_str_to_id(&qualified_method_name(unsafe { (*cme).owner }, unsafe { (*cme).called_id })); let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, - cfunc, + cfunc: cfunc_ptr, args: cfunc_args, cme, name, @@ -3047,9 +3049,37 @@ impl Function { } // Variadic method -1 => { + // The method gets a pointer to the first argument // func(int argc, VALUE *argv, VALUE recv) - fun.set_dynamic_send_reason(send_insn_id, SendCfuncVariadic); - Err(()) + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); + + if recv_class.instance_can_have_singleton_class() { + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); + } + if let Some(profiled_type) = profiled_type { + // Guard receiver class + recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + fun.insn_types[recv.0] = fun.infer_type(recv); + } + + if get_option!(stats) { + count_not_inlined_cfunc(fun, block, cme); + } + + let ccall = fun.push_insn(block, Insn::CCallVariadic { + cfunc: cfunc_ptr, + recv, + args, + cme, + name: method_id, + state, + return_type: types::BasicObject, + elidable: false, + blockiseq + }); + + fun.make_equal_to(send_insn_id, ccall); + Ok(()) } -2 => { // (self, args_ruby_array) @@ -3252,6 +3282,7 @@ impl Function { state, return_type, elidable, + blockiseq: None, }); fun.make_equal_to(send_insn_id, ccall); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index b32da5a9ebb8e5..26dad38b580f0a 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5302,26 +5302,46 @@ mod hir_opt_tests { } #[test] - fn test_do_not_optimize_send_variadic_with_block() { + fn test_optimize_send_variadic_with_block() { eval(r#" - def test = [1, 2, 3].index { |x| x == 2 } + A = [1, 2, 3] + B = ["a", "b", "c"] + + def test + result = [] + A.zip(B) { |x, y| result << [x, y] } + result + end + test; test "#); assert_snapshot!(hir_string("test"), @r" - fn test@:2: + fn test@:6: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - Jump bb2(v1) - bb1(v4:BasicObject): + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): EntryPoint JIT(0) - Jump bb2(v4) - bb2(v6:BasicObject): - v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v11:ArrayExact = ArrayDup v10 - v13:BasicObject = Send v11, 0x1008, :index + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:ArrayExact = NewArray + SetLocal l0, EP@3, v13 + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, A) + v36:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1010, B) + v39:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) + PatchPoint NoSingletonClass(Array@0x1020) + v43:BasicObject = CCallVariadic zip@0x1058, v36, v39 + v25:BasicObject = GetLocal l0, EP@3 + v29:BasicObject = GetLocal l0, EP@3 CheckInterrupts - Return v13 + Return v29 "); } From e02eda194f1d1ff6998b5eb462dd2a2afc54281c Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Mon, 1 Dec 2025 12:55:39 -0500 Subject: [PATCH 047/367] Speedup RBASIC_FIELDS_COUNT (#15273) We know the argument is not a class, module or special const, so we can skip these checks. --- shape.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shape.h b/shape.h index ec5c25b32f16f5..d9cfe48759c333 100644 --- a/shape.h +++ b/shape.h @@ -395,7 +395,7 @@ ROBJECT_FIELDS_COUNT(VALUE obj) static inline uint32_t RBASIC_FIELDS_COUNT(VALUE obj) { - return RSHAPE(rb_obj_shape_id(obj))->next_field_index; + return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index; } static inline bool From 5f92d6da1e48ce62205858cdcc7e60108e585f5f Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Mon, 1 Dec 2025 18:01:12 +0000 Subject: [PATCH 048/367] ZJIT: Standardize method dispatch insns' `recv` field (#15334) ZJIT: Standardize C call related insn fields - Add `recv` field to `CCall` and `CCallWithFrame` so now all method dispatch related instructions have `recv` field, separate from `args` field. This ensures consistent pointer arithmetic when generating code for these instructions. - Standardize `recv` field's display position in send related instructions. --- zjit/src/codegen.rs | 31 ++++++----- zjit/src/cruby_methods.rs | 3 +- zjit/src/hir.rs | 58 +++++++++++---------- zjit/src/hir/opt_tests.rs | 106 +++++++++++++++++++------------------- zjit/src/hir/tests.rs | 2 +- 5 files changed, 106 insertions(+), 94 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 5200894f878d4f..f77b8cc4bf2ca6 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -425,13 +425,14 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), &Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))), - Insn::CCall { cfunc, args, name, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnds!(args)), - // Give up CCallWithFrame for 7+ args since asm.ccall() doesn't support it. - Insn::CCallWithFrame { cd, state, args, .. } if args.len() > C_ARG_OPNDS.len() => + Insn::CCall { cfunc, recv, args, name, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnd!(recv), opnds!(args)), + // Give up CCallWithFrame for 7+ args since asm.ccall() supports at most 6 args (recv + args). + // There's no test case for this because no core cfuncs have this many parameters. But C extensions could have such methods. + Insn::CCallWithFrame { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::CCallWithFrameTooManyArgs), - Insn::CCallWithFrame { cfunc, name, args, cme, state, blockiseq, .. } => - gen_ccall_with_frame(jit, asm, *cfunc, *name, opnds!(args), *cme, *blockiseq, &function.frame_state(*state)), - Insn::CCallVariadic { cfunc, recv, args, name, cme, state, blockiseq, .. } => { + Insn::CCallWithFrame { cfunc, recv, name, args, cme, state, blockiseq, .. } => + gen_ccall_with_frame(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *blockiseq, &function.frame_state(*state)), + Insn::CCallVariadic { cfunc, recv, name, args, cme, state, blockiseq, return_type: _, elidable: _ } => { gen_ccall_variadic(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *blockiseq, &function.frame_state(*state)) } Insn::GetIvar { self_val, id, ic, state: _ } => gen_getivar(jit, asm, opnd!(self_val), *id, *ic), @@ -766,6 +767,7 @@ fn gen_ccall_with_frame( asm: &mut Assembler, cfunc: *const u8, name: ID, + recv: Opnd, args: Vec, cme: *const rb_callable_method_entry_t, blockiseq: Option, @@ -774,7 +776,8 @@ fn gen_ccall_with_frame( gen_incr_counter(asm, Counter::non_variadic_cfunc_optimized_send_count); gen_stack_overflow_check(jit, asm, state, state.stack_size()); - let caller_stack_size = state.stack_size() - args.len(); + let args_with_recv_len = args.len() + 1; + let caller_stack_size = state.stack().len() - args_with_recv_len; // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP // to account for the receiver and arguments (and block arguments if any) @@ -794,8 +797,8 @@ fn gen_ccall_with_frame( VM_BLOCK_HANDLER_NONE.into() }; - gen_push_frame(asm, args.len(), state, ControlFrame { - recv: args[0], + gen_push_frame(asm, args_with_recv_len, state, ControlFrame { + recv, iseq: None, cme, frame_type: VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL, @@ -813,8 +816,10 @@ fn gen_ccall_with_frame( asm.mov(CFP, new_cfp); asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP); + let mut cfunc_args = vec![recv]; + cfunc_args.extend(args); asm.count_call_to(&name.contents_lossy()); - let result = asm.ccall(cfunc, args); + let result = asm.ccall(cfunc, cfunc_args); asm_comment!(asm, "pop C frame"); let new_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into()); @@ -830,9 +835,11 @@ fn gen_ccall_with_frame( /// Lowering for [`Insn::CCall`]. This is a low-level raw call that doesn't know /// anything about the callee, so handling for e.g. GC safety is dealt with elsewhere. -fn gen_ccall(asm: &mut Assembler, cfunc: *const u8, name: ID, args: Vec) -> lir::Opnd { +fn gen_ccall(asm: &mut Assembler, cfunc: *const u8, name: ID, recv: Opnd, args: Vec) -> lir::Opnd { + let mut cfunc_args = vec![recv]; + cfunc_args.extend(args); asm.count_call_to(&name.contents_lossy()); - asm.ccall(cfunc, args) + asm.ccall(cfunc, cfunc_args) } /// Generate code for a variadic C function call diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 23c05e6d58fb54..56d4de280bff71 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -440,7 +440,8 @@ fn inline_string_eq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::Ins // TODO(max): Make StringEqual its own opcode so that we can later constant-fold StringEqual(a, a) => true let result = fun.push_insn(block, hir::Insn::CCall { cfunc: rb_yarv_str_eql_internal as *const u8, - args: vec![recv, other], + recv, + args: vec![other], name: ID!(string_eq), return_type, elidable, diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 8c1ec664d03a5f..e5c1f7a54293da 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -778,12 +778,13 @@ pub enum Insn { /// Call a C function without pushing a frame /// `name` is for printing purposes only - CCall { cfunc: *const u8, args: Vec, name: ID, return_type: Type, elidable: bool }, + CCall { cfunc: *const u8, recv: InsnId, args: Vec, name: ID, return_type: Type, elidable: bool }, /// Call a C function that pushes a frame CCallWithFrame { cd: *const rb_call_data, // cd for falling back to SendWithoutBlock cfunc: *const u8, + recv: InsnId, args: Vec, cme: *const rb_callable_method_entry_t, name: ID, @@ -1180,8 +1181,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } - Insn::SendForward { cd, args, blockiseq, .. } => { - write!(f, "SendForward {:p}, :{}", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd))?; + Insn::SendForward { recv, cd, args, blockiseq, .. } => { + write!(f, "SendForward {recv}, {:p}, :{}", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd))?; for arg in args { write!(f, ", {arg}")?; } @@ -1240,15 +1241,15 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) }, Insn::IsBlockGiven => { write!(f, "IsBlockGiven") }, Insn::FixnumBitCheck {val, index} => { write!(f, "FixnumBitCheck {val}, {index}") }, - Insn::CCall { cfunc, args, name, return_type: _, elidable: _ } => { - write!(f, "CCall {}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; + Insn::CCall { cfunc, recv, args, name, return_type: _, elidable: _ } => { + write!(f, "CCall {recv}, :{}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; for arg in args { write!(f, ", {arg}")?; } Ok(()) }, - Insn::CCallWithFrame { cfunc, args, name, blockiseq, .. } => { - write!(f, "CCallWithFrame {}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; + Insn::CCallWithFrame { cfunc, recv, args, name, blockiseq, .. } => { + write!(f, "CCallWithFrame {recv}, :{}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; for arg in args { write!(f, ", {arg}")?; } @@ -1257,8 +1258,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) }, - Insn::CCallVariadic { cfunc, recv, args, name, .. } => { - write!(f, "CCallVariadic {}@{:p}, {recv}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; + Insn::CCallVariadic { cfunc, recv, args, name, .. } => { + write!(f, "CCallVariadic {recv}, :{}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?; for arg in args { write!(f, ", {arg}")?; } @@ -1909,10 +1910,11 @@ impl Function { &HashAref { hash, key, state } => HashAref { hash: find!(hash), key: find!(key), state }, &ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state }, &ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) }, - &CCall { cfunc, ref args, name, return_type, elidable } => CCall { cfunc, args: find_vec!(args), name, return_type, elidable }, - &CCallWithFrame { cd, cfunc, ref args, cme, name, state, return_type, elidable, blockiseq } => CCallWithFrame { + &CCall { cfunc, recv, ref args, name, return_type, elidable } => CCall { cfunc, recv: find!(recv), args: find_vec!(args), name, return_type, elidable }, + &CCallWithFrame { cd, cfunc, recv, ref args, cme, name, state, return_type, elidable, blockiseq } => CCallWithFrame { cd, cfunc, + recv: find!(recv), args: find_vec!(args), cme, name, @@ -2959,7 +2961,7 @@ impl Function { send: Insn, send_insn_id: InsnId, ) -> Result<(), ()> { - let Insn::Send { mut recv, cd, blockiseq, mut args, state, .. } = send else { + let Insn::Send { mut recv, cd, blockiseq, args, state, .. } = send else { return Err(()); }; @@ -3029,14 +3031,14 @@ impl Function { } // Emit a call - let mut cfunc_args = vec![recv]; - cfunc_args.append(&mut args); + let cfunc = unsafe { get_mct_func(cfunc) }.cast(); let name = rust_str_to_id(&qualified_method_name(unsafe { (*cme).owner }, unsafe { (*cme).called_id })); let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, - cfunc: cfunc_ptr, - args: cfunc_args, + cfunc, + recv, + args, cme, name, state, @@ -3098,7 +3100,7 @@ impl Function { send: Insn, send_insn_id: InsnId, ) -> Result<(), ()> { - let Insn::SendWithoutBlock { mut recv, cd, mut args, state, .. } = send else { + let Insn::SendWithoutBlock { mut recv, cd, args, state, .. } = send else { return Err(()); }; @@ -3191,14 +3193,12 @@ impl Function { // No inlining; emit a call let cfunc = unsafe { get_mct_func(cfunc) }.cast(); let name = rust_str_to_id(&qualified_method_name(unsafe { (*cme).owner }, unsafe { (*cme).called_id })); - let mut cfunc_args = vec![recv]; - cfunc_args.append(&mut args); let return_type = props.return_type; let elidable = props.elidable; // Filter for a leaf and GC free function if props.leaf && props.no_gc { fun.push_insn(block, Insn::IncrCounter(Counter::inline_cfunc_optimized_send_count)); - let ccall = fun.push_insn(block, Insn::CCall { cfunc, args: cfunc_args, name, return_type, elidable }); + let ccall = fun.push_insn(block, Insn::CCall { cfunc, recv, args, name, return_type, elidable }); fun.make_equal_to(send_insn_id, ccall); } else { if get_option!(stats) { @@ -3207,7 +3207,8 @@ impl Function { let ccall = fun.push_insn(block, Insn::CCallWithFrame { cd, cfunc, - args: cfunc_args, + recv, + args, cme, name, state, @@ -3662,19 +3663,22 @@ impl Function { | &Insn::SendForward { recv, ref args, state, .. } | &Insn::SendWithoutBlock { recv, ref args, state, .. } | &Insn::CCallVariadic { recv, ref args, state, .. } + | &Insn::CCallWithFrame { recv, ref args, state, .. } | &Insn::SendWithoutBlockDirect { recv, ref args, state, .. } | &Insn::InvokeSuper { recv, ref args, state, .. } => { worklist.push_back(recv); worklist.extend(args); worklist.push_back(state); } - &Insn::CCallWithFrame { ref args, state, .. } - | &Insn::InvokeBuiltin { ref args, state, .. } + &Insn::InvokeBuiltin { ref args, state, .. } | &Insn::InvokeBlock { ref args, state, .. } => { worklist.extend(args); worklist.push_back(state) } - Insn::CCall { args, .. } => worklist.extend(args), + &Insn::CCall { recv, ref args, .. } => { + worklist.push_back(recv); + worklist.extend(args); + } &Insn::GetIvar { self_val, state, .. } | &Insn::DefinedIvar { self_val, state, .. } => { worklist.push_back(self_val); worklist.push_back(state); @@ -4228,7 +4232,6 @@ impl Function { | Insn::IncrCounter { .. } | Insn::IncrCounterPtr { .. } | Insn::CheckInterrupts { .. } - | Insn::CCall { .. } | Insn::GetClassVar { .. } | Insn::GetSpecialNumber { .. } | Insn::GetSpecialSymbol { .. } @@ -4276,6 +4279,8 @@ impl Function { | Insn::Send { recv, ref args, .. } | Insn::SendForward { recv, ref args, .. } | Insn::InvokeSuper { recv, ref args, .. } + | Insn::CCall { recv, ref args, .. } + | Insn::CCallWithFrame { recv, ref args, .. } | Insn::CCallVariadic { recv, ref args, .. } | Insn::ArrayInclude { target: recv, elements: ref args, .. } => { self.assert_subtype(insn_id, recv, types::BasicObject)?; @@ -4285,8 +4290,7 @@ impl Function { Ok(()) } // Instructions with a Vec of Ruby objects - Insn::CCallWithFrame { ref args, .. } - | Insn::InvokeBuiltin { ref args, .. } + Insn::InvokeBuiltin { ref args, .. } | Insn::InvokeBlock { ref args, .. } | Insn::NewArray { elements: ref args, .. } | Insn::ArrayHash { elements: ref args, .. } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 26dad38b580f0a..e8c8d50dbe9256 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -560,7 +560,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(CustomEq@0x1000) v28:HeapObject[class_exact:CustomEq] = GuardType v9, HeapObject[class_exact:CustomEq] - v29:BoolExact = CCallWithFrame BasicObject#!=@0x1038, v28, v9 + v29:BoolExact = CCallWithFrame v28, :BasicObject#!=@0x1038, v9 v20:NilClass = Const Value(nil) CheckInterrupts Return v20 @@ -783,7 +783,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] - v24:BasicObject = CCallWithFrame C#fun_new_map@0x1038, v23, block=0x1040 + v24:BasicObject = CCallWithFrame v23, :C#fun_new_map@0x1038, block=0x1040 v15:BasicObject = GetLocal l0, EP@3 CheckInterrupts Return v24 @@ -1042,7 +1042,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Object@0x1008) v22:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] - v23:BasicObject = CCallVariadic Kernel#puts@0x1040, v22, v12 + v23:BasicObject = CCallVariadic v22, :Kernel#puts@0x1040, v12 CheckInterrupts Return v23 "); @@ -2240,7 +2240,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) IncrCounter inline_cfunc_optimized_send_count - v34:StringExact|NilClass = CCall Module#name@0x1048, v29 + v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) v22:Fixnum[1] = Const Value(1) CheckInterrupts @@ -2272,7 +2272,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) IncrCounter inline_cfunc_optimized_send_count - v29:Fixnum = CCall Array#length@0x1038, v13 + v29:Fixnum = CCall v13, :Array#length@0x1038 v20:Fixnum[5] = Const Value(5) CheckInterrupts Return v20 @@ -2416,7 +2416,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) IncrCounter inline_cfunc_optimized_send_count - v29:Fixnum = CCall Array#size@0x1038, v13 + v29:Fixnum = CCall v13, :Array#size@0x1038 v20:Fixnum[5] = Const Value(5) CheckInterrupts Return v20 @@ -3149,7 +3149,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) - v57:BasicObject = CCallVariadic Array.new@0x1040, v46, v16 + v57:BasicObject = CCallVariadic v46, :Array.new@0x1040, v16 CheckInterrupts Return v57 "); @@ -3180,7 +3180,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(Set@0x1008) v49:SetExact = GuardType v18, SetExact - v50:BasicObject = CCallVariadic Set#initialize@0x1068, v49 + v50:BasicObject = CCallVariadic v49, :Set#initialize@0x1068 CheckInterrupts CheckInterrupts Return v18 @@ -3210,7 +3210,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) - v54:BasicObject = CCallVariadic String.new@0x1040, v43 + v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts Return v54 "); @@ -3242,7 +3242,7 @@ mod hir_opt_tests { v50:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) PatchPoint NoSingletonClass(Regexp@0x1008) - v54:BasicObject = CCallVariadic Regexp#initialize@0x1078, v50, v17 + v54:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v17 CheckInterrupts CheckInterrupts Return v50 @@ -3270,7 +3270,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) IncrCounter inline_cfunc_optimized_send_count - v30:Fixnum = CCall Array#length@0x1038, v18 + v30:Fixnum = CCall v18, :Array#length@0x1038 CheckInterrupts Return v30 "); @@ -3297,7 +3297,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) IncrCounter inline_cfunc_optimized_send_count - v30:Fixnum = CCall Array#size@0x1038, v18 + v30:Fixnum = CCall v18, :Array#size@0x1038 CheckInterrupts Return v30 "); @@ -3717,7 +3717,7 @@ mod hir_opt_tests { v10:HashExact = NewHash PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) - v22:BasicObject = CCallWithFrame Kernel#dup@0x1038, v10 + v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = SendWithoutBlock v22, :freeze CheckInterrupts Return v14 @@ -3810,7 +3810,7 @@ mod hir_opt_tests { v10:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) - v22:BasicObject = CCallWithFrame Kernel#dup@0x1038, v10 + v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = SendWithoutBlock v22, :freeze CheckInterrupts Return v14 @@ -3904,7 +3904,7 @@ mod hir_opt_tests { v11:StringExact = StringCopy v10 PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) - v23:BasicObject = CCallWithFrame String#dup@0x1040, v11 + v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = SendWithoutBlock v23, :freeze CheckInterrupts Return v15 @@ -3999,7 +3999,7 @@ mod hir_opt_tests { v11:StringExact = StringCopy v10 PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) - v23:BasicObject = CCallWithFrame String#dup@0x1040, v11 + v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = SendWithoutBlock v23, :-@ CheckInterrupts Return v15 @@ -4141,7 +4141,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) v31:ArrayExact = GuardType v9, ArrayExact - v32:BasicObject = CCallWithFrame Array#to_s@0x1040, v31 + v32:BasicObject = CCallWithFrame v31, :Array#to_s@0x1040 v19:String = AnyToString v9, str: v32 v21:StringExact = StringConcat v13, v19 CheckInterrupts @@ -5004,7 +5004,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1000) v23:ArrayExact = GuardType v9, ArrayExact IncrCounter inline_cfunc_optimized_send_count - v25:BoolExact = CCall Array#empty?@0x1038, v23 + v25:BoolExact = CCall v23, :Array#empty?@0x1038 CheckInterrupts Return v25 "); @@ -5032,7 +5032,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Hash@0x1000) v23:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count - v25:BoolExact = CCall Hash#empty?@0x1038, v23 + v25:BoolExact = CCall v23, :Hash#empty?@0x1038 CheckInterrupts Return v25 "); @@ -5295,7 +5295,7 @@ mod hir_opt_tests { v11:ArrayExact = ArrayDup v10 PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v21:BasicObject = CCallWithFrame Array#map@0x1040, v11, block=0x1048 + v21:BasicObject = CCallWithFrame v11, :Array#map@0x1040, block=0x1048 CheckInterrupts Return v21 "); @@ -5337,7 +5337,7 @@ mod hir_opt_tests { v39:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) PatchPoint NoSingletonClass(Array@0x1020) - v43:BasicObject = CCallVariadic zip@0x1058, v36, v39 + v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39 v25:BasicObject = GetLocal l0, EP@3 v29:BasicObject = GetLocal l0, EP@3 CheckInterrupts @@ -5765,7 +5765,7 @@ mod hir_opt_tests { v10:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) - v20:ArrayExact = CCallWithFrame Array#reverse@0x1038, v10 + v20:ArrayExact = CCallWithFrame v10, :Array#reverse@0x1038 CheckInterrupts Return v20 "); @@ -5818,7 +5818,7 @@ mod hir_opt_tests { v13:StringExact = StringCopy v12 PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v23:StringExact = CCallVariadic Array#join@0x1040, v10, v13 + v23:StringExact = CCallVariadic v10, :Array#join@0x1040, v13 CheckInterrupts Return v23 "); @@ -6172,7 +6172,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) v31:ArrayExact = GuardType v9, ArrayExact - v32:BasicObject = CCallVariadic Array#[]=@0x1038, v31, v16, v18 + v32:BasicObject = CCallVariadic v31, :Array#[]=@0x1038, v16, v18 CheckInterrupts Return v18 "); @@ -6263,7 +6263,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) v28:ArrayExact = GuardType v9, ArrayExact - v29:BasicObject = CCallVariadic Array#push@0x1038, v28, v14, v16, v18 + v29:BasicObject = CCallVariadic v28, :Array#push@0x1038, v14, v16, v18 CheckInterrupts Return v29 "); @@ -6291,7 +6291,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1000) v23:ArrayExact = GuardType v9, ArrayExact IncrCounter inline_cfunc_optimized_send_count - v25:Fixnum = CCall Array#length@0x1038, v23 + v25:Fixnum = CCall v23, :Array#length@0x1038 CheckInterrupts Return v25 "); @@ -6319,7 +6319,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1000) v23:ArrayExact = GuardType v9, ArrayExact IncrCounter inline_cfunc_optimized_send_count - v25:Fixnum = CCall Array#size@0x1038, v23 + v25:Fixnum = CCall v23, :Array#size@0x1038 CheckInterrupts Return v25 "); @@ -6347,7 +6347,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) v25:StringExact = GuardType v9, StringExact - v26:BasicObject = CCallWithFrame String#=~@0x1040, v25, v14 + v26:BasicObject = CCallWithFrame v25, :String#=~@0x1040, v14 CheckInterrupts Return v26 "); @@ -6518,7 +6518,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) v30:StringExact = GuardType v13, StringExact - v31:BasicObject = CCallWithFrame String#setbyte@0x1038, v30, v14, v15 + v31:BasicObject = CCallWithFrame v30, :String#setbyte@0x1038, v14, v15 CheckInterrupts Return v31 "); @@ -6634,7 +6634,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, succ@0x1008, cme:0x1010) v22:Integer = GuardType v9, Integer - v23:BasicObject = CCallWithFrame Integer#succ@0x1038, v22 + v23:BasicObject = CCallWithFrame v22, :Integer#succ@0x1038 CheckInterrupts Return v23 "); @@ -6689,7 +6689,7 @@ mod hir_opt_tests { v14:Fixnum[-5] = Const Value(-5) PatchPoint MethodRedefined(Integer@0x1000, <<@0x1008, cme:0x1010) v24:Fixnum = GuardType v9, Fixnum - v25:BasicObject = CCallWithFrame Integer#<<@0x1038, v24, v14 + v25:BasicObject = CCallWithFrame v24, :Integer#<<@0x1038, v14 CheckInterrupts Return v25 "); @@ -6716,7 +6716,7 @@ mod hir_opt_tests { v14:Fixnum[64] = Const Value(64) PatchPoint MethodRedefined(Integer@0x1000, <<@0x1008, cme:0x1010) v24:Fixnum = GuardType v9, Fixnum - v25:BasicObject = CCallWithFrame Integer#<<@0x1038, v24, v14 + v25:BasicObject = CCallWithFrame v24, :Integer#<<@0x1038, v14 CheckInterrupts Return v25 "); @@ -6743,7 +6743,7 @@ mod hir_opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, <<@0x1008, cme:0x1010) v26:Fixnum = GuardType v11, Fixnum - v27:BasicObject = CCallWithFrame Integer#<<@0x1038, v26, v12 + v27:BasicObject = CCallWithFrame v26, :Integer#<<@0x1038, v12 CheckInterrupts Return v27 "); @@ -6800,7 +6800,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) v27:StringExact = GuardType v11, StringExact - v28:BasicObject = CCallWithFrame String#<<@0x1038, v27, v12 + v28:BasicObject = CCallWithFrame v27, :String#<<@0x1038, v12 CheckInterrupts Return v28 "); @@ -6860,7 +6860,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyString@0x1000) v27:StringSubclass[class_exact:MyString] = GuardType v11, StringSubclass[class_exact:MyString] - v28:BasicObject = CCallWithFrame String#<<@0x1038, v27, v12 + v28:BasicObject = CCallWithFrame v27, :String#<<@0x1038, v12 CheckInterrupts Return v28 "); @@ -7017,7 +7017,7 @@ mod hir_opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) v25:Integer = GuardType v11, Integer - v26:BasicObject = CCallWithFrame Integer#^@0x1038, v25, v12 + v26:BasicObject = CCallWithFrame v25, :Integer#^@0x1038, v12 CheckInterrupts Return v26 "); @@ -7040,7 +7040,7 @@ mod hir_opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) v25:Fixnum = GuardType v11, Fixnum - v26:BasicObject = CCallWithFrame Integer#^@0x1038, v25, v12 + v26:BasicObject = CCallWithFrame v25, :Integer#^@0x1038, v12 CheckInterrupts Return v26 "); @@ -7063,7 +7063,7 @@ mod hir_opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint MethodRedefined(TrueClass@0x1000, ^@0x1008, cme:0x1010) v25:TrueClass = GuardType v11, TrueClass - v26:BasicObject = CCallWithFrame TrueClass#^@0x1038, v25, v12 + v26:BasicObject = CCallWithFrame v25, :TrueClass#^@0x1038, v12 CheckInterrupts Return v26 "); @@ -7113,7 +7113,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Hash@0x1000) v23:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count - v25:Fixnum = CCall Hash#size@0x1038, v23 + v25:Fixnum = CCall v23, :Hash#size@0x1038 CheckInterrupts Return v25 "); @@ -7481,7 +7481,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v25:BasicObject = CCallVariadic Kernel#respond_to?@0x1040, v24, v14 + v25:BasicObject = CCallVariadic v24, :Kernel#respond_to?@0x1040, v14 CheckInterrupts Return v25 "); @@ -7849,7 +7849,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String - v29:BoolExact = CCall String#==@0x1038, v27, v28 + v29:BoolExact = CCall v27, :String#==@0x1038, v28 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v29 @@ -7880,7 +7880,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1000) v27:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v28:String = GuardType v12, String - v29:BoolExact = CCall String#==@0x1038, v27, v28 + v29:BoolExact = CCall v27, :String#==@0x1038, v28 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v29 @@ -7911,7 +7911,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String - v29:BoolExact = CCall String#==@0x1038, v27, v28 + v29:BoolExact = CCall v27, :String#==@0x1038, v28 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v29 @@ -7940,7 +7940,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v26:StringExact = GuardType v11, StringExact v27:String = GuardType v12, String - v28:BoolExact = CCall String#==@0x1038, v26, v27 + v28:BoolExact = CCall v26, :String#==@0x1038, v27 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v28 @@ -7971,7 +7971,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1000) v26:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v27:String = GuardType v12, String - v28:BoolExact = CCall String#==@0x1038, v26, v27 + v28:BoolExact = CCall v26, :String#==@0x1038, v27 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v28 @@ -8002,7 +8002,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v26:StringExact = GuardType v11, StringExact v27:String = GuardType v12, String - v28:BoolExact = CCall String#==@0x1038, v26, v27 + v28:BoolExact = CCall v26, :String#==@0x1038, v27 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v28 @@ -8032,7 +8032,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count - v25:Fixnum = CCall String#size@0x1038, v23 + v25:Fixnum = CCall v23, :String#size@0x1038 CheckInterrupts Return v25 "); @@ -8151,7 +8151,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count - v25:Fixnum = CCall String#length@0x1038, v23 + v25:Fixnum = CCall v23, :String#length@0x1038 CheckInterrupts Return v25 "); @@ -8211,7 +8211,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) IncrCounter inline_cfunc_optimized_send_count - v31:BoolExact = CCall Module#===@0x1048, v26, v9 + v31:BoolExact = CCall v26, :Module#===@0x1048, v9 CheckInterrupts Return v31 "); @@ -8270,7 +8270,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) v28:StringExact = GuardType v9, StringExact - v29:BasicObject = CCallWithFrame Kernel#is_a?@0x1048, v28, v24 + v29:BasicObject = CCallWithFrame v28, :Kernel#is_a?@0x1048, v24 CheckInterrupts Return v29 "); @@ -8395,7 +8395,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) v28:StringExact = GuardType v9, StringExact - v29:BasicObject = CCallWithFrame Kernel#kind_of?@0x1048, v28, v24 + v29:BasicObject = CCallWithFrame v28, :Kernel#kind_of?@0x1048, v24 CheckInterrupts Return v29 "); @@ -8594,7 +8594,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Class@0x1038) v30:ModuleSubclass[class_exact*:Class@VALUE(0x1038)] = GuardType v26, ModuleSubclass[class_exact*:Class@VALUE(0x1038)] IncrCounter inline_cfunc_optimized_send_count - v32:StringExact|NilClass = CCall Module#name@0x1070, v30 + v32:StringExact|NilClass = CCall v30, :Module#name@0x1070 CheckInterrupts Return v32 "); @@ -8644,7 +8644,7 @@ mod hir_opt_tests { v45:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) PatchPoint MethodRedefined(Class@0x1008, lambda@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Class@0x1008) - v60:BasicObject = CCallWithFrame RubyVM::FrozenCore.lambda@0x1040, v45, block=0x1048 + v60:BasicObject = CCallWithFrame v45, :RubyVM::FrozenCore.lambda@0x1040, block=0x1048 v48:BasicObject = GetLocal l0, EP@6 v49:BasicObject = GetLocal l0, EP@5 v50:BasicObject = GetLocal l0, EP@4 diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index f8a6abc2bcbc86..314eb7777cbf6c 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -1834,7 +1834,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendForward 0x1000, :foo, v9 + v15:BasicObject = SendForward v8, 0x1000, :foo, v9 CheckInterrupts Return v15 "); From f92001344d0595bb6ef3a9576853edf1fa7588ca Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Mon, 1 Dec 2025 13:02:35 -0500 Subject: [PATCH 049/367] ZJIT: Fix erroneous version number for Iongraph (#15357) As per https://github.com/mozilla-spidermonkey/iongraph/blob/8d5e531305320216f86a24bfc9bc136a3627e832/src/iongraph.ts#L147, correct version number for the web-based tool is 1, rather than 2. --- doc/jit/zjit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/jit/zjit.md b/doc/jit/zjit.md index bb20b9f6924bac..f3b36b1fd5aaa5 100644 --- a/doc/jit/zjit.md +++ b/doc/jit/zjit.md @@ -166,7 +166,7 @@ stackprof path/to/zjit_exits_{pid}.dump Using `--zjit-dump-hir-iongraph` will dump all compiled functions into a directory named `/tmp/zjit-iongraph-{PROCESS_PID}`. Each file will be named `func_{ZJIT_FUNC_NAME}.json`. In order to use them in the Iongraph viewer, you'll need to use `jq` to collate them to a single file. An example invocation of `jq` is shown below for reference. -`jq --slurp --null-input '.functions=inputs | .version=2' /tmp/zjit-iongraph-{PROCESS_PID}/func*.json > ~/Downloads/ion.json` +`jq --slurp --null-input '.functions=inputs | .version=1' /tmp/zjit-iongraph-{PROCESS_PID}/func*.json > ~/Downloads/ion.json` From there, you can use https://mozilla-spidermonkey.github.io/iongraph/ to view your trace. From a8b49ab48703dfb8862ca28a443da0e764d692c6 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Mon, 1 Dec 2025 16:51:29 -0500 Subject: [PATCH 050/367] Test CC invalidation for singleton classes of objects (#15360) I made a recent change where all the tests passed but it turns out it was still wrong. We didn't have any tests for CC invalidation on singletons of objects that aren't classes or modules. --- test/ruby/test_class.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index f40817e7a1ef54..10b7655e9a5562 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -887,4 +887,34 @@ def test_method_table_assignment_just_after_class_init class C; end end; end + + def test_singleton_cc_invalidation + assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}") + begin; + class T + def hi + "hi" + end + end + + t = T.new + t.singleton_class + + def hello(t) + t.hi + end + + 5.times do + hello(t) # populate inline cache on `t.singleton_class`. + end + + class T + remove_method :hi # invalidate `t.singleton_class` ccs for `hi` + end + + assert_raise NoMethodError do + hello(t) + end + end; + end end From e68fcf111b48a25ccf465e7acef13e4e145bcfb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berkan=20=C3=9Cnal?= <32255826+brkn@users.noreply.github.com> Date: Tue, 2 Dec 2025 01:02:03 +0300 Subject: [PATCH 051/367] [ruby/strscan] [DOC] Fix broken link to helper methods (https://github.com/ruby/strscan/pull/179) ### Helper methods link is broken at master branch To reproduce 1. go to [StringScanner docs](https://docs.ruby-lang.org/en/master/StringScanner.html) 2. Click to link at line > See examples at **helper_methods** 3. Resolved url gives 404: https://docs.ruby-lang.org/en/master/strscan/helper_methods_md.html ### Fix Currently link resolves as `href="doc/strscan/helper_methods_md.html"` Correct link should be resolved as `href="helper_methods_md.html"` https://github.com/ruby/strscan/commit/adb8678aa6 --- doc/strscan/strscan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/strscan/strscan.md b/doc/strscan/strscan.md index c0bf5413623b07..c4ab2034624947 100644 --- a/doc/strscan/strscan.md +++ b/doc/strscan/strscan.md @@ -37,7 +37,7 @@ Some examples here assume that certain helper methods are defined: - `match_values_cleared?(scanner)`: Returns whether the scanner's [match values][9] are cleared. -See examples at [helper methods](doc/strscan/helper_methods.md). +See examples at [helper methods](helper_methods.md). ## The `StringScanner` \Object From 4161c78a9d0c84f5afe6eb40b2cea6266cd59987 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Fri, 28 Nov 2025 15:01:37 -0800 Subject: [PATCH 052/367] Add remembered flag to heap dump This should be less common than than many of the other flags, so should not inflate the heap too much. This is desirable because reducing the number of remembered objects will improve minor GC speeds. --- gc/default/default.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gc/default/default.c b/gc/default/default.c index 2d862b0b212489..54b93dc8d07cf6 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -6197,7 +6197,7 @@ rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj) struct rb_gc_object_metadata_names { // Must be ID only ID ID_wb_protected, ID_age, ID_old, ID_uncollectible, ID_marking, - ID_marked, ID_pinned, ID_object_id, ID_shareable; + ID_marked, ID_pinned, ID_remembered, ID_object_id, ID_shareable; }; #define RB_GC_OBJECT_METADATA_ENTRY_COUNT (sizeof(struct rb_gc_object_metadata_names) / sizeof(ID)) @@ -6219,6 +6219,7 @@ rb_gc_impl_object_metadata(void *objspace_ptr, VALUE obj) I(marking); I(marked); I(pinned); + I(remembered); I(object_id); I(shareable); #undef I @@ -6238,6 +6239,7 @@ rb_gc_impl_object_metadata(void *objspace_ptr, VALUE obj) if (RVALUE_MARKING(objspace, obj)) SET_ENTRY(marking, Qtrue); if (RVALUE_MARKED(objspace, obj)) SET_ENTRY(marked, Qtrue); if (RVALUE_PINNED(objspace, obj)) SET_ENTRY(pinned, Qtrue); + if (RVALUE_REMEMBERED(objspace, obj)) SET_ENTRY(remembered, Qtrue); if (rb_obj_id_p(obj)) SET_ENTRY(object_id, rb_obj_id(obj)); if (FL_TEST(obj, FL_SHAREABLE)) SET_ENTRY(shareable, Qtrue); From 8ce78821cd8713d149ff27d9fbed38799e983349 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 22:18:16 -0500 Subject: [PATCH 053/367] ZJIT: Mark Integer#to_s as returning StringExact --- zjit/src/cruby_methods.rs | 1 + zjit/src/hir/opt_tests.rs | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 56d4de280bff71..f7bf92b31aa6ba 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -242,6 +242,7 @@ pub fn init() -> Annotations { annotate!(rb_cInteger, "<", inline_integer_lt); annotate!(rb_cInteger, "<=", inline_integer_le); annotate!(rb_cInteger, "<<", inline_integer_lshift); + annotate!(rb_cInteger, "to_s", types::StringExact); annotate!(rb_cString, "to_s", inline_string_to_s, types::StringExact); let thread_singleton = unsafe { rb_singleton_class(rb_cThread) }; annotate!(thread_singleton, "current", inline_thread_current, types::BasicObject, no_gc, leaf); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index e8c8d50dbe9256..4faaa402d87543 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5900,6 +5900,56 @@ mod hir_opt_tests { "); } + #[test] + fn test_fixnum_to_s_returns_string() { + eval(r#" + def test(x) = x.to_s + test 5 + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, to_s@0x1008, cme:0x1010) + v21:Fixnum = GuardType v9, Fixnum + v22:StringExact = CCallVariadic Integer#to_s@0x1038, v21 + CheckInterrupts + Return v22 + "); + } + + #[test] + fn test_bignum_to_s_returns_string() { + eval(r#" + def test(x) = x.to_s + test (2**65) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, to_s@0x1008, cme:0x1010) + v21:Integer = GuardType v9, Integer + v22:StringExact = CCallVariadic Integer#to_s@0x1038, v21 + CheckInterrupts + Return v22 + "); + } + #[test] fn test_array_aref_fixnum_literal() { eval(" From fd7d17abde4943edaefac7840f7258c8283d1d8e Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 22:18:43 -0500 Subject: [PATCH 054/367] ZJIT: Don't use GuardTypeNot Use actual receiver type. This gives us better method lookup. --- zjit/src/hir.rs | 4 ++-- zjit/src/hir/opt_tests.rs | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index e5c1f7a54293da..bab6d1c841e136 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2677,8 +2677,8 @@ impl Function { self.insn_types[guard.0] = self.infer_type(guard); self.make_equal_to(insn_id, guard); } else { - self.push_insn(block, Insn::GuardTypeNot { val, guard_type: types::String, state}); - let send_to_s = self.push_insn(block, Insn::SendWithoutBlock { recv: val, cd, args: vec![], state, reason: ObjToStringNotString }); + let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state}); + let send_to_s = self.push_insn(block, Insn::SendWithoutBlock { recv, cd, args: vec![], state, reason: ObjToStringNotString }); self.make_equal_to(insn_id, send_to_s); } } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 4faaa402d87543..029b3735f565ee 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -4137,12 +4137,11 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v26:BasicObject = GuardTypeNot v9, String + v26:ArrayExact = GuardType v9, ArrayExact PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) - v31:ArrayExact = GuardType v9, ArrayExact - v32:BasicObject = CCallWithFrame v31, :Array#to_s@0x1040 - v19:String = AnyToString v9, str: v32 + v31:BasicObject = CCallWithFrame v26, :Array#to_s@0x1040 + v19:String = AnyToString v9, str: v31 v21:StringExact = StringConcat v13, v19 CheckInterrupts Return v21 @@ -5919,7 +5918,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, to_s@0x1008, cme:0x1010) v21:Fixnum = GuardType v9, Fixnum - v22:StringExact = CCallVariadic Integer#to_s@0x1038, v21 + v22:StringExact = CCallVariadic v21, :Integer#to_s@0x1038 CheckInterrupts Return v22 "); @@ -5944,7 +5943,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, to_s@0x1008, cme:0x1010) v21:Integer = GuardType v9, Integer - v22:StringExact = CCallVariadic Integer#to_s@0x1038, v21 + v22:StringExact = CCallVariadic v21, :Integer#to_s@0x1038 CheckInterrupts Return v22 "); From 91432a6bad7c52859e2920e780d578adcf4ac3f3 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 22:19:15 -0500 Subject: [PATCH 055/367] ZJIT: Add late pass to fold AnyToString This otherwise would miss annotations of C methods. --- zjit/src/hir.rs | 5 +++++ zjit/src/hir/opt_tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index bab6d1c841e136..4bbbbd150e444b 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3399,6 +3399,11 @@ impl Function { // Don't bother re-inferring the type of val; we already know it. continue; } + Insn::AnyToString { str, .. } if self.is_a(str, types::String) => { + self.make_equal_to(insn_id, str); + // Don't bother re-inferring the type of str; we already know it. + continue; + } Insn::FixnumAdd { left, right, .. } => { self.fold_fixnum_bop(insn_id, left, right, |l, r| match (l, r) { (Some(l), Some(r)) => l.checked_add(r), diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 029b3735f565ee..90675965c2f67a 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5949,6 +5949,33 @@ mod hir_opt_tests { "); } + #[test] + fn test_fold_any_to_string_with_known_string_exact() { + eval(r##" + def test(x) = "#{x}" + test 123 + "##); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v26:Fixnum = GuardType v9, Fixnum + PatchPoint MethodRedefined(Integer@0x1008, to_s@0x1010, cme:0x1018) + v30:StringExact = CCallVariadic Integer#to_s@0x1040, v26 + v21:StringExact = StringConcat v13, v30 + CheckInterrupts + Return v21 + "); + } + #[test] fn test_array_aref_fixnum_literal() { eval(" From 8aed31103874524395912fb7ffaee88b5e59a9c3 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 22:24:45 -0500 Subject: [PATCH 056/367] ZJIT: Specialize String#<< with Fixnum Append a codepoint. --- jit.c | 2 ++ string.c | 4 ++-- yjit/bindgen/src/main.rs | 2 +- yjit/src/codegen.rs | 2 +- yjit/src/cruby.rs | 1 - yjit/src/cruby_bindings.inc.rs | 1 + zjit/bindgen/src/main.rs | 1 + zjit/src/codegen.rs | 6 ++++++ zjit/src/cruby.rs | 1 - zjit/src/cruby_bindings.inc.rs | 1 + zjit/src/cruby_methods.rs | 11 +++++++--- zjit/src/hir.rs | 14 ++++++++++++- zjit/src/hir/opt_tests.rs | 37 +++++++++++++++++++++++++++++----- 13 files changed, 68 insertions(+), 15 deletions(-) diff --git a/jit.c b/jit.c index 43c932e5a00ffa..dd28bd6ad02aef 100644 --- a/jit.c +++ b/jit.c @@ -765,3 +765,5 @@ rb_yarv_str_eql_internal(VALUE str1, VALUE str2) // We wrap this since it's static inline return rb_str_eql_internal(str1, str2); } + +void rb_jit_str_concat_codepoint(VALUE str, VALUE codepoint); diff --git a/string.c b/string.c index 2dec3a11e61246..c794b36748e6e6 100644 --- a/string.c +++ b/string.c @@ -12580,9 +12580,9 @@ rb_enc_interned_str_cstr(const char *ptr, rb_encoding *enc) return rb_enc_interned_str(ptr, strlen(ptr), enc); } -#if USE_YJIT +#if USE_YJIT || USE_ZJIT void -rb_yjit_str_concat_codepoint(VALUE str, VALUE codepoint) +rb_jit_str_concat_codepoint(VALUE str, VALUE codepoint) { if (RB_LIKELY(ENCODING_GET_INLINED(str) == rb_ascii8bit_encindex())) { ssize_t code = RB_NUM2SSIZE(codepoint); diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 67a461cd16d95c..9c28177a6054b2 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -273,7 +273,7 @@ fn main() { .allowlist_function("rb_yjit_sendish_sp_pops") .allowlist_function("rb_yjit_invokeblock_sp_pops") .allowlist_function("rb_yjit_set_exception_return") - .allowlist_function("rb_yjit_str_concat_codepoint") + .allowlist_function("rb_jit_str_concat_codepoint") .allowlist_type("rstring_offsets") .allowlist_function("rb_assert_holding_vm_lock") .allowlist_function("rb_jit_shape_too_complex_p") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index a426ad07737496..50762c64d3eceb 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -6244,7 +6244,7 @@ fn jit_rb_str_concat_codepoint( guard_object_is_fixnum(jit, asm, codepoint, StackOpnd(0)); - asm.ccall(rb_yjit_str_concat_codepoint as *const u8, vec![recv, codepoint]); + asm.ccall(rb_jit_str_concat_codepoint as *const u8, vec![recv, codepoint]); // The receiver is the return value, so we only need to pop the codepoint argument off the stack. // We can reuse the receiver slot in the stack as the return value. diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index cfaf48c3f0b68e..5562f73be26dab 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -123,7 +123,6 @@ extern "C" { pub fn rb_float_new(d: f64) -> VALUE; pub fn rb_hash_empty_p(hash: VALUE) -> VALUE; - pub fn rb_yjit_str_concat_codepoint(str: VALUE, codepoint: VALUE); pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE; pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE; pub fn rb_vm_concat_array(ary1: VALUE, ary2st: VALUE) -> VALUE; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 66d4e5111d7bbd..04ca3494acf5a7 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1277,4 +1277,5 @@ extern "C" { ); pub fn rb_jit_fix_mod_fix(recv: VALUE, obj: VALUE) -> VALUE; pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; + pub fn rb_jit_str_concat_codepoint(str_: VALUE, codepoint: VALUE); } diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index fe082504b82302..6659049242983b 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -278,6 +278,7 @@ fn main() { .allowlist_function("rb_jit_get_page_size") .allowlist_function("rb_jit_array_len") .allowlist_function("rb_jit_iseq_builtin_attrs") + .allowlist_function("rb_jit_str_concat_codepoint") .allowlist_function("rb_zjit_iseq_inspect") .allowlist_function("rb_zjit_iseq_insn_set") .allowlist_function("rb_zjit_local_id") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index f77b8cc4bf2ca6..6fc85664693fa3 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -368,6 +368,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::StringGetbyteFixnum { string, index } => gen_string_getbyte_fixnum(asm, opnd!(string), opnd!(index)), Insn::StringSetbyteFixnum { string, index, value } => gen_string_setbyte_fixnum(asm, opnd!(string), opnd!(index), opnd!(value)), Insn::StringAppend { recv, other, state } => gen_string_append(jit, asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), + Insn::StringAppendCodepoint { recv, other, state } => gen_string_append_codepoint(jit, asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), Insn::StringIntern { val, state } => gen_intern(asm, opnd!(val), &function.frame_state(*state)), Insn::ToRegexp { opt, values, state } => gen_toregexp(jit, asm, *opt, opnds!(values), &function.frame_state(*state)), Insn::Param => unreachable!("block.insns should not have Insn::Param"), @@ -2495,6 +2496,11 @@ fn gen_string_append(jit: &mut JITState, asm: &mut Assembler, string: Opnd, val: asm_ccall!(asm, rb_str_buf_append, string, val) } +fn gen_string_append_codepoint(jit: &mut JITState, asm: &mut Assembler, string: Opnd, val: Opnd, state: &FrameState) -> Opnd { + gen_prepare_non_leaf_call(jit, asm, state); + asm_ccall!(asm, rb_jit_str_concat_codepoint, string, val) +} + /// Generate a JIT entry that just increments exit_compilation_failure and exits fn gen_compile_error_counter(cb: &mut CodeBlock, compile_error: &CompileError) -> Result { let mut asm = Assembler::new(); diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 9070cb38be5f42..72c44ccc6e412f 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -130,7 +130,6 @@ unsafe extern "C" { pub fn rb_float_new(d: f64) -> VALUE; pub fn rb_hash_empty_p(hash: VALUE) -> VALUE; - pub fn rb_yjit_str_concat_codepoint(str: VALUE, codepoint: VALUE); pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE; pub fn rb_str_getbyte(str: VALUE, index: VALUE) -> VALUE; pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE; diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index aaecfa2f89769a..2256b7e32d3979 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -2224,4 +2224,5 @@ unsafe extern "C" { end: *mut ::std::os::raw::c_void, ); pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; + pub fn rb_jit_str_concat_codepoint(str_: VALUE, codepoint: VALUE); } diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index f7bf92b31aa6ba..f86d383876fbfc 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -425,10 +425,15 @@ fn inline_string_append(fun: &mut hir::Function, block: hir::BlockId, recv: hir: let recv = fun.coerce_to(block, recv, types::StringExact, state); let other = fun.coerce_to(block, other, types::String, state); let _ = fun.push_insn(block, hir::Insn::StringAppend { recv, other, state }); - Some(recv) - } else { - None + return Some(recv); } + if fun.likely_a(recv, types::StringExact, state) && fun.likely_a(other, types::Fixnum, state) { + let recv = fun.coerce_to(block, recv, types::StringExact, state); + let other = fun.coerce_to(block, other, types::Fixnum, state); + let _ = fun.push_insn(block, hir::Insn::StringAppendCodepoint { recv, other, state }); + return Some(recv); + } + None } fn inline_string_eq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 4bbbbd150e444b..6604c52a8276e6 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -652,6 +652,7 @@ pub enum Insn { StringGetbyteFixnum { string: InsnId, index: InsnId }, StringSetbyteFixnum { string: InsnId, index: InsnId, value: InsnId }, StringAppend { recv: InsnId, other: InsnId, state: InsnId }, + StringAppendCodepoint { recv: InsnId, other: InsnId, state: InsnId }, /// Combine count stack values into a regexp ToRegexp { opt: usize, values: Vec, state: InsnId }, @@ -1124,6 +1125,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::StringAppend { recv, other, .. } => { write!(f, "StringAppend {recv}, {other}") } + Insn::StringAppendCodepoint { recv, other, .. } => { + write!(f, "StringAppendCodepoint {recv}, {other}") + } Insn::ToRegexp { values, opt, .. } => { write!(f, "ToRegexp")?; let mut prefix = " "; @@ -1814,6 +1818,7 @@ impl Function { &StringGetbyteFixnum { string, index } => StringGetbyteFixnum { string: find!(string), index: find!(index) }, &StringSetbyteFixnum { string, index, value } => StringSetbyteFixnum { string: find!(string), index: find!(index), value: find!(value) }, &StringAppend { recv, other, state } => StringAppend { recv: find!(recv), other: find!(other), state: find!(state) }, + &StringAppendCodepoint { recv, other, state } => StringAppendCodepoint { recv: find!(recv), other: find!(other), state: find!(state) }, &ToRegexp { opt, ref values, state } => ToRegexp { opt, values: find_vec!(values), state }, &Test { val } => Test { val: find!(val) }, &IsNil { val } => IsNil { val: find!(val) }, @@ -2032,6 +2037,7 @@ impl Function { Insn::StringGetbyteFixnum { .. } => types::Fixnum.union(types::NilClass), Insn::StringSetbyteFixnum { .. } => types::Fixnum, Insn::StringAppend { .. } => types::StringExact, + Insn::StringAppendCodepoint { .. } => types::StringExact, Insn::ToRegexp { .. } => types::RegexpExact, Insn::NewArray { .. } => types::ArrayExact, Insn::ArrayDup { .. } => types::ArrayExact, @@ -3564,7 +3570,9 @@ impl Function { worklist.push_back(index); worklist.push_back(value); } - &Insn::StringAppend { recv, other, state } => { + &Insn::StringAppend { recv, other, state } + | &Insn::StringAppendCodepoint { recv, other, state } + => { worklist.push_back(recv); worklist.push_back(other); worklist.push_back(state); @@ -4328,6 +4336,10 @@ impl Function { self.assert_subtype(insn_id, recv, types::StringExact)?; self.assert_subtype(insn_id, other, types::String) } + Insn::StringAppendCodepoint { recv, other, .. } => { + self.assert_subtype(insn_id, recv, types::StringExact)?; + self.assert_subtype(insn_id, other, types::Fixnum) + } // Instructions with Array operands Insn::ArrayDup { val, .. } => self.assert_subtype(insn_id, val, types::ArrayExact), Insn::ArrayExtend { left, right, .. } => { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 90675965c2f67a..60f5814973c482 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5969,7 +5969,7 @@ mod hir_opt_tests { v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v26:Fixnum = GuardType v9, Fixnum PatchPoint MethodRedefined(Integer@0x1008, to_s@0x1010, cme:0x1018) - v30:StringExact = CCallVariadic Integer#to_s@0x1040, v26 + v30:StringExact = CCallVariadic v26, :Integer#to_s@0x1040 v21:StringExact = StringConcat v13, v30 CheckInterrupts Return v21 @@ -6854,9 +6854,8 @@ mod hir_opt_tests { "); } - // TODO: This should be inlined just as in the interpreter #[test] - fn test_optimize_string_append_non_string() { + fn test_optimize_string_append_codepoint() { eval(r#" def test(x, y) = x << y test("iron", 4) @@ -6876,9 +6875,11 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) v27:StringExact = GuardType v11, StringExact - v28:BasicObject = CCallWithFrame v27, :String#<<@0x1038, v12 + v28:Fixnum = GuardType v12, Fixnum + v29:StringExact = StringAppendCodepoint v27, v28 + IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v28 + Return v27 "); } @@ -6942,6 +6943,32 @@ mod hir_opt_tests { "); } + #[test] + fn test_dont_optimize_string_append_non_string() { + eval(r#" + def test = "iron" << :a + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v11:StringExact = StringCopy v10 + v13:StaticSymbol[:a] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(String@0x1010, <<@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(String@0x1010) + v24:BasicObject = CCallWithFrame v11, :String#<<@0x1048, v13 + CheckInterrupts + Return v24 + "); + } + #[test] fn test_dont_optimize_when_passing_too_many_args() { eval(r#" From a25196395e7502e4d6faad0856c697690d8a202e Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 22:44:15 -0500 Subject: [PATCH 057/367] Add BOP_GTGT This will help JITs (and maybe later the interpreter) optimize Integer#>>. --- internal/basic_operators.h | 1 + vm.c | 1 + yjit/src/cruby_bindings.inc.rs | 33 +++++++++++++++++---------------- zjit/src/cruby_bindings.inc.rs | 33 +++++++++++++++++---------------- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/internal/basic_operators.h b/internal/basic_operators.h index 5dc8d7fe8d69fb..493d2fa7f733d7 100644 --- a/internal/basic_operators.h +++ b/internal/basic_operators.h @@ -24,6 +24,7 @@ enum ruby_basic_operators { BOP_SUCC, BOP_GT, BOP_GE, + BOP_GTGT, BOP_NOT, BOP_NEQ, BOP_MATCH, diff --git a/vm.c b/vm.c index 1a8a6d059b569b..b15ee0ba236b57 100644 --- a/vm.c +++ b/vm.c @@ -2444,6 +2444,7 @@ vm_init_redefined_flag(void) OP(GT, GT), (C(Integer), C(Float)); OP(GE, GE), (C(Integer), C(Float)); OP(LTLT, LTLT), (C(String), C(Array)); + OP(GTGT, GTGT), (C(Integer)); OP(AREF, AREF), (C(Array), C(Hash), C(Integer)); OP(ASET, ASET), (C(Array), C(Hash)); OP(Length, LENGTH), (C(Array), C(String), C(Hash)); diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 04ca3494acf5a7..6efef9c55c39c4 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -330,22 +330,23 @@ pub const BOP_NIL_P: ruby_basic_operators = 15; pub const BOP_SUCC: ruby_basic_operators = 16; pub const BOP_GT: ruby_basic_operators = 17; pub const BOP_GE: ruby_basic_operators = 18; -pub const BOP_NOT: ruby_basic_operators = 19; -pub const BOP_NEQ: ruby_basic_operators = 20; -pub const BOP_MATCH: ruby_basic_operators = 21; -pub const BOP_FREEZE: ruby_basic_operators = 22; -pub const BOP_UMINUS: ruby_basic_operators = 23; -pub const BOP_MAX: ruby_basic_operators = 24; -pub const BOP_MIN: ruby_basic_operators = 25; -pub const BOP_HASH: ruby_basic_operators = 26; -pub const BOP_CALL: ruby_basic_operators = 27; -pub const BOP_AND: ruby_basic_operators = 28; -pub const BOP_OR: ruby_basic_operators = 29; -pub const BOP_CMP: ruby_basic_operators = 30; -pub const BOP_DEFAULT: ruby_basic_operators = 31; -pub const BOP_PACK: ruby_basic_operators = 32; -pub const BOP_INCLUDE_P: ruby_basic_operators = 33; -pub const BOP_LAST_: ruby_basic_operators = 34; +pub const BOP_GTGT: ruby_basic_operators = 19; +pub const BOP_NOT: ruby_basic_operators = 20; +pub const BOP_NEQ: ruby_basic_operators = 21; +pub const BOP_MATCH: ruby_basic_operators = 22; +pub const BOP_FREEZE: ruby_basic_operators = 23; +pub const BOP_UMINUS: ruby_basic_operators = 24; +pub const BOP_MAX: ruby_basic_operators = 25; +pub const BOP_MIN: ruby_basic_operators = 26; +pub const BOP_HASH: ruby_basic_operators = 27; +pub const BOP_CALL: ruby_basic_operators = 28; +pub const BOP_AND: ruby_basic_operators = 29; +pub const BOP_OR: ruby_basic_operators = 30; +pub const BOP_CMP: ruby_basic_operators = 31; +pub const BOP_DEFAULT: ruby_basic_operators = 32; +pub const BOP_PACK: ruby_basic_operators = 33; +pub const BOP_INCLUDE_P: ruby_basic_operators = 34; +pub const BOP_LAST_: ruby_basic_operators = 35; pub type ruby_basic_operators = u32; pub type rb_serial_t = ::std::os::raw::c_ulonglong; pub const imemo_env: imemo_type = 0; diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 2256b7e32d3979..fb329a07166914 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -392,22 +392,23 @@ pub const BOP_NIL_P: ruby_basic_operators = 15; pub const BOP_SUCC: ruby_basic_operators = 16; pub const BOP_GT: ruby_basic_operators = 17; pub const BOP_GE: ruby_basic_operators = 18; -pub const BOP_NOT: ruby_basic_operators = 19; -pub const BOP_NEQ: ruby_basic_operators = 20; -pub const BOP_MATCH: ruby_basic_operators = 21; -pub const BOP_FREEZE: ruby_basic_operators = 22; -pub const BOP_UMINUS: ruby_basic_operators = 23; -pub const BOP_MAX: ruby_basic_operators = 24; -pub const BOP_MIN: ruby_basic_operators = 25; -pub const BOP_HASH: ruby_basic_operators = 26; -pub const BOP_CALL: ruby_basic_operators = 27; -pub const BOP_AND: ruby_basic_operators = 28; -pub const BOP_OR: ruby_basic_operators = 29; -pub const BOP_CMP: ruby_basic_operators = 30; -pub const BOP_DEFAULT: ruby_basic_operators = 31; -pub const BOP_PACK: ruby_basic_operators = 32; -pub const BOP_INCLUDE_P: ruby_basic_operators = 33; -pub const BOP_LAST_: ruby_basic_operators = 34; +pub const BOP_GTGT: ruby_basic_operators = 19; +pub const BOP_NOT: ruby_basic_operators = 20; +pub const BOP_NEQ: ruby_basic_operators = 21; +pub const BOP_MATCH: ruby_basic_operators = 22; +pub const BOP_FREEZE: ruby_basic_operators = 23; +pub const BOP_UMINUS: ruby_basic_operators = 24; +pub const BOP_MAX: ruby_basic_operators = 25; +pub const BOP_MIN: ruby_basic_operators = 26; +pub const BOP_HASH: ruby_basic_operators = 27; +pub const BOP_CALL: ruby_basic_operators = 28; +pub const BOP_AND: ruby_basic_operators = 29; +pub const BOP_OR: ruby_basic_operators = 30; +pub const BOP_CMP: ruby_basic_operators = 31; +pub const BOP_DEFAULT: ruby_basic_operators = 32; +pub const BOP_PACK: ruby_basic_operators = 33; +pub const BOP_INCLUDE_P: ruby_basic_operators = 34; +pub const BOP_LAST_: ruby_basic_operators = 35; pub type ruby_basic_operators = u32; pub type rb_serial_t = ::std::os::raw::c_ulonglong; #[repr(C)] From 6db83a00a4272eb1089d67da83e1cd9d4e10227b Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 22:44:53 -0500 Subject: [PATCH 058/367] ZJIT: Specialize Integer#>> Same as Integer#>>. Also add more strict type checks for both Integer#>> and Integer#<<. --- zjit/src/codegen.rs | 15 ++++++ zjit/src/cruby_methods.rs | 11 ++++ zjit/src/hir.rs | 22 +++++++- zjit/src/hir/opt_tests.rs | 105 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 1 deletion(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 6fc85664693fa3..df9a9299cf2d80 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -409,6 +409,12 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio let shift_amount = function.type_of(right).fixnum_value().unwrap() as u64; gen_fixnum_lshift(jit, asm, opnd!(left), shift_amount, &function.frame_state(state)) } + &Insn::FixnumRShift { left, right } => { + // We only create FixnumRShift when we know the shift amount statically and it's in [0, + // 63]. + let shift_amount = function.type_of(right).fixnum_value().unwrap() as u64; + gen_fixnum_rshift(asm, opnd!(left), shift_amount) + } &Insn::FixnumMod { left, right, state } => gen_fixnum_mod(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), Insn::IsNil { val } => gen_isnil(asm, opnd!(val)), &Insn::IsMethodCfunc { val, cd, cfunc, state: _ } => gen_is_method_cfunc(jit, asm, opnd!(val), cd, cfunc), @@ -1754,6 +1760,15 @@ fn gen_fixnum_lshift(jit: &mut JITState, asm: &mut Assembler, left: lir::Opnd, s out_val } +/// Compile Fixnum >> Fixnum +fn gen_fixnum_rshift(asm: &mut Assembler, left: lir::Opnd, shift_amount: u64) -> lir::Opnd { + // Shift amount is known statically to be in the range [0, 63] + assert!(shift_amount < 64); + let result = asm.rshift(left, shift_amount.into()); + // Re-tag the output value + asm.or(result, 1.into()) +} + fn gen_fixnum_mod(jit: &mut JITState, asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd, state: &FrameState) -> lir::Opnd { // Check for left % 0, which raises ZeroDivisionError asm.cmp(right, Opnd::from(VALUE::fixnum_from_usize(0))); diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index f86d383876fbfc..f84882101cc271 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -242,6 +242,7 @@ pub fn init() -> Annotations { annotate!(rb_cInteger, "<", inline_integer_lt); annotate!(rb_cInteger, "<=", inline_integer_le); annotate!(rb_cInteger, "<<", inline_integer_lshift); + annotate!(rb_cInteger, ">>", inline_integer_rshift); annotate!(rb_cInteger, "to_s", types::StringExact); annotate!(rb_cString, "to_s", inline_string_to_s, types::StringExact); let thread_singleton = unsafe { rb_singleton_class(rb_cThread) }; @@ -575,6 +576,16 @@ fn inline_integer_lshift(fun: &mut hir::Function, block: hir::BlockId, recv: hir try_inline_fixnum_op(fun, block, &|left, right| hir::Insn::FixnumLShift { left, right, state }, BOP_LTLT, recv, other, state) } +fn inline_integer_rshift(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[other] = args else { return None; }; + // Only convert to FixnumLShift if we know the shift amount is known at compile-time and could + // plausibly create a fixnum. + let Some(other_value) = fun.type_of(other).fixnum_value() else { return None; }; + // TODO(max): If other_value > 63, rewrite to constant zero. + if other_value < 0 || other_value > 63 { return None; } + try_inline_fixnum_op(fun, block, &|left, right| hir::Insn::FixnumRShift { left, right }, BOP_GTGT, recv, other, state) +} + fn inline_basic_object_eq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { let &[other] = args else { return None; }; let c_result = fun.push_insn(block, hir::Insn::IsBitEqual { left: recv, right: other }); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 6604c52a8276e6..a69628b8690d67 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -891,6 +891,7 @@ pub enum Insn { FixnumOr { left: InsnId, right: InsnId }, FixnumXor { left: InsnId, right: InsnId }, FixnumLShift { left: InsnId, right: InsnId, state: InsnId }, + FixnumRShift { left: InsnId, right: InsnId }, // Distinct from `SendWithoutBlock` with `mid:to_s` because does not have a patch point for String to_s being redefined ObjToString { val: InsnId, cd: *const rb_call_data, state: InsnId }, @@ -989,6 +990,7 @@ impl Insn { Insn::FixnumOr { .. } => false, Insn::FixnumXor { .. } => false, Insn::FixnumLShift { .. } => false, + Insn::FixnumRShift { .. } => false, Insn::GetLocal { .. } => false, Insn::IsNil { .. } => false, Insn::LoadPC => false, @@ -1233,6 +1235,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::FixnumOr { left, right, .. } => { write!(f, "FixnumOr {left}, {right}") }, Insn::FixnumXor { left, right, .. } => { write!(f, "FixnumXor {left}, {right}") }, Insn::FixnumLShift { left, right, .. } => { write!(f, "FixnumLShift {left}, {right}") }, + Insn::FixnumRShift { left, right, .. } => { write!(f, "FixnumRShift {left}, {right}") }, Insn::GuardType { val, guard_type, .. } => { write!(f, "GuardType {val}, {}", guard_type.print(self.ptr_map)) }, Insn::GuardTypeNot { val, guard_type, .. } => { write!(f, "GuardTypeNot {val}, {}", guard_type.print(self.ptr_map)) }, Insn::GuardBitEquals { val, expected, .. } => { write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map)) }, @@ -1854,6 +1857,7 @@ impl Function { &FixnumOr { left, right } => FixnumOr { left: find!(left), right: find!(right) }, &FixnumXor { left, right } => FixnumXor { left: find!(left), right: find!(right) }, &FixnumLShift { left, right, state } => FixnumLShift { left: find!(left), right: find!(right), state }, + &FixnumRShift { left, right } => FixnumRShift { left: find!(left), right: find!(right) }, &ObjToString { val, cd, state } => ObjToString { val: find!(val), cd, @@ -2076,6 +2080,7 @@ impl Function { Insn::FixnumOr { .. } => types::Fixnum, Insn::FixnumXor { .. } => types::Fixnum, Insn::FixnumLShift { .. } => types::Fixnum, + Insn::FixnumRShift { .. } => types::Fixnum, Insn::PutSpecialObject { .. } => types::BasicObject, Insn::SendWithoutBlock { .. } => types::BasicObject, Insn::SendWithoutBlockDirect { .. } => types::BasicObject, @@ -3639,6 +3644,7 @@ impl Function { | &Insn::FixnumAnd { left, right } | &Insn::FixnumOr { left, right } | &Insn::FixnumXor { left, right } + | &Insn::FixnumRShift { left, right } | &Insn::IsBitEqual { left, right } | &Insn::IsBitNotEqual { left, right } => { @@ -4403,12 +4409,26 @@ impl Function { | Insn::FixnumAnd { left, right } | Insn::FixnumOr { left, right } | Insn::FixnumXor { left, right } - | Insn::FixnumLShift { left, right, .. } | Insn::NewRangeFixnum { low: left, high: right, .. } => { self.assert_subtype(insn_id, left, types::Fixnum)?; self.assert_subtype(insn_id, right, types::Fixnum) } + Insn::FixnumLShift { left, right, .. } + | Insn::FixnumRShift { left, right, .. } => { + self.assert_subtype(insn_id, left, types::Fixnum)?; + self.assert_subtype(insn_id, right, types::Fixnum)?; + let Some(obj) = self.type_of(right).fixnum_value() else { + return Err(ValidationError::MismatchedOperandType(insn_id, right, "".into(), "".into())); + }; + if obj < 0 { + return Err(ValidationError::MismatchedOperandType(insn_id, right, "".into(), format!("{obj}"))); + } + if obj > 63 { + return Err(ValidationError::MismatchedOperandType(insn_id, right, "".into(), format!("{obj}"))); + } + Ok(()) + } Insn::GuardBitEquals { val, expected, .. } => { match expected { Const::Value(_) => self.assert_subtype(insn_id, val, types::RubyValue), diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 60f5814973c482..610986d62fe07f 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -6825,6 +6825,111 @@ mod hir_opt_tests { "); } + #[test] + fn test_inline_integer_gtgt_with_known_fixnum() { + eval(" + def test(x) = x >> 5 + test(4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:Fixnum[5] = Const Value(5) + PatchPoint MethodRedefined(Integer@0x1000, >>@0x1008, cme:0x1010) + v23:Fixnum = GuardType v9, Fixnum + v24:Fixnum = FixnumRShift v23, v14 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_dont_inline_integer_gtgt_with_negative() { + eval(" + def test(x) = x >> -5 + test(4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:Fixnum[-5] = Const Value(-5) + PatchPoint MethodRedefined(Integer@0x1000, >>@0x1008, cme:0x1010) + v23:Fixnum = GuardType v9, Fixnum + v24:BasicObject = CCallWithFrame v23, :Integer#>>@0x1038, v14 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_dont_inline_integer_gtgt_with_out_of_range() { + eval(" + def test(x) = x >> 64 + test(4) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:Fixnum[64] = Const Value(64) + PatchPoint MethodRedefined(Integer@0x1000, >>@0x1008, cme:0x1010) + v23:Fixnum = GuardType v9, Fixnum + v24:BasicObject = CCallWithFrame v23, :Integer#>>@0x1038, v14 + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_dont_inline_integer_gtgt_with_unknown_fixnum() { + eval(" + def test(x, y) = x >> y + test(4, 5) + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@5 + v3:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + EntryPoint JIT(0) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): + PatchPoint MethodRedefined(Integer@0x1000, >>@0x1008, cme:0x1010) + v25:Fixnum = GuardType v11, Fixnum + v26:BasicObject = CCallWithFrame v25, :Integer#>>@0x1038, v12 + CheckInterrupts + Return v26 + "); + } + #[test] fn test_optimize_string_append() { eval(r#" From 74e9f717200106be553e954e8ac354b54acf1c60 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 23:18:13 -0500 Subject: [PATCH 059/367] ZJIT: Mark String#ascii_only? as leaf --- zjit/src/cruby_methods.rs | 4 ++++ zjit/src/hir/opt_tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index f84882101cc271..155979d105ae16 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -206,6 +206,10 @@ pub fn init() -> Annotations { annotate!(rb_cString, "empty?", inline_string_empty_p, types::BoolExact, no_gc, leaf, elidable); annotate!(rb_cString, "<<", inline_string_append); annotate!(rb_cString, "==", inline_string_eq); + // Not elidable; has a side effect of setting the encoding if ENC_CODERANGE_UNKNOWN. + // TOOD(max): Turn this into a load/compare. Will need to side-exit or do the full call if + // ENC_CODERANGE_UNKNOWN. + annotate!(rb_cString, "ascii_only?", types::BoolExact, no_gc, leaf); annotate!(rb_cModule, "name", types::StringExact.union(types::NilClass), no_gc, leaf, elidable); annotate!(rb_cModule, "===", inline_module_eqq, types::BoolExact, no_gc, leaf); annotate!(rb_cArray, "length", types::Fixnum, no_gc, leaf, elidable); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 610986d62fe07f..60135b6ac9e969 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -7100,6 +7100,33 @@ mod hir_opt_tests { "); } + #[test] + fn test_optimize_string_ascii_only_p() { + eval(r#" + def test(x) = x.ascii_only? + test("iron") + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(String@0x1000, ascii_only?@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(String@0x1000) + v22:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v24:BoolExact = CCall v22, :String#ascii_only?@0x1038 + CheckInterrupts + Return v24 + "); + } + #[test] fn test_dont_optimize_when_passing_too_few_args() { eval(r#" From 985a4d977f1c1a5051c88b0c2f8af8913bd93008 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 23:30:05 -0500 Subject: [PATCH 060/367] ZJIT: Open-code String#getbyte Don't call a C function. --- zjit/src/codegen.rs | 33 +++++++++++++++++++++++++++++---- zjit/src/cruby_methods.rs | 16 +++++++++++++++- zjit/src/hir.rs | 18 +++++++++--------- zjit/src/hir/opt_tests.rs | 14 ++++++++++++-- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index df9a9299cf2d80..ac1c65b26e1c13 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -365,7 +365,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio // If it happens we abort the compilation for now Insn::StringConcat { strings, state, .. } if strings.is_empty() => return Err(*state), Insn::StringConcat { strings, state } => gen_string_concat(jit, asm, opnds!(strings), &function.frame_state(*state)), - &Insn::StringGetbyteFixnum { string, index } => gen_string_getbyte_fixnum(asm, opnd!(string), opnd!(index)), + &Insn::StringGetbyte { string, index } => gen_string_getbyte(asm, opnd!(string), opnd!(index)), Insn::StringSetbyteFixnum { string, index, value } => gen_string_setbyte_fixnum(asm, opnd!(string), opnd!(index), opnd!(value)), Insn::StringAppend { recv, other, state } => gen_string_append(jit, asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), Insn::StringAppendCodepoint { recv, other, state } => gen_string_append_codepoint(jit, asm, opnd!(recv), opnd!(other), &function.frame_state(*state)), @@ -2496,9 +2496,34 @@ fn gen_string_concat(jit: &mut JITState, asm: &mut Assembler, strings: Vec result } -fn gen_string_getbyte_fixnum(asm: &mut Assembler, string: Opnd, index: Opnd) -> Opnd { - // TODO(max): Open-code rb_str_getbyte to avoid a call - asm_ccall!(asm, rb_str_getbyte, string, index) +// Generate RSTRING_PTR +fn get_string_ptr(asm: &mut Assembler, string: Opnd) -> Opnd { + asm_comment!(asm, "get string pointer for embedded or heap"); + let string = asm.load(string); + let flags = Opnd::mem(VALUE_BITS, string, RUBY_OFFSET_RBASIC_FLAGS); + asm.test(flags, (RSTRING_NOEMBED as u64).into()); + let heap_ptr = asm.load(Opnd::mem( + usize::BITS as u8, + string, + RUBY_OFFSET_RSTRING_AS_HEAP_PTR, + )); + // Load the address of the embedded array + // (struct RString *)(obj)->as.ary + let ary = asm.lea(Opnd::mem(VALUE_BITS, string, RUBY_OFFSET_RSTRING_AS_ARY)); + asm.csel_nz(heap_ptr, ary) +} + +fn gen_string_getbyte(asm: &mut Assembler, string: Opnd, index: Opnd) -> Opnd { + let string_ptr = get_string_ptr(asm, string); + // TODO(max): Use SIB indexing here once the backend supports it + let string_ptr = asm.add(string_ptr, index); + let byte = asm.load(Opnd::mem(8, string_ptr, 0)); + // Zero-extend the byte to 64 bits + let byte = byte.with_num_bits(64); + let byte = asm.and(byte, 0xFF.into()); + // Tag the byte + let byte = asm.lshift(byte, Opnd::UImm(1)); + asm.or(byte, Opnd::UImm(1)) } fn gen_string_setbyte_fixnum(asm: &mut Assembler, string: Opnd, index: Opnd, value: Opnd) -> Opnd { diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 155979d105ae16..0f5c992b88f892 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -376,7 +376,21 @@ fn inline_string_getbyte(fun: &mut hir::Function, block: hir::BlockId, recv: hir // String#getbyte with a Fixnum is leaf and nogc; otherwise it may run arbitrary Ruby code // when converting the index to a C integer. let index = fun.coerce_to(block, index, types::Fixnum, state); - let result = fun.push_insn(block, hir::Insn::StringGetbyteFixnum { string: recv, index }); + let unboxed_index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); + let len = fun.push_insn(block, hir::Insn::LoadField { + recv, + id: ID!(len), + offset: RUBY_OFFSET_RSTRING_LEN as i32, + return_type: types::CInt64, + }); + // TODO(max): Find a way to mark these guards as not needed for correctness... as in, once + // the data dependency is gone (say, the StringGetbyte is elided), they can also be elided. + // + // This is unlike most other guards. + let unboxed_index = fun.push_insn(block, hir::Insn::GuardLess { left: unboxed_index, right: len, state }); + let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); + let _ = fun.push_insn(block, hir::Insn::GuardGreaterEq { left: unboxed_index, right: zero, state }); + let result = fun.push_insn(block, hir::Insn::StringGetbyte { string: recv, index: unboxed_index }); return Some(result); } None diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index a69628b8690d67..49f139b45e76a7 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -649,7 +649,7 @@ pub enum Insn { StringIntern { val: InsnId, state: InsnId }, StringConcat { strings: Vec, state: InsnId }, /// Call rb_str_getbyte with known-Fixnum index - StringGetbyteFixnum { string: InsnId, index: InsnId }, + StringGetbyte { string: InsnId, index: InsnId }, StringSetbyteFixnum { string: InsnId, index: InsnId, value: InsnId }, StringAppend { recv: InsnId, other: InsnId, state: InsnId }, StringAppendCodepoint { recv: InsnId, other: InsnId, state: InsnId }, @@ -1004,7 +1004,7 @@ impl Insn { // but we don't have type information here in `impl Insn`. See rb_range_new(). Insn::NewRange { .. } => true, Insn::NewRangeFixnum { .. } => false, - Insn::StringGetbyteFixnum { .. } => false, + Insn::StringGetbyte { .. } => false, Insn::IsBlockGiven => false, Insn::BoxFixnum { .. } => false, Insn::BoxBool { .. } => false, @@ -1118,8 +1118,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Ok(()) } - Insn::StringGetbyteFixnum { string, index, .. } => { - write!(f, "StringGetbyteFixnum {string}, {index}") + Insn::StringGetbyte { string, index, .. } => { + write!(f, "StringGetbyte {string}, {index}") } Insn::StringSetbyteFixnum { string, index, value, .. } => { write!(f, "StringSetbyteFixnum {string}, {index}, {value}") @@ -1818,7 +1818,7 @@ impl Function { &StringCopy { val, chilled, state } => StringCopy { val: find!(val), chilled, state }, &StringIntern { val, state } => StringIntern { val: find!(val), state: find!(state) }, &StringConcat { ref strings, state } => StringConcat { strings: find_vec!(strings), state: find!(state) }, - &StringGetbyteFixnum { string, index } => StringGetbyteFixnum { string: find!(string), index: find!(index) }, + &StringGetbyte { string, index } => StringGetbyte { string: find!(string), index: find!(index) }, &StringSetbyteFixnum { string, index, value } => StringSetbyteFixnum { string: find!(string), index: find!(index), value: find!(value) }, &StringAppend { recv, other, state } => StringAppend { recv: find!(recv), other: find!(other), state: find!(state) }, &StringAppendCodepoint { recv, other, state } => StringAppendCodepoint { recv: find!(recv), other: find!(other), state: find!(state) }, @@ -2038,7 +2038,7 @@ impl Function { Insn::StringCopy { .. } => types::StringExact, Insn::StringIntern { .. } => types::Symbol, Insn::StringConcat { .. } => types::StringExact, - Insn::StringGetbyteFixnum { .. } => types::Fixnum.union(types::NilClass), + Insn::StringGetbyte { .. } => types::Fixnum, Insn::StringSetbyteFixnum { .. } => types::Fixnum, Insn::StringAppend { .. } => types::StringExact, Insn::StringAppendCodepoint { .. } => types::StringExact, @@ -3566,7 +3566,7 @@ impl Function { worklist.extend(strings); worklist.push_back(state); } - &Insn::StringGetbyteFixnum { string, index } => { + &Insn::StringGetbyte { string, index } => { worklist.push_back(string); worklist.push_back(index); } @@ -4450,9 +4450,9 @@ impl Function { self.assert_subtype(insn_id, left, types::CInt64)?; self.assert_subtype(insn_id, right, types::CInt64) }, - Insn::StringGetbyteFixnum { string, index } => { + Insn::StringGetbyte { string, index } => { self.assert_subtype(insn_id, string, types::String)?; - self.assert_subtype(insn_id, index, types::Fixnum) + self.assert_subtype(insn_id, index, types::CInt64) }, Insn::StringSetbyteFixnum { string, index, value } => { self.assert_subtype(insn_id, string, types::String)?; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 60135b6ac9e969..bdc26f758e107e 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -6451,10 +6451,15 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v26:StringExact = GuardType v11, StringExact v27:Fixnum = GuardType v12, Fixnum - v28:NilClass|Fixnum = StringGetbyteFixnum v26, v27 + v28:CInt64 = UnboxFixnum v27 + v29:CInt64 = LoadField v26, :len@0x1038 + v30:CInt64 = GuardLess v28, v29 + v31:CInt64[0] = Const CInt64(0) + v32:CInt64 = GuardGreaterEq v30, v31 + v33:Fixnum = StringGetbyte v26, v30 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v28 + Return v33 "); } @@ -6483,6 +6488,11 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1000) v30:StringExact = GuardType v11, StringExact v31:Fixnum = GuardType v12, Fixnum + v32:CInt64 = UnboxFixnum v31 + v33:CInt64 = LoadField v30, :len@0x1038 + v34:CInt64 = GuardLess v32, v33 + v35:CInt64[0] = Const CInt64(0) + v36:CInt64 = GuardGreaterEq v34, v35 IncrCounter inline_cfunc_optimized_send_count v22:Fixnum[5] = Const Value(5) CheckInterrupts From b9f1976f2334b4d9f55be5607de408eb7973c188 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 24 Nov 2025 23:37:24 -0500 Subject: [PATCH 061/367] ZJIT: Add HIR test for VM_OPT_NEWARRAY_SEND_PACK_BUFFER --- zjit/src/hir/tests.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 314eb7777cbf6c..79ba1d30c53443 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2121,7 +2121,42 @@ pub mod hir_build_tests { "); } - // TODO(max): Add a test for VM_OPT_NEWARRAY_SEND_PACK_BUFFER + #[test] + fn test_opt_newarray_send_pack_buffer() { + eval(r#" + def test(a,b) + sum = a+b + buf = "" + [a,b].pack 'C', buffer: buf + buf + end + "#); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@7 + v3:BasicObject = GetLocal l0, SP@6 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + v29:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v30:StringExact = StringCopy v29 + v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v37:StringExact = StringCopy v36 + v39:BasicObject = GetLocal l0, EP@3 + SideExit UnhandledNewarraySend(PACK_BUFFER) + "); + } #[test] fn test_opt_newarray_send_include_p() { From d0bb505a04bcd6a3d86c01d7c402c1f6205e69b4 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 1 Dec 2025 11:57:11 -0800 Subject: [PATCH 062/367] ZJIT: Split Lea memory reads on x86_64 --- zjit/src/backend/x86_64/mod.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/zjit/src/backend/x86_64/mod.rs b/zjit/src/backend/x86_64/mod.rs index d17286f51fda25..a9bd57f368c59b 100644 --- a/zjit/src/backend/x86_64/mod.rs +++ b/zjit/src/backend/x86_64/mod.rs @@ -559,7 +559,8 @@ impl Assembler { asm.store(mem_out, SCRATCH0_OPND); } } - Insn::Lea { out, .. } => { + Insn::Lea { opnd, out } => { + *opnd = split_stack_membase(asm, *opnd, SCRATCH0_OPND, &stack_state); let mem_out = split_memory_write(out, SCRATCH0_OPND); asm.push_insn(insn); if let Some(mem_out) = mem_out { @@ -1804,4 +1805,20 @@ mod tests { "); assert_snapshot!(cb.hexdump(), @"4c8b55f84c8b5df04d8b5b024d0f441a4c895df8"); } + + #[test] + fn test_lea_split_memory_read() { + let (mut asm, mut cb) = setup_asm(); + + let opnd = Opnd::Mem(Mem { base: MemBase::Stack { stack_idx: 0, num_bits: 64 }, disp: 0, num_bits: 64 }); + let _ = asm.lea(opnd); + asm.compile_with_num_regs(&mut cb, 0); + + assert_disasm_snapshot!(cb.disasm(), @" + 0x0: mov r11, qword ptr [rbp - 8] + 0x4: lea r11, [r11] + 0x7: mov qword ptr [rbp - 8], r11 + "); + assert_snapshot!(cb.hexdump(), @"4c8b5df84d8d1b4c895df8"); + } } From 2dfb8149d58694cd578dbe2222640499ac021231 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 18:26:09 +0900 Subject: [PATCH 063/367] Clean generated transcoders --- enc/Makefile.in | 1 + enc/depend | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/enc/Makefile.in b/enc/Makefile.in index 17888f8f9b5e10..2a3c45169fbb22 100644 --- a/enc/Makefile.in +++ b/enc/Makefile.in @@ -31,6 +31,7 @@ BUILTIN_ENCS = enc/ascii.c enc/us_ascii.c\ enc/unicode.c enc/utf_8.c BUILTIN_TRANSES = enc/trans/newline.trans +BUILTIN_TRANS_CSRCS = $(BUILTIN_TRANSES:.trans=.c) RUBY_SO_NAME = @RUBY_SO_NAME@ LIBRUBY = @LIBRUBY@ diff --git a/enc/depend b/enc/depend index a458b63887e858..4bf97dc880f195 100644 --- a/enc/depend +++ b/enc/depend @@ -157,15 +157,15 @@ clean: % end % unless inplace $(Q)$(RM) enc/unicode/*/casefold.h enc/unicode/*/name2ctype.h - $(Q)$(RM) enc/jis/props.h - -$(Q)$(RMDIR) enc/unicode<%=ignore_error%> + $(Q)$(RM) enc/trans/newline.c enc/jis/props.h + -$(Q)$(RMDIR) enc/trnas enc/unicode<%=ignore_error%> % end % workdirs.reverse_each do|d| -$(Q)$(RMDIR) <%=pathrep[d]%><%=ignore_error%> % end clean-srcs: - $(Q)$(RM) <%=pathrep['$(TRANSCSRCS)']%> + $(Q)$(RM) <%=pathrep['$(TRANSCSRCS)']%> <%=pathrep['$(BUILTIN_TRANS_CSRCS)']%> -$(Q)$(RMDIR) <%=pathrep['enc/trans']%><%=ignore_error%> $(Q)$(RM) enc/unicode/*/casefold.h enc/unicode/*/name2ctype.h $(Q)$(RM) enc/jis/props.h From 07ea9a38097c088efd6c2872f2a563dbc69a544a Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 25 Nov 2025 23:26:02 +0100 Subject: [PATCH 064/367] ZJIT: Optimize GetIvar for non-T_OBJECT * All Invariant::SingleRactorMode PatchPoint are replaced by assume_single_ractor_mode() to fix https://github.com/Shopify/ruby/issues/875 for SingleRactorMode patchpoints. --- test/ruby/test_zjit.rb | 30 ++++++- zjit/bindgen/src/main.rs | 1 + zjit/src/codegen.rs | 5 ++ zjit/src/cruby.rs | 1 + zjit/src/cruby_bindings.inc.rs | 1 + zjit/src/hir.rs | 87 +++++++++++++------- zjit/src/hir/opt_tests.rs | 142 +++++++++++++++++++++++++++++++++ 7 files changed, 236 insertions(+), 31 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index d821d8ad5cca69..6609bb2461c31d 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -3017,7 +3017,35 @@ def test test Ractor.new { test }.value - } + }, call_threshold: 2 + end + + def test_ivar_get_with_already_multi_ractor_mode + assert_compiles '42', %q{ + class Foo + def self.set_bar + @bar = [] # needs to be a ractor unshareable object + end + + def self.bar + @bar + rescue Ractor::IsolationError + 42 + end + end + + Foo.set_bar + r = Ractor.new { + Ractor.receive + Foo.bar + } + + Foo.bar + Foo.bar + + r << :go + r.value + }, call_threshold: 2 end def test_ivar_set_with_multi_ractor_mode diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 6659049242983b..0860c073cd17ed 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -326,6 +326,7 @@ fn main() { .allowlist_function("rb_attr_get") .allowlist_function("rb_ivar_defined") .allowlist_function("rb_ivar_get") + .allowlist_function("rb_ivar_get_at_no_ractor_check") .allowlist_function("rb_ivar_set") .allowlist_function("rb_mod_name") .allowlist_var("rb_vm_insn_count") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index ac1c65b26e1c13..bb19d7d8209ff8 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -349,6 +349,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::Const { val: Const::Value(val) } => gen_const_value(val), &Insn::Const { val: Const::CPtr(val) } => gen_const_cptr(val), &Insn::Const { val: Const::CInt64(val) } => gen_const_long(val), + &Insn::Const { val: Const::CUInt16(val) } => gen_const_uint16(val), Insn::Const { .. } => panic!("Unexpected Const in gen_insn: {insn}"), Insn::NewArray { elements, state } => gen_new_array(asm, opnds!(elements), &function.frame_state(*state)), Insn::NewHash { elements, state } => gen_new_hash(jit, asm, opnds!(elements), &function.frame_state(*state)), @@ -1154,6 +1155,10 @@ fn gen_const_long(val: i64) -> lir::Opnd { Opnd::Imm(val) } +fn gen_const_uint16(val: u16) -> lir::Opnd { + Opnd::UImm(val as u64) +} + /// Compile a basic block argument fn gen_param(asm: &mut Assembler, idx: usize) -> lir::Opnd { // Allocate a register or a stack slot diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 72c44ccc6e412f..9ed3a1abf79ca2 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1381,6 +1381,7 @@ pub(crate) mod ids { name: _as_heap name: thread_ptr name: self_ content: b"self" + name: rb_ivar_get_at_no_ractor_check } /// Get an CRuby `ID` to an interned string, e.g. a particular method name. diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index fb329a07166914..7bd392563987a3 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -2034,6 +2034,7 @@ unsafe extern "C" { pub fn rb_obj_shape_id(obj: VALUE) -> shape_id_t; pub fn rb_shape_get_iv_index(shape_id: shape_id_t, id: ID, value: *mut attr_index_t) -> bool; pub fn rb_shape_transition_add_ivar_no_warnings(obj: VALUE, id: ID) -> shape_id_t; + pub fn rb_ivar_get_at_no_ractor_check(obj: VALUE, index: attr_index_t) -> VALUE; pub fn rb_gvar_get(arg1: ID) -> VALUE; pub fn rb_gvar_set(arg1: ID, arg2: VALUE) -> VALUE; pub fn rb_ensure_iv_list_size(obj: VALUE, current_len: u32, newsize: u32); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 49f139b45e76a7..5ede64d42c2d80 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1742,6 +1742,15 @@ impl Function { self.blocks.len() } + pub fn assume_single_ractor_mode(&mut self, block: BlockId, state: InsnId) -> bool { + if unsafe { rb_jit_multi_ractor_p() } { + false + } else { + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state }); + true + } + } + /// Return a copy of the instruction where the instruction and its operands have been read from /// the union-find table (to find the current most-optimized version of this instruction). See /// [`UnionFind`] for more. @@ -2377,6 +2386,17 @@ impl Function { } } + fn is_metaclass(&self, object: VALUE) -> bool { + unsafe { + if RB_TYPE_P(object, RUBY_T_CLASS) && rb_zjit_singleton_class_p(object) { + let attached = rb_class_attached_object(object); + RB_TYPE_P(attached, RUBY_T_CLASS) || RB_TYPE_P(attached, RUBY_T_MODULE) + } else { + false + } + } + } + /// Rewrite SendWithoutBlock opcodes into SendWithoutBlockDirect opcodes if we know the target /// ISEQ statically. This removes run-time method lookups and opens the door for inlining. /// Also try and inline constant caches, specialize object allocations, and more. @@ -2483,9 +2503,9 @@ impl Function { // Patch points: // Check for "defined with an un-shareable Proc in a different Ractor" - if !procv.shareable_p() { + if !procv.shareable_p() && !self.assume_single_ractor_mode(block, state) { // TODO(alan): Turn this into a ractor belonging guard to work better in multi ractor mode. - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state }); + self.push_insn_id(block, insn_id); continue; } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if klass.instance_can_have_singleton_class() { @@ -2498,6 +2518,12 @@ impl Function { let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { recv, cd, cme, iseq, args, state }); self.make_equal_to(insn_id, send_direct); } else if def_type == VM_METHOD_TYPE_IVAR && args.is_empty() { + // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. + // We omit gen_prepare_non_leaf_call on gen_getivar, so it's unsafe to raise for multi-ractor mode. + if self.is_metaclass(klass) && !self.assume_single_ractor_mode(block, state) { + self.push_insn_id(block, insn_id); continue; + } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if klass.instance_can_have_singleton_class() { self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); @@ -2507,31 +2533,21 @@ impl Function { } let id = unsafe { get_cme_def_body_attr_id(cme) }; - // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. - // We omit gen_prepare_non_leaf_call on gen_getivar, so it's unsafe to raise for multi-ractor mode. - if unsafe { rb_zjit_singleton_class_p(klass) } { - let attached = unsafe { rb_class_attached_object(klass) }; - if unsafe { RB_TYPE_P(attached, RUBY_T_CLASS) || RB_TYPE_P(attached, RUBY_T_MODULE) } { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state }); - } - } let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, ic: std::ptr::null(), state }); self.make_equal_to(insn_id, getivar); } else if let (VM_METHOD_TYPE_ATTRSET, &[val]) = (def_type, args.as_slice()) { + // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. + // We omit gen_prepare_non_leaf_call on gen_getivar, so it's unsafe to raise for multi-ractor mode. + if self.is_metaclass(klass) && !self.assume_single_ractor_mode(block, state) { + self.push_insn_id(block, insn_id); continue; + } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } let id = unsafe { get_cme_def_body_attr_id(cme) }; - // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. - // We omit gen_prepare_non_leaf_call on gen_setivar, so it's unsafe to raise for multi-ractor mode. - if unsafe { rb_zjit_singleton_class_p(klass) } { - let attached = unsafe { rb_class_attached_object(klass) }; - if unsafe { RB_TYPE_P(attached, RUBY_T_CLASS) || RB_TYPE_P(attached, RUBY_T_MODULE) } { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state }); - } - } self.push_insn(block, Insn::SetIvar { self_val: recv, id, ic: std::ptr::null(), val, state }); self.make_equal_to(insn_id, val); } else if def_type == VM_METHOD_TYPE_OPTIMIZED { @@ -2657,12 +2673,9 @@ impl Function { self.push_insn_id(block, insn_id); continue; } let cref_sensitive = !unsafe { (*ice).ic_cref }.is_null(); - let multi_ractor_mode = unsafe { rb_jit_multi_ractor_p() }; - if cref_sensitive || multi_ractor_mode { + if cref_sensitive || !self.assume_single_ractor_mode(block, state) { self.push_insn_id(block, insn_id); continue; } - // Assume single-ractor mode. - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state }); // Invalidate output code on any constant writes associated with constants // referenced after the PatchPoint. self.push_insn(block, Insn::PatchPoint { invariant: Invariant::StableConstantNames { idlist }, state }); @@ -2829,11 +2842,6 @@ impl Function { self.push_insn_id(block, insn_id); continue; } assert!(recv_type.shape().is_valid()); - if !recv_type.flags().is_t_object() { - // Check if the receiver is a T_OBJECT - self.push_insn(block, Insn::IncrCounter(Counter::getivar_fallback_not_t_object)); - self.push_insn_id(block, insn_id); continue; - } if recv_type.shape().is_too_complex() { // too-complex shapes can't use index access self.push_insn(block, Insn::IncrCounter(Counter::getivar_fallback_too_complex)); @@ -2847,6 +2855,17 @@ impl Function { // entered the compiler. That means we can just return nil for this // shape + iv name self.push_insn(block, Insn::Const { val: Const::Value(Qnil) }) + } else if !recv_type.flags().is_t_object() { + // NOTE: it's fine to use rb_ivar_get_at_no_ractor_check because + // getinstancevariable does assume_single_ractor_mode() + let ivar_index_insn: InsnId = self.push_insn(block, Insn::Const { val: Const::CUInt16(ivar_index as u16) }); + self.push_insn(block, Insn::CCall { + cfunc: rb_ivar_get_at_no_ractor_check as *const u8, + recv: self_val, + args: vec![ivar_index_insn], + name: ID!(rb_ivar_get_at_no_ractor_check), + return_type: types::BasicObject, + elidable: true }) } else if recv_type.flags().is_embedded() { // See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h let offset = ROBJECT_OFFSET_AS_ARY as i32 + (SIZEOF_VALUE * ivar_index.to_usize()) as i32; @@ -4277,6 +4296,7 @@ impl Function { | Insn::ObjectAlloc { val, .. } | Insn::DupArrayInclude { target: val, .. } | Insn::GetIvar { self_val: val, .. } + | Insn::CCall { recv: val, .. } | Insn::FixnumBitCheck { val, .. } // TODO (https://github.com/Shopify/ruby/issues/859) this should check Fixnum, but then test_checkkeyword_tests_fixnum_bit fails | Insn::DefinedIvar { self_val: val, .. } => { self.assert_subtype(insn_id, val, types::BasicObject) @@ -4298,7 +4318,6 @@ impl Function { | Insn::Send { recv, ref args, .. } | Insn::SendForward { recv, ref args, .. } | Insn::InvokeSuper { recv, ref args, .. } - | Insn::CCall { recv, ref args, .. } | Insn::CCallWithFrame { recv, ref args, .. } | Insn::CCallVariadic { recv, ref args, .. } | Insn::ArrayInclude { target: recv, elements: ref args, .. } => { @@ -5693,7 +5712,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // ic is in arg 1 // Assume single-Ractor mode to omit gen_prepare_non_leaf_call on gen_getivar // TODO: We only really need this if self_val is a class/module - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state: exit_id }); + if !fun.assume_single_ractor_mode(block, exit_id) { + // gen_getivar assumes single Ractor; side-exit into the interpreter + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledYARVInsn(opcode) }); + break; // End the block + } let result = fun.push_insn(block, Insn::GetIvar { self_val: self_param, id, ic, state: exit_id }); state.stack_push(result); } @@ -5702,7 +5725,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let ic = get_arg(pc, 1).as_ptr(); // Assume single-Ractor mode to omit gen_prepare_non_leaf_call on gen_setivar // TODO: We only really need this if self_val is a class/module - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state: exit_id }); + if !fun.assume_single_ractor_mode(block, exit_id) { + // gen_setivar assumes single Ractor; side-exit into the interpreter + fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledYARVInsn(opcode) }); + break; // End the block + } let val = state.stack_pop()?; fun.push_insn(block, Insn::SetIvar { self_val: self_param, id, ic, val, state: exit_id }); } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index bdc26f758e107e..9809c8bffbee01 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5231,6 +5231,148 @@ mod hir_opt_tests { "); } + #[test] + fn test_optimize_getivar_on_module() { + eval(" + module M + @foo = 42 + def self.test = @foo + end + M.test + "); + assert_snapshot!(hir_string_proc("M.method(:test)"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + v16:HeapBasicObject = GuardType v6, HeapBasicObject + v17:HeapBasicObject = GuardShape v16, 0x1000 + v18:CUInt16[0] = Const CUInt16(0) + v19:BasicObject = CCall v17, :rb_ivar_get_at_no_ractor_check@0x1008, v18 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_getivar_on_class() { + eval(" + class C + @foo = 42 + def self.test = @foo + end + C.test + "); + assert_snapshot!(hir_string_proc("C.method(:test)"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + v16:HeapBasicObject = GuardType v6, HeapBasicObject + v17:HeapBasicObject = GuardShape v16, 0x1000 + v18:CUInt16[0] = Const CUInt16(0) + v19:BasicObject = CCall v17, :rb_ivar_get_at_no_ractor_check@0x1008, v18 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_getivar_on_t_data() { + eval(" + class C < Range + def test = @a + end + obj = C.new 0, 1 + obj.instance_variable_set(:@a, 1) + obj.test + TEST = C.instance_method(:test) + "); + assert_snapshot!(hir_string_proc("TEST"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + v16:HeapBasicObject = GuardType v6, HeapBasicObject + v17:HeapBasicObject = GuardShape v16, 0x1000 + v18:CUInt16[0] = Const CUInt16(0) + v19:BasicObject = CCall v17, :rb_ivar_get_at_no_ractor_check@0x1008, v18 + CheckInterrupts + Return v19 + "); + } + + #[test] + fn test_optimize_getivar_on_module_multi_ractor() { + eval(" + module M + @foo = 42 + def self.test = @foo + end + Ractor.new {}.value + M.test + "); + assert_snapshot!(hir_string_proc("M.method(:test)"), @r" + fn test@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit UnhandledYARVInsn(getinstancevariable) + "); + } + + #[test] + fn test_optimize_attr_reader_on_module_multi_ractor() { + eval(" + module M + @foo = 42 + class << self + attr_reader :foo + end + def self.test = foo + end + Ractor.new {}.value + M.test + "); + assert_snapshot!(hir_string_proc("M.method(:test)"), @r" + fn test@:7: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = SendWithoutBlock v6, :foo + CheckInterrupts + Return v11 + "); + } + #[test] fn test_dont_optimize_getivar_polymorphic() { set_call_threshold(3); From 01fd348847a22cee75426373799ebc2b1b797209 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 12 Jan 2025 22:42:06 +0900 Subject: [PATCH 065/367] Win32: Fix @ in middle of commands `@` is not a command, and cannot be placed after `||`. --- win32/Makefile.sub | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index edbe2a340209d3..c27419c9905b63 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1196,12 +1196,12 @@ ext/clean.sub ext/distclean.sub ext/realclean.sub \ $(MAKE) $(MFLAGS) $(@F) & \ cd %CD% & \ $(RMDIRS) %I \ - ))) || @ + ))) || $(NULLCMD) ext/distclean ext/realclean .bundle/distclean .bundle/realclean:: $(Q)cd $(@D) 2>nul && (for /R $(EXTS) %I in (exts.mk*) \ - do $(Q)(del %I & rmdir %~dpI)) || @ - -$(Q)rmdir $(@D) 2> nul || @ + do $(Q)(del %I & rmdir %~dpI)) || $(NULLCMD) + -$(Q)rmdir $(@D) 2> nul || $(NULLCMD) .bundle/realclean:: @$(RMALL) $(tooldir)/bunlder/*.lock $(srcdir)/.bundle From 9cdee9db9b3bfc58f773912c072ce7ab78a7d724 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 10 Jan 2025 11:55:09 +0900 Subject: [PATCH 066/367] Win32: Fix rm.bat removing non existent file --- win32/rm.bat | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/win32/rm.bat b/win32/rm.bat index 500a4abe2e5562..8cffd663c81558 100755 --- a/win32/rm.bat +++ b/win32/rm.bat @@ -8,11 +8,12 @@ if "%1" == "--debug" (shift & set PROMPT=$E[34m+$E[m$S & echo on & goto :optloop :begin if "%1" == "" goto :end set p=%1 +shift set p=%p:/=\% -if exist "%p%" del /q "%p%" > nul +if not exist "%p%" goto :begin +del /q "%p%" > nul && goto :begin if "%recursive%" == "1" for /D %%I in (%p%) do ( rd /s /q %%I ) -shift goto :begin :end From bb76e65f5696e936f72aa2ee0d17b5087f4a26ad Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 10 Jan 2025 13:21:48 +0900 Subject: [PATCH 067/367] Win32: Add `DLEXT` for clean-spec --- win32/Makefile.sub | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index c27419c9905b63..a8ad28cb1a7d4e 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -435,6 +435,7 @@ EXTSTATIC = OBJEXT = obj ASMEXT = asm +DLEXT = so INSTALLED_LIST= .installed.list @@ -872,7 +873,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define THREAD_IMPL_H "$(THREAD_IMPL_H)" #define THREAD_IMPL_SRC "$(THREAD_IMPL_SRC)" #define LOAD_RELATIVE 1 -#define DLEXT ".so" +#define DLEXT ".$(DLEXT)" !if "$(libdir_basename)" != "lib" #define LIBDIR_BASENAME "$(libdir_basename)" !endif @@ -985,7 +986,7 @@ s,@STATIC@,$(STATIC),;t t s,@CCDLFLAGS@,,;t t s,@LDSHARED@,$(LDSHARED),;t t s,@SOEXT@,dll,;t t -s,@DLEXT@,so,;t t +s,@DLEXT@,$(DLEXT),;t t s,@LIBEXT@,lib,;t t s,@STRIP@,$(STRIP),;t t s,@ENCSTATIC@,$(ENCSTATIC),;t t From b0b8eb6a31fbe484319c9fd042433094e0e69c93 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 10 Jan 2025 21:17:02 +0900 Subject: [PATCH 068/367] Win32: Fix removing symlink Try `rd` first for symlink to a directory; `del` attemps to remove all files under the target directory, instead of the symlink itself. --- win32/rm.bat | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/win32/rm.bat b/win32/rm.bat index 8cffd663c81558..8263dadd28272e 100755 --- a/win32/rm.bat +++ b/win32/rm.bat @@ -11,7 +11,11 @@ set p=%1 shift set p=%p:/=\% if not exist "%p%" goto :begin -del /q "%p%" > nul && goto :begin + +::- Try `rd` first for symlink to a directory; `del` attemps to remove all +::- files under the target directory, instead of the symlink itself. +(rd /q "%p%" || del /q "%p%") 2> nul && goto :begin + if "%recursive%" == "1" for /D %%I in (%p%) do ( rd /s /q %%I ) From 00d05dfca5584537d4581420c26ce0864d3eff96 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 10 Jan 2025 21:19:36 +0900 Subject: [PATCH 069/367] Win32: Remove extra suffix for sub-make --- win32/Makefile.sub | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index a8ad28cb1a7d4e..2670e775dccc41 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1193,8 +1193,8 @@ ext/clean.sub ext/distclean.sub ext/realclean.sub \ call set n=%I && \ call set n=%n:%CD%\$(@D)\=% && \ call set n=%n:\.=% && \ - call echo $(@F)ing %n:\=/% & \ - $(MAKE) $(MFLAGS) $(@F) & \ + call echo $(@F:.sub=)ing %n:\=/% & \ + $(MAKE) $(MFLAGS) $(@F:.sub=) & \ cd %CD% & \ $(RMDIRS) %I \ ))) || $(NULLCMD) From a6c5d290abaa232a60c21fa2a48ac0228de8270b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 01:06:34 +0900 Subject: [PATCH 070/367] Win32: Clean prism - intermediate source files - timestamp files - build directories --- win32/Makefile.sub | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 2670e775dccc41..79fbdb30c411e9 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1168,12 +1168,39 @@ clean-local:: $(Q)$(RM) miniruby.rc $(RUBY_INSTALL_NAME).rc $(RUBYW_INSTALL_NAME).rc $(RUBY_SO_NAME).rc $(Q)$(RM) *.map *.pdb *.ilk *.exp $(RUBYDEF) ext\ripper\y.output +clean-local:: clean-prism + +clean-prism: + @for /R $(PRISM_BUILD_DIR:/=\) %I in (.time) do @(del /q %I && $(RMDIRS) %~pI) 2> nul || $(NULLCMD) + distclean-local:: $(Q)$(RM) ext\config.cache $(RBCONFIG:/=\) $(CONFIG_H:/=\) -$(Q)$(RM) $(INSTALLED_LIST:/=\) $(arch_hdrdir:/=\)\ruby\config.h verconf.h -$(Q)$(RMDIRS) $(arch_hdrdir:/=\)\ruby -$(Q)$(RMDIR) win32 +distclean-local:: distclean-prism + +distclean-prism: clean-prism +!if "$(srcdir)" != "." + $(Q)for %I in ( \ + $(PRISM_SRCDIR:/=\)\templates\ext\prism\*.c.erb \ + $(PRISM_SRCDIR:/=\)\templates\include\prism\*.h.erb \ + $(PRISM_SRCDIR:/=\)\templates\src\*.c.erb \ + ) do $(Q)(del /q prism\%~nI 2> nul || $(NULLCMD)) + $(Q)for /D %I in (prism\*) do $(Q)$(RMDIRS) %I + $(Q)$(RMDIRS) prism +!endif + +realclean-prism: distclean-prism +!if "$(srcdir)" == "." + $(Q)for %I in ( \ + $(PRISM_SRCDIR:/=\)\templates\ext\prism\*.c.erb \ + $(PRISM_SRCDIR:/=\)\templates\include\prism\*.h.erb \ + $(PRISM_SRCDIR:/=\)\templates\src\*.c.erb \ + ) do $(Q)(del /q $(PRISM_SRCDIR:/=\)\%~nI 2> nul || $(NULLCMD)) +!endif + .bundle/clean:: .bundle/clean.sub .bundle/distclean:: .bundle/distclean.sub .bundle/realclean:: .bundle/realclean.sub From fa513886410a85c2229acfa558c5375e5fad278b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 01:14:23 +0900 Subject: [PATCH 071/367] Win32: Append `-p` option to `RMDIRS` `rmdirs.bat` may require this option explicitly to remove parent directories, in the future. --- win32/Makefile.sub | 4 ++-- win32/rmdirs.bat | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 79fbdb30c411e9..737c8f6bd6026b 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -117,7 +117,7 @@ IFCHANGE = $(COMSPEC) /C $(srcdir:/=\)\win32\ifchange.bat RM = $(COMSPEC) /C $(srcdir:/=\)\win32\rm.bat RM1 = del RMDIR = $(COMSPEC) /C $(srcdir:/=\)\win32\rmdirs.bat -RMDIRS = $(COMSPEC) /C $(srcdir:/=\)\win32\rmdirs.bat +RMDIRS = $(COMSPEC) /C $(srcdir:/=\)\win32\rmdirs.bat -p RMALL = $(COMSPEC) /C $(srcdir:/=\)\win32\rm.bat -f -r MAKEDIRS = $(COMSPEC) /E:ON /C $(srcdir:/=\)\win32\makedirs.bat TOUCH = $(BASERUBY) -run -e touch -- @@ -968,7 +968,7 @@ s,@LN_S@,$(LN_S),;t t s,@SET_MAKE@,MFLAGS = -$$(MAKEFLAGS),;t t s,@RM@,$$(COMSPEC) /C $$(top_srcdir:/=\)\win32\rm.bat,;t t s,@RMDIR@,$$(COMSPEC) /C $$(top_srcdir:/=\)\win32\rmdirs.bat,:t t -s,@RMDIRS@,$$(COMSPEC) /C $$(top_srcdir:/=\)\win32\rmdirs.bat,;t t +s,@RMDIRS@,$$(COMSPEC) /C $$(top_srcdir:/=\)\win32\rmdirs.bat -p,;t t s,@RMALL@,$$(COMSPEC) /C $$(top_srcdir:/=\)\win32\rm.bat -f -r,:t t s,@MAKEDIRS@,$$(COMSPEC) /E:ON /C $$(top_srcdir:/=\)\win32\makedirs.bat,;t t s,@LIBOBJS@,$(LIBOBJS),;t t diff --git a/win32/rmdirs.bat b/win32/rmdirs.bat index c3d7b637b3ea2a..a8abebd3839051 100755 --- a/win32/rmdirs.bat +++ b/win32/rmdirs.bat @@ -1,6 +1,9 @@ @echo off @setlocal EnableExtensions DisableDelayedExpansion || exit /b -1 -if "%1" == "-p" shift +set parents=1 +:optloop +if "%1" == "--debug" (shift & set PROMPT=$E[34m+$E[m$S & echo on & goto :optloop) +if "%1" == "-p" (shift & (set parents=1) & goto :optloop) :begin if "%1" == "" goto :end set dir=%1 @@ -12,6 +15,7 @@ if "%1" == "" goto :end if "%dir%" == "." goto :begin if "%dir%" == ".." goto :begin rd "%dir%" 2> nul || goto :begin + if "%parents%" == "" goto :begin :trim_sep if not /%dir:~-1%/ == /\/ goto :trim_base set dir=%dir:~0,-1% From ec80e92a4789533a2ae0be3aa3709c6d6ab229d2 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 11:53:03 +0900 Subject: [PATCH 072/367] Win32: Refine outputs - Suppress logos from sub makes. - Set the prompt for `for` command when `echo` is on. --- win32/Makefile.sub | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 737c8f6bd6026b..68de9d5cf4178b 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -13,6 +13,10 @@ PWD = $(MAKEDIR) empty = tooldir = $(srcdir)/tool +PROMPT = +$$S + +MAKEFLAGS = l$(MAKEFLAGS) + !ifndef MFLAGS MFLAGS=-l !endif @@ -1210,7 +1214,7 @@ realclean-prism: distclean-prism .bundle/realclean.sub:: ext/realclean.mk ext/clean.mk ext/distclean.mk ext/realclean.mk:: - $(Q)if exist $(EXTS_MK) $(MAKE) -k -f $(EXTS_MK) top_srcdir=$(srcdir) $(*F) + $(Q)if exist $(EXTS_MK) $(MAKE) $(MFLAGS) -k -f $(EXTS_MK) top_srcdir=$(srcdir) $(*F) ext/clean.sub ext/distclean.sub ext/realclean.sub \ .bundle/clean.sub .bundle/distclean.sub .bundle/realclean.sub:: From c4fb79caa72d667512d1ca7647ac0443b22a3d47 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 18:25:24 +0900 Subject: [PATCH 073/367] Win32: Clean generated sources --- win32/Makefile.sub | 3 +++ 1 file changed, 3 insertions(+) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 68de9d5cf4178b..f93f344b0d2d2d 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1183,6 +1183,9 @@ distclean-local:: -$(Q)$(RMDIRS) $(arch_hdrdir:/=\)\ruby -$(Q)$(RMDIR) win32 +distclean-local:: clean-srcs-local +distclean-ext:: clean-srcs-ext + distclean-local:: distclean-prism distclean-prism: clean-prism From b2b674562db8e922fa6a6bd0469f20f4caf0954e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 18:26:52 +0900 Subject: [PATCH 074/367] Win32: Support removing wildcards in middle of path --- win32/rm.bat | 71 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/win32/rm.bat b/win32/rm.bat index 8263dadd28272e..c41ebfa5ee0177 100755 --- a/win32/rm.bat +++ b/win32/rm.bat @@ -1,23 +1,64 @@ @echo off @setlocal EnableExtensions DisableDelayedExpansion || exit /b -1 + +set prog=%~n0 +set dryrun= set recursive= +set debug= +set error=0 +set parent= + :optloop if "%1" == "-f" shift -if "%1" == "-r" (shift & set "recursive=1" & goto :optloop) -if "%1" == "--debug" (shift & set PROMPT=$E[34m+$E[m$S & echo on & goto :optloop) +if "%1" == "-n" (shift & set "dryrun=%1" & goto :optloop) +if "%1" == "-r" (shift & set "recursive=%1" & goto :optloop) +if "%1" == "--debug" (shift & set "debug=%1" & set PROMPT=$E[34m+$E[m$S & echo on & goto :optloop) :begin -if "%1" == "" goto :end -set p=%1 -shift -set p=%p:/=\% -if not exist "%p%" goto :begin +if "%1" == "" goto :EOF + set p=%1 + shift + set p=%p:/=\% + call :remove %p% +goto :begin -::- Try `rd` first for symlink to a directory; `del` attemps to remove all -::- files under the target directory, instead of the symlink itself. -(rd /q "%p%" || del /q "%p%") 2> nul && goto :begin +:remove +setlocal -if "%recursive%" == "1" for /D %%I in (%p%) do ( - rd /s /q %%I -) -goto :begin -:end +::- Split %1 by '?' and '*', wildcard characters +for /f "usebackq delims=?* tokens=1*" %%I in ('%1') do (set "par=%%I" & set "sub=%%J") +if "%sub%" == "" goto :remove_plain +if "%sub:\=%" == "%sub%" goto :remove_plain + ::- Extract the first wildcard + set "q=%1" + call set "q=%%q:%par%=%%" + set q=%q:~0,1% + + ::- `delims` chars at the beginning are removed in `for` + if "%sub:~0,1%" == "\" ( + set "sub=%sub:~1%" + set "par=%par%%q%" + ) else ( + for /f "usebackq delims=\\ tokens=1*" %%I in ('%sub%') do (set "par=%par%%q%%%I" & set "sub=%%J") + ) + + ::- Recursive search + for /d %%D in (%par%) do ( + call :remove %sub% %2%%D\ + ) +goto :remove_end +:remove_plain + set p=%2%1 + if not exist "%1" goto :remove_end + if not "%dryrun%" == "" ( + echo Removing %p:\=/% + goto :remove_end + ) + ::- Try `rd` first for symlink to a directory; `del` attemps to remove all + ::- files under the target directory, instead of the symlink itself. + (rd /q "%p%" || del /q "%p%") 2> nul && goto :remove_end + + if "%recursive%" == "-r" for /D %%I in (%p%) do ( + rd /s /q %%I || call set error=%%ERRORLEVEL%% + ) +:remove_end +endlocal & set "error=%error%" & goto :EOF From 0e222991bf11e28457819d42d751b517686c71e9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 18:28:53 +0900 Subject: [PATCH 075/367] Win32: Remove DLL files linked by `prepare-vcpkg` --- win32/Makefile.sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index f93f344b0d2d2d..13e64d6e40ef27 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1170,7 +1170,7 @@ clean-local:: $(Q)$(RM) $(WINMAINOBJ) ext\extinit.c ext\extinit.$(OBJEXT) ext\vc*.pdb miniruby.lib $(Q)$(RM) $(RUBY_INSTALL_NAME).res $(RUBYW_INSTALL_NAME).res $(RUBY_SO_NAME).res $(Q)$(RM) miniruby.rc $(RUBY_INSTALL_NAME).rc $(RUBYW_INSTALL_NAME).rc $(RUBY_SO_NAME).rc - $(Q)$(RM) *.map *.pdb *.ilk *.exp $(RUBYDEF) ext\ripper\y.output + $(Q)$(RM) *.map *.pdb *.ilk *.exp *.dll $(RUBYDEF) ext\ripper\y.output clean-local:: clean-prism From b17f6a2aae96bb0faf0d379dc54eda9878541f77 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 11 Jan 2025 23:11:08 +0900 Subject: [PATCH 076/367] Win32: Clean miniprelude --- win32/Makefile.sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 13e64d6e40ef27..2904147b76f22f 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1178,7 +1178,7 @@ clean-prism: @for /R $(PRISM_BUILD_DIR:/=\) %I in (.time) do @(del /q %I && $(RMDIRS) %~pI) 2> nul || $(NULLCMD) distclean-local:: - $(Q)$(RM) ext\config.cache $(RBCONFIG:/=\) $(CONFIG_H:/=\) + $(Q)$(RM) ext\config.cache $(RBCONFIG:/=\) $(CONFIG_H:/=\) miniprelude.c -$(Q)$(RM) $(INSTALLED_LIST:/=\) $(arch_hdrdir:/=\)\ruby\config.h verconf.h -$(Q)$(RMDIRS) $(arch_hdrdir:/=\)\ruby -$(Q)$(RMDIR) win32 From a9961701582d842bb5866d57d8e22cf4aa46eba8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 12 Jan 2025 17:24:06 +0900 Subject: [PATCH 077/367] Win32: Remove bundled gems directories --- win32/Makefile.sub | 3 +++ 1 file changed, 3 insertions(+) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 2904147b76f22f..da666951b2580c 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1236,6 +1236,9 @@ ext/clean.sub ext/distclean.sub ext/realclean.sub \ ext/distclean ext/realclean .bundle/distclean .bundle/realclean:: $(Q)cd $(@D) 2>nul && (for /R $(EXTS) %I in (exts.mk*) \ do $(Q)(del %I & rmdir %~dpI)) || $(NULLCMD) + +.bundle/distclean .bundle/realclean:: + $(Q)for /D %I in ($(@D)\*) do $(Q)$(RMDIRS) %I || $(NULLCMD) -$(Q)rmdir $(@D) 2> nul || $(NULLCMD) .bundle/realclean:: From 0be626e7551f9e9ed201b9c4a66aab5fcf3e6fbb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 29 Nov 2025 17:13:21 +0900 Subject: [PATCH 078/367] Win32: Clean timestamp directory for platform --- common.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.mk b/common.mk index eb9b75ca7e3dd0..35c23159800160 100644 --- a/common.mk +++ b/common.mk @@ -801,7 +801,7 @@ clean-capi distclean-capi realclean-capi: clean-platform distclean-platform realclean-platform: $(Q) $(RM) $(PLATFORM_D) - -$(Q) $(RMDIR) $(PLATFORM_DIR) 2> $(NULL) || $(NULLCMD) + -$(Q) $(RMDIR) $(PLATFORM_DIR) $(TIMESTAMPDIR) 2> $(NULL) || $(NULLCMD) RUBYSPEC_CAPIEXT = spec/ruby/optional/capi/ext RUBYSPEC_CAPIEXT_SRCDIR = $(srcdir)/$(RUBYSPEC_CAPIEXT) From b563be302f635116e30e3d80812962650f5564ef Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 29 Nov 2025 17:13:56 +0900 Subject: [PATCH 079/367] Win32: Clean empty directories --- win32/Makefile.sub | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index da666951b2580c..c3db450abc30a0 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -1234,8 +1234,7 @@ ext/clean.sub ext/distclean.sub ext/realclean.sub \ ))) || $(NULLCMD) ext/distclean ext/realclean .bundle/distclean .bundle/realclean:: - $(Q)cd $(@D) 2>nul && (for /R $(EXTS) %I in (exts.mk*) \ - do $(Q)(del %I & rmdir %~dpI)) || $(NULLCMD) + $(Q)(for /D /R $(@D) %I in (.) do $(Q)$(RMDIRS) %I) || $(NULLCMD) .bundle/distclean .bundle/realclean:: $(Q)for /D %I in ($(@D)\*) do $(Q)$(RMDIRS) %I || $(NULLCMD) From e2d176556e36ae7911af3ff6163420a48b82af92 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 2 Dec 2025 08:12:45 +0900 Subject: [PATCH 080/367] CI: Distclean mswin Use the given `make-command` instead of the hard-coded `make` command. TODO: Use it for `make up` as well, in the future. --- .github/actions/setup/directories/action.yml | 9 ++++++++- .github/workflows/windows.yml | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml index b64227e4359c71..8389ef54b78e5e 100644 --- a/.github/actions/setup/directories/action.yml +++ b/.github/actions/setup/directories/action.yml @@ -19,6 +19,13 @@ inputs: Where binaries and other generated contents go. This will be created if absent. + make-command: + required: false + type: string + default: 'make' + description: >- + The command of `make`. + makeup: required: false type: boolean @@ -169,7 +176,7 @@ runs: shell: bash id: clean run: | - echo distclean='make -C ${{ inputs.builddir }} distclean' >> $GITHUB_OUTPUT + echo distclean='cd ${{ inputs.builddir }} && ${{ inputs.make-command }} distclean' >> $GITHUB_OUTPUT echo remained-files='find ${{ inputs.builddir }} -ls' >> $GITHUB_OUTPUT [ "${{ inputs.builddir }}" = "${{ inputs.srcdir }}" ] || echo final='rmdir ${{ inputs.builddir }}' >> $GITHUB_OUTPUT diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4659fb80f8434d..dd4bf0de968108 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -75,6 +75,8 @@ jobs: with: srcdir: src builddir: build + make-command: nmake + clean: true - name: Install tools with scoop run: | From 7df97983be41e893afee6d0012c6dced55ff98f8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 2 Dec 2025 11:39:29 +0900 Subject: [PATCH 081/367] [ruby/rubygems] Improve banner message for the default command. Co-authored-by: Benoit Daloze https://github.com/ruby/rubygems/commit/463488b439 Co-authored-by: Patrik Ragnarsson --- lib/bundler/cli.rb | 6 +++--- spec/bundler/bundler/cli_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index f5977863ddcc26..9c29751a7c31fb 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -133,10 +133,10 @@ def self.default_command(meth = nil) unless Bundler.settings[:default_cli_command] Bundler.ui.info <<-MSG - In the feature version of Bundler, running `bundle` without argument will no longer run `bundle install`. + In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. - If you wish to use feature behavior now with `bundle config set default_cli_command cli_help --global` - or you can continue to use the old behavior with `bundle config set default_cli_command install_or_cli_help --global`. + You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, + or you can continue to use the current behavior with `bundle config set default_cli_command install_or_cli_help --global`. This message will be removed after a default_cli_command value is set. MSG end diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index ac6b77ad7483bf..4503cea6a0058c 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -90,7 +90,7 @@ def out_with_macos_man_workaround it "tries to installs by default but print help on missing Gemfile" do bundle "", raise_on_error: false expect(err).to include("Could not locate Gemfile") - expect(out).to include("In the feature version of Bundler") + expect(out).to include("In a future version of Bundler") expect(out).to include("Bundler version #{Bundler::VERSION}"). and include("\n\nBundler commands:\n\n"). @@ -102,7 +102,7 @@ def out_with_macos_man_workaround it "runs bundle install when default_cli_command set to install" do bundle "config set default_cli_command install_or_cli_help" bundle "", raise_on_error: false - expect(out).to_not include("In the feature version of Bundler") + expect(out).to_not include("In a future version of Bundler") expect(err).to include("Could not locate Gemfile") end end From 0e22108d60fbd0e338fb6e110ddd81a93b45b592 Mon Sep 17 00:00:00 2001 From: Sam Westerman Date: Tue, 2 Dec 2025 07:00:22 +0000 Subject: [PATCH 082/367] [ruby/optparse] Remove `const_set` and instead use explicit assignments https://github.com/ruby/optparse/commit/6e2709a5fd --- lib/optparse.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/optparse.rb b/lib/optparse.rb index e0b0ff011b5455..aae73c86b32787 100644 --- a/lib/optparse.rb +++ b/lib/optparse.rb @@ -2314,42 +2314,42 @@ def message # Raises when ambiguously completable string is encountered. # class AmbiguousOption < ParseError - const_set(:Reason, 'ambiguous option') + Reason = 'ambiguous option' # :nodoc: end # # Raises when there is an argument for a switch which takes no argument. # class NeedlessArgument < ParseError - const_set(:Reason, 'needless argument') + Reason = 'needless argument' # :nodoc: end # # Raises when a switch with mandatory argument has no argument. # class MissingArgument < ParseError - const_set(:Reason, 'missing argument') + Reason = 'missing argument' # :nodoc: end # # Raises when switch is undefined. # class InvalidOption < ParseError - const_set(:Reason, 'invalid option') + Reason = 'invalid option' # :nodoc: end # # Raises when the given argument does not match required format. # class InvalidArgument < ParseError - const_set(:Reason, 'invalid argument') + Reason = 'invalid argument' # :nodoc: end # # Raises when the given argument word can't be completed uniquely. # class AmbiguousArgument < InvalidArgument - const_set(:Reason, 'ambiguous argument') + Reason = 'ambiguous argument' # :nodoc: end # @@ -2448,9 +2448,11 @@ def initialize(*args) # :nodoc: # and DecimalNumeric. See Acceptable argument classes (in source code). # module Acceptables - const_set(:DecimalInteger, OptionParser::DecimalInteger) - const_set(:OctalInteger, OptionParser::OctalInteger) - const_set(:DecimalNumeric, OptionParser::DecimalNumeric) + # :stopdoc: + DecimalInteger = OptionParser::DecimalInteger + OctalInteger = OptionParser::OctalInteger + DecimalNumeric = OptionParser::DecimalNumeric + # :startdoc: end end From 456ba321a84d34e76c8837ac96f47a11457480cb Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sun, 30 Nov 2025 19:16:52 -0800 Subject: [PATCH 083/367] [ruby/rubygems] Make BUNDLE_LOCKFILE environment variable have precedence over lockfile method in Gemfile It would be simpler to do `options[:lockfile] ||= ENV["BUNDLE_LOCKFILE"]`, but that doesn't work as `options` is frozen. Fixes https://github.com/ruby/rubygems/pull/9117 https://github.com/ruby/rubygems/commit/6e3603a0e9 --- lib/bundler/cli.rb | 6 ++++-- lib/bundler/man/gemfile.5 | 4 ++-- lib/bundler/man/gemfile.5.ronn | 2 +- spec/bundler/commands/install_spec.rb | 14 ++++++++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 9c29751a7c31fb..36ce04eb2a5d05 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -69,7 +69,7 @@ def initialize(*args) # lock --lockfile works differently than install --lockfile unless current_cmd == "lock" - custom_lockfile = options[:lockfile] || Bundler.settings[:lockfile] + custom_lockfile = options[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile] if custom_lockfile && !custom_lockfile.empty? Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", File.expand_path(custom_lockfile) reset_settings = true @@ -282,8 +282,10 @@ def install end require_relative "cli/install" + options = self.options.dup + options["lockfile"] ||= ENV["BUNDLE_LOCKFILE"] Bundler.settings.temporary(no_install: false) do - Install.new(options.dup).run + Install.new(options).run end end diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index fce7d8e178439b..a8c055a0c1a456 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -494,9 +494,9 @@ The \fBbundle install\fR \fB\-\-no\-lock\fR option (which disables lockfile crea .IP "2." 4 The \fBbundle install\fR \fB\-\-lockfile\fR option\. .IP "3." 4 -The \fBlockfile\fR method in the Gemfile\. -.IP "4." 4 The \fBBUNDLE_LOCKFILE\fR environment variable\. +.IP "4." 4 +The \fBlockfile\fR method in the Gemfile\. .IP "5." 4 The default behavior of adding \fB\.lock\fR to the end of the Gemfile name\. .IP "" 0 diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn index e4bc91359e3cc5..18d7bb826e4439 100644 --- a/lib/bundler/man/gemfile.5.ronn +++ b/lib/bundler/man/gemfile.5.ronn @@ -581,6 +581,6 @@ following precedence is used: 1. The `bundle install` `--no-lock` option (which disables lockfile creation). 1. The `bundle install` `--lockfile` option. -1. The `lockfile` method in the Gemfile. 1. The `BUNDLE_LOCKFILE` environment variable. +1. The `lockfile` method in the Gemfile. 1. The default behavior of adding `.lock` to the end of the Gemfile name. diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index bacd8d64f2d6c3..3dc8aa0dc01757 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -41,6 +41,20 @@ expect(bundled_app("OmgFile.lock")).to exist end + it "creates lockfile using BUNDLE_LOCKFILE instead of lockfile method" do + ENV["BUNDLE_LOCKFILE"] = "ReallyOmgFile.lock" + install_gemfile <<-G + lockfile "OmgFile.lock" + source "https://gem.repo1" + gem "myrack", "1.0" + G + + expect(bundled_app("ReallyOmgFile.lock")).to exist + expect(bundled_app("OmgFile.lock")).not_to exist + ensure + ENV.delete("BUNDLE_LOCKFILE") + end + it "creates lockfile based on --lockfile option is given" do gemfile bundled_app("OmgFile"), <<-G source "https://gem.repo1" From e515fa7ab1981df439a3a6b3635a0389f4216cce Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 26 Nov 2025 13:28:44 +0100 Subject: [PATCH 084/367] ZJIT: Improve documentation and make it easy to generate the types graph --- doc/jit/zjit.md | 49 ++++++++++++++++++++++++++----- zjit/src/hir_type/gen_hir_type.rb | 18 +++++++----- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/doc/jit/zjit.md b/doc/jit/zjit.md index f3b36b1fd5aaa5..7434d44b9d7d60 100644 --- a/doc/jit/zjit.md +++ b/doc/jit/zjit.md @@ -16,6 +16,35 @@ To build ZJIT on macOS: make -j miniruby ``` +To build ZJIT on Linux: + +```bash +./autogen.sh + +./configure \ + --enable-zjit=dev \ + --prefix="$HOME"/.rubies/ruby-zjit \ + --disable-install-doc + +make -j miniruby +``` + +Note that `--enable-zjit=dev` does a lot of IR validation, which will help to catch errors early but mean compilation and warmup are significantly slower. + +The valid values for `--enable-zjit` are, from fastest to slowest: +* `--enable-zjit`: enable ZJIT in release mode for maximum performance +* `--enable-zjit=stats`: enable ZJIT in extended-stats mode +* `--enable-zjit=dev_nodebug`: enable ZJIT in development mode but without slow runtime checks +* `--enable-zjit=dev`: enable ZJIT in debug mode for development, also enables `RUBY_DEBUG` + +### Regenerate bindings + +When modifying `zjit/bindgen/src/main.rs` you need to regenerate bindings in `zjit/src/cruby_bindings.inc.rs` with: + +```bash +make zjit-bindgen +``` + ## Documentation You can generate and open the source level documentation in your browser using: @@ -24,6 +53,16 @@ You can generate and open the source level documentation in your browser using: cargo doc --document-private-items -p zjit --open ``` +### Graph of the Type System + +You can generate a graph of the ZJIT type hierarchy using: + +```bash +ruby zjit/src/hir_type/gen_hir_type.rb > zjit/src/hir_type/hir_type.inc.rs +dot -O -Tpdf zjit_types.dot +open zjit_types.dot.pdf +``` + ## Testing Note that tests link against CRuby, so directly calling `cargo test`, or `cargo nextest` should not build. All tests are instead accessed through `make`. @@ -139,14 +178,8 @@ end ### Performance Ratio -The `ratio_in_zjit` stat shows the percentage of Ruby instructions executed in JIT code vs interpreter. This metric only appears when ZJIT is built with `--enable-zjit=stats` (which enables `rb_vm_insn_count` tracking) and represents a key performance indicator for ZJIT effectiveness. - -To build with stats support: - -```bash -./configure --enable-zjit=stats -make -j -``` +The `ratio_in_zjit` stat shows the percentage of Ruby instructions executed in JIT code vs interpreter. +This metric only appears when ZJIT is built with `--enable-zjit=stats` [or more](#build-instructions) (which enables `rb_vm_insn_count` tracking) and represents a key performance indicator for ZJIT effectiveness. ### Tracing side exits diff --git a/zjit/src/hir_type/gen_hir_type.rb b/zjit/src/hir_type/gen_hir_type.rb index 4e0ecc718f8f82..e51d0c04e1a324 100644 --- a/zjit/src/hir_type/gen_hir_type.rb +++ b/zjit/src/hir_type/gen_hir_type.rb @@ -25,20 +25,20 @@ def subtype name end # Helper to generate graphviz. -def to_graphviz_rec type +def to_graphviz_rec type, f type.subtypes.each {|subtype| - puts type.name + "->" + subtype.name + ";" + f.puts type.name + "->" + subtype.name + ";" } type.subtypes.each {|subtype| - to_graphviz_rec subtype + to_graphviz_rec subtype, f } end # Generate graphviz. -def to_graphviz type - puts "digraph G {" - to_graphviz_rec type - puts "}" +def to_graphviz type, f + f.puts "digraph G {" + to_graphviz_rec type, f + f.puts "}" end # ===== Start generating the type DAG ===== @@ -218,3 +218,7 @@ def add_union name, type_names } puts " ];" puts "}" + +File.open("zjit_types.dot", "w") do |f| + to_graphviz(any, f) +end From 03ed220cb0581d36606e13b1709d5ad133a45dc0 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sat, 22 Nov 2025 17:48:41 +0900 Subject: [PATCH 085/367] Box: load_wrapping is not needed now Top level constants are defined on the Object class's constant table, and those constants can be referred as box::CONST_NAME from outside box. So load_wrapping() is not needed now. --- load.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/load.c b/load.c index c519efe2a13f8d..5a0697a2626707 100644 --- a/load.c +++ b/load.c @@ -1344,16 +1344,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa else { switch (found) { case 'r': - // iseq_eval_in_box will be called with the loading box eventually - if (BOX_OPTIONAL_P(box)) { - // check with BOX_OPTIONAL_P (not BOX_USER_P) for NS1::xxx naming - // it is not expected for the main box - // TODO: no need to use load_wrapping() here? - load_wrapping(saved.ec, path, box->box_object); - } - else { - load_iseq_eval(saved.ec, path); - } + load_iseq_eval(saved.ec, path); break; case 's': From 75f8a116c94f538ae18cf3c6921c3b0d2724dee7 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 30 Nov 2025 17:33:05 +0900 Subject: [PATCH 086/367] Box: Fix data type name --- box.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/box.c b/box.c index 81f285b3212ccd..282c5756384d5f 100644 --- a/box.c +++ b/box.c @@ -250,7 +250,7 @@ box_entry_memsize(const void *ptr) } const rb_data_type_t rb_box_data_type = { - "Namespace::Entry", + "Ruby::Box::Entry", { rb_box_entry_mark, box_entry_free, @@ -261,7 +261,7 @@ const rb_data_type_t rb_box_data_type = { }; const rb_data_type_t rb_root_box_data_type = { - "Namespace::Root", + "Ruby::Box::Root", { rb_box_entry_mark, box_root_free, From 84bc1f038a4df63ff09490c981753d7234a52ce4 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 30 Nov 2025 17:36:17 +0900 Subject: [PATCH 087/367] Box: Mark boxes when a class/module is originally defined in it. When a class/module defined by extension libraries in a box, checking types of instances of the class needs to access its data type (rb_data_type_t). So if a class still exists (not GCed), the box must exist too (to be marked). --- gc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gc.c b/gc.c index 4f6751316f07da..97b7362c9fbb85 100644 --- a/gc.c +++ b/gc.c @@ -3154,12 +3154,18 @@ rb_gc_mark_children(void *objspace, VALUE obj) foreach_args.objspace = objspace; foreach_args.obj = obj; rb_class_classext_foreach(obj, gc_mark_classext_module, (void *)&foreach_args); + if (BOX_USER_P(RCLASS_PRIME_BOX(obj))) { + gc_mark_internal(RCLASS_PRIME_BOX(obj)->box_object); + } break; case T_ICLASS: foreach_args.objspace = objspace; foreach_args.obj = obj; rb_class_classext_foreach(obj, gc_mark_classext_iclass, (void *)&foreach_args); + if (BOX_USER_P(RCLASS_PRIME_BOX(obj))) { + gc_mark_internal(RCLASS_PRIME_BOX(obj)->box_object); + } break; case T_ARRAY: From 9eafeaed67903bbf2f2dca5eb473c1bf774712f6 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Sun, 30 Nov 2025 17:50:50 +0900 Subject: [PATCH 088/367] Box: Free rb_classext_t struct for a box when the box is GCed --- box.c | 32 ++++++++++++++++++++++++++++++++ class.c | 18 ++++++++++++++++-- internal/box.h | 1 + internal/class.h | 1 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/box.c b/box.c index 282c5756384d5f..3fbc4e9fe23a14 100644 --- a/box.c +++ b/box.c @@ -149,6 +149,7 @@ box_entry_initialize(rb_box_t *box) box->loading_table = st_init_strtable(); box->ruby_dln_libmap = rb_hash_new_with_size(0); box->gvar_tbl = rb_hash_new_with_size(0); + box->classext_cow_classes = st_init_numtable(); box->is_user = true; box->is_optional = true; @@ -199,6 +200,9 @@ rb_box_entry_mark(void *ptr) } rb_gc_mark(box->ruby_dln_libmap); rb_gc_mark(box->gvar_tbl); + if (box->classext_cow_classes) { + rb_mark_tbl(box->classext_cow_classes); + } } static int @@ -233,9 +237,36 @@ box_root_free(void *ptr) } } +static int +free_classext_for_box(st_data_t _key, st_data_t obj_value, st_data_t box_arg) +{ + rb_classext_t *ext; + VALUE obj = (VALUE)obj_value; + const rb_box_t *box = (const rb_box_t *)box_arg; + + if (RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)) { + ext = rb_class_unlink_classext(obj, box); + rb_class_classext_free(obj, ext, false); + } + else if (RB_TYPE_P(obj, T_ICLASS)) { + ext = rb_class_unlink_classext(obj, box); + rb_iclass_classext_free(obj, ext, false); + } + else { + rb_bug("Invalid type of object in classext_cow_classes: %s", rb_type_str(BUILTIN_TYPE(obj))); + } + return ST_CONTINUE; +} + static void box_entry_free(void *ptr) { + const rb_box_t *box = (const rb_box_t *)ptr; + + if (box->classext_cow_classes) { + st_foreach(box->classext_cow_classes, free_classext_for_box, (st_data_t)box); + } + box_root_free(ptr); xfree(ptr); } @@ -750,6 +781,7 @@ initialize_root_box(void) root->ruby_dln_libmap = rb_hash_new_with_size(0); root->gvar_tbl = rb_hash_new_with_size(0); + root->classext_cow_classes = NULL; // classext CoW never happen on the root box vm->root_box = root; diff --git a/class.c b/class.c index 2f94b801471104..0cec526b74fe98 100644 --- a/class.c +++ b/class.c @@ -86,6 +86,17 @@ cvar_table_free_i(VALUE value, void *ctx) return ID_TABLE_CONTINUE; } +rb_classext_t * +rb_class_unlink_classext(VALUE klass, const rb_box_t *box) +{ + st_data_t ext; + st_data_t key = (st_data_t)box->box_object; + VALUE obj_id = rb_obj_id(klass); + st_delete(box->classext_cow_classes, &obj_id, 0); + st_delete(RCLASS_CLASSEXT_TBL(klass), &key, &ext); + return (rb_classext_t *)ext; +} + void rb_class_classext_free(VALUE klass, rb_classext_t *ext, bool is_prime) { @@ -156,7 +167,7 @@ struct rb_class_set_box_classext_args { }; static int -rb_class_set_box_classext_update(st_data_t *key_ptr, st_data_t *val_ptr, st_data_t a, int existing) +set_box_classext_update(st_data_t *key_ptr, st_data_t *val_ptr, st_data_t a, int existing) { struct rb_class_set_box_classext_args *args = (struct rb_class_set_box_classext_args *)a; @@ -182,7 +193,10 @@ rb_class_set_box_classext(VALUE obj, const rb_box_t *box, rb_classext_t *ext) .ext = ext, }; - st_update(RCLASS_CLASSEXT_TBL(obj), (st_data_t)box->box_object, rb_class_set_box_classext_update, (st_data_t)&args); + VM_ASSERT(BOX_USER_P(box)); + + st_update(RCLASS_CLASSEXT_TBL(obj), (st_data_t)box->box_object, set_box_classext_update, (st_data_t)&args); + st_insert(box->classext_cow_classes, (st_data_t)rb_obj_id(obj), obj); // FIXME: This is done here because this is the first time the objects in // the classext are exposed via this class. It's likely that if GC diff --git a/internal/box.h b/internal/box.h index 41cad634823f71..c341f046d3e147 100644 --- a/internal/box.h +++ b/internal/box.h @@ -34,6 +34,7 @@ struct rb_box_struct { VALUE ruby_dln_libmap; VALUE gvar_tbl; + struct st_table *classext_cow_classes; bool is_user; bool is_optional; diff --git a/internal/class.h b/internal/class.h index f122d2f189c580..296a07ae29e9f3 100644 --- a/internal/class.h +++ b/internal/class.h @@ -513,6 +513,7 @@ void rb_undef_methods_from(VALUE klass, VALUE super); VALUE rb_class_inherited(VALUE, VALUE); VALUE rb_keyword_error_new(const char *, VALUE); +rb_classext_t *rb_class_unlink_classext(VALUE klass, const rb_box_t *box); void rb_class_classext_free(VALUE klass, rb_classext_t *ext, bool is_prime); void rb_iclass_classext_free(VALUE klass, rb_classext_t *ext, bool is_prime); From cc929bff7ddfeccf320c621a6ae0f106e76a52f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Tue, 2 Dec 2025 15:45:01 +0100 Subject: [PATCH 089/367] [ruby/json] Don't call to_json on the return value of as_json for Float::NAN https://github.com/ruby/json/commit/28c57df8f7 --- test/json/json_generator_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index c01ed678fcfda4..9600f4be8d15d0 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -883,6 +883,15 @@ def test_json_generate_as_json_convert_to_proc assert_equal object.object_id.to_json, JSON.generate(object, strict: true, as_json: -> (o, is_key) { o.object_id }) end + def test_as_json_nan_does_not_call_to_json + def (obj = Object.new).to_json(*) + "null" + end + assert_raise(JSON::GeneratorError) do + JSON.generate(Float::NAN, strict: true, as_json: proc { obj }) + end + end + def assert_float_roundtrip(expected, actual) assert_equal(expected, JSON.generate(actual)) assert_equal(actual, JSON.parse(JSON.generate(actual)), "JSON: #{JSON.generate(actual)}") From c06c2203ed14e67dc12486c815b246395ef711e1 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:54:10 +0100 Subject: [PATCH 090/367] [ruby/prism] Fix the ripper translator to parse as the current ruby Otherwise, it uses the latest prism version https://github.com/ruby/prism/commit/86406f63aa --- lib/prism/translation/ripper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 6ea98fc1eaa598..e488b7c5cf0c72 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -71,7 +71,7 @@ def self.parse(src, filename = "(ripper)", lineno = 1) # [[1, 13], :on_kw, "end", END ]] # def self.lex(src, filename = "-", lineno = 1, raise_errors: false) - result = Prism.lex_compat(src, filepath: filename, line: lineno) + result = Prism.lex_compat(src, filepath: filename, line: lineno, version: "current") if result.failure? && raise_errors raise SyntaxError, result.errors.first.message @@ -3295,7 +3295,7 @@ def visit_yield_node(node) # Lazily initialize the parse result. def result - @result ||= Prism.parse(source, partial_script: true) + @result ||= Prism.parse(source, partial_script: true, version: "current") end ########################################################################## From 17bcd71e4218994bfb6c2d398fa784ccd74d2f2c Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:41:44 +0100 Subject: [PATCH 091/367] [ruby/prism] Clean up test excludes Mostly not having to list version-specific excludes when testing against ripper/parse.y Also don't test new syntax additions against the parser gems. The version support for them may (or may not) be expanded but we shouldn't bother while the ruby version hasn't even released yet. (ruby_parser translation is not versioned, so let as is for now) I also removed excludes that have since been implemented by parse.y https://github.com/ruby/prism/commit/e5a0221c37 --- test/prism/errors_test.rb | 2 +- test/prism/fixtures_test.rb | 16 +--------------- test/prism/lex_test.rb | 32 +++++++++----------------------- test/prism/locals_test.rb | 19 +++---------------- test/prism/ruby/parser_test.rb | 13 ++----------- test/prism/ruby/ripper_test.rb | 32 +++++++++++--------------------- test/prism/snippets_test.rb | 2 +- test/prism/test_helper.rb | 22 ++++++++++++++++------ 8 files changed, 44 insertions(+), 94 deletions(-) diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index bd7a8a638166c5..706b7395574e05 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -88,7 +88,7 @@ def assert_errors(filepath, version) expected = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8) source = expected.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "") - refute_valid_syntax(source) if current_major_minor == version + refute_valid_syntax(source) if CURRENT_MAJOR_MINOR == version result = Prism.parse(source, version: version) errors = result.errors diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 2aebb1847782dc..7df97029d3aa52 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -24,23 +24,9 @@ class FixturesTest < TestCase except << "whitequark/ruby_bug_19281.txt" end - if RUBY_VERSION < "3.4.0" - except << "3.4/circular_parameters.txt" - end - - # Valid only on Ruby 3.3 - except << "3.3-3.3/block_args_in_array_assignment.txt" - except << "3.3-3.3/it_with_ordinary_parameter.txt" - except << "3.3-3.3/keyword_args_in_array_assignment.txt" - except << "3.3-3.3/return_in_sclass.txt" - - # Leaving these out until they are supported by parse.y. - except << "4.0/leading_logical.txt" - except << "4.0/endless_methods_command_call.txt" - # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" - Fixture.each(except: except) do |fixture| + Fixture.each_for_current_ruby(except: except) do |fixture| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } end end diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 19dd845d755621..68e47a096408cd 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -7,19 +7,13 @@ module Prism class LexTest < TestCase except = [ - # It seems like there are some oddities with nested heredocs and ripper. - # Waiting for feedback on https://bugs.ruby-lang.org/issues/19838. - "seattlerb/heredoc_nested.txt", - "whitequark/dedenting_heredoc.txt", - # Ripper seems to have a bug that the regex portions before and after - # the heredoc are combined into a single token. See - # https://bugs.ruby-lang.org/issues/19838. + # https://bugs.ruby-lang.org/issues/21756 "spanning_heredoc.txt", - "spanning_heredoc_newlines.txt", - # Prism emits a single :on_tstring_content in <<- style heredocs when there - # is a line continuation preceded by escaped backslashes. It should emit two, same - # as if the backslashes are not present. + # Prism emits a single string in some cases when ripper splits them up + "whitequark/dedenting_heredoc.txt", "heredocs_with_fake_newlines.txt", + # Prism emits BEG for `on_regexp_end` + "spanning_heredoc_newlines.txt", ] if RUBY_VERSION < "3.3.0" @@ -42,17 +36,11 @@ class LexTest < TestCase except << "whitequark/ruby_bug_19281.txt" end - # https://bugs.ruby-lang.org/issues/20925 - except << "4.0/leading_logical.txt" - - # https://bugs.ruby-lang.org/issues/17398#note-12 - except << "4.0/endless_methods_command_call.txt" - # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" - Fixture.each_with_version(except: except) do |fixture, version| - define_method(fixture.test_name(version)) { assert_lex(fixture, version) } + Fixture.each_for_current_ruby(except: except) do |fixture| + define_method(fixture.test_name) { assert_lex(fixture) } end def test_lex_file @@ -97,12 +85,10 @@ def test_parse_lex_file private - def assert_lex(fixture, version) - return unless current_major_minor == version - + def assert_lex(fixture) source = fixture.read - result = Prism.lex_compat(source, version: version) + result = Prism.lex_compat(source, version: "current") assert_equal [], result.errors Prism.lex_ripper(source).zip(result.value).each do |(ripper, prism)| diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 814c9a9978d84a..4f8d6080e8075d 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -13,11 +13,6 @@ # in comparing the locals because they will be the same. return if RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism -# In Ruby 3.4.0, the local table for method forwarding changed. But 3.4.0 can -# refer to the dev version, so while 3.4.0 still isn't released, we need to -# check if we have a high enough revision. -return if RubyVM::InstructionSequence.compile("def foo(...); end").to_a[13][2][2][10].length != 1 - # Omit tests if running on a 32-bit machine because there is a bug with how # Ruby is handling large ISeqs on 32-bit machines return if RUBY_PLATFORM =~ /i686/ @@ -31,19 +26,11 @@ class LocalsTest < TestCase # CRuby is eliminating dead code. "whitequark/ruby_bug_10653.txt", - # Valid only on Ruby 3.3 - "3.3-3.3/block_args_in_array_assignment.txt", - "3.3-3.3/it_with_ordinary_parameter.txt", - "3.3-3.3/keyword_args_in_array_assignment.txt", - "3.3-3.3/return_in_sclass.txt", - - # Leaving these out until they are supported by parse.y. - "4.0/leading_logical.txt", - "4.0/endless_methods_command_call.txt", - "command_method_call_2.txt" + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] - Fixture.each(except: except) do |fixture| + Fixture.each_for_current_ruby(except: except) do |fixture| define_method(fixture.test_name) { assert_locals(fixture) } end diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 648c44e77a6573..c972f0962bb6f9 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -65,15 +65,6 @@ class ParserTest < TestCase # 1.. && 2 "ranges.txt", - # https://bugs.ruby-lang.org/issues/20478 - "3.4/circular_parameters.txt", - - # Cannot yet handling leading logical operators. - "4.0/leading_logical.txt", - - # Ruby >= 4.0 specific syntax - "4.0/endless_methods_command_call.txt", - # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", ] @@ -148,7 +139,7 @@ class ParserTest < TestCase "whitequark/space_args_block.txt" ] - Fixture.each(except: skip_syntax_error) do |fixture| + Fixture.each_for_version(except: skip_syntax_error, version: "3.3") do |fixture| define_method(fixture.test_name) do assert_equal_parses( fixture, @@ -171,7 +162,7 @@ def test_non_prism_builder_class_deprecated if RUBY_VERSION >= "3.3" def test_current_parser_for_current_ruby - major, minor = current_major_minor.split(".") + major, minor = CURRENT_MAJOR_MINOR.split(".") # Let's just hope there never is a Ruby 3.10 or similar expected = major.to_i * 10 + minor.to_i assert_equal(expected, Translation::ParserCurrent.new.version) diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 400139acc03d01..bd63302efcf908 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -8,44 +8,34 @@ module Prism class RipperTest < TestCase # Skip these tests that Ripper is reporting the wrong results for. incorrect = [ - # Not yet supported. - "4.0/leading_logical.txt", - # Ripper incorrectly attributes the block to the keyword. - "seattlerb/block_break.txt", - "seattlerb/block_next.txt", "seattlerb/block_return.txt", - "whitequark/break_block.txt", - "whitequark/next_block.txt", "whitequark/return_block.txt", - # Ripper is not accounting for locals created by patterns using the ** - # operator within an `in` clause. - "seattlerb/parse_pattern_058.txt", - # Ripper cannot handle named capture groups in regular expressions. "regex.txt", - "regex_char_width.txt", - "whitequark/lvar_injecting_match.txt", # Ripper fails to understand some structures that span across heredocs. "spanning_heredoc.txt", - "3.3-3.3/block_args_in_array_assignment.txt", - "3.3-3.3/it_with_ordinary_parameter.txt", - "3.3-3.3/keyword_args_in_array_assignment.txt", - "3.3-3.3/return_in_sclass.txt", - - # https://bugs.ruby-lang.org/issues/20478 + # Ripper interprets circular keyword arguments as method calls. "3.4/circular_parameters.txt", - # https://bugs.ruby-lang.org/issues/17398#note-12 + # Ripper doesn't emit `args_add_block` when endless method is prefixed by modifier. "4.0/endless_methods_command_call.txt", # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", ] + if RUBY_VERSION.start_with?("3.3.") + incorrect += [ + "whitequark/lvar_injecting_match.txt", + "seattlerb/parse_pattern_058.txt", + "regex_char_width.txt", + ] + end + # Skip these tests that we haven't implemented yet. omitted = [ "dos_endings.txt", @@ -68,7 +58,7 @@ class RipperTest < TestCase "whitequark/slash_newline_in_heredocs.txt" ] - Fixture.each(except: incorrect | omitted) do |fixture| + Fixture.each_for_current_ruby(except: incorrect | omitted) do |fixture| define_method(fixture.test_name) { assert_ripper(fixture.read) } end diff --git a/test/prism/snippets_test.rb b/test/prism/snippets_test.rb index 3160442cc07653..3c28d27a250e22 100644 --- a/test/prism/snippets_test.rb +++ b/test/prism/snippets_test.rb @@ -18,7 +18,7 @@ class SnippetsTest < TestCase "whitequark/multiple_pattern_matches.txt" ] - Fixture.each_with_version(except: except) do |fixture, version| + Fixture.each_with_all_versions(except: except) do |fixture, version| define_method(fixture.test_name(version)) { assert_snippets(fixture, version) } end diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb index 42555738cf318f..f78e68e87c107a 100644 --- a/test/prism/test_helper.rb +++ b/test/prism/test_helper.rb @@ -72,7 +72,18 @@ def self.each(except: [], &block) paths.each { |path| yield Fixture.new(path) } end - def self.each_with_version(except: [], &block) + def self.each_for_version(except: [], version:, &block) + each(except: except) do |fixture| + next unless TestCase.ruby_versions_for(fixture.path).include?(version) + yield fixture + end + end + + def self.each_for_current_ruby(except: [], &block) + each_for_version(except: except, version: CURRENT_MAJOR_MINOR, &block) + end + + def self.each_with_all_versions(except: [], &block) each(except: except) do |fixture| TestCase.ruby_versions_for(fixture.path).each do |version| yield fixture, version @@ -232,6 +243,9 @@ def self.windows? # All versions that prism can parse SYNTAX_VERSIONS = %w[3.3 3.4 4.0] + # `RUBY_VERSION` with the patch version excluded + CURRENT_MAJOR_MINOR = RUBY_VERSION.split(".")[0, 2].join(".") + # Returns an array of ruby versions that a given filepath should test against: # test.txt # => all available versions # 3.4/test.txt # => versions since 3.4 (inclusive) @@ -250,13 +264,9 @@ def self.ruby_versions_for(filepath) end end - def current_major_minor - RUBY_VERSION.split(".")[0, 2].join(".") - end - if RUBY_VERSION >= "3.3.0" def test_all_syntax_versions_present - assert_include(SYNTAX_VERSIONS, current_major_minor) + assert_include(SYNTAX_VERSIONS, CURRENT_MAJOR_MINOR) end end From d3907245d7928279fcffd1af2903d16b2dbb2d29 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 5 Nov 2025 11:13:09 +0100 Subject: [PATCH 092/367] [ruby/psych] Fix usage of rb_struct_initialize() to pass an Array of members values and not a Hash * rb_struct_initialize() does not accept a Hash, and it's very brittle to pass `[{...}]` and to rely on that C function using rb_keyword_given_p(). It basically worked accidentally, by having **members in the caller of the caller. Such logic when Struct#initialize is defined in Ruby (as in TruffleRuby) is basically impossible to implement, because it's incorrectly treating positional arguments as keyword arguments. * rb_struct_initialize() is used in CRuby to set members of Data instances in marshal.c (there is no rb_data_initialize() yet). There, the code passes an Array of members values for Data (and for Struct which are not `keyword_init: true`): https://github.com/ruby/ruby/blob/48c7f349f68846e10d60ae77ad299a38ee014479/marshal.c#L2150-L2176 So we should do the same in psych. * Rename to init_data since it's only used for Data. * See https://github.com/ruby/psych/pull/692#discussion_r2483947279. https://github.com/ruby/psych/commit/3550148378 --- ext/psych/lib/psych/visitors/to_ruby.rb | 3 ++- ext/psych/psych_to_ruby.c | 9 +++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index 580a74e9fb034d..2814ce1a695df0 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -219,7 +219,8 @@ def visit_Psych_Nodes_Mapping o revive_data_members(members, o) end data ||= allocate_anon_data(o, members) - init_struct(data, **members) + values = data.members.map { |m| members[m] } + init_data(data, values) data.freeze data diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c index d473a5f840a1d7..53a038270c4d70 100644 --- a/ext/psych/psych_to_ruby.c +++ b/ext/psych/psych_to_ruby.c @@ -24,12 +24,9 @@ static VALUE path2class(VALUE self, VALUE path) return rb_path_to_class(path); } -static VALUE init_struct(VALUE self, VALUE data, VALUE attrs) +static VALUE init_data(VALUE self, VALUE data, VALUE values) { - VALUE args = rb_ary_new2(1); - rb_ary_push(args, attrs); - rb_struct_initialize(data, args); - + rb_struct_initialize(data, values); return data; } @@ -42,7 +39,7 @@ void Init_psych_to_ruby(void) VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor); - rb_define_private_method(cPsychVisitorsToRuby, "init_struct", init_struct, 2); + rb_define_private_method(cPsychVisitorsToRuby, "init_data", init_data, 2); rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2); rb_define_private_method(class_loader, "path2class", path2class, 1); } From 3aa674ad9923a4a362a29840fad8421a6be7131f Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 5 Nov 2025 12:01:21 +0100 Subject: [PATCH 093/367] [ruby/psych] Properly set the message of Exceptions on TruffleRuby * From https://github.com/truffleruby/truffleruby/commit/1f81db82d2969ff7c5de0dacdecb38252664f42c https://github.com/ruby/psych/commit/dbabe7aac6 --- ext/psych/psych_to_ruby.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c index 53a038270c4d70..0132b2c94e28c7 100644 --- a/ext/psych/psych_to_ruby.c +++ b/ext/psych/psych_to_ruby.c @@ -10,7 +10,11 @@ static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg) { VALUE e = rb_obj_alloc(klass); +#ifdef TRUFFLERUBY + rb_exc_set_message(e, mesg); +#else rb_iv_set(e, "mesg", mesg); +#endif return e; } From 8d2f73d70a6f99335c2fc7ebb49fb9d95e431047 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Dec 2025 14:28:36 -0500 Subject: [PATCH 094/367] [ruby/prism] Introduce PM_NODE_UPCAST macro for readability https://github.com/ruby/prism/commit/7eb169513a --- prism/prism.c | 1024 ++++++++++++----------- prism/templates/include/prism/ast.h.erb | 37 +- 2 files changed, 536 insertions(+), 525 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index ac6fbd9f6b461a..044e61a1b1d5c5 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -18,6 +18,12 @@ pm_version(void) { #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) +/******************************************************************************/ +/* Helpful node-related macros */ +/******************************************************************************/ + +#define UP PM_NODE_UPCAST + /******************************************************************************/ /* Lex mode manipulations */ /******************************************************************************/ @@ -1049,25 +1055,25 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { if (cast->ensure_clause != NULL) { if (cast->rescue_clause != NULL) { - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->rescue_clause); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause)); if (vn != NULL) return vn; } if (cast->statements != NULL) { - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn != NULL) return vn; } - node = (pm_node_t *) cast->ensure_clause; + node = UP(cast->ensure_clause); } else if (cast->rescue_clause != NULL) { if (cast->statements == NULL) return NULL; - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn == NULL) return NULL; if (void_node == NULL) void_node = vn; for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) { - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) rescue_clause->statements); + pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements)); if (vn == NULL) { void_node = NULL; break; @@ -1078,24 +1084,24 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { } if (cast->else_clause != NULL) { - node = (pm_node_t *) cast->else_clause; + node = UP(cast->else_clause); } else { return void_node; } } else { - node = (pm_node_t *) cast->statements; + node = UP(cast->statements); } break; } case PM_ENSURE_NODE: { pm_ensure_node_t *cast = (pm_ensure_node_t *) node; - node = (pm_node_t *) cast->statements; + node = UP(cast->statements); break; } case PM_PARENTHESES_NODE: { pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; - node = (pm_node_t *) cast->body; + node = UP(cast->body); break; } case PM_STATEMENTS_NODE: { @@ -1108,7 +1114,7 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { if (cast->statements == NULL || cast->subsequent == NULL) { return NULL; } - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn == NULL) { return NULL; } @@ -1123,19 +1129,19 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { if (cast->statements == NULL || cast->else_clause == NULL) { return NULL; } - pm_node_t *vn = pm_check_value_expression(parser, (pm_node_t *) cast->statements); + pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements)); if (vn == NULL) { return NULL; } if (void_node == NULL) { void_node = vn; } - node = (pm_node_t *) cast->else_clause; + node = UP(cast->else_clause); break; } case PM_ELSE_NODE: { pm_else_node_t *cast = (pm_else_node_t *) node; - node = (pm_node_t *) cast->statements; + node = UP(cast->statements); break; } case PM_AND_NODE: { @@ -1635,7 +1641,7 @@ pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_b // If we didn't hit a case before this check, then at this point we need to // add a syntax error. - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_UNEXPECTED_BLOCK); + pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK); } /******************************************************************************/ @@ -2059,9 +2065,9 @@ pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argumen if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) { if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { - pm_node_flag_set((pm_node_t *) node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS); + pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS); } else { - pm_node_flag_set((pm_node_t *) node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT); + pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT); } } } @@ -2098,11 +2104,11 @@ pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) { // If the element is not a static literal, then the array is not a static // literal. Turn that flag off. if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || !PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL)) { - pm_node_flag_unset((pm_node_t *)node, PM_NODE_FLAG_STATIC_LITERAL); + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); } if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) { - pm_node_flag_set((pm_node_t *)node, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT); + pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT); } } @@ -2475,7 +2481,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) */ static void pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) { - pm_node_list_append(&node->locals, (pm_node_t *) local); + pm_node_list_append(&node->locals, UP(local)); if (node->base.location.start == NULL) node->base.location.start = local->base.location.start; node->base.location.end = local->base.location.end; @@ -2623,7 +2629,7 @@ pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *o node->block = arguments->block; if (operator->type == PM_TOKEN_AMPERSAND_DOT) { - pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); + pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); } /** @@ -2736,7 +2742,7 @@ pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token node->block = arguments->block; if (operator->type == PM_TOKEN_AMPERSAND_DOT) { - pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); + pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION); } node->name = pm_parser_constant_id_constant(parser, "call", 4); @@ -3306,7 +3312,7 @@ pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_ *node = (pm_class_variable_write_node_t) { .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node->base.location.start, value->location.end), .name = read_node->name, - .name_loc = PM_LOCATION_NODE_VALUE((pm_node_t *) read_node), + .name_loc = PM_LOCATION_NODE_VALUE(UP(read_node)), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value }; @@ -3510,7 +3516,7 @@ pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_BEGIN_NODE: { const pm_begin_node_t *cast = (pm_begin_node_t *) node; - if (cast->statements != NULL) pm_def_node_receiver_check(parser, (pm_node_t *) cast->statements); + if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements)); break; } case PM_PARENTHESES_NODE: { @@ -3719,7 +3725,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { pm_node_t *right; if (nodes->size == 1) { - right = (pm_node_t *) pm_missing_node_create(parser, left->location.end, left->location.end); + right = UP(pm_missing_node_create(parser, left->location.end, left->location.end)); } else { right = nodes->nodes[nodes->size - 1]; assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE)); @@ -3851,11 +3857,11 @@ pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), - .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) { + .numeric = UP(pm_float_node_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -3920,11 +3926,11 @@ pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *t pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), - .numeric = (pm_node_t *) pm_float_node_rational_create(parser, &((pm_token_t) { + .numeric = UP(pm_float_node_rational_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT_RATIONAL, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -4252,7 +4258,7 @@ pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) { } if (!static_literal) { - pm_node_flag_unset((pm_node_t *)hash, PM_NODE_FLAG_STATIC_LITERAL); + pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL); } } @@ -4350,7 +4356,7 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to .predicate = predicate, .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark), .statements = if_statements, - .subsequent = (pm_node_t *) else_node, + .subsequent = UP(else_node), .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE }; @@ -4438,11 +4444,11 @@ pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, cons pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), - .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) { + .numeric = UP(pm_integer_node_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -4488,11 +4494,11 @@ pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t b pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), - .numeric = (pm_node_t *) pm_integer_node_rational_create(parser, base, &((pm_token_t) { + .numeric = UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER_RATIONAL, .start = token->start, .end = token->end - 1 - })) + }))) }; return node; @@ -4652,7 +4658,7 @@ pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *p break; } case PM_EMBEDDED_VARIABLE_NODE: - pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); break; default: assert(false && "unexpected node type"); @@ -4688,14 +4694,14 @@ pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expressio node->base.location.end = part->location.end; } - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(UP(node), &node->parts, part); } static inline void pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) { node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing); node->base.location.end = closing->end; - pm_node_flag_set((pm_node_t *) node, pm_regular_expression_flags_create(parser, closing)); + pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing)); } /** @@ -4741,7 +4747,7 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for // as long as this interpolation only consists of other string literals. if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) { - pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); + pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL); } part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE); break; @@ -4860,7 +4866,7 @@ pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_ node->base.location.start = part->location.start; } - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(UP(node), &node->parts, part); node->base.location.end = MAX(node->base.location.end, part->location.end); } @@ -4913,7 +4919,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi static inline void pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) { - pm_interpolated_node_append((pm_node_t *) node, &node->parts, part); + pm_interpolated_node_append(UP(node), &node->parts, part); node->base.location.end = part->location.end; } @@ -4974,7 +4980,7 @@ pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *el // If the element being added is not an AssocNode or does not have a symbol // key, then we want to turn the SYMBOL_KEYS flag off. if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) { - pm_node_flag_unset((pm_node_t *)hash, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS); + pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS); } pm_node_list_append(&hash->elements, element); @@ -5618,8 +5624,8 @@ pm_parameters_node_requireds_append(pm_parameters_node_t *params, pm_node_t *par */ static void pm_parameters_node_optionals_append(pm_parameters_node_t *params, pm_optional_parameter_node_t *param) { - pm_parameters_node_location_set(params, (pm_node_t *) param); - pm_node_list_append(¶ms->optionals, (pm_node_t *) param); + pm_parameters_node_location_set(params, UP(param)); + pm_node_list_append(¶ms->optionals, UP(param)); } /** @@ -5665,7 +5671,7 @@ pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *par static void pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_node_t *param) { assert(params->block == NULL); - pm_parameters_node_location_set(params, (pm_node_t *) param); + pm_parameters_node_location_set(params, UP(param)); params->block = param; } @@ -6546,7 +6552,7 @@ pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) { assert((label.end - label.start) >= 0); pm_string_shared_init(&node->unescaped, label.start, label.end); - pm_node_flag_set((pm_node_t *) node, parse_symbol_encoding(parser, &label, &node->unescaped, false)); + pm_node_flag_set(UP(node), parse_symbol_encoding(parser, &label, &node->unescaped, false)); break; } @@ -6621,7 +6627,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const }; pm_token_t content = { .type = PM_TOKEN_IDENTIFIER, .start = node->content_loc.start, .end = node->content_loc.end }; - pm_node_flag_set((pm_node_t *) new_node, parse_symbol_encoding(parser, &content, &node->unescaped, true)); + pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, &content, &node->unescaped, true)); // We are explicitly _not_ using pm_node_destroy here because we don't want // to trash the unescaped string. We could instead copy the string if we @@ -12434,7 +12440,7 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, if (accept1(parser, PM_TOKEN_USTAR)) { pm_token_t operator = parser->previous; pm_node_t *expression = parse_value_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + return UP(pm_splat_node_create(parser, &operator, expression)); } return parse_value_expression(parser, binding_power, accepts_command_call, false, diag_id, depth); @@ -12559,7 +12565,7 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0); pm_node_destroy(parser, target); - return (pm_node_t *) result; + return UP(result); } /** @@ -12634,7 +12640,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p } case PM_IT_LOCAL_VARIABLE_READ_NODE: { pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); - pm_node_t *node = (pm_node_t *) pm_local_variable_target_node_create(parser, &target->location, name, 0); + pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0)); pm_node_unreference(parser, target); pm_node_destroy(parser, target); @@ -12660,7 +12666,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p splat->expression = parse_target(parser, splat->expression, multiple, true); } - return (pm_node_t *) splat; + return UP(splat); } case PM_CALL_NODE: { pm_call_node_t *call = (pm_call_node_t *) target; @@ -12691,7 +12697,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p pm_constant_id_t name = pm_parser_local_add_location(parser, message_loc.start, message_loc.end, 0); pm_node_destroy(parser, target); - return (pm_node_t *) pm_local_variable_target_node_create(parser, &message_loc, name, 0); + return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0)); } if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { @@ -12700,7 +12706,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p } parse_write_name(parser, &call->name); - return (pm_node_t *) pm_call_target_node_create(parser, call); + return UP(pm_call_target_node_create(parser, call)); } } @@ -12708,7 +12714,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p // an aref expression, and we can transform it into an aset // expression. if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) { - return (pm_node_t *) pm_index_target_node_create(parser, call); + return UP(pm_index_target_node_create(parser, call)); } } PRISM_FALLTHROUGH @@ -12751,7 +12757,7 @@ parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) { pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser); if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) { - return (pm_node_t *) pm_shareable_constant_node_create(parser, write, shareable_constant); + return UP(pm_shareable_constant_node_create(parser, write, shareable_constant)); } return write; @@ -12769,10 +12775,10 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod case PM_CLASS_VARIABLE_READ_NODE: { pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value); pm_node_destroy(parser, target); - return (pm_node_t *) node; + return UP(node); } case PM_CONSTANT_PATH_NODE: { - pm_node_t *node = (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value); + pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value)); if (context_def_p(parser)) { pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); @@ -12781,7 +12787,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod return parse_shareable_constant_write(parser, node); } case PM_CONSTANT_READ_NODE: { - pm_node_t *node = (pm_node_t *) pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value); + pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value)); if (context_def_p(parser)) { pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD); @@ -12797,7 +12803,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod case PM_GLOBAL_VARIABLE_READ_NODE: { pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value); pm_node_destroy(parser, target); - return (pm_node_t *) node; + return UP(node); } case PM_LOCAL_VARIABLE_READ_NODE: { pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; @@ -12817,11 +12823,11 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_locals_unread(&scope->locals, name); pm_node_destroy(parser, target); - return (pm_node_t *) pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator); + return UP(pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator)); } case PM_IT_LOCAL_VARIABLE_READ_NODE: { pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2); - pm_node_t *node = (pm_node_t *) pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator); + pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator)); pm_node_unreference(parser, target); pm_node_destroy(parser, target); @@ -12829,12 +12835,12 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod return node; } case PM_INSTANCE_VARIABLE_READ_NODE: { - pm_node_t *write_node = (pm_node_t *) pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value); + pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value)); pm_node_destroy(parser, target); return write_node; } case PM_MULTI_TARGET_NODE: - return (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value); + return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value)); case PM_SPLAT_NODE: { pm_splat_node_t *splat = (pm_splat_node_t *) target; @@ -12843,9 +12849,9 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod } pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser); - pm_multi_target_node_targets_append(parser, multi_target, (pm_node_t *) splat); + pm_multi_target_node_targets_append(parser, multi_target, UP(splat)); - return (pm_node_t *) pm_multi_write_node_create(parser, multi_target, operator, value); + return UP(pm_multi_write_node_create(parser, multi_target, operator, value)); } case PM_CALL_NODE: { pm_call_node_t *call = (pm_call_node_t *) target; @@ -12877,7 +12883,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_node_destroy(parser, target); pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end); - target = (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator); + target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator)); pm_refute_numbered_parameter(parser, message.start, message.end); return target; @@ -12902,9 +12908,9 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator); parse_write_name(parser, &call->name); - pm_node_flag_set((pm_node_t *) call, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); + pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); - return (pm_node_t *) call; + return UP(call); } } @@ -12925,7 +12931,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod // Ensure that the arguments for []= don't contain keywords pm_index_arguments_check(parser, call->arguments, call->block); - pm_node_flag_set((pm_node_t *) call, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); + pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)); return target; } @@ -12976,7 +12982,7 @@ parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals); pm_node_destroy(parser, target); - return (pm_node_t *) result; + return UP(result); } /** @@ -13013,7 +13019,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b name = parse_target(parser, name, true, true); } - pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name); + pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name)); pm_multi_target_node_targets_append(parser, result, splat); has_rest = true; } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { @@ -13031,13 +13037,13 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b } else if (!match1(parser, PM_TOKEN_EOF)) { // If we get here, then we have a trailing , in a multi target node. // We'll add an implicit rest node to represent this. - pm_node_t *rest = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous)); pm_multi_target_node_targets_append(parser, result, rest); break; } } - return (pm_node_t *) result; + return UP(result); } /** @@ -13231,7 +13237,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_parser_scope_forwarding_keywords_check(parser, &operator); } - element = (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator); + element = UP(pm_assoc_splat_node_create(parser, value, &operator)); contains_keyword_splat = true; break; } @@ -13239,7 +13245,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_token_t label = parser->current; parser_lex(parser); - pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &label); + pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label)); pm_hash_key_static_literals_add(parser, literals, key); pm_token_t operator = not_provided(parser); @@ -13250,7 +13256,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } else { if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) { pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 }; - value = (pm_node_t *) pm_constant_read_node_create(parser, &constant); + value = UP(pm_constant_read_node_create(parser, &constant)); } else { int depth = -1; pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }; @@ -13262,17 +13268,17 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } if (depth == -1) { - value = (pm_node_t *) pm_call_node_variable_call_create(parser, &identifier); + value = UP(pm_call_node_variable_call_create(parser, &identifier)); } else { - value = (pm_node_t *) pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth); + value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth)); } } value->location.end++; - value = (pm_node_t *) pm_implicit_node_create(parser, value); + value = UP(pm_implicit_node_create(parser, value)); } - element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + element = UP(pm_assoc_node_create(parser, key, &operator, value)); break; } default: { @@ -13295,7 +13301,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod } pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); - element = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + element = UP(pm_assoc_node_create(parser, key, &operator, value)); break; } } @@ -13373,16 +13379,16 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for } pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser); - argument = (pm_node_t *) hash; + argument = UP(hash); pm_static_literals_t hash_keys = { 0 }; - bool contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) hash, (uint16_t) (depth + 1)); + bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1)); parse_arguments_append(parser, arguments, argument); pm_node_flags_t flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; - pm_node_flag_set((pm_node_t *) arguments->arguments, flags); + pm_node_flag_set(UP(arguments->arguments), flags); pm_static_literals_free(&hash_keys); parsed_bare_hash = true; @@ -13400,7 +13406,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_parser_scope_forwarding_block_check(parser, &operator); } - argument = (pm_node_t *) pm_block_argument_node_create(parser, &operator, expression); + argument = UP(pm_block_argument_node_create(parser, &operator, expression)); if (parsed_block_argument) { parse_arguments_append(parser, arguments, argument); } else { @@ -13420,7 +13426,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) { pm_parser_scope_forwarding_positionals_check(parser, &operator); - argument = (pm_node_t *) pm_splat_node_create(parser, &operator, NULL); + argument = UP(pm_splat_node_create(parser, &operator, NULL)); if (parsed_bare_hash) { pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); } @@ -13431,7 +13437,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_parser_err(parser, operator.start, expression->location.end, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); } - argument = (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + argument = UP(pm_splat_node_create(parser, &operator, expression)); } parse_arguments_append(parser, arguments, argument); @@ -13456,16 +13462,16 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_parser_err(parser, range->operator_loc.start, range->operator_loc.end, PM_ERR_UNEXPECTED_RANGE_OPERATOR); } - argument = (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + argument = UP(pm_range_node_create(parser, NULL, &operator, right)); } else { pm_parser_scope_forwarding_all_check(parser, &parser->previous); if (parsed_first_argument && terminator == PM_TOKEN_EOF) { pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND); } - argument = (pm_node_t *) pm_forwarding_arguments_node_create(parser, &parser->previous); + argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous)); parse_arguments_append(parser, arguments, argument); - pm_node_flag_set((pm_node_t *) arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING); + pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING); arguments->has_forwarding = true; parsed_forwarding_arguments = true; break; @@ -13502,17 +13508,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for // Finish parsing the one we are part way through. pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); - argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value); + argument = UP(pm_assoc_node_create(parser, argument, &operator, value)); pm_keyword_hash_node_elements_append(bare_hash, argument); - argument = (pm_node_t *) bare_hash; + argument = UP(bare_hash); // Then parse more if we have a comma if (accept1(parser, PM_TOKEN_COMMA) && ( token_begins_expression_p(parser->current.type) || match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL) )) { - contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) bare_hash, (uint16_t) (depth + 1)); + contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1)); } pm_static_literals_free(&hash_keys); @@ -13524,7 +13530,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_node_flags_t flags = 0; if (contains_keywords) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; - pm_node_flag_set((pm_node_t *) arguments->arguments, flags); + pm_node_flag_set(UP(arguments->arguments), flags); break; } @@ -13601,33 +13607,33 @@ parse_required_destructured_parameter(pm_parser_t *parser) { // commas, so here we'll assume this is a mistake of the user not // knowing it's not allowed here. if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { - param = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + param = UP(pm_implicit_rest_node_create(parser, &parser->previous)); pm_multi_target_node_targets_append(parser, node, param); pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); break; } if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { - param = (pm_node_t *) parse_required_destructured_parameter(parser); + param = UP(parse_required_destructured_parameter(parser)); } else if (accept1(parser, PM_TOKEN_USTAR)) { pm_token_t star = parser->previous; pm_node_t *value = NULL; if (accept1(parser, PM_TOKEN_IDENTIFIER)) { pm_token_t name = parser->previous; - value = (pm_node_t *) pm_required_parameter_node_create(parser, &name); + value = UP(pm_required_parameter_node_create(parser, &name)); if (pm_parser_parameter_name_check(parser, &name)) { pm_node_flag_set_repeated_parameter(value); } pm_parser_local_add_token(parser, &name, 1); } - param = (pm_node_t *) pm_splat_node_create(parser, &star, value); + param = UP(pm_splat_node_create(parser, &star, value)); } else { expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER); pm_token_t name = parser->previous; - param = (pm_node_t *) pm_required_parameter_node_create(parser, &name); + param = UP(pm_required_parameter_node_create(parser, &name)); if (pm_parser_parameter_name_check(parser, &name)) { pm_node_flag_set_repeated_parameter(param); } @@ -13740,7 +13746,7 @@ parse_parameters( switch (parser->current.type) { case PM_TOKEN_PARENTHESIS_LEFT: { update_parameter_state(parser, &parser->current, &order); - pm_node_t *param = (pm_node_t *) parse_required_destructured_parameter(parser); + pm_node_t *param = UP(parse_required_destructured_parameter(parser)); if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { pm_parameters_node_requireds_append(params, param); @@ -13769,13 +13775,13 @@ parse_parameters( pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator); if (repeated) { - pm_node_flag_set_repeated_parameter((pm_node_t *)param); + pm_node_flag_set_repeated_parameter(UP(param)); } if (params->block == NULL) { pm_parameters_node_block_set(params, param); } else { - pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_BLOCK_MULTI); - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_BLOCK_MULTI); + pm_parameters_node_posts_append(params, UP(param)); } break; @@ -13800,7 +13806,7 @@ parse_parameters( params->keyword_rest = NULL; } - pm_parameters_node_keyword_rest_set(params, (pm_node_t *) param); + pm_parameters_node_keyword_rest_set(params, UP(param)); break; } case PM_TOKEN_CLASS_VARIABLE: @@ -13854,7 +13860,7 @@ parse_parameters( pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value); if (repeated) { - pm_node_flag_set_repeated_parameter((pm_node_t *) param); + pm_node_flag_set_repeated_parameter(UP(param)); } pm_parameters_node_optionals_append(params, param); @@ -13877,15 +13883,15 @@ parse_parameters( } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); if (repeated) { - pm_node_flag_set_repeated_parameter((pm_node_t *)param); + pm_node_flag_set_repeated_parameter(UP(param)); } - pm_parameters_node_requireds_append(params, (pm_node_t *) param); + pm_parameters_node_requireds_append(params, UP(param)); } else { pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name); if (repeated) { - pm_node_flag_set_repeated_parameter((pm_node_t *)param); + pm_node_flag_set_repeated_parameter(UP(param)); } - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parameters_node_posts_append(params, UP(param)); } break; @@ -13916,7 +13922,7 @@ parse_parameters( case PM_TOKEN_PIPE: { context_pop(parser); - pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -13933,7 +13939,7 @@ parse_parameters( break; } - pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -13956,10 +13962,10 @@ parse_parameters( PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); } - param = (pm_node_t *) pm_optional_keyword_parameter_node_create(parser, &name, value); + param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value)); } else { - param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name); + param = UP(pm_required_keyword_parameter_node_create(parser, &name)); } if (repeated) { @@ -14000,7 +14006,7 @@ parse_parameters( parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS; } - pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name); + pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -14028,7 +14034,7 @@ parse_parameters( pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW); } - param = (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous)); } else { pm_token_t name; @@ -14042,7 +14048,7 @@ parse_parameters( parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS; } - param = (pm_node_t *) pm_keyword_rest_parameter_node_create(parser, &operator, &name); + param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, &name)); if (repeated) { pm_node_flag_set_repeated_parameter(param); } @@ -14062,13 +14068,13 @@ parse_parameters( if (allows_trailing_comma && order >= PM_PARAMETERS_ORDER_NAMED) { // If we get here, then we have a trailing comma in a // block parameter list. - pm_node_t *param = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous)); if (params->rest == NULL) { pm_parameters_node_rest_set(params, param); } else { - pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_SPLAT_MULTI); - pm_parameters_node_posts_append(params, (pm_node_t *) param); + pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI); + pm_parameters_node_posts_append(params, UP(param)); } } else { pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); @@ -14105,7 +14111,7 @@ parse_parameters( // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`. if (params->base.location.start == params->base.location.end) { - pm_node_destroy(parser, (pm_node_t *) params); + pm_node_destroy(parser, UP(params)); return NULL; } @@ -14377,7 +14383,7 @@ parse_rescues(pm_parser_t *parser, size_t opening_newline_index, const pm_token_ // If we don't have a `current` rescue node, then this is a dangling // else, and it's an error. - if (current == NULL) pm_parser_err_node(parser, (pm_node_t *) else_clause, PM_ERR_BEGIN_LONELY_ELSE); + if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE); } if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) { @@ -14501,7 +14507,7 @@ parse_block_parameters( pm_parser_local_add_token(parser, &parser->previous, 1); pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous); - if (repeated) pm_node_flag_set_repeated_parameter((pm_node_t *) local); + if (repeated) pm_node_flag_set_repeated_parameter(UP(local)); pm_block_parameters_node_append_local(block_parameters, local); } while (accept1(parser, PM_TOKEN_COMMA)); @@ -14605,11 +14611,11 @@ parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_ } const pm_location_t location = { .start = opening->start, .end = closing->end }; - return (pm_node_t *) pm_numbered_parameters_node_create(parser, &location, numbered_parameter); + return UP(pm_numbered_parameters_node_create(parser, &location, numbered_parameter)); } if (it_parameter) { - return (pm_node_t *) pm_it_parameters_node_create(parser, opening, closing); + return UP(pm_it_parameters_node_create(parser, opening, closing)); } return NULL; @@ -14649,7 +14655,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) { if (opening.type == PM_TOKEN_BRACE_LEFT) { if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) { - statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)); + statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1))); } expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE); @@ -14657,13 +14663,13 @@ parse_block(pm_parser_t *parser, uint16_t depth) { if (!match1(parser, PM_TOKEN_KEYWORD_END)) { if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) { pm_accepts_block_stack_push(parser, true); - statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)); + statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1))); pm_accepts_block_stack_pop(parser); } if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)); + statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1))); } } @@ -14672,7 +14678,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) { pm_constant_id_list_t locals; pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser)); - pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &opening, &parser->previous); + pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous); pm_parser_scope_pop(parser); pm_accepts_block_stack_pop(parser); @@ -14744,9 +14750,9 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept if (block != NULL) { if (arguments->block == NULL && !arguments->has_forwarding) { - arguments->block = (pm_node_t *) block; + arguments->block = UP(block); } else { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); + pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI); if (arguments->block != NULL) { if (arguments->arguments == NULL) { @@ -14754,7 +14760,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } pm_arguments_node_arguments_append(arguments->arguments, arguments->block); } - arguments->block = (pm_node_t *) block; + arguments->block = UP(block); } } } @@ -15042,10 +15048,10 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl switch (context) { case PM_CONTEXT_IF: - parent = (pm_node_t *) pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword); + parent = UP(pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword)); break; case PM_CONTEXT_UNLESS: - parent = (pm_node_t *) pm_unless_node_create(parser, &keyword, predicate, &then_keyword, statements); + parent = UP(pm_unless_node_create(parser, &keyword, predicate, &then_keyword, statements)); break; default: assert(false && "unreachable"); @@ -15073,7 +15079,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl pm_accepts_block_stack_pop(parser); accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); - pm_node_t *elsif = (pm_node_t *) pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword); + pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword)); ((pm_if_node_t *) current)->subsequent = elsif; current = elsif; } @@ -15098,7 +15104,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl switch (context) { case PM_CONTEXT_IF: - ((pm_if_node_t *) current)->subsequent = (pm_node_t *) else_node; + ((pm_if_node_t *) current)->subsequent = UP(else_node); break; case PM_CONTEXT_UNLESS: ((pm_unless_node_t *) parent)->else_clause = else_node; @@ -15256,7 +15262,7 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + pm_node_t *node = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); pm_node_flag_set(node, parse_unescaped_encoding(parser)); parser_lex(parser); @@ -15302,7 +15308,7 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE); } - return (pm_node_t *) pm_embedded_statements_node_create(parser, &opening, statements, &closing); + return UP(pm_embedded_statements_node_create(parser, &opening, statements, &closing)); } // Here the lexer has returned the beginning of an embedded variable. @@ -15327,42 +15333,42 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { // create a global variable read node. case PM_TOKEN_BACK_REFERENCE: parser_lex(parser); - variable = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + variable = UP(pm_back_reference_read_node_create(parser, &parser->previous)); break; // In this case an nth reference is being interpolated. We'll // create a global variable read node. case PM_TOKEN_NUMBERED_REFERENCE: parser_lex(parser); - variable = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); break; // In this case a global variable is being interpolated. We'll // create a global variable read node. case PM_TOKEN_GLOBAL_VARIABLE: parser_lex(parser); - variable = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + variable = UP(pm_global_variable_read_node_create(parser, &parser->previous)); break; // In this case an instance variable is being interpolated. // We'll create an instance variable read node. case PM_TOKEN_INSTANCE_VARIABLE: parser_lex(parser); - variable = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous)); break; // In this case a class variable is being interpolated. We'll // create a class variable read node. case PM_TOKEN_CLASS_VARIABLE: parser_lex(parser); - variable = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + variable = UP(pm_class_variable_read_node_create(parser, &parser->previous)); break; // We can hit here if we got an invalid token. In that case // we'll not attempt to lex this token and instead just return a // missing node. default: expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID); - variable = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + variable = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); break; } - return (pm_node_t *) pm_embedded_variable_node_create(parser, &operator, variable); + return UP(pm_embedded_variable_node_create(parser, &operator, variable)); } default: parser_lex(parser); @@ -15399,9 +15405,9 @@ parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_sta parser_lex(parser); pm_string_shared_init(&symbol->unescaped, parser->previous.start, end); - pm_node_flag_set((pm_node_t *) symbol, PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING); + pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING); - return (pm_node_t *) symbol; + return UP(symbol); } /** @@ -15439,9 +15445,9 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); - pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); - return (pm_node_t *) symbol; + return UP(symbol); } if (lex_mode->as.string.interpolation) { @@ -15452,7 +15458,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s pm_token_t content = not_provided(parser); pm_token_t closing = parser->previous; - return (pm_node_t *) pm_symbol_node_create(parser, &opening, &content, &closing); + return UP(pm_symbol_node_create(parser, &opening, &content, &closing)); } // Now we can parse the first part of the symbol. @@ -15464,7 +15470,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state); expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED); - return (pm_node_t *) pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous); + return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous)); } pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); @@ -15484,7 +15490,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s } pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous); - return (pm_node_t *) symbol; + return UP(symbol); } pm_token_t content; @@ -15508,10 +15514,10 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); pm_token_t bounds = not_provided(parser); - pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped); + pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped)); pm_interpolated_symbol_node_append(symbol, part); - part = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string); + part = UP(pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string)); pm_interpolated_symbol_node_append(symbol, part); if (next_state != PM_LEX_STATE_NONE) { @@ -15522,7 +15528,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC); pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous); - return (pm_node_t *) symbol; + return UP(symbol); } } else { content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end }; @@ -15539,7 +15545,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC); } - return (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, false)); + return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, false))); } /** @@ -15564,9 +15570,9 @@ parse_undef_argument(pm_parser_t *parser, uint16_t depth) { pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); - pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); - return (pm_node_t *) symbol; + return UP(symbol); } case PM_TOKEN_SYMBOL_BEGIN: { pm_lex_mode_t lex_mode = *parser->lex_modes.current; @@ -15576,7 +15582,7 @@ parse_undef_argument(pm_parser_t *parser, uint16_t depth) { } default: pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT); - return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } } @@ -15605,9 +15611,9 @@ parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) { pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing); pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end); - pm_node_flag_set((pm_node_t *) symbol, parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); + pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false)); - return (pm_node_t *) symbol; + return UP(symbol); } case PM_TOKEN_SYMBOL_BEGIN: { pm_lex_mode_t lex_mode = *parser->lex_modes.current; @@ -15617,16 +15623,16 @@ parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) { } case PM_TOKEN_BACK_REFERENCE: parser_lex(parser); - return (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + return UP(pm_back_reference_read_node_create(parser, &parser->previous)); case PM_TOKEN_NUMBERED_REFERENCE: parser_lex(parser); - return (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + return UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); case PM_TOKEN_GLOBAL_VARIABLE: parser_lex(parser); - return (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + return UP(pm_global_variable_read_node_create(parser, &parser->previous)); default: pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT); - return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } } @@ -15641,7 +15647,7 @@ parse_variable(pm_parser_t *parser) { bool is_numbered_param = pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end); if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) { - return (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false); + return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false)); } pm_scope_t *current_scope = parser->current_scope; @@ -15660,12 +15666,12 @@ parse_variable(pm_parser_t *parser) { parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND; } - pm_node_t *node = (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false); + pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false)); pm_node_list_append(¤t_scope->implicit_parameters, node); return node; } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) { - pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous)); pm_node_list_append(¤t_scope->implicit_parameters, node); return node; @@ -15689,9 +15695,9 @@ parse_variable_call(pm_parser_t *parser) { } pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous); - pm_node_flag_set((pm_node_t *)node, flags); + pm_node_flag_set(UP(node), flags); - return (pm_node_t *) node; + return UP(node); } /** @@ -15840,7 +15846,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous); pm_string_shared_init(&string->unescaped, content.start, content.end); - node = (pm_node_t *) string; + node = UP(string); } else if (accept1(parser, PM_TOKEN_LABEL_END)) { // If we get here, then we have an end of a label immediately // after a start. In that case we'll create an empty symbol @@ -15849,7 +15855,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &content, &parser->previous); pm_string_shared_init(&symbol->unescaped, content.start, content.end); - node = (pm_node_t *) symbol; + node = UP(symbol); if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); } else if (!lex_interpolation) { @@ -15882,32 +15888,32 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 pm_node_list_t parts = { 0 }; pm_token_t delimiters = not_provided(parser); - pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped); + pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped)); pm_node_list_append(&parts, part); do { - part = (pm_node_t *) pm_string_node_create_current_string(parser, &delimiters, &parser->current, &delimiters); + part = UP(pm_string_node_create_current_string(parser, &delimiters, &parser->current, &delimiters)); pm_node_list_append(&parts, part); parser_lex(parser); } while (match1(parser, PM_TOKEN_STRING_CONTENT)); expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF); - node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous)); pm_node_list_free(&parts); } else if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); + node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true))); if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); } else if (match1(parser, PM_TOKEN_EOF)) { pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF); - node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped)); } else if (accept1(parser, PM_TOKEN_STRING_END)) { - node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped)); } else { PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_type_human(parser->previous.type)); parser->previous.start = parser->previous.end; parser->previous.type = PM_TOKEN_MISSING; - node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped); + node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped)); } } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) { // In this case we've hit string content so we know the string @@ -15919,7 +15925,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 parser_lex(parser); if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { - node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped)); pm_node_flag_set(node, parse_unescaped_encoding(parser)); // Kind of odd behavior, but basically if we have an @@ -15935,7 +15941,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 parser->previous.type = PM_TOKEN_MISSING; } } else if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)); + node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true))); if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); } else { // If we get here, then we have interpolation so we'll need @@ -15944,7 +15950,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 pm_token_t string_opening = not_provided(parser); pm_token_t string_closing = not_provided(parser); - pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped); + pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped)); pm_node_flag_set(part, parse_unescaped_encoding(parser)); pm_node_list_append(&parts, part); @@ -15955,14 +15961,14 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 } if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous)); if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); } else if (match1(parser, PM_TOKEN_EOF)) { pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM); - node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current)); } else { expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM); - node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous)); } pm_node_list_free(&parts); @@ -15981,14 +15987,14 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 } if (accept1(parser, PM_TOKEN_LABEL_END)) { - node = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous)); if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL); } else if (match1(parser, PM_TOKEN_EOF)) { pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM); - node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current)); } else { expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM); - node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous)); } pm_node_list_free(&parts); @@ -16025,7 +16031,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, &bounds, NULL, &bounds); pm_interpolated_string_node_append(container, current); - current = (pm_node_t *) container; + current = UP(container); } pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); @@ -16069,7 +16075,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures while (accept1(parser, PM_TOKEN_COLON_COLON)) { pm_token_t delimiter = parser->previous; expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - node = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); + node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); } // If there is a [ or ( that follows, then this is part of a larger pattern @@ -16111,7 +16117,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures if (!inner) { // If there was no inner pattern, then we have something like Foo() or // Foo[]. In that case we'll create an array pattern with no requireds. - return (pm_node_t *) pm_array_pattern_node_constant_create(parser, node, &opening, &closing); + return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing)); } // Now that we have the inner pattern, check to see if it's an array, find, @@ -16130,7 +16136,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16146,7 +16152,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16162,7 +16168,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16176,7 +16182,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures // attach our constant to it. pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing); pm_array_pattern_node_requireds_append(pattern_node, inner); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } /** @@ -16201,12 +16207,12 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) { } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier)); - name = (pm_node_t *) pm_local_variable_target_node_create( + name = UP(pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&identifier), constant_id, (uint32_t) (depth == -1 ? 0 : depth) - ); + )); } // Finally we can return the created node. @@ -16225,7 +16231,7 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) pm_node_t *value = NULL; if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { - return (pm_node_t *) pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous)); } if (accept1(parser, PM_TOKEN_IDENTIFIER)) { @@ -16237,15 +16243,15 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); - value = (pm_node_t *) pm_local_variable_target_node_create( + value = UP(pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&parser->previous), constant_id, (uint32_t) (depth == -1 ? 0 : depth) - ); + )); } - return (pm_node_t *) pm_assoc_splat_node_create(parser, value, &operator); + return UP(pm_assoc_splat_node_create(parser, value, &operator)); } /** @@ -16308,7 +16314,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca (uint32_t) (depth == -1 ? 0 : depth) ); - return (pm_node_t *) pm_implicit_node_create(parser, (pm_node_t *) target); + return UP(pm_implicit_node_create(parser, UP(target))); } /** @@ -16352,7 +16358,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } pm_token_t operator = not_provided(parser); - pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value)); pm_node_list_append(&assocs, assoc); break; @@ -16367,8 +16373,8 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_parser_err_node(parser, first_node, diag_id); pm_token_t operator = not_provided(parser); - pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, first_node->location.start, first_node->location.end); - pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, first_node, &operator, value); + pm_node_t *value = UP(pm_missing_node_create(parser, first_node->location.start, first_node->location.end)); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value)); pm_node_list_append(&assocs, assoc); break; @@ -16409,7 +16415,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } } else { expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA); - key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + key = UP(pm_symbol_node_label_create(parser, &parser->previous)); } parse_pattern_hash_key(parser, &keys, key); @@ -16419,14 +16425,14 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) { value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); } else { - value = (pm_node_t *) pm_missing_node_create(parser, key->location.end, key->location.end); + value = UP(pm_missing_node_create(parser, key->location.end, key->location.end)); } } else { value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); } pm_token_t operator = not_provided(parser); - pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, key, &operator, value); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, &operator, value)); if (rest != NULL) { pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); @@ -16460,12 +16466,12 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); - return (pm_node_t *) pm_local_variable_target_node_create( + return UP(pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&parser->previous), constant_id, (uint32_t) (depth == -1 ? 0 : depth) - ); + )); } case PM_TOKEN_BRACKET_LEFT_ARRAY: { pm_token_t opening = parser->current; @@ -16474,7 +16480,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) { // If we have an empty array pattern, then we'll just return a new // array pattern node. - return (pm_node_t *) pm_array_pattern_node_empty_create(parser, &opening, &parser->previous); + return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous)); } // Otherwise, we'll parse the inner pattern, then deal with it depending @@ -16495,7 +16501,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16509,7 +16515,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening); pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing); - return (pm_node_t *) pattern_node; + return UP(pattern_node); } break; @@ -16520,7 +16526,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing); pm_array_pattern_node_requireds_append(node, inner); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_BRACE_LEFT: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -16540,7 +16546,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm switch (parser->current.type) { case PM_TOKEN_LABEL: parser_lex(parser); - first_node = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); + first_node = UP(pm_symbol_node_label_create(parser, &parser->previous)); break; case PM_TOKEN_USTAR_STAR: first_node = parse_pattern_keyword_rest(parser, captures); @@ -16552,7 +16558,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_type_human(parser->current.type)); parser_lex(parser); - first_node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + first_node = UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); break; } } @@ -16571,7 +16577,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } parser->pattern_matching_newlines = previous_pattern_matching_newlines; - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_UDOT_DOT: case PM_TOKEN_UDOT_DOT_DOT: { @@ -16583,12 +16589,12 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm switch (parser->current.type) { case PM_CASE_PRIMITIVE: { pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, false, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + return UP(pm_range_node_create(parser, NULL, &operator, right)); } default: { pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE); - pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, operator.start, operator.end); - return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + pm_node_t *right = UP(pm_missing_node_create(parser, operator.start, operator.end)); + return UP(pm_range_node_create(parser, NULL, &operator, right)); } } } @@ -16603,7 +16609,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pm_parser_err_node(parser, node, diag_id); pm_missing_node_t *missing_node = pm_missing_node_create(parser, node->location.start, node->location.end); pm_node_destroy(parser, node); - return (pm_node_t *) missing_node; + return UP(missing_node); } // Now that we have a primitive, we need to check if it's part of a range. @@ -16616,10 +16622,10 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm switch (parser->current.type) { case PM_CASE_PRIMITIVE: { pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, false, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_range_node_create(parser, node, &operator, right); + return UP(pm_range_node_create(parser, node, &operator, right)); } default: - return (pm_node_t *) pm_range_node_create(parser, node, &operator, NULL); + return UP(pm_range_node_create(parser, node, &operator, NULL)); } } @@ -16634,44 +16640,44 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm switch (parser->current.type) { case PM_TOKEN_IDENTIFIER: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) parse_variable(parser); + pm_node_t *variable = UP(parse_variable(parser)); if (variable == NULL) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE); - variable = (pm_node_t *) pm_local_variable_read_node_missing_create(parser, &parser->previous, 0); + variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0)); } - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_INSTANCE_VARIABLE: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_CLASS_VARIABLE: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_GLOBAL_VARIABLE: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_NUMBERED_REFERENCE: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + pm_node_t *variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_BACK_REFERENCE: { parser_lex(parser); - pm_node_t *variable = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + pm_node_t *variable = UP(pm_back_reference_read_node_create(parser, &parser->previous)); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } case PM_TOKEN_PARENTHESIS_LEFT: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -16685,14 +16691,14 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); - return (pm_node_t *) pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous); + return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous)); } default: { // If we get here, then we have a pin operator followed by something // not understood. We'll create a missing node and return that. pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN); - pm_node_t *variable = (pm_node_t *) pm_missing_node_create(parser, operator.start, operator.end); - return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); + pm_node_t *variable = UP(pm_missing_node_create(parser, operator.start, operator.end)); + return UP(pm_pinned_variable_node_create(parser, &operator, variable)); } } } @@ -16703,18 +16709,18 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); - return parse_pattern_constant_path(parser, captures, (pm_node_t *) node, (uint16_t) (depth + 1)); + return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1)); } case PM_TOKEN_CONSTANT: { pm_token_t constant = parser->current; parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &constant); + pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant)); return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1)); } default: pm_parser_err_current(parser, diag_id); - return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } } @@ -16769,7 +16775,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1)); if (captures->size) parse_pattern_alternation_error(parser, right); - node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator)); } break; @@ -16783,26 +16789,26 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1)); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); - pm_node_t *right = (pm_node_t *) pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0); + pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0)); if (!alternation) { node = right; } else { if (captures->size) parse_pattern_alternation_error(parser, right); - node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator)); } break; } default: { pm_parser_err_current(parser, diag_id); - pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + pm_node_t *right = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); if (!alternation) { node = right; } else { if (captures->size) parse_pattern_alternation_error(parser, right); - node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &parser->previous); + node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous)); } break; @@ -16831,7 +16837,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p (uint32_t) (depth == -1 ? 0 : depth) ); - node = (pm_node_t *) pm_capture_pattern_node_create(parser, node, target, &operator); + node = UP(pm_capture_pattern_node_create(parser, node, target, &operator)); } return node; @@ -16850,8 +16856,8 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag switch (parser->current.type) { case PM_TOKEN_LABEL: { parser_lex(parser); - pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); - node = (pm_node_t *) parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)); + pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous)); + node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1))); if (!(flags & PM_PARSE_PATTERN_TOP)) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); @@ -16861,7 +16867,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } case PM_TOKEN_USTAR_STAR: { node = parse_pattern_keyword_rest(parser, captures); - node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); + node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); if (!(flags & PM_PARSE_PATTERN_TOP)) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); @@ -16875,7 +16881,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); if (pm_symbol_node_label_p(node)) { - node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); + node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); if (!(flags & PM_PARSE_PATTERN_TOP)) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); @@ -16890,7 +16896,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag case PM_TOKEN_USTAR: { if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) { parser_lex(parser); - node = (pm_node_t *) parse_pattern_rest(parser, captures); + node = UP(parse_pattern_rest(parser, captures)); leading_rest = true; break; } @@ -16904,7 +16910,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // If we got a dynamic label symbol, then we need to treat it like the // beginning of a hash pattern. if (pm_symbol_node_label_p(node)) { - return (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); + return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1))); } if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) { @@ -16918,14 +16924,14 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag while (accept1(parser, PM_TOKEN_COMMA)) { // Break early here in case we have a trailing comma. if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) { - node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous); + node = UP(pm_implicit_rest_node_create(parser, &parser->previous)); pm_node_list_append(&nodes, node); trailing_rest = true; break; } if (accept1(parser, PM_TOKEN_USTAR)) { - node = (pm_node_t *) parse_pattern_rest(parser, captures); + node = UP(parse_pattern_rest(parser, captures)); // If we have already parsed a splat pattern, then this is an // error. We will continue to parse the rest of the patterns, @@ -16947,13 +16953,13 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // are in between because we know we already added the appropriate // errors. Otherwise we will create an array pattern. if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) { - node = (pm_node_t *) pm_find_pattern_node_create(parser, &nodes); + node = UP(pm_find_pattern_node_create(parser, &nodes)); if (nodes.size == 2) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER); } } else { - node = (pm_node_t *) pm_array_pattern_node_node_list_create(parser, &nodes); + node = UP(pm_array_pattern_node_node_list_create(parser, &nodes)); if (leading_rest && trailing_rest) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS); @@ -16964,7 +16970,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } else if (leading_rest) { // Otherwise, if we parsed a single splat pattern, then we know we have // an array pattern, so we can go ahead and create that node. - node = (pm_node_t *) pm_array_pattern_node_rest_create(parser, node); + node = UP(pm_array_pattern_node_rest_create(parser, node)); } return node; @@ -17342,13 +17348,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); } - element = (pm_node_t *) pm_splat_node_create(parser, &operator, expression); + element = UP(pm_splat_node_create(parser, &operator, expression)); } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) { if (parsed_bare_hash) { pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH); } - element = (pm_node_t *) pm_keyword_hash_node_create(parser); + element = UP(pm_keyword_hash_node_create(parser)); pm_static_literals_t hash_keys = { 0 }; if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) { @@ -17377,10 +17383,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); - pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, element, &operator, value); + pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, &operator, value)); pm_keyword_hash_node_elements_append(hash, assoc); - element = (pm_node_t *) hash; + element = UP(hash); if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1)); } @@ -17405,7 +17411,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_array_node_close_set(array, &parser->previous); pm_accepts_block_stack_pop(parser); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PARENTHESIS_LEFT: case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { @@ -17432,7 +17438,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, flags); + return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, flags)); } // Otherwise, we're going to parse the first statement in the list @@ -17501,10 +17507,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *result; if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) { - result = parse_targets(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); accept1(parser, PM_TOKEN_NEWLINE); } else { - result = (pm_node_t *) multi_target; + result = UP(multi_target); } if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) { @@ -17533,7 +17539,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_statements_node_t *statements = pm_statements_node_create(parser); pm_statements_node_body_append(parser, statements, statement, true); - return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous, flags); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags)); } // If we have more than one statement in the set of parentheses, @@ -17598,16 +17604,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser); pm_multi_target_node_targets_append(parser, multi_target, statement); - statement = (pm_node_t *) multi_target; + statement = UP(multi_target); statements->body.nodes[statements->body.size - 1] = statement; } if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) { const uint8_t *offset = statement->location.end; pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset }; - pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, offset, offset); + pm_node_t *value = UP(pm_missing_node_create(parser, offset, offset)); - statement = (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value); + statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value)); statements->body.nodes[statements->body.size - 1] = statement; pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED); @@ -17618,7 +17624,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_list_free(¤t_block_exits); pm_void_statements_check(parser, statements, true); - return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous, flags); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags)); } case PM_TOKEN_BRACE_LEFT: { // If we were passed a current_hash_keys via the parser, then that @@ -17638,10 +17644,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) { if (current_hash_keys != NULL) { - parse_assocs(parser, current_hash_keys, (pm_node_t *) node, (uint16_t) (depth + 1)); + parse_assocs(parser, current_hash_keys, UP(node), (uint16_t) (depth + 1)); } else { pm_static_literals_t hash_keys = { 0 }; - parse_assocs(parser, &hash_keys, (pm_node_t *) node, (uint16_t) (depth + 1)); + parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1)); pm_static_literals_free(&hash_keys); } @@ -17652,11 +17658,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM); pm_hash_node_closing_loc_set(node, &parser->previous); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_CHARACTER_LITERAL: { pm_token_t closing = not_provided(parser); - pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string( + pm_node_t *node = UP(pm_string_node_create_current_string( parser, &(pm_token_t) { .type = PM_TOKEN_STRING_BEGIN, @@ -17669,7 +17675,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b .end = parser->current.end }, &closing - ); + )); pm_node_flag_set(node, parse_unescaped_encoding(parser)); @@ -17687,7 +17693,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_CLASS_VARIABLE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_class_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_class_variable_read_node_create(parser, &parser->previous)); if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -17709,10 +17715,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b ) { pm_arguments_t arguments = { 0 }; parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_node_fcall_create(parser, &constant, &arguments); + return UP(pm_call_node_fcall_create(parser, &constant, &arguments)); } - pm_node_t *node = (pm_node_t *) pm_constant_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_constant_read_node_create(parser, &parser->previous)); if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { // If we get here, then we have a comma immediately following a @@ -17727,7 +17733,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t delimiter = parser->previous; expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - pm_node_t *node = (pm_node_t *) pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous); + pm_node_t *node = UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous)); if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -17750,23 +17756,23 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR); } - return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right); + return UP(pm_range_node_create(parser, NULL, &operator, right)); } case PM_TOKEN_FLOAT: parser_lex(parser); - return (pm_node_t *) pm_float_node_create(parser, &parser->previous); + return UP(pm_float_node_create(parser, &parser->previous)); case PM_TOKEN_FLOAT_IMAGINARY: parser_lex(parser); - return (pm_node_t *) pm_float_node_imaginary_create(parser, &parser->previous); + return UP(pm_float_node_imaginary_create(parser, &parser->previous)); case PM_TOKEN_FLOAT_RATIONAL: parser_lex(parser); - return (pm_node_t *) pm_float_node_rational_create(parser, &parser->previous); + return UP(pm_float_node_rational_create(parser, &parser->previous)); case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: parser_lex(parser); - return (pm_node_t *) pm_float_node_rational_imaginary_create(parser, &parser->previous); + return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous)); case PM_TOKEN_NUMBERED_REFERENCE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_numbered_reference_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_numbered_reference_read_node_create(parser, &parser->previous)); if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -17776,7 +17782,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_GLOBAL_VARIABLE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_global_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_global_variable_read_node_create(parser, &parser->previous)); if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -17786,7 +17792,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_BACK_REFERENCE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_back_reference_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_back_reference_read_node_create(parser, &parser->previous)); if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -17811,7 +17817,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1))) { // Since we found arguments, we need to turn off the // variable call bit in the flags. - pm_node_flag_unset((pm_node_t *)call, PM_CALL_NODE_FLAGS_VARIABLE_CALL); + pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL); call->opening_loc = arguments.opening_loc; call->arguments = arguments.arguments; @@ -17858,7 +17864,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_destroy(parser, node); - return (pm_node_t *) fcall; + return UP(fcall); } } @@ -17890,9 +17896,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t content = parse_strings_empty_content(parser->previous.start); if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) { - node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY); + node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY)); } else { - node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY); + node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY)); } node->location.end = opening.end; @@ -17903,7 +17909,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // // parse_string_part handles its own errors, so there is no need // for us to add one here. - node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + node = UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { // If we get here, then the part that we parsed was plain string // content and we're at the end of the heredoc, so we can return @@ -17925,7 +17931,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parse_heredoc_dedent_string(&cast->unescaped, common_whitespace); } - node = (pm_node_t *) cast; + node = UP(cast); expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length); } else { // If we get here, then we have multiple parts in the heredoc, @@ -17950,7 +17956,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_xstring_node_closing_set(cast, &parser->previous); cast->base.location = cast->opening_loc; - node = (pm_node_t *) cast; + node = UP(cast); } else { pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening); pm_node_list_free(&parts); @@ -17959,7 +17965,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_string_node_closing_set(cast, &parser->previous); cast->base.location = cast->opening_loc; - node = (pm_node_t *) cast; + node = UP(cast); } // If this is a heredoc that is indented with a ~, then we need @@ -17984,7 +17990,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } case PM_TOKEN_INSTANCE_VARIABLE: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_instance_variable_read_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_instance_variable_read_node_create(parser, &parser->previous)); if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -17995,32 +18001,32 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b case PM_TOKEN_INTEGER: { pm_node_flags_t base = parser->integer_base; parser_lex(parser); - return (pm_node_t *) pm_integer_node_create(parser, base, &parser->previous); + return UP(pm_integer_node_create(parser, base, &parser->previous)); } case PM_TOKEN_INTEGER_IMAGINARY: { pm_node_flags_t base = parser->integer_base; parser_lex(parser); - return (pm_node_t *) pm_integer_node_imaginary_create(parser, base, &parser->previous); + return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous)); } case PM_TOKEN_INTEGER_RATIONAL: { pm_node_flags_t base = parser->integer_base; parser_lex(parser); - return (pm_node_t *) pm_integer_node_rational_create(parser, base, &parser->previous); + return UP(pm_integer_node_rational_create(parser, base, &parser->previous)); } case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: { pm_node_flags_t base = parser->integer_base; parser_lex(parser); - return (pm_node_t *) pm_integer_node_rational_imaginary_create(parser, base, &parser->previous); + return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous)); } case PM_TOKEN_KEYWORD___ENCODING__: parser_lex(parser); - return (pm_node_t *) pm_source_encoding_node_create(parser, &parser->previous); + return UP(pm_source_encoding_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD___FILE__: parser_lex(parser); - return (pm_node_t *) pm_source_file_node_create(parser, &parser->previous); + return UP(pm_source_file_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD___LINE__: parser_lex(parser); - return (pm_node_t *) pm_source_line_node_create(parser, &parser->previous); + return UP(pm_source_line_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_ALIAS: { if (binding_power != PM_BINDING_POWER_STATEMENT) { pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS); @@ -18044,7 +18050,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT); } - return (pm_node_t *) pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name); + return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name)); } case PM_SYMBOL_NODE: case PM_INTERPOLATED_SYMBOL_NODE: { @@ -18054,7 +18060,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } PRISM_FALLTHROUGH default: - return (pm_node_t *) pm_alias_method_node_create(parser, &keyword, new_name, old_name); + return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name)); } } case PM_TOKEN_KEYWORD_CASE: { @@ -18087,7 +18093,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_list_free(¤t_block_exits); pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); - return (pm_node_t *) pm_case_node_create(parser, &case_keyword, predicate, &parser->previous); + return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous)); } // At this point we can create a case node, though we don't yet know @@ -18115,7 +18121,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression); - pm_when_node_conditions_append(when_node, (pm_node_t *) splat_node); + pm_when_node_conditions_append(when_node, UP(splat_node)); if (PM_NODE_TYPE_P(expression, PM_MISSING_NODE)) break; } else { @@ -18154,7 +18160,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_case_node_condition_append(case_node, (pm_node_t *) when_node); + pm_case_node_condition_append(case_node, UP(when_node)); } // If we didn't parse any conditions (in or when) then we need @@ -18164,7 +18170,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_static_literals_free(&literals); - node = (pm_node_t *) case_node; + node = UP(case_node); } else { pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate, &end_keyword); @@ -18201,11 +18207,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) { pm_token_t keyword = parser->previous; pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1)); - pattern = (pm_node_t *) pm_if_node_modifier_create(parser, pattern, &keyword, predicate); + pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate)); } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) { pm_token_t keyword = parser->previous; pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); - pattern = (pm_node_t *) pm_unless_node_modifier_create(parser, pattern, &keyword, predicate); + pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate)); } // Now we need to check for the terminator of the in node's @@ -18234,7 +18240,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // Now that we have the full pattern and statements, we can // create the node and attach it to the case node. - pm_node_t *condition = (pm_node_t *) pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword); + pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword)); pm_case_match_node_condition_append(case_node, condition); } @@ -18244,7 +18250,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); } - node = (pm_node_t *) case_node; + node = UP(case_node); } accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); @@ -18307,7 +18313,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) begin_node; + return UP(begin_node); } case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { pm_node_list_t current_block_exits = { 0 }; @@ -18333,7 +18339,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b flush_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); + return UP(pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous)); } case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_NEXT: @@ -18362,23 +18368,23 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b switch (keyword.type) { case PM_TOKEN_KEYWORD_BREAK: { - pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments); + pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments)); if (!parser->partial_script) parse_block_exit(parser, node); return node; } case PM_TOKEN_KEYWORD_NEXT: { - pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments); + pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments)); if (!parser->partial_script) parse_block_exit(parser, node); return node; } case PM_TOKEN_KEYWORD_RETURN: { - pm_node_t *node = (pm_node_t *) pm_return_node_create(parser, &keyword, arguments.arguments); + pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments)); parse_return(parser, node); return node; } default: assert(false && "unreachable"); - return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } } case PM_TOKEN_KEYWORD_SUPER: { @@ -18393,10 +18399,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b arguments.arguments == NULL && ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE)) ) { - return (pm_node_t *) pm_forwarding_super_node_create(parser, &keyword, &arguments); + return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments)); } - return (pm_node_t *) pm_super_node_create(parser, &keyword, &arguments); + return UP(pm_super_node_create(parser, &keyword, &arguments)); } case PM_TOKEN_KEYWORD_YIELD: { parser_lex(parser); @@ -18415,7 +18421,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b arguments.block = NULL; } - pm_node_t *node = (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc); + pm_node_t *node = UP(pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc)); if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node); return node; @@ -18442,13 +18448,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *statements = NULL; if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { pm_accepts_block_stack_push(parser, true); - statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)); + statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1))); pm_accepts_block_stack_pop(parser); } if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS, (uint16_t) (depth + 1)); + statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS, (uint16_t) (depth + 1))); } else { parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false); } @@ -18464,7 +18470,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b flush_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous); + return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous)); } pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_CLASS_NAME, (uint16_t) (depth + 1)); @@ -18500,13 +18506,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { pm_accepts_block_stack_push(parser, true); - statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)); + statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1))); pm_accepts_block_stack_pop(parser); } if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS, (uint16_t) (depth + 1)); + statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS, (uint16_t) (depth + 1))); } else { parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false); } @@ -18530,7 +18536,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous); + return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous)); } case PM_TOKEN_KEYWORD_DEF: { pm_node_list_t current_block_exits = { 0 }; @@ -18607,37 +18613,37 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b switch (identifier.type) { case PM_TOKEN_CONSTANT: - receiver = (pm_node_t *) pm_constant_read_node_create(parser, &identifier); + receiver = UP(pm_constant_read_node_create(parser, &identifier)); break; case PM_TOKEN_INSTANCE_VARIABLE: - receiver = (pm_node_t *) pm_instance_variable_read_node_create(parser, &identifier); + receiver = UP(pm_instance_variable_read_node_create(parser, &identifier)); break; case PM_TOKEN_CLASS_VARIABLE: - receiver = (pm_node_t *) pm_class_variable_read_node_create(parser, &identifier); + receiver = UP(pm_class_variable_read_node_create(parser, &identifier)); break; case PM_TOKEN_GLOBAL_VARIABLE: - receiver = (pm_node_t *) pm_global_variable_read_node_create(parser, &identifier); + receiver = UP(pm_global_variable_read_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_NIL: - receiver = (pm_node_t *) pm_nil_node_create(parser, &identifier); + receiver = UP(pm_nil_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_SELF: - receiver = (pm_node_t *) pm_self_node_create(parser, &identifier); + receiver = UP(pm_self_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_TRUE: - receiver = (pm_node_t *) pm_true_node_create(parser, &identifier); + receiver = UP(pm_true_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD_FALSE: - receiver = (pm_node_t *) pm_false_node_create(parser, &identifier); + receiver = UP(pm_false_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD___FILE__: - receiver = (pm_node_t *) pm_source_file_node_create(parser, &identifier); + receiver = UP(pm_source_file_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD___LINE__: - receiver = (pm_node_t *) pm_source_line_node_create(parser, &identifier); + receiver = UP(pm_source_line_node_create(parser, &identifier)); break; case PM_TOKEN_KEYWORD___ENCODING__: - receiver = (pm_node_t *) pm_source_encoding_node_create(parser, &identifier); + receiver = UP(pm_source_encoding_node_create(parser, &identifier)); break; default: break; @@ -18672,7 +18678,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM); operator = parser->previous; - receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0); + receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0)); // To push `PM_CONTEXT_DEF_PARAMS` again is for the same // reason as described the above. @@ -18765,7 +18771,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b context_push(parser, PM_CONTEXT_DEF); pm_do_loop_stack_push(parser, false); - statements = (pm_node_t *) pm_statements_node_create(parser); + statements = UP(pm_statements_node_create(parser)); bool allow_command_call; if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { @@ -18784,7 +18790,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *value = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); - statement = (pm_node_t *) pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); + statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value)); } pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false); @@ -18807,13 +18813,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { pm_accepts_block_stack_push(parser, true); - statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)); + statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1))); pm_accepts_block_stack_pop(parser); } if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &def_keyword, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF, (uint16_t) (depth + 1)); + statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &def_keyword, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF, (uint16_t) (depth + 1))); } else { parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false); } @@ -18839,7 +18845,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b flush_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_def_node_create( + return UP(pm_def_node_create( parser, name_id, &name, @@ -18853,7 +18859,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b &rparen, &equal, &end_keyword - ); + )); } case PM_TOKEN_KEYWORD_DEFINED: { parser_lex(parser); @@ -18870,7 +18876,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b lparen = parser->previous; if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { - expression = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0); + expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); lparen = not_provided(parser); rparen = not_provided(parser); } else { @@ -18891,13 +18897,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } context_pop(parser); - return (pm_node_t *) pm_defined_node_create( + return UP(pm_defined_node_create( parser, &lparen, expression, &rparen, &PM_LOCATION_TOKEN_VALUE(&keyword) - ); + )); } case PM_TOKEN_KEYWORD_END_UPCASE: { if (binding_power != PM_BINDING_POWER_STATEMENT) { @@ -18916,11 +18922,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1)); expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM); - return (pm_node_t *) pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); + return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous)); } case PM_TOKEN_KEYWORD_FALSE: parser_lex(parser); - return (pm_node_t *) pm_false_node_create(parser, &parser->previous); + return UP(pm_false_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_FOR: { size_t opening_newline_index = token_newline_index(parser); parser_lex(parser); @@ -18939,12 +18945,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b name = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); } - index = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name); + index = UP(pm_splat_node_create(parser, &star_operator, name)); } else if (token_begins_expression_p(parser->current.type)) { index = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); } else { pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX); - index = (pm_node_t *) pm_missing_node_create(parser, for_keyword.start, for_keyword.end); + index = UP(pm_missing_node_create(parser, for_keyword.start, for_keyword.end)); } // Now, if there are multiple index expressions, parse them out. @@ -18981,7 +18987,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false); expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM); - return (pm_node_t *) pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous); + return UP(pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous)); } case PM_TOKEN_KEYWORD_IF: if (parser_end_of_line_p(parser)) { @@ -19021,7 +19027,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - return (pm_node_t *) undef; + return UP(undef); } case PM_TOKEN_KEYWORD_NOT: { parser_lex(parser); @@ -19041,7 +19047,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER); } - return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } accept1(parser, PM_TOKEN_NEWLINE); @@ -19050,7 +19056,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t lparen = parser->previous; if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { - receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0); + receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); } else { arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&lparen); receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); @@ -19065,7 +19071,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b receiver = parse_expression(parser, PM_BINDING_POWER_NOT, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); } - return (pm_node_t *) pm_call_node_not_create(parser, receiver, &message, &arguments); + return UP(pm_call_node_not_create(parser, receiver, &message, &arguments)); } case PM_TOKEN_KEYWORD_UNLESS: { size_t opening_newline_index = token_newline_index(parser); @@ -19091,14 +19097,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_list_free(¤t_block_exits); pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; - return (pm_node_t *) pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing); + return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing)); } while (accept1(parser, PM_TOKEN_COLON_COLON)) { pm_token_t double_colon = parser->previous; expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - constant_path = (pm_node_t *) pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous); + constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous)); } // Here we retrieve the name of the module. If it wasn't a constant, @@ -19115,13 +19121,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { pm_accepts_block_stack_push(parser, true); - statements = (pm_node_t *) parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)); + statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1))); pm_accepts_block_stack_pop(parser); } if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &module_keyword, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE, (uint16_t) (depth + 1)); + statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &module_keyword, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE, (uint16_t) (depth + 1))); } else { parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false); } @@ -19139,15 +19145,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous); + return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous)); } case PM_TOKEN_KEYWORD_NIL: parser_lex(parser); - return (pm_node_t *) pm_nil_node_create(parser, &parser->previous); + return UP(pm_nil_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_REDO: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous)); if (!parser->partial_script) parse_block_exit(parser, node); return node; @@ -19155,17 +19161,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b case PM_TOKEN_KEYWORD_RETRY: { parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_retry_node_create(parser, &parser->previous); + pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous)); parse_retry(parser, node); return node; } case PM_TOKEN_KEYWORD_SELF: parser_lex(parser); - return (pm_node_t *) pm_self_node_create(parser, &parser->previous); + return UP(pm_self_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_TRUE: parser_lex(parser); - return (pm_node_t *) pm_true_node_create(parser, &parser->previous); + return UP(pm_true_node_create(parser, &parser->previous)); case PM_TOKEN_KEYWORD_UNTIL: { size_t opening_newline_index = token_newline_index(parser); @@ -19198,7 +19204,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM); - return (pm_node_t *) pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0); + return UP(pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0)); } case PM_TOKEN_KEYWORD_WHILE: { size_t opening_newline_index = token_newline_index(parser); @@ -19232,7 +19238,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false); expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM); - return (pm_node_t *) pm_while_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0); + return UP(pm_while_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0)); } case PM_TOKEN_PERCENT_LOWER_I: { parser_lex(parser); @@ -19246,7 +19252,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_STRING_CONTENT)) { pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_array_node_elements_append(array, (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing)); + pm_array_node_elements_append(array, UP(pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing))); } expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); @@ -19261,7 +19267,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PERCENT_UPPER_I: { parser_lex(parser); @@ -19296,13 +19302,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // If we hit content and the current node is NULL, then this is // the first string content we've seen. In that case we're going // to create a new string node and set that to the current. - current = (pm_node_t *) pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing); + current = UP(pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing)); parser_lex(parser); } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) { // If we hit string content and the current node is an // interpolated string, then we need to append the string content // to the list of child nodes. - pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); parser_lex(parser); pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string); @@ -19314,8 +19320,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t bounds = not_provided(parser); pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = cast->value_loc.start, .end = cast->value_loc.end }; - pm_node_t *first_string = (pm_node_t *) pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped); - pm_node_t *second_string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing); + pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped)); + pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing)); parser_lex(parser); pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); @@ -19323,7 +19329,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_append(interpolated, second_string); xfree(current); - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else { assert(false && "unreachable"); } @@ -19338,7 +19344,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // node to a new interpolated string. pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - current = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + current = UP(pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing)); } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { // If we hit an embedded variable and the current node is a string // node, then we'll convert the current into an interpolated @@ -19347,11 +19353,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t closing = not_provided(parser); pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current); + current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current)); pm_interpolated_symbol_node_append(interpolated, current); interpolated->base.location.start = current->location.start; start_location_set = true; - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else { // If we hit an embedded variable and the current node is an // interpolated string, then we'll just add the embedded variable. @@ -19372,7 +19378,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // node to a new interpolated string. pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - current = (pm_node_t *) pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + current = UP(pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing)); } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { // If we hit an embedded expression and the current node is a // string node, then we'll convert the current into an @@ -19382,11 +19388,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t closing = not_provided(parser); pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - current = (pm_node_t *) pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current); + current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current)); pm_interpolated_symbol_node_append(interpolated, current); interpolated->base.location.start = current->location.start; start_location_set = true; - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) { // If we hit an embedded expression and the current node is an // interpolated string, then we'll just continue on. @@ -19422,7 +19428,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PERCENT_LOWER_W: { parser_lex(parser); @@ -19440,7 +19446,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); pm_array_node_elements_append(array, string); } @@ -19456,7 +19462,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_PERCENT_UPPER_W: { parser_lex(parser); @@ -19492,7 +19498,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing); + pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); pm_node_flag_set(string, parse_unescaped_encoding(parser)); parser_lex(parser); @@ -19515,7 +19521,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); pm_interpolated_string_node_append(interpolated, current); pm_interpolated_string_node_append(interpolated, string); - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else { assert(false && "unreachable"); } @@ -19530,7 +19536,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // interpolated string. pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - current = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + current = UP(pm_interpolated_string_node_create(parser, &opening, NULL, &closing)); } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { // If we hit an embedded variable and the current // node is a string node, then we'll convert the @@ -19540,7 +19546,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t closing = not_provided(parser); pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); pm_interpolated_string_node_append(interpolated, current); - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else { // If we hit an embedded variable and the current // node is an interpolated string, then we'll just @@ -19559,7 +19565,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // interpolated string. pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - current = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + current = UP(pm_interpolated_string_node_create(parser, &opening, NULL, &closing)); } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { // If we hit an embedded expression and the current // node is a string node, then we'll convert the @@ -19569,7 +19575,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t closing = not_provided(parser); pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); pm_interpolated_string_node_append(interpolated, current); - current = (pm_node_t *) interpolated; + current = UP(interpolated); } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { // If we hit an embedded expression and the current // node is an interpolated string, then we'll just @@ -19603,7 +19609,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_array_node_close_set(array, &closing); - return (pm_node_t *) array; + return UP(array); } case PM_TOKEN_REGEXP_BEGIN: { pm_token_t opening = parser->current; @@ -19621,7 +19627,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); - pm_node_t *node = (pm_node_t *) pm_regular_expression_node_create(parser, &opening, &content, &parser->previous); + pm_node_t *node = UP(pm_regular_expression_node_create(parser, &opening, &content, &parser->previous)); pm_node_flag_set(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING); return node; @@ -19653,8 +19659,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parse_regular_expression_errors(parser, node); } - pm_node_flag_set((pm_node_t *) node, parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, node->base.flags)); - return (pm_node_t *) node; + pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, node->base.flags)); + return UP(node); } // If we get here, then we have interpolation so we'll need to create @@ -19663,7 +19669,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped); + pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped)); if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { // This is extremely strange, but the first string part of a @@ -19698,7 +19704,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing); - return (pm_node_t *) interpolated; + return UP(interpolated); } case PM_TOKEN_BACKTICK: case PM_TOKEN_PERCENT_LOWER_X: { @@ -19720,7 +19726,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b }; parser_lex(parser); - return (pm_node_t *) pm_xstring_node_create(parser, &opening, &content, &parser->previous); + return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous)); } pm_interpolated_x_string_node_t *node; @@ -19735,7 +19741,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); if (match1(parser, PM_TOKEN_STRING_END)) { - pm_node_t *node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped); + pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped)); pm_node_flag_set(node, parse_unescaped_encoding(parser)); parser_lex(parser); return node; @@ -19748,7 +19754,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped); + pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped)); pm_node_flag_set(part, parse_unescaped_encoding(parser)); pm_interpolated_xstring_node_append(node, part); @@ -19775,7 +19781,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_interpolated_xstring_node_closing_set(node, &closing); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_USTAR: { parser_lex(parser); @@ -19785,7 +19791,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // still lex past it though and create a missing node place. if (binding_power != PM_BINDING_POWER_STATEMENT) { pm_parser_err_prefix(parser, diag_id); - return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } pm_token_t operator = parser->previous; @@ -19795,7 +19801,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b name = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); } - pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &operator, name); + pm_node_t *splat = UP(pm_splat_node_create(parser, &operator, name)); if (match1(parser, PM_TOKEN_COMMA)) { return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); @@ -19815,7 +19821,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!"); pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_TILDE: { if (binding_power > PM_BINDING_POWER_UNARY) { @@ -19827,7 +19833,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~"); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_UMINUS: { if (binding_power > PM_BINDING_POWER_UNARY) { @@ -19839,7 +19845,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@"); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_UMINUS_NUM: { parser_lex(parser); @@ -19850,8 +19856,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (accept1(parser, PM_TOKEN_STAR_STAR)) { pm_token_t exponent_operator = parser->previous; pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, false, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); - node = (pm_node_t *) pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0); - node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@"); + node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0)); + node = UP(pm_call_node_unary_create(parser, &operator, node, "-@")); } else { switch (PM_NODE_TYPE(node)) { case PM_INTEGER_NODE: @@ -19861,7 +19867,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parse_negative_numeric(node); break; default: - node = (pm_node_t *) pm_call_node_unary_create(parser, &operator, node, "-@"); + node = UP(pm_call_node_unary_create(parser, &operator, node, "-@")); break; } } @@ -19919,7 +19925,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b opening = parser->previous; if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) { - body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)); + body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1))); } parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false); @@ -19930,13 +19936,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { pm_accepts_block_stack_push(parser, true); - body = (pm_node_t *) parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)); + body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1))); pm_accepts_block_stack_pop(parser); } if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)); - body = (pm_node_t *) parse_rescues_implicit_begin(parser, opening_newline_index, &operator, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA, (uint16_t) (depth + 1)); + body = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &operator, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA, (uint16_t) (depth + 1))); } else { parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false); } @@ -19946,12 +19952,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_constant_id_list_t locals; pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser)); - pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &operator, &parser->previous); + pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous); pm_parser_scope_pop(parser); pm_accepts_block_stack_pop(parser); - return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body); + return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body)); } case PM_TOKEN_UPLUS: { if (binding_power > PM_BINDING_POWER_UNARY) { @@ -19963,7 +19969,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@"); - return (pm_node_t *) node; + return UP(node); } case PM_TOKEN_STRING_BEGIN: return parse_strings(parser, NULL, accepts_label, (uint16_t) (depth + 1)); @@ -19999,7 +20005,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_prefix(parser, diag_id); } - return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end); + return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end)); } } } @@ -20028,7 +20034,7 @@ parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_ pm_node_t *right = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); - return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); + return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right)); } return value; @@ -20100,7 +20106,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding pm_array_node_t *array = pm_array_node_create(parser, &opening); pm_array_node_elements_append(array, value); - value = (pm_node_t *) array; + value = UP(array); while (accept1(parser, PM_TOKEN_COMMA)) { pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1)); @@ -20134,7 +20140,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding pm_node_t *right = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, accepts_command_call_inner, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); - return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); + return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right)); } return value; @@ -20151,15 +20157,15 @@ static void parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) { if (call_node->arguments != NULL) { pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS); - pm_node_unreference(parser, (pm_node_t *) call_node->arguments); - pm_node_destroy(parser, (pm_node_t *) call_node->arguments); + pm_node_unreference(parser, UP(call_node->arguments)); + pm_node_destroy(parser, UP(call_node->arguments)); call_node->arguments = NULL; } if (call_node->block != NULL) { pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK); - pm_node_unreference(parser, (pm_node_t *) call_node->block); - pm_node_destroy(parser, (pm_node_t *) call_node->block); + pm_node_unreference(parser, UP(call_node->block)); + pm_node_destroy(parser, UP(call_node->block)); call_node->block = NULL; } } @@ -20391,7 +20397,7 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // Next, create the local variable target and add it to the list of // targets for the match. - pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth); + pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth)); pm_node_list_append(&callback_data->match->targets, target); } @@ -20422,9 +20428,9 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t * pm_constant_id_list_free(&callback_data.names); if (callback_data.match != NULL) { - return (pm_node_t *) callback_data.match; + return UP(callback_data.match); } else { - return (pm_node_t *) call; + return UP(call); } } @@ -20469,7 +20475,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1)); - return parse_write(parser, (pm_node_t *) multi_target, &token, value); + return parse_write(parser, UP(multi_target), &token, value); } case PM_SOURCE_ENCODING_NODE: case PM_FALSE_NODE: @@ -20503,7 +20509,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_global_variable_and_write_node_create(parser, node, &token, value); + pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20512,7 +20518,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value); + pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20521,7 +20527,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *write = (pm_node_t *) pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value)); return parse_shareable_constant_write(parser, write); } @@ -20529,7 +20535,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *write = (pm_node_t *) pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return parse_shareable_constant_write(parser, write); @@ -20538,7 +20544,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value); + pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20548,7 +20554,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0); + pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0)); pm_node_unreference(parser, node); pm_node_destroy(parser, node); @@ -20564,7 +20570,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth); + pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth)); pm_node_destroy(parser, node); return result; @@ -20583,9 +20589,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); + pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); - pm_node_destroy(parser, (pm_node_t *) cast); + pm_node_destroy(parser, UP(cast)); return result; } @@ -20598,7 +20604,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // an aset expression. if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) { pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_index_and_write_node_create(parser, cast, &token, value); + return UP(pm_index_and_write_node_create(parser, cast, &token, value)); } // If this node cannot be writable, then we have an error. @@ -20610,7 +20616,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parse_call_operator_write(parser, cast, &token); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_and_write_node_create(parser, cast, &token, value); + return UP(pm_call_and_write_node_create(parser, cast, &token, value)); } case PM_MULTI_WRITE_NODE: { parser_lex(parser); @@ -20637,7 +20643,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_global_variable_or_write_node_create(parser, node, &token, value); + pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20646,7 +20652,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value); + pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20655,7 +20661,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *write = (pm_node_t *) pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value)); return parse_shareable_constant_write(parser, write); } @@ -20663,7 +20669,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *write = (pm_node_t *) pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return parse_shareable_constant_write(parser, write); @@ -20672,7 +20678,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value); + pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20682,7 +20688,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0); + pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0)); pm_node_unreference(parser, node); pm_node_destroy(parser, node); @@ -20698,7 +20704,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth); + pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth)); pm_node_destroy(parser, node); return result; @@ -20717,9 +20723,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); + pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); - pm_node_destroy(parser, (pm_node_t *) cast); + pm_node_destroy(parser, UP(cast)); return result; } @@ -20732,7 +20738,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // an aset expression. if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) { pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_index_or_write_node_create(parser, cast, &token, value); + return UP(pm_index_or_write_node_create(parser, cast, &token, value)); } // If this node cannot be writable, then we have an error. @@ -20744,7 +20750,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parse_call_operator_write(parser, cast, &token); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_or_write_node_create(parser, cast, &token, value); + return UP(pm_call_or_write_node_create(parser, cast, &token, value)); } case PM_MULTI_WRITE_NODE: { parser_lex(parser); @@ -20781,7 +20787,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_global_variable_operator_write_node_create(parser, node, &token, value); + pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20790,7 +20796,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value); + pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20799,7 +20805,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *write = (pm_node_t *) pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value); + pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value)); return parse_shareable_constant_write(parser, write); } @@ -20807,7 +20813,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *write = (pm_node_t *) pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value); + pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return parse_shareable_constant_write(parser, write); @@ -20816,7 +20822,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value); + pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value)); pm_node_destroy(parser, node); return result; @@ -20826,7 +20832,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0); + pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0)); pm_node_unreference(parser, node); pm_node_destroy(parser, node); @@ -20842,7 +20848,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth); + pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth)); pm_node_destroy(parser, node); return result; @@ -20860,9 +20866,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); + pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0)); - pm_node_destroy(parser, (pm_node_t *) cast); + pm_node_destroy(parser, UP(cast)); return result; } @@ -20871,7 +20877,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // an aset expression. if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) { pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_index_operator_write_node_create(parser, cast, &token, value); + return UP(pm_index_operator_write_node_create(parser, cast, &token, value)); } // If this node cannot be writable, then we have an error. @@ -20883,7 +20889,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parse_call_operator_write(parser, cast, &token); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_operator_write_node_create(parser, cast, &token, value); + return UP(pm_call_operator_write_node_create(parser, cast, &token, value)); } case PM_MULTI_WRITE_NODE: { parser_lex(parser); @@ -20905,14 +20911,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_AND, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_and_node_create(parser, node, &token, right); + return UP(pm_and_node_create(parser, node, &token, right)); } case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_PIPE_PIPE: { parser_lex(parser); pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_OR, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_or_node_create(parser, node, &token, right); + return UP(pm_or_node_create(parser, node, &token, right)); } case PM_TOKEN_EQUAL_TILDE: { // Note that we _must_ parse the value before adding the local @@ -20927,7 +20933,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // By default, we're going to create a call node and then return it. pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0); - pm_node_t *result = (pm_node_t *) call; + pm_node_t *result = UP(call); // If the receiver of this =~ is a regular expression node, then we // need to introduce local variables for it based on its named @@ -21030,7 +21036,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, 0); + return UP(pm_call_node_binary_create(parser, node, &token, argument, 0)); } case PM_TOKEN_GREATER: case PM_TOKEN_GREATER_EQUAL: @@ -21042,7 +21048,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser_lex(parser); pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON); + return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON)); } case PM_TOKEN_AMPERSAND_DOT: case PM_TOKEN_DOT: { @@ -21053,7 +21059,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // This if statement handles the foo.() syntax. if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &operator, &arguments); + return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments)); } switch (PM_NODE_TYPE(node)) { @@ -21109,9 +21115,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t arguments.opening_loc.start == NULL && match1(parser, PM_TOKEN_COMMA) ) { - return parse_targets_validate(parser, (pm_node_t *) call, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); } else { - return (pm_node_t *) call; + return UP(call); } } case PM_TOKEN_DOT_DOT: @@ -21123,21 +21129,21 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t right = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); } - return (pm_node_t *) pm_range_node_create(parser, node, &token, right); + return UP(pm_range_node_create(parser, node, &token, right)); } case PM_TOKEN_KEYWORD_IF_MODIFIER: { pm_token_t keyword = parser->current; parser_lex(parser); pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_if_node_modifier_create(parser, node, &keyword, predicate); + return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate)); } case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: { pm_token_t keyword = parser->current; parser_lex(parser); pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_unless_node_modifier_create(parser, node, &keyword, predicate); + return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate)); } case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: { parser_lex(parser); @@ -21145,7 +21151,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_statements_node_body_append(parser, statements, node, true); pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0); + return UP(pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0)); } case PM_TOKEN_KEYWORD_WHILE_MODIFIER: { parser_lex(parser); @@ -21153,7 +21159,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_statements_node_body_append(parser, statements, node, true); pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0); + return UP(pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0)); } case PM_TOKEN_QUESTION_MARK: { context_push(parser, PM_CONTEXT_TERNARY); @@ -21173,13 +21179,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // accidentally move past a ':' token that occurs after the syntax // error. pm_token_t colon = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; - pm_node_t *false_expression = (pm_node_t *) pm_missing_node_create(parser, colon.start, colon.end); + pm_node_t *false_expression = UP(pm_missing_node_create(parser, colon.start, colon.end)); context_pop(parser); pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); + return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression)); } accept1(parser, PM_TOKEN_NEWLINE); @@ -21192,7 +21198,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pop_block_exits(parser, previous_block_exits); pm_node_list_free(¤t_block_exits); - return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); + return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression)); } case PM_TOKEN_COLON_COLON: { parser_lex(parser); @@ -21217,10 +21223,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_arguments_t arguments = { 0 }; parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); - path = (pm_node_t *) pm_call_node_call_create(parser, node, &delimiter, &message, &arguments); + path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments)); } else { // Otherwise, this is a constant path. That would look like Foo::Bar. - path = (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); + path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); } // If this is followed by a comma then it is a multiple assignment. @@ -21245,10 +21251,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // If this is followed by a comma then it is a multiple assignment. if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { - return parse_targets_validate(parser, (pm_node_t *) call, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); } - return (pm_node_t *) call; + return UP(call); } case PM_TOKEN_PARENTHESIS_LEFT: { // If we have a parenthesis following a '::' operator, then it is the @@ -21256,11 +21262,11 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_arguments_t arguments = { 0 }; parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1)); - return (pm_node_t *) pm_call_node_shorthand_create(parser, node, &delimiter, &arguments); + return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments)); } default: { expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT); - return (pm_node_t *) pm_constant_path_node_create(parser, node, &delimiter, &parser->previous); + return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous)); } } } @@ -21272,7 +21278,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_node_t *value = parse_expression(parser, binding_power, true, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); - return (pm_node_t *) pm_rescue_modifier_node_create(parser, node, &token, value); + return UP(pm_rescue_modifier_node_create(parser, node, &token, value)); } case PM_TOKEN_BRACKET_LEFT: { parser_lex(parser); @@ -21293,7 +21299,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // assignment and we should parse the targets. if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) { pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments); - return parse_targets_validate(parser, (pm_node_t *) aref, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); + return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1)); } // If we're at the end of the arguments, we can now check if there is a @@ -21309,17 +21315,17 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t if (block != NULL) { if (arguments.block != NULL) { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_AFTER_BLOCK); + pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK); if (arguments.arguments == NULL) { arguments.arguments = pm_arguments_node_create(parser); } pm_arguments_node_arguments_append(arguments.arguments, arguments.block); } - arguments.block = (pm_node_t *) block; + arguments.block = UP(block); } - return (pm_node_t *) pm_call_node_aref_create(parser, node, &arguments); + return UP(pm_call_node_aref_create(parser, node, &arguments)); } case PM_TOKEN_KEYWORD_IN: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -21336,7 +21342,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser->pattern_matching_newlines = previous_pattern_matching_newlines; pm_constant_id_list_free(&captures); - return (pm_node_t *) pm_match_predicate_node_create(parser, node, pattern, &operator); + return UP(pm_match_predicate_node_create(parser, node, pattern, &operator)); } case PM_TOKEN_EQUAL_GREATER: { bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; @@ -21353,7 +21359,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t parser->pattern_matching_newlines = previous_pattern_matching_newlines; pm_constant_id_list_free(&captures); - return (pm_node_t *) pm_match_required_node_create(parser, node, pattern, &operator); + return UP(pm_match_required_node_create(parser, node, pattern, &operator)); } default: assert(false && "unreachable"); @@ -21390,7 +21396,7 @@ static pm_node_t * parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) { if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) { pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP); - return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); + return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end)); } pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth); @@ -21584,14 +21590,14 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( arguments, - (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)) + UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2))) ); - pm_statements_node_body_append(parser, statements, (pm_node_t *) pm_call_node_fcall_synthesized_create( + pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create( parser, arguments, pm_parser_constant_id_constant(parser, "print", 5) - ), true); + )), true); } if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) { @@ -21603,46 +21609,46 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( arguments, - (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)) + UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2))) ); pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)); - pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, (pm_node_t *) receiver, "split", arguments); + pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments); pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create( parser, pm_parser_constant_id_constant(parser, "$F", 2), - (pm_node_t *) call + UP(call) ); - pm_statements_node_body_prepend(statements, (pm_node_t *) write); + pm_statements_node_body_prepend(statements, UP(write)); } pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( arguments, - (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)) + UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2))) ); if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) { pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser); - pm_keyword_hash_node_elements_append(keywords, (pm_node_t *) pm_assoc_node_create( + pm_keyword_hash_node_elements_append(keywords, UP(pm_assoc_node_create( parser, - (pm_node_t *) pm_symbol_node_synthesized_create(parser, "chomp"), + UP(pm_symbol_node_synthesized_create(parser, "chomp")), &(pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }, - (pm_node_t *) pm_true_node_synthesized_create(parser) - )); + UP(pm_true_node_synthesized_create(parser)) + ))); - pm_arguments_node_arguments_append(arguments, (pm_node_t *) keywords); - pm_node_flag_set((pm_node_t *) arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS); + pm_arguments_node_arguments_append(arguments, UP(keywords)); + pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS); } pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser); - pm_statements_node_body_append(parser, wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create( + pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create( parser, - (pm_node_t *) pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4)), + UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))), statements - ), true); + )), true); statements = wrapped_statements; } @@ -21698,7 +21704,7 @@ parse_program(pm_parser_t *parser) { pm_statements_node_location_set(statements, parser->start, parser->start); } - return (pm_node_t *) pm_program_node_create(parser, &locals, statements); + return UP(pm_program_node_create(parser, &locals, statements)); } /******************************************************************************/ diff --git a/prism/templates/include/prism/ast.h.erb b/prism/templates/include/prism/ast.h.erb index e82cb78fe3106f..d69a0d74569a2f 100644 --- a/prism/templates/include/prism/ast.h.erb +++ b/prism/templates/include/prism/ast.h.erb @@ -105,22 +105,6 @@ typedef uint16_t pm_node_flags_t; static const pm_node_flags_t PM_NODE_FLAG_NEWLINE = 0x1; static const pm_node_flags_t PM_NODE_FLAG_STATIC_LITERAL = 0x2; -/** - * Cast the type to an enum to allow the compiler to provide exhaustiveness - * checking. - */ -#define PM_NODE_TYPE(node) ((enum pm_node_type) (node)->type) - -/** - * Return true if the type of the given node matches the given type. - */ -#define PM_NODE_TYPE_P(node, type) (PM_NODE_TYPE(node) == (type)) - -/** - * Return true if the given flag is set on the given node. - */ -#define PM_NODE_FLAG_P(node, flag) ((((pm_node_t *)(node))->flags & (flag)) != 0) - /** * This is the base structure that represents a node in the syntax tree. It is * embedded into every node type. @@ -150,6 +134,27 @@ typedef struct pm_node { */ pm_location_t location; } pm_node_t; + +/** + * Cast the given node to the base pm_node_t type. + */ +#define PM_NODE_UPCAST(node_) ((pm_node_t *) (node_)) + +/** + * Cast the type to an enum to allow the compiler to provide exhaustiveness + * checking. + */ +#define PM_NODE_TYPE(node_) ((enum pm_node_type) (node_)->type) + +/** + * Return true if the type of the given node matches the given type. + */ +#define PM_NODE_TYPE_P(node_, type_) (PM_NODE_TYPE(node_) == (type_)) + +/** + * Return true if the given flag is set on the given node. + */ +#define PM_NODE_FLAG_P(node_, flag_) ((PM_NODE_UPCAST(node_)->flags & (flag_)) != 0) <%- nodes.each do |node| -%> /** From 68617fb62b943b9d2cb67f177a55b63b7508f504 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Dec 2025 15:05:04 -0500 Subject: [PATCH 095/367] [ruby/prism] Specialize PM_NODE_INIT to reduce calls to location https://github.com/ruby/prism/commit/3e0b5c9eb7 --- prism/prism.c | 269 ++++++++++++++++++++++++++------------------------ 1 file changed, 142 insertions(+), 127 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 044e61a1b1d5c5..2c930d8deb4122 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -24,6 +24,12 @@ pm_version(void) { #define UP PM_NODE_UPCAST +#define PM_TOKEN_START(token_) ((token_)->start) +#define PM_TOKEN_END(token_) ((token_)->end) + +#define PM_NODE_START(node_) (UP(node_)->location.start) +#define PM_NODE_END(node_) (UP(node_)->location.end) + /******************************************************************************/ /* Lex mode manipulations */ /******************************************************************************/ @@ -1942,6 +1948,15 @@ pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { .location = { .start = (start_), .end = (end_) } \ } +#define PM_NODE_INIT_UNSET(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, (parser_)->start, (parser_)->start) +#define PM_NODE_INIT_TOKEN(parser_, type_, flags_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_TOKEN_END(token_)) +#define PM_NODE_INIT_NODE(parser_, type_, flags_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_NODE_END(node_)) + +#define PM_NODE_INIT_TOKENS(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(left_), PM_TOKEN_END(right_)) +#define PM_NODE_INIT_NODES(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(left_), PM_NODE_END(right_)) +#define PM_NODE_INIT_TOKEN_NODE(parser_, type_, flags_, token_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_NODE_END(node_)) +#define PM_NODE_INIT_NODE_TOKEN(parser_, type_, flags_, node_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_TOKEN_END(token_)) + /** * Allocate a new MissingNode node. */ @@ -1965,7 +1980,7 @@ pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyw pm_alias_global_variable_node_t *node = PM_NODE_ALLOC(parser, pm_alias_global_variable_node_t); *node = (pm_alias_global_variable_node_t) { - .base = PM_NODE_INIT(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, keyword->start, old_name->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, keyword, old_name), .new_name = new_name, .old_name = old_name, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) @@ -1983,7 +1998,7 @@ pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_n pm_alias_method_node_t *node = PM_NODE_ALLOC(parser, pm_alias_method_node_t); *node = (pm_alias_method_node_t) { - .base = PM_NODE_INIT(parser, PM_ALIAS_METHOD_NODE, 0, keyword->start, old_name->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_METHOD_NODE, 0, keyword, old_name), .new_name = new_name, .old_name = old_name, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) @@ -2000,7 +2015,7 @@ pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node pm_alternation_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_alternation_pattern_node_t); *node = (pm_alternation_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_ALTERNATION_PATTERN_NODE, 0, left->location.start, right->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_ALTERNATION_PATTERN_NODE, 0, left, right), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -2019,7 +2034,7 @@ pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *opera pm_and_node_t *node = PM_NODE_ALLOC(parser, pm_and_node_t); *node = (pm_and_node_t) { - .base = PM_NODE_INIT(parser, PM_AND_NODE, 0, left->location.start, right->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_AND_NODE, 0, left, right), .left = left, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .right = right @@ -2036,7 +2051,7 @@ pm_arguments_node_create(pm_parser_t *parser) { pm_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_arguments_node_t); *node = (pm_arguments_node_t) { - .base = PM_NODE_INIT(parser, PM_ARGUMENTS_NODE, 0, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_ARGUMENTS_NODE, 0), .arguments = { 0 } }; @@ -2080,7 +2095,7 @@ pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) { pm_array_node_t *node = PM_NODE_ALLOC(parser, pm_array_node_t); *node = (pm_array_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, opening->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .elements = { 0 } @@ -2131,7 +2146,7 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, nodes->nodes[0]->location.start, nodes->nodes[nodes->size - 1]->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_ARRAY_PATTERN_NODE, 0, nodes->nodes[0], nodes->nodes[nodes->size - 1]), .constant = NULL, .rest = NULL, .requireds = { 0 }, @@ -2167,7 +2182,7 @@ pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, rest->location.start, rest->location.end), + .base = PM_NODE_INIT_NODE(parser, PM_ARRAY_PATTERN_NODE, 0, rest), .constant = NULL, .rest = rest, .requireds = { 0 }, @@ -2188,7 +2203,7 @@ pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, constant->location.start, closing->end), + .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_ARRAY_PATTERN_NODE, 0, constant, closing), .constant = constant, .rest = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -2209,7 +2224,7 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t); *node = (pm_array_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_PATTERN_NODE, 0, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_ARRAY_PATTERN_NODE, 0, opening, closing), .constant = NULL, .rest = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -2295,7 +2310,7 @@ pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) pm_back_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_back_reference_read_node_t); *node = (pm_back_reference_read_node_t) { - .base = PM_NODE_INIT(parser, PM_BACK_REFERENCE_READ_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_BACK_REFERENCE_READ_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -2385,7 +2400,7 @@ pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p pm_block_node_t *node = PM_NODE_ALLOC(parser, pm_block_node_t); *node = (pm_block_node_t) { - .base = PM_NODE_INIT(parser, PM_BLOCK_NODE, 0, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_BLOCK_NODE, 0, opening, closing), .locals = *locals, .parameters = parameters, .body = body, @@ -2469,7 +2484,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) pm_block_local_variable_node_t *node = PM_NODE_ALLOC(parser, pm_block_local_variable_node_t); *node = (pm_block_local_variable_node_t) { - .base = PM_NODE_INIT(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -2523,7 +2538,7 @@ pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) { pm_call_node_t *node = PM_NODE_ALLOC(parser, pm_call_node_t); *node = (pm_call_node_t) { - .base = PM_NODE_INIT(parser, PM_CALL_NODE, flags, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_CALL_NODE, flags), .receiver = NULL, .call_operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .message_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -2830,7 +2845,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_and_write_node_t); *node = (pm_call_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CALL_AND_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CALL_AND_WRITE_NODE, target->base.flags, target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -2885,7 +2900,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INDEX_AND_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_AND_WRITE_NODE, target->base.flags, target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -2913,7 +2928,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, pm_call_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_operator_write_node_t); *node = (pm_call_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CALL_OPERATOR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OPERATOR_WRITE_NODE, target->base.flags, target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -2945,7 +2960,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INDEX_OPERATOR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OPERATOR_WRITE_NODE, target->base.flags, target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -2975,7 +2990,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_or_write_node_t); *node = (pm_call_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CALL_OR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OR_WRITE_NODE, target->base.flags, target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3007,7 +3022,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INDEX_OR_WRITE_NODE, target->base.flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OR_WRITE_NODE, target->base.flags, target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3035,7 +3050,7 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { pm_call_target_node_t *node = PM_NODE_ALLOC(parser, pm_call_target_node_t); *node = (pm_call_target_node_t) { - .base = PM_NODE_INIT(parser, PM_CALL_TARGET_NODE, target->base.flags, target->base.location.start, target->base.location.end), + .base = PM_NODE_INIT_NODE(parser, PM_CALL_TARGET_NODE, target->base.flags, target), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .name = target->name, @@ -3063,7 +3078,7 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_target_node_t) { - .base = PM_NODE_INIT(parser, PM_INDEX_TARGET_NODE, flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target->base.location.start, target->base.location.end), + .base = PM_NODE_INIT_NODE(parser, PM_INDEX_TARGET_NODE, flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target), .receiver = target->receiver, .opening_loc = target->opening_loc, .arguments = target->arguments, @@ -3087,7 +3102,7 @@ pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_v pm_capture_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_capture_pattern_node_t); *node = (pm_capture_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_CAPTURE_PATTERN_NODE, 0, value->location.start, target->base.location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CAPTURE_PATTERN_NODE, 0, value, target), .value = value, .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -3104,7 +3119,7 @@ pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node pm_case_node_t *node = PM_NODE_ALLOC(parser, pm_case_node_t); *node = (pm_case_node_t) { - .base = PM_NODE_INIT(parser, PM_CASE_NODE, 0, case_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_NODE, 0, case_keyword, end_keyword), .predicate = predicate, .else_clause = NULL, .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), @@ -3152,7 +3167,7 @@ pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, p pm_case_match_node_t *node = PM_NODE_ALLOC(parser, pm_case_match_node_t); *node = (pm_case_match_node_t) { - .base = PM_NODE_INIT(parser, PM_CASE_MATCH_NODE, 0, case_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_MATCH_NODE, 0, case_keyword, end_keyword), .predicate = predicate, .else_clause = NULL, .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword), @@ -3200,7 +3215,7 @@ pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p pm_class_node_t *node = PM_NODE_ALLOC(parser, pm_class_node_t); *node = (pm_class_node_t) { - .base = PM_NODE_INIT(parser, PM_CLASS_NODE, 0, class_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_CLASS_NODE, 0, class_keyword, end_keyword), .locals = *locals, .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), .constant_path = constant_path, @@ -3223,7 +3238,7 @@ pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_r pm_class_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_and_write_node_t); *node = (pm_class_variable_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3241,7 +3256,7 @@ pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_varia pm_class_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_operator_write_node_t); *node = (pm_class_variable_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3261,7 +3276,7 @@ pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_re pm_class_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_or_write_node_t); *node = (pm_class_variable_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3280,7 +3295,7 @@ pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) pm_class_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_read_node_t); *node = (pm_class_variable_read_node_t) { - .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_READ_NODE, 0, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_CLASS_VARIABLE_READ_NODE, 0, token), .name = pm_parser_constant_id_token(parser, token) }; @@ -3310,7 +3325,7 @@ pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_ pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_class_variable_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node, value), .name = read_node->name, .name_loc = PM_LOCATION_NODE_VALUE(UP(read_node)), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3329,7 +3344,7 @@ pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_nod pm_constant_path_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_and_write_node_t); *node = (pm_constant_path_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, target, value), .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3346,7 +3361,7 @@ pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_pat pm_constant_path_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_operator_write_node_t); *node = (pm_constant_path_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, target, value), .target = target, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -3365,7 +3380,7 @@ pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node pm_constant_path_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_or_write_node_t); *node = (pm_constant_path_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, target, value), .target = target, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3407,7 +3422,7 @@ pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_constant_path_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, target, value), .target = target, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value @@ -3425,7 +3440,7 @@ pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t * pm_constant_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_and_write_node_t); *node = (pm_constant_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_AND_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3443,7 +3458,7 @@ pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_nod pm_constant_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_operator_write_node_t); *node = (pm_constant_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3463,7 +3478,7 @@ pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *t pm_constant_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_or_write_node_t); *node = (pm_constant_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -3482,7 +3497,7 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { pm_constant_read_node_t *node = PM_NODE_ALLOC(parser, pm_constant_read_node_t); *node = (pm_constant_read_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_READ_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_CONSTANT_READ_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -3498,7 +3513,7 @@ pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *targ pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_constant_write_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_WRITE_NODE, flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_WRITE_NODE, flags, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -3653,7 +3668,7 @@ pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *openin pm_embedded_statements_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_statements_node_t); *node = (pm_embedded_statements_node_t) { - .base = PM_NODE_INIT(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, opening, closing), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .statements = statements, .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) @@ -3670,7 +3685,7 @@ pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator pm_embedded_variable_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_variable_node_t); *node = (pm_embedded_variable_node_t) { - .base = PM_NODE_INIT(parser, PM_EMBEDDED_VARIABLE_NODE, 0, operator->start, variable->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_EMBEDDED_VARIABLE_NODE, 0, operator, variable), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .variable = variable }; @@ -3686,7 +3701,7 @@ pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_ pm_ensure_node_t *node = PM_NODE_ALLOC(parser, pm_ensure_node_t); *node = (pm_ensure_node_t) { - .base = PM_NODE_INIT(parser, PM_ENSURE_NODE, 0, ensure_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_ENSURE_NODE, 0, ensure_keyword, end_keyword), .ensure_keyword_loc = PM_LOCATION_TOKEN_VALUE(ensure_keyword), .statements = statements, .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword) @@ -3704,7 +3719,7 @@ pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_false_node_t *node = PM_NODE_ALLOC(parser, pm_false_node_t); *node = (pm_false_node_t) { - .base = PM_NODE_INIT(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) }; return node; @@ -3739,7 +3754,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { pm_node_t *right_splat_node = right; #endif *node = (pm_find_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_FIND_PATTERN_NODE, 0, left->location.start, right->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_FIND_PATTERN_NODE, 0, left, right), .constant = NULL, .left = left_splat_node, .right = right_splat_node, @@ -3840,7 +3855,7 @@ pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_float_node_t *node = PM_NODE_ALLOC(parser, pm_float_node_t); *node = (pm_float_node_t) { - .base = PM_NODE_INIT(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), .value = pm_double_parse(parser, token) }; @@ -3856,7 +3871,7 @@ pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) { pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), .numeric = UP(pm_float_node_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT, .start = token->start, @@ -3876,7 +3891,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t); *node = (pm_rational_node_t) { - .base = PM_NODE_INIT(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, token), .numerator = { 0 }, .denominator = { 0 } }; @@ -3925,7 +3940,7 @@ pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *t pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), .numeric = UP(pm_float_node_rational_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT_RATIONAL, .start = token->start, @@ -3953,7 +3968,7 @@ pm_for_node_create( pm_for_node_t *node = PM_NODE_ALLOC(parser, pm_for_node_t); *node = (pm_for_node_t) { - .base = PM_NODE_INIT(parser, PM_FOR_NODE, 0, for_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_FOR_NODE, 0, for_keyword, end_keyword), .index = index, .collection = collection, .statements = statements, @@ -3975,7 +3990,7 @@ pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token pm_forwarding_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_arguments_node_t); *node = (pm_forwarding_arguments_node_t) { - .base = PM_NODE_INIT(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, token) }; return node; @@ -3990,7 +4005,7 @@ pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token pm_forwarding_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_parameter_node_t); *node = (pm_forwarding_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_FORWARDING_PARAMETER_NODE, 0, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_PARAMETER_NODE, 0, token) }; return node; @@ -4028,7 +4043,7 @@ pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening pm_hash_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_hash_pattern_node_t); *node = (pm_hash_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_HASH_PATTERN_NODE, 0, opening, closing), .constant = NULL, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -4109,7 +4124,7 @@ pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, pm_global_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_and_write_node_t); *node = (pm_global_variable_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4127,7 +4142,7 @@ pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *ta pm_global_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_operator_write_node_t); *node = (pm_global_variable_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4147,7 +4162,7 @@ pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, pm_global_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_or_write_node_t); *node = (pm_global_variable_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4165,7 +4180,7 @@ pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, name), .name = pm_parser_constant_id_token(parser, name) }; @@ -4180,7 +4195,7 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0), .name = name }; @@ -4196,7 +4211,7 @@ pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, con pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_global_variable_write_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, target, value), .name = pm_global_variable_write_name(parser, target), .name_loc = PM_LOCATION_NODE_VALUE(target), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -4214,7 +4229,7 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); *node = (pm_global_variable_write_node_t) { - .base = PM_NODE_INIT(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0), .name = name, .name_loc = PM_LOCATION_NULL_VALUE(parser), .operator_loc = PM_LOCATION_NULL_VALUE(parser), @@ -4233,7 +4248,7 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) { pm_hash_node_t *node = PM_NODE_ALLOC(parser, pm_hash_node_t); *node = (pm_hash_node_t) { - .base = PM_NODE_INIT(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, opening->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_NULL_VALUE(parser), .elements = { 0 } @@ -4319,7 +4334,7 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t pm_statements_node_body_append(parser, statements, statement, true); *node = (pm_if_node_t) { - .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement->location.start, predicate->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -4351,7 +4366,7 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); *node = (pm_if_node_t) { - .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate->location.start, false_expression->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate, false_expression), .if_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .predicate = predicate, .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark), @@ -4384,7 +4399,7 @@ pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) { pm_implicit_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_node_t); *node = (pm_implicit_node_t) { - .base = PM_NODE_INIT(parser, PM_IMPLICIT_NODE, 0, value->location.start, value->location.end), + .base = PM_NODE_INIT_NODE(parser, PM_IMPLICIT_NODE, 0, value), .value = value }; @@ -4401,7 +4416,7 @@ pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_implicit_rest_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_rest_node_t); *node = (pm_implicit_rest_node_t) { - .base = PM_NODE_INIT(parser, PM_IMPLICIT_REST_NODE, 0, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_IMPLICIT_REST_NODE, 0, token) }; return node; @@ -4416,7 +4431,7 @@ pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token pm_integer_node_t *node = PM_NODE_ALLOC(parser, pm_integer_node_t); *node = (pm_integer_node_t) { - .base = PM_NODE_INIT(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token), .value = { 0 } }; @@ -4443,7 +4458,7 @@ pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, cons pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), .numeric = UP(pm_integer_node_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER, .start = token->start, @@ -4464,7 +4479,7 @@ pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t); *node = (pm_rational_node_t) { - .base = PM_NODE_INIT(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token), .numerator = { 0 }, .denominator = { .value = 1, 0 } }; @@ -4493,7 +4508,7 @@ pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t b pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t); *node = (pm_imaginary_node_t) { - .base = PM_NODE_INIT(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token), .numeric = UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER_RATIONAL, .start = token->start, @@ -4540,7 +4555,7 @@ pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_vari pm_instance_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_and_write_node_t); *node = (pm_instance_variable_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4558,7 +4573,7 @@ pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance pm_instance_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_operator_write_node_t); *node = (pm_instance_variable_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4578,7 +4593,7 @@ pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_varia pm_instance_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_or_write_node_t); *node = (pm_instance_variable_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, target, value), .name = target->name, .name_loc = target->base.location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -4597,7 +4612,7 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok pm_instance_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_read_node_t); *node = (pm_instance_variable_read_node_t) { - .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, token), .name = pm_parser_constant_id_token(parser, token) }; @@ -4614,7 +4629,7 @@ pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_instance_variable_write_node_t) { - .base = PM_NODE_INIT(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node, value), .name = read_node->name, .name_loc = PM_LOCATION_NODE_BASE_VALUE(read_node), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), @@ -4835,7 +4850,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin } *node = (pm_interpolated_string_node_t) { - .base = PM_NODE_INIT(parser, PM_INTERPOLATED_STRING_NODE, flags, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_STRING_NODE, flags, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -4884,7 +4899,7 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin pm_interpolated_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_symbol_node_t); *node = (pm_interpolated_symbol_node_t) { - .base = PM_NODE_INIT(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -4908,7 +4923,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi pm_interpolated_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_x_string_node_t); *node = (pm_interpolated_x_string_node_t) { - .base = PM_NODE_INIT(parser, PM_INTERPOLATED_X_STRING_NODE, 0, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_X_STRING_NODE, 0, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), .parts = { 0 } @@ -4937,7 +4952,7 @@ pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *nam pm_it_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_it_local_variable_read_node_t); *node = (pm_it_local_variable_read_node_t) { - .base = PM_NODE_INIT(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, name), }; return node; @@ -4951,7 +4966,7 @@ pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, con pm_it_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_it_parameters_node_t); *node = (pm_it_parameters_node_t) { - .base = PM_NODE_INIT(parser, PM_IT_PARAMETERS_NODE, 0, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_IT_PARAMETERS_NODE, 0, opening, closing), }; return node; @@ -4998,7 +5013,7 @@ pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t pm_required_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_keyword_parameter_node_t); *node = (pm_required_keyword_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, name), .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), .name_loc = PM_LOCATION_TOKEN_VALUE(name), }; @@ -5014,7 +5029,7 @@ pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t pm_optional_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_keyword_parameter_node_t); *node = (pm_optional_keyword_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, name->start, value->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, name, value), .name = pm_parser_constant_id_location(parser, name->start, name->end - 1), .name_loc = PM_LOCATION_TOKEN_VALUE(name), .value = value @@ -5056,7 +5071,7 @@ pm_lambda_node_create( pm_lambda_node_t *node = PM_NODE_ALLOC(parser, pm_lambda_node_t); *node = (pm_lambda_node_t) { - .base = PM_NODE_INIT(parser, PM_LAMBDA_NODE, 0, operator->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_LAMBDA_NODE, 0, operator, closing), .locals = *locals, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -5078,7 +5093,7 @@ pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, pm_local_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_and_write_node_t); *node = (pm_local_variable_and_write_node_t) { - .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, target, value), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5097,7 +5112,7 @@ pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *tar pm_local_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_operator_write_node_t); *node = (pm_local_variable_operator_write_node_t) { - .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value), .name_loc = target->location, .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5119,7 +5134,7 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c pm_local_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_or_write_node_t); *node = (pm_local_variable_or_write_node_t) { - .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, target->location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, target, value), .name_loc = target->location, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .value = value, @@ -5140,7 +5155,7 @@ pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_tok pm_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_read_node_t); *node = (pm_local_variable_read_node_t) { - .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, name), .name = name_id, .depth = depth }; @@ -5243,7 +5258,7 @@ pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t pm_match_predicate_node_t *node = PM_NODE_ALLOC(parser, pm_match_predicate_node_t); *node = (pm_match_predicate_node_t) { - .base = PM_NODE_INIT(parser, PM_MATCH_PREDICATE_NODE, 0, value->location.start, pattern->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_MATCH_PREDICATE_NODE, 0, value, pattern), .value = value, .pattern = pattern, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5262,7 +5277,7 @@ pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t * pm_match_required_node_t *node = PM_NODE_ALLOC(parser, pm_match_required_node_t); *node = (pm_match_required_node_t) { - .base = PM_NODE_INIT(parser, PM_MATCH_REQUIRED_NODE, 0, value->location.start, pattern->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_MATCH_REQUIRED_NODE, 0, value, pattern), .value = value, .pattern = pattern, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5279,7 +5294,7 @@ pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) { pm_match_write_node_t *node = PM_NODE_ALLOC(parser, pm_match_write_node_t); *node = (pm_match_write_node_t) { - .base = PM_NODE_INIT(parser, PM_MATCH_WRITE_NODE, 0, call->base.location.start, call->base.location.end), + .base = PM_NODE_INIT_NODE(parser, PM_MATCH_WRITE_NODE, 0, call), .call = call, .targets = { 0 } }; @@ -5295,7 +5310,7 @@ pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_module_node_t *node = PM_NODE_ALLOC(parser, pm_module_node_t); *node = (pm_module_node_t) { - .base = PM_NODE_INIT(parser, PM_MODULE_NODE, 0, module_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_MODULE_NODE, 0, module_keyword, end_keyword), .locals = (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals), .module_keyword_loc = PM_LOCATION_TOKEN_VALUE(module_keyword), .constant_path = constant_path, @@ -5387,7 +5402,7 @@ pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_multi_write_node_t) { - .base = PM_NODE_INIT(parser, PM_MULTI_WRITE_NODE, flags, target->base.location.start, value->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_MULTI_WRITE_NODE, flags, target, value), .lefts = target->lefts, .rest = target->rest, .rights = target->rights, @@ -5430,7 +5445,7 @@ pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_nil_node_t *node = PM_NODE_ALLOC(parser, pm_nil_node_t); *node = (pm_nil_node_t) { - .base = PM_NODE_INIT(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) }; return node; @@ -5446,7 +5461,7 @@ pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *oper pm_no_keywords_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_no_keywords_parameter_node_t); *node = (pm_no_keywords_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, operator->start, keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, operator, keyword), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; @@ -5527,7 +5542,7 @@ pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *na pm_numbered_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_reference_read_node_t); *node = (pm_numbered_reference_read_node_t) { - .base = PM_NODE_INIT(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, name->start, name->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, name), .number = pm_numbered_reference_read_node_number(parser, name) }; @@ -5542,7 +5557,7 @@ pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, c pm_optional_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_parameter_node_t); *node = (pm_optional_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_OPTIONAL_PARAMETER_NODE, 0, name->start, value->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_PARAMETER_NODE, 0, name, value), .name = pm_parser_constant_id_token(parser, name), .name_loc = PM_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -5562,7 +5577,7 @@ pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operat pm_or_node_t *node = PM_NODE_ALLOC(parser, pm_or_node_t); *node = (pm_or_node_t) { - .base = PM_NODE_INIT(parser, PM_OR_NODE, 0, left->location.start, right->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_OR_NODE, 0, left, right), .left = left, .right = right, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5702,7 +5717,7 @@ pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_no pm_parentheses_node_t *node = PM_NODE_ALLOC(parser, pm_parentheses_node_t); *node = (pm_parentheses_node_t) { - .base = PM_NODE_INIT(parser, PM_PARENTHESES_NODE, flags, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_PARENTHESES_NODE, flags, opening, closing), .body = body, .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing) @@ -5719,7 +5734,7 @@ pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, con pm_pinned_expression_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_expression_node_t); *node = (pm_pinned_expression_node_t) { - .base = PM_NODE_INIT(parser, PM_PINNED_EXPRESSION_NODE, 0, operator->start, rparen->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_PINNED_EXPRESSION_NODE, 0, operator, rparen), .expression = expression, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen), @@ -5737,7 +5752,7 @@ pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_pinned_variable_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_variable_node_t); *node = (pm_pinned_variable_node_t) { - .base = PM_NODE_INIT(parser, PM_PINNED_VARIABLE_NODE, 0, operator->start, variable->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_PINNED_VARIABLE_NODE, 0, operator, variable), .variable = variable, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -5753,7 +5768,7 @@ pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, co pm_post_execution_node_t *node = PM_NODE_ALLOC(parser, pm_post_execution_node_t); *node = (pm_post_execution_node_t) { - .base = PM_NODE_INIT(parser, PM_POST_EXECUTION_NODE, 0, keyword->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_POST_EXECUTION_NODE, 0, keyword, closing), .statements = statements, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -5771,7 +5786,7 @@ pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, con pm_pre_execution_node_t *node = PM_NODE_ALLOC(parser, pm_pre_execution_node_t); *node = (pm_pre_execution_node_t) { - .base = PM_NODE_INIT(parser, PM_PRE_EXECUTION_NODE, 0, keyword->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_PRE_EXECUTION_NODE, 0, keyword, closing), .statements = statements, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), @@ -5826,7 +5841,7 @@ pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_redo_node_t *node = PM_NODE_ALLOC(parser, pm_redo_node_t); *node = (pm_redo_node_t) { - .base = PM_NODE_INIT(parser, PM_REDO_NODE, 0, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_REDO_NODE, 0, token) }; return node; @@ -5868,7 +5883,7 @@ pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) pm_required_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_parameter_node_t); *node = (pm_required_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_REQUIRED_PARAMETER_NODE, 0, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_PARAMETER_NODE, 0, token), .name = pm_parser_constant_id_token(parser, token) }; @@ -5883,7 +5898,7 @@ pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_rescue_modifier_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_modifier_node_t); *node = (pm_rescue_modifier_node_t) { - .base = PM_NODE_INIT(parser, PM_RESCUE_MODIFIER_NODE, 0, expression->location.start, rescue_expression->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_RESCUE_MODIFIER_NODE, 0, expression, rescue_expression), .expression = expression, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .rescue_expression = rescue_expression @@ -5900,7 +5915,7 @@ pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) { pm_rescue_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_node_t); *node = (pm_rescue_node_t) { - .base = PM_NODE_INIT(parser, PM_RESCUE_NODE, 0, keyword->start, keyword->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_RESCUE_NODE, 0, keyword), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -5982,7 +5997,7 @@ pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_retry_node_t *node = PM_NODE_ALLOC(parser, pm_retry_node_t); *node = (pm_retry_node_t) { - .base = PM_NODE_INIT(parser, PM_RETRY_NODE, 0, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_RETRY_NODE, 0, token) }; return node; @@ -6013,7 +6028,7 @@ pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_self_node_t *node = PM_NODE_ALLOC(parser, pm_self_node_t); *node = (pm_self_node_t) { - .base = PM_NODE_INIT(parser, PM_SELF_NODE, 0, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_SELF_NODE, 0, token) }; return node; @@ -6027,7 +6042,7 @@ pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shar pm_shareable_constant_node_t *node = PM_NODE_ALLOC(parser, pm_shareable_constant_node_t); *node = (pm_shareable_constant_node_t) { - .base = PM_NODE_INIT(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) value, write->location.start, write->location.end), + .base = PM_NODE_INIT_NODE(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) value, write), .write = write }; @@ -6042,7 +6057,7 @@ pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *local pm_singleton_class_node_t *node = PM_NODE_ALLOC(parser, pm_singleton_class_node_t); *node = (pm_singleton_class_node_t) { - .base = PM_NODE_INIT(parser, PM_SINGLETON_CLASS_NODE, 0, class_keyword->start, end_keyword->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_SINGLETON_CLASS_NODE, 0, class_keyword, end_keyword), .locals = *locals, .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), @@ -6063,7 +6078,7 @@ pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_source_encoding_node_t *node = PM_NODE_ALLOC(parser, pm_source_encoding_node_t); *node = (pm_source_encoding_node_t) { - .base = PM_NODE_INIT(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) }; return node; @@ -6089,7 +6104,7 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) } *node = (pm_source_file_node_t) { - .base = PM_NODE_INIT(parser, PM_SOURCE_FILE_NODE, flags, file_keyword->start, file_keyword->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_FILE_NODE, flags, file_keyword), .filepath = parser->filepath }; @@ -6105,7 +6120,7 @@ pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_source_line_node_t *node = PM_NODE_ALLOC(parser, pm_source_line_node_t); *node = (pm_source_line_node_t) { - .base = PM_NODE_INIT(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) }; return node; @@ -6135,7 +6150,7 @@ pm_statements_node_create(pm_parser_t *parser) { pm_statements_node_t *node = PM_NODE_ALLOC(parser, pm_statements_node_t); *node = (pm_statements_node_t) { - .base = PM_NODE_INIT(parser, PM_STATEMENTS_NODE, 0, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_STATEMENTS_NODE, 0), .body = { 0 } }; @@ -6581,7 +6596,7 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *node = (pm_symbol_node_t) { - .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING), .value_loc = PM_LOCATION_NULL_VALUE(parser), .unescaped = { 0 } }; @@ -6619,7 +6634,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_symbol_node_t *new_node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *new_node = (pm_symbol_node_t) { - .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing), .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), .value_loc = node->content_loc, .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -6655,7 +6670,7 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { } *new_node = (pm_string_node_t) { - .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, node->base.location.start, node->base.location.end), + .base = PM_NODE_INIT_NODE(parser, PM_STRING_NODE, flags, node), .opening_loc = node->opening_loc, .content_loc = node->value_loc, .closing_loc = node->closing_loc, @@ -6679,7 +6694,7 @@ pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); *node = (pm_true_node_t) { - .base = PM_NODE_INIT(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token->start, token->end) + .base = PM_NODE_INIT_TOKEN(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token) }; return node; @@ -6693,7 +6708,7 @@ pm_true_node_synthesized_create(pm_parser_t *parser) { pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); *node = (pm_true_node_t) { - .base = PM_NODE_INIT(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, parser->start, parser->end) + .base = PM_NODE_INIT_UNSET(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL) }; return node; @@ -6708,7 +6723,7 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) { pm_undef_node_t *node = PM_NODE_ALLOC(parser, pm_undef_node_t); *node = (pm_undef_node_t) { - .base = PM_NODE_INIT(parser, PM_UNDEF_NODE, 0, token->start, token->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_UNDEF_NODE, 0, token), .keyword_loc = PM_LOCATION_TOKEN_VALUE(token), .names = { 0 } }; @@ -6765,7 +6780,7 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_statements_node_body_append(parser, statements, statement, true); *node = (pm_unless_node_t) { - .base = PM_NODE_INIT(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement->location.start, predicate->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -6815,7 +6830,7 @@ pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); *node = (pm_until_node_t) { - .base = PM_NODE_INIT(parser, PM_UNTIL_NODE, flags, keyword->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_UNTIL_NODE, flags, keyword, closing), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -6836,7 +6851,7 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm pm_loop_modifier_block_exits(parser, statements); *node = (pm_until_node_t) { - .base = PM_NODE_INIT(parser, PM_UNTIL_NODE, flags, statements->base.location.start, predicate->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_UNTIL_NODE, flags, statements, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -6855,7 +6870,7 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) { pm_when_node_t *node = PM_NODE_ALLOC(parser, pm_when_node_t); *node = (pm_when_node_t) { - .base = PM_NODE_INIT(parser, PM_WHEN_NODE, 0, keyword->start, NULL), + .base = PM_NODE_INIT_TOKEN(parser, PM_WHEN_NODE, 0, keyword), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .statements = NULL, .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -6904,7 +6919,7 @@ pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); *node = (pm_while_node_t) { - .base = PM_NODE_INIT(parser, PM_WHILE_NODE, flags, keyword->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_WHILE_NODE, flags, keyword, closing), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing), @@ -6925,7 +6940,7 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm pm_loop_modifier_block_exits(parser, statements); *node = (pm_while_node_t) { - .base = PM_NODE_INIT(parser, PM_WHILE_NODE, flags, statements->base.location.start, predicate->location.end), + .base = PM_NODE_INIT_NODES(parser, PM_WHILE_NODE, flags, statements, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -6944,7 +6959,7 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); *node = (pm_while_node_t) { - .base = PM_NODE_INIT(parser, PM_WHILE_NODE, 0, parser->start, parser->start), + .base = PM_NODE_INIT_UNSET(parser, PM_WHILE_NODE, 0), .keyword_loc = PM_LOCATION_NULL_VALUE(parser), .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser), .closing_loc = PM_LOCATION_NULL_VALUE(parser), @@ -6964,7 +6979,7 @@ pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, pm_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_x_string_node_t); *node = (pm_x_string_node_t) { - .base = PM_NODE_INIT(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, opening->start, closing->end), + .base = PM_NODE_INIT_TOKENS(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, opening, closing), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), From 56ee55b162088a3b6d2e3a5fc9326b17fc65eac6 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Dec 2025 15:09:59 -0500 Subject: [PATCH 096/367] [ruby/prism] Introduce PM_NODE_FLAGS macro https://github.com/ruby/prism/commit/a20afe1674 --- prism/prism.c | 26 ++++++++++++------------- prism/templates/include/prism/ast.h.erb | 7 ++++++- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 2c930d8deb4122..65949190f18036 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -22,6 +22,7 @@ pm_version(void) { /* Helpful node-related macros */ /******************************************************************************/ +#define FL PM_NODE_FLAGS #define UP PM_NODE_UPCAST #define PM_TOKEN_START(token_) ((token_)->start) @@ -2845,7 +2846,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_and_write_node_t); *node = (pm_call_and_write_node_t) { - .base = PM_NODE_INIT_NODES(parser, PM_CALL_AND_WRITE_NODE, target->base.flags, target, value), + .base = PM_NODE_INIT_NODES(parser, PM_CALL_AND_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -2900,7 +2901,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_and_write_node_t) { - .base = PM_NODE_INIT_NODES(parser, PM_INDEX_AND_WRITE_NODE, target->base.flags, target, value), + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_AND_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -2928,7 +2929,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, pm_call_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_operator_write_node_t); *node = (pm_call_operator_write_node_t) { - .base = PM_NODE_INIT_NODES(parser, PM_CALL_OPERATOR_WRITE_NODE, target->base.flags, target, value), + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OPERATOR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -2960,7 +2961,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_operator_write_node_t) { - .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OPERATOR_WRITE_NODE, target->base.flags, target, value), + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OPERATOR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -2990,7 +2991,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_call_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_or_write_node_t); *node = (pm_call_or_write_node_t) { - .base = PM_NODE_INIT_NODES(parser, PM_CALL_OR_WRITE_NODE, target->base.flags, target, value), + .base = PM_NODE_INIT_NODES(parser, PM_CALL_OR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .message_loc = target->message_loc, @@ -3022,7 +3023,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); *node = (pm_index_or_write_node_t) { - .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OR_WRITE_NODE, target->base.flags, target, value), + .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OR_WRITE_NODE, FL(target), target, value), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .opening_loc = target->opening_loc, @@ -3050,7 +3051,7 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { pm_call_target_node_t *node = PM_NODE_ALLOC(parser, pm_call_target_node_t); *node = (pm_call_target_node_t) { - .base = PM_NODE_INIT_NODE(parser, PM_CALL_TARGET_NODE, target->base.flags, target), + .base = PM_NODE_INIT_NODE(parser, PM_CALL_TARGET_NODE, FL(target), target), .receiver = target->receiver, .call_operator_loc = target->call_operator_loc, .name = target->name, @@ -3072,13 +3073,12 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { static pm_index_target_node_t * pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { pm_index_target_node_t *node = PM_NODE_ALLOC(parser, pm_index_target_node_t); - pm_node_flags_t flags = target->base.flags; pm_index_arguments_check(parser, target->arguments, target->block); - assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE)); + *node = (pm_index_target_node_t) { - .base = PM_NODE_INIT_NODE(parser, PM_INDEX_TARGET_NODE, flags | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target), + .base = PM_NODE_INIT_NODE(parser, PM_INDEX_TARGET_NODE, FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target), .receiver = target->receiver, .opening_loc = target->opening_loc, .arguments = target->arguments, @@ -4745,10 +4745,10 @@ pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_inte static inline void pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) { #define CLEAR_FLAGS(node) \ - node->base.flags = (pm_node_flags_t) (node->base.flags & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) + node->base.flags = (pm_node_flags_t) (FL(node) & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) #define MUTABLE_FLAGS(node) \ - node->base.flags = (pm_node_flags_t) ((node->base.flags | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN); + node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN); if (node->parts.size == 0 && node->opening_loc.start == NULL) { node->base.location.start = part->location.start; @@ -19674,7 +19674,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parse_regular_expression_errors(parser, node); } - pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, node->base.flags)); + pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, FL(node))); return UP(node); } diff --git a/prism/templates/include/prism/ast.h.erb b/prism/templates/include/prism/ast.h.erb index d69a0d74569a2f..790cf9ebb8ade1 100644 --- a/prism/templates/include/prism/ast.h.erb +++ b/prism/templates/include/prism/ast.h.erb @@ -151,10 +151,15 @@ typedef struct pm_node { */ #define PM_NODE_TYPE_P(node_, type_) (PM_NODE_TYPE(node_) == (type_)) +/** + * Return the flags associated with the given node. + */ +#define PM_NODE_FLAGS(node_) (PM_NODE_UPCAST(node_)->flags) + /** * Return true if the given flag is set on the given node. */ -#define PM_NODE_FLAG_P(node_, flag_) ((PM_NODE_UPCAST(node_)->flags & (flag_)) != 0) +#define PM_NODE_FLAG_P(node_, flag_) ((PM_NODE_FLAGS(node_) & (flag_)) != 0) <%- nodes.each do |node| -%> /** From a55040cac5b91b2d197fd604251f60f4bff289e8 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Dec 2025 15:21:40 -0500 Subject: [PATCH 097/367] [ruby/prism] Further specialize PM_NODE_INIT https://github.com/ruby/prism/commit/7ab6d9df47 --- prism/prism.c | 180 +++++++++++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 68 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 65949190f18036..e17163091af291 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -1949,7 +1949,8 @@ pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { .location = { .start = (start_), .end = (end_) } \ } -#define PM_NODE_INIT_UNSET(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, (parser_)->start, (parser_)->start) +#define PM_NODE_INIT_UNSET(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, NULL, NULL) +#define PM_NODE_INIT_BASE(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, (parser_)->start, (parser_)->start) #define PM_NODE_INIT_TOKEN(parser_, type_, flags_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_TOKEN_END(token_)) #define PM_NODE_INIT_NODE(parser_, type_, flags_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_NODE_END(node_)) @@ -2052,7 +2053,7 @@ pm_arguments_node_create(pm_parser_t *parser) { pm_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_arguments_node_t); *node = (pm_arguments_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_ARGUMENTS_NODE, 0), + .base = PM_NODE_INIT_BASE(parser, PM_ARGUMENTS_NODE, 0), .arguments = { 0 } }; @@ -2294,7 +2295,11 @@ pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token pm_assoc_splat_node_t *node = PM_NODE_ALLOC(parser, pm_assoc_splat_node_t); *node = (pm_assoc_splat_node_t) { - .base = PM_NODE_INIT(parser, PM_ASSOC_SPLAT_NODE, 0, operator->start, value == NULL ? operator->end : value->location.end), + .base = ( + (value == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_ASSOC_SPLAT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_ASSOC_SPLAT_NODE, 0, operator, value) + ), .value = value, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -2326,7 +2331,11 @@ pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_st pm_begin_node_t *node = PM_NODE_ALLOC(parser, pm_begin_node_t); *node = (pm_begin_node_t) { - .base = PM_NODE_INIT(parser, PM_BEGIN_NODE, 0, begin_keyword->start, statements == NULL ? begin_keyword->end : statements->base.location.end), + .base = ( + (statements == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BEGIN_NODE, 0, begin_keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BEGIN_NODE, 0, begin_keyword, statements) + ), .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), .statements = statements, .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE @@ -2385,7 +2394,11 @@ pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, p pm_block_argument_node_t *node = PM_NODE_ALLOC(parser, pm_block_argument_node_t); *node = (pm_block_argument_node_t) { - .base = PM_NODE_INIT(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator->start, expression == NULL ? operator->end : expression->location.end), + .base = ( + (expression == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator, expression) + ), .expression = expression, .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) }; @@ -2421,7 +2434,11 @@ pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, cons pm_block_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_block_parameter_node_t); *node = (pm_block_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETER_NODE, 0, operator->start, name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end), + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_BLOCK_PARAMETER_NODE, 0, operator, name) + ), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -2512,7 +2529,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument pm_break_node_t *node = PM_NODE_ALLOC(parser, pm_break_node_t); *node = (pm_break_node_t) { - .base = PM_NODE_INIT(parser, PM_BREAK_NODE, 0, keyword->start, arguments == NULL ? keyword->end : arguments->base.location.end), + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_BREAK_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_BREAK_NODE, 0, keyword, arguments) + ), .arguments = arguments, .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; @@ -2539,7 +2560,7 @@ pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) { pm_call_node_t *node = PM_NODE_ALLOC(parser, pm_call_node_t); *node = (pm_call_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_CALL_NODE, flags), + .base = PM_NODE_INIT_BASE(parser, PM_CALL_NODE, flags), .receiver = NULL, .call_operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .message_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, @@ -3402,13 +3423,23 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to name = pm_parser_constant_id_token(parser, name_token); } - *node = (pm_constant_path_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_NODE, 0, parent == NULL ? delimiter->start : parent->location.start, name_token->end), - .parent = parent, - .name = name, - .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), - .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) - }; + if (parent == NULL) { + *node = (pm_constant_path_node_t) { + .base = PM_NODE_INIT_TOKENS(parser, PM_CONSTANT_PATH_NODE, 0, delimiter, name_token), + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + } else { + *node = (pm_constant_path_node_t) { + .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_CONSTANT_PATH_NODE, 0, parent, name_token), + .parent = parent, + .name = name, + .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter), + .name_loc = PM_LOCATION_TOKEN_VALUE(name_token) + }; + } return node; } @@ -3587,20 +3618,17 @@ pm_def_node_create( const pm_token_t *end_keyword ) { pm_def_node_t *node = PM_NODE_ALLOC(parser, pm_def_node_t); - const uint8_t *end; - - if (end_keyword->type == PM_TOKEN_NOT_PROVIDED) { - end = body->location.end; - } else { - end = end_keyword->end; - } if (receiver != NULL) { pm_def_node_receiver_check(parser, receiver); } *node = (pm_def_node_t) { - .base = PM_NODE_INIT(parser, PM_DEF_NODE, 0, def_keyword->start, end), + .base = ( + (end_keyword->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEF_NODE, 0, def_keyword, body) + : PM_NODE_INIT_TOKENS(parser, PM_DEF_NODE, 0, def_keyword, end_keyword) + ), .name = name, .name_loc = PM_LOCATION_TOKEN_VALUE(name_loc), .receiver = receiver, @@ -3622,16 +3650,19 @@ pm_def_node_create( * Allocate a new DefinedNode node. */ static pm_defined_node_t * -pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_location_t *keyword_loc) { +pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_token_t *keyword) { pm_defined_node_t *node = PM_NODE_ALLOC(parser, pm_defined_node_t); - const uint8_t *end = rparen->type == PM_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end; *node = (pm_defined_node_t) { - .base = PM_NODE_INIT(parser, PM_DEFINED_NODE, 0, keyword_loc->start, end), + .base = ( + (rparen->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEFINED_NODE, 0, keyword, value) + : PM_NODE_INIT_TOKENS(parser, PM_DEFINED_NODE, 0, keyword, rparen) + ), .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), .value = value, .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), - .keyword_loc = *keyword_loc + .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword) }; return node; @@ -3643,15 +3674,13 @@ pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t static pm_else_node_t * pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) { pm_else_node_t *node = PM_NODE_ALLOC(parser, pm_else_node_t); - const uint8_t *end = NULL; - if ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) { - end = statements->base.location.end; - } else { - end = end_keyword->end; - } *node = (pm_else_node_t) { - .base = PM_NODE_INIT(parser, PM_ELSE_NODE, 0, else_keyword->start, end), + .base = ( + ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL)) + ? PM_NODE_INIT_TOKEN_NODE(parser, PM_ELSE_NODE, 0, else_keyword, statements) + : PM_NODE_INIT_TOKENS(parser, PM_ELSE_NODE, 0, else_keyword, end_keyword) + ), .else_keyword_loc = PM_LOCATION_TOKEN_VALUE(else_keyword), .statements = statements, .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) @@ -4025,9 +4054,12 @@ pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm block = (pm_block_node_t *) arguments->block; } - const uint8_t *end = block != NULL ? block->base.location.end : token->end; *node = (pm_forwarding_super_node_t) { - .base = PM_NODE_INIT(parser, PM_FORWARDING_SUPER_NODE, 0, token->start, end), + .base = ( + (block == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_SUPER_NODE, 0, token) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_FORWARDING_SUPER_NODE, 0, token, block) + ), .block = block }; @@ -4195,7 +4227,7 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t); *node = (pm_global_variable_read_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0), + .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0), .name = name }; @@ -4229,7 +4261,7 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t); *node = (pm_global_variable_write_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0), + .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0), .name = name, .name_loc = PM_LOCATION_NULL_VALUE(parser), .operator_loc = PM_LOCATION_NULL_VALUE(parser), @@ -4691,7 +4723,7 @@ pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_tok pm_interpolated_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_regular_expression_node_t); *node = (pm_interpolated_regular_expression_node_t) { - .base = PM_NODE_INIT(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening->start, NULL), + .base = PM_NODE_INIT_TOKEN(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .closing_loc = PM_LOCATION_TOKEN_VALUE(opening), .parts = { 0 } @@ -4980,7 +5012,7 @@ pm_keyword_hash_node_create(pm_parser_t *parser) { pm_keyword_hash_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_hash_node_t); *node = (pm_keyword_hash_node_t) { - .base = PM_NODE_INIT(parser, PM_KEYWORD_HASH_NODE, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, NULL, NULL), + .base = PM_NODE_INIT_UNSET(parser, PM_KEYWORD_HASH_NODE, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS), .elements = { 0 } }; @@ -5046,7 +5078,11 @@ pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *ope pm_keyword_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_rest_parameter_node_t); *node = (pm_keyword_rest_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator->start, (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)), + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator, name) + ), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -5191,7 +5227,7 @@ pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY); *node = (pm_local_variable_write_node_t) { - .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, name_loc->start, value->location.end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, name_loc, value), .name = name, .depth = depth, .value = value, @@ -5240,7 +5276,7 @@ pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *l pm_local_variable_target_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_target_node_t); *node = (pm_local_variable_target_node_t) { - .base = PM_NODE_INIT(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, location->start, location->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, location), .name = name, .depth = depth }; @@ -5330,7 +5366,7 @@ pm_multi_target_node_create(pm_parser_t *parser) { pm_multi_target_node_t *node = PM_NODE_ALLOC(parser, pm_multi_target_node_t); *node = (pm_multi_target_node_t) { - .base = PM_NODE_INIT(parser, PM_MULTI_TARGET_NODE, 0, NULL, NULL), + .base = PM_NODE_INIT_UNSET(parser, PM_MULTI_TARGET_NODE, 0), .lefts = { 0 }, .rest = NULL, .rights = { 0 }, @@ -5428,7 +5464,11 @@ pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments pm_next_node_t *node = PM_NODE_ALLOC(parser, pm_next_node_t); *node = (pm_next_node_t) { - .base = PM_NODE_INIT(parser, PM_NEXT_NODE, 0, keyword->start, (arguments == NULL ? keyword->end : arguments->base.location.end)), + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_NEXT_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_NEXT_NODE, 0, keyword, arguments) + ), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .arguments = arguments }; @@ -5477,7 +5517,7 @@ pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *loc pm_numbered_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_parameters_node_t); *node = (pm_numbered_parameters_node_t) { - .base = PM_NODE_INIT(parser, PM_NUMBERED_PARAMETERS_NODE, 0, location->start, location->end), + .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_PARAMETERS_NODE, 0, location), .maximum = maximum }; @@ -5594,7 +5634,7 @@ pm_parameters_node_create(pm_parser_t *parser) { pm_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_parameters_node_t); *node = (pm_parameters_node_t) { - .base = PM_NODE_INIT(parser, PM_PARAMETERS_NODE, 0, NULL, NULL), + .base = PM_NODE_INIT_UNSET(parser, PM_PARAMETERS_NODE, 0), .rest = NULL, .keyword_rest = NULL, .block = NULL, @@ -5697,11 +5737,8 @@ static pm_program_node_t * pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) { pm_program_node_t *node = PM_NODE_ALLOC(parser, pm_program_node_t); - const uint8_t *start = statements == NULL ? parser->start : statements->base.location.start; - const uint8_t *end = statements == NULL ? parser->end : statements->base.location.end; - *node = (pm_program_node_t) { - .base = PM_NODE_INIT(parser, PM_PROGRAM_NODE, 0, start, end), + .base = PM_NODE_INIT_NODE(parser, PM_PROGRAM_NODE, 0, statements), .locals = *locals, .statements = statements }; @@ -5857,7 +5894,7 @@ pm_regular_expression_node_create_unescaped(pm_parser_t *parser, const pm_token_ pm_node_flags_t flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL; *node = (pm_regular_expression_node_t) { - .base = PM_NODE_INIT(parser, PM_REGULAR_EXPRESSION_NODE, flags, MIN(opening->start, closing->start), MAX(opening->end, closing->end)), + .base = PM_NODE_INIT_TOKENS(parser, PM_REGULAR_EXPRESSION_NODE, flags, opening, closing), .opening_loc = PM_LOCATION_TOKEN_VALUE(opening), .content_loc = PM_LOCATION_TOKEN_VALUE(content), .closing_loc = PM_LOCATION_TOKEN_VALUE(closing), @@ -5979,7 +6016,11 @@ pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, c pm_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_rest_parameter_node_t); *node = (pm_rest_parameter_node_t) { - .base = PM_NODE_INIT(parser, PM_REST_PARAMETER_NODE, 0, operator->start, (name->type == PM_TOKEN_NOT_PROVIDED ? operator->end : name->end)), + .base = ( + (name->type == PM_TOKEN_NOT_PROVIDED) + ? PM_NODE_INIT_TOKEN(parser, PM_REST_PARAMETER_NODE, 0, operator) + : PM_NODE_INIT_TOKENS(parser, PM_REST_PARAMETER_NODE, 0, operator, name) + ), .name = pm_parser_optional_constant_id_token(parser, name), .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator) @@ -6011,7 +6052,11 @@ pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argumen pm_return_node_t *node = PM_NODE_ALLOC(parser, pm_return_node_t); *node = (pm_return_node_t) { - .base = PM_NODE_INIT(parser, PM_RETURN_NODE, 0, keyword->start, (arguments == NULL ? keyword->end : arguments->base.location.end)), + .base = ( + (arguments == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_RETURN_NODE, 0, keyword) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_RETURN_NODE, 0, keyword, arguments) + ), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .arguments = arguments }; @@ -6134,7 +6179,11 @@ pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t pm_splat_node_t *node = PM_NODE_ALLOC(parser, pm_splat_node_t); *node = (pm_splat_node_t) { - .base = PM_NODE_INIT(parser, PM_SPLAT_NODE, 0, operator->start, (expression == NULL ? operator->end : expression->location.end)), + .base = ( + (expression == NULL) + ? PM_NODE_INIT_TOKEN(parser, PM_SPLAT_NODE, 0, operator) + : PM_NODE_INIT_TOKEN_NODE(parser, PM_SPLAT_NODE, 0, operator, expression) + ), .operator_loc = PM_LOCATION_TOKEN_VALUE(operator), .expression = expression }; @@ -6150,7 +6199,7 @@ pm_statements_node_create(pm_parser_t *parser) { pm_statements_node_t *node = PM_NODE_ALLOC(parser, pm_statements_node_t); *node = (pm_statements_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_STATEMENTS_NODE, 0), + .base = PM_NODE_INIT_BASE(parser, PM_STATEMENTS_NODE, 0), .body = { 0 } }; @@ -6596,7 +6645,7 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) { pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t); *node = (pm_symbol_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING), + .base = PM_NODE_INIT_BASE(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING), .value_loc = PM_LOCATION_NULL_VALUE(parser), .unescaped = { 0 } }; @@ -6708,7 +6757,7 @@ pm_true_node_synthesized_create(pm_parser_t *parser) { pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t); *node = (pm_true_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL) + .base = PM_NODE_INIT_BASE(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL) }; return node; @@ -6746,17 +6795,12 @@ pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { static pm_unless_node_t * pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, const pm_token_t *then_keyword, pm_statements_node_t *statements) { pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); - pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t); - const uint8_t *end; - if (statements != NULL) { - end = statements->base.location.end; - } else { - end = predicate->location.end; - } + pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t); + pm_node_t *end = statements == NULL ? predicate : UP(statements); *node = (pm_unless_node_t) { - .base = PM_NODE_INIT(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, keyword->start, end), + .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, keyword, end), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .predicate = predicate, .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), @@ -6959,7 +7003,7 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t); *node = (pm_while_node_t) { - .base = PM_NODE_INIT_UNSET(parser, PM_WHILE_NODE, 0), + .base = PM_NODE_INIT_BASE(parser, PM_WHILE_NODE, 0), .keyword_loc = PM_LOCATION_NULL_VALUE(parser), .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser), .closing_loc = PM_LOCATION_NULL_VALUE(parser), @@ -18917,7 +18961,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b &lparen, expression, &rparen, - &PM_LOCATION_TOKEN_VALUE(&keyword) + &keyword )); } case PM_TOKEN_KEYWORD_END_UPCASE: { From a1ea824b8550f214ecc501536eed3468cbfd11ae Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Dec 2025 15:44:33 -0500 Subject: [PATCH 098/367] [ruby/prism] Remove PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE macro https://github.com/ruby/prism/commit/1988615ce1 --- prism/prism.c | 65 +++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index e17163091af291..f6edce5d6f0e4a 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -1576,8 +1576,7 @@ not_provided(pm_parser_t *parser) { #define PM_LOCATION_TOKEN_VALUE(token) ((pm_location_t) { .start = (token)->start, .end = (token)->end }) #define PM_LOCATION_NODE_VALUE(node) ((pm_location_t) { .start = (node)->location.start, .end = (node)->location.end }) #define PM_LOCATION_NODE_BASE_VALUE(node) ((pm_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) -#define PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE ((pm_location_t) { .start = NULL, .end = NULL }) -#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE : PM_LOCATION_TOKEN_VALUE(token)) +#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? ((pm_location_t) { 0 }) : PM_LOCATION_TOKEN_VALUE(token)) /** * This is a special out parameter to the parse_arguments_list function that @@ -2153,8 +2152,8 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node .rest = NULL, .requireds = { 0 }, .posts = { 0 }, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; // For now we're going to just copy over each pointer manually. This could be @@ -2189,8 +2188,8 @@ pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { .rest = rest, .requireds = { 0 }, .posts = { 0 }, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; return node; @@ -2338,7 +2337,7 @@ pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_st ), .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), .statements = statements, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -2476,7 +2475,7 @@ pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *param .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETERS_NODE, 0, start, end), .parameters = parameters, .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening), - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = { 0 }, .locals = { 0 } }; @@ -2562,12 +2561,12 @@ pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) { *node = (pm_call_node_t) { .base = PM_NODE_INIT_BASE(parser, PM_CALL_NODE, flags), .receiver = NULL, - .call_operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .message_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .call_operator_loc = { 0 }, + .message_loc = { 0 }, + .opening_loc = { 0 }, .arguments = NULL, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .equal_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = { 0 }, + .equal_loc = { 0 }, .block = NULL, .name = 0 }; @@ -3788,8 +3787,8 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { .left = left_splat_node, .right = right_splat_node, .requireds = { 0 }, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; // For now we're going to just copy over each pointer manually. This could be @@ -4115,8 +4114,8 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme .constant = NULL, .elements = { 0 }, .rest = rest, - .opening_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .opening_loc = { 0 }, + .closing_loc = { 0 } }; pm_node_t *element; @@ -4369,10 +4368,10 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword), .predicate = predicate, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = { 0 }, .statements = statements, .subsequent = NULL, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -4399,12 +4398,12 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to *node = (pm_if_node_t) { .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate, false_expression), - .if_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .if_keyword_loc = { 0 }, .predicate = predicate, .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark), .statements = if_statements, .subsequent = UP(else_node), - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -5370,8 +5369,8 @@ pm_multi_target_node_create(pm_parser_t *parser) { .lefts = { 0 }, .rest = NULL, .rights = { 0 }, - .lparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .rparen_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .lparen_loc = { 0 }, + .rparen_loc = { 0 } }; return node; @@ -5954,8 +5953,8 @@ pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) { *node = (pm_rescue_node_t) { .base = PM_NODE_INIT_TOKEN(parser, PM_RESCUE_NODE, 0, keyword), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), - .operator_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .operator_loc = { 0 }, + .then_keyword_loc = { 0 }, .reference = NULL, .statements = NULL, .subsequent = NULL, @@ -6806,7 +6805,7 @@ pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword), .statements = statements, .else_clause = NULL, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -6827,10 +6826,10 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const .base = PM_NODE_INIT_NODES(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword), .predicate = predicate, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = { 0 }, .statements = statements, .else_clause = NULL, - .end_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + .end_keyword_loc = { 0 } }; return node; @@ -6897,8 +6896,8 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm *node = (pm_until_node_t) { .base = PM_NODE_INIT_NODES(parser, PM_UNTIL_NODE, flags, statements, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), - .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .do_keyword_loc = { 0 }, + .closing_loc = { 0 }, .predicate = predicate, .statements = statements }; @@ -6917,7 +6916,7 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) { .base = PM_NODE_INIT_TOKEN(parser, PM_WHEN_NODE, 0, keyword), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), .statements = NULL, - .then_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .then_keyword_loc = { 0 }, .conditions = { 0 } }; @@ -6986,8 +6985,8 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm *node = (pm_while_node_t) { .base = PM_NODE_INIT_NODES(parser, PM_WHILE_NODE, flags, statements, predicate), .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword), - .do_keyword_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, - .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .do_keyword_loc = { 0 }, + .closing_loc = { 0 }, .predicate = predicate, .statements = statements }; From e0746cc443dfe948fa8d0eac5ecaa36e7eb6c972 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 2 Dec 2025 15:51:34 -0500 Subject: [PATCH 099/367] [ruby/prism] Consolidate macro definitions https://github.com/ruby/prism/commit/cc0ca08757 --- prism/prism.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index f6edce5d6f0e4a..cd4d166a124ef1 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -19,17 +19,22 @@ pm_version(void) { #define MAX(a,b) (((a)>(b))?(a):(b)) /******************************************************************************/ -/* Helpful node-related macros */ +/* Helpful AST-related macros */ /******************************************************************************/ #define FL PM_NODE_FLAGS #define UP PM_NODE_UPCAST #define PM_TOKEN_START(token_) ((token_)->start) -#define PM_TOKEN_END(token_) ((token_)->end) +#define PM_TOKEN_END(token_) ((token_)->end) #define PM_NODE_START(node_) (UP(node_)->location.start) -#define PM_NODE_END(node_) (UP(node_)->location.end) +#define PM_NODE_END(node_) (UP(node_)->location.end) + +#define PM_LOCATION_NULL_VALUE(parser_) ((pm_location_t) { .start = (parser_)->start, .end = (parser_)->start }) +#define PM_LOCATION_TOKEN_VALUE(token_) ((pm_location_t) { .start = PM_TOKEN_START(token_), .end = PM_TOKEN_END(token_) }) +#define PM_LOCATION_NODE_VALUE(node_) ((pm_location_t) { .start = PM_NODE_START(node_), .end = PM_NODE_END(node_) }) +#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? ((pm_location_t) { 0 }) : PM_LOCATION_TOKEN_VALUE(token)) /******************************************************************************/ /* Lex mode manipulations */ @@ -1572,12 +1577,6 @@ not_provided(pm_parser_t *parser) { return (pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }; } -#define PM_LOCATION_NULL_VALUE(parser) ((pm_location_t) { .start = (parser)->start, .end = (parser)->start }) -#define PM_LOCATION_TOKEN_VALUE(token) ((pm_location_t) { .start = (token)->start, .end = (token)->end }) -#define PM_LOCATION_NODE_VALUE(node) ((pm_location_t) { .start = (node)->location.start, .end = (node)->location.end }) -#define PM_LOCATION_NODE_BASE_VALUE(node) ((pm_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) -#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? ((pm_location_t) { 0 }) : PM_LOCATION_TOKEN_VALUE(token)) - /** * This is a special out parameter to the parse_arguments_list function that * includes opening and closing parentheses in addition to the arguments since @@ -4662,7 +4661,7 @@ pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable *node = (pm_instance_variable_write_node_t) { .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node, value), .name = read_node->name, - .name_loc = PM_LOCATION_NODE_BASE_VALUE(read_node), + .name_loc = PM_LOCATION_NODE_VALUE(read_node), .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator), .value = value }; @@ -7069,8 +7068,6 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo return node; } -#undef PM_NODE_ALLOC - /** * Check if any of the currently visible scopes contain a local variable * described by the given constant id. @@ -22293,10 +22290,6 @@ pm_parse_success_p(const uint8_t *source, size_t size, const char *data) { #undef PM_CASE_OPERATOR #undef PM_CASE_WRITABLE #undef PM_STRING_EMPTY -#undef PM_LOCATION_NODE_BASE_VALUE -#undef PM_LOCATION_NODE_VALUE -#undef PM_LOCATION_NULL_VALUE -#undef PM_LOCATION_TOKEN_VALUE // We optionally support serializing to a binary string. For systems that don't // want or need this functionality, it can be turned off with the From a63147eed1184cb812664d5917d6687635a23ab6 Mon Sep 17 00:00:00 2001 From: Yuki Kurihara Date: Wed, 3 Dec 2025 07:24:01 +0900 Subject: [PATCH 100/367] [ruby/strscan] [DOC] Avoid being interpreted as a link (https://github.com/ruby/strscan/pull/180) Since `[](n)` is being interpreted as a Markdown link, it cannot be displayed as a method call. I have corrected this by escaping the brackets so that they are interpreted as strings rather than links. ### Before ri ``` #{}[n] | nth captured substring. | +nil+. ``` html image ### After ri ``` #[](n) | nth captured substring. | +nil+. ``` html image https://github.com/ruby/strscan/commit/b3d56867fd --- doc/strscan/strscan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/strscan/strscan.md b/doc/strscan/strscan.md index c4ab2034624947..385e92f84e3542 100644 --- a/doc/strscan/strscan.md +++ b/doc/strscan/strscan.md @@ -417,7 +417,7 @@ Each of these methods returns a captured match value: | Method | Return After Match | Return After No Match | |-----------------|-----------------------------------------|-----------------------| | #size | Count of captured substrings. | +nil+. | -| #[](n) | nth captured substring. | +nil+. | +| #\[\](n) | nth captured substring. | +nil+. | | #captures | Array of all captured substrings. | +nil+. | | #values_at(*n) | Array of specified captured substrings. | +nil+. | | #named_captures | Hash of named captures. | {}. | From a211abbcbd87f6d59a99cfcf2cb63a39d61b16ea Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Tue, 2 Dec 2025 17:35:53 -0500 Subject: [PATCH 101/367] Cache array length in `rb_ary_join` (#15362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When all elements are strings, we never have to recalculate the length of the array because there are no conversion methods that are called, so the length will never change. This speeds up the fast path by ~10%. ```ruby a = ["1"*10, "2"*10, "3"*10, "4"*10, "5"*10] * 10 10_000_000.times do a.join end ``` ``` hyperfine --warmup 1 'ruby ../ruby2/test.rb' './exe/ruby ../ruby2/test.rb' Benchmark 1: ruby ../ruby2/test.rb Time (mean ± σ): 3.779 s ± 0.053 s [User: 3.754 s, System: 0.017 s] Range (min … max): 3.715 s … 3.874 s 10 runs Benchmark 2: ./exe/ruby ../ruby2/test.rb Time (mean ± σ): 3.411 s ± 0.038 s [User: 3.387 s, System: 0.017 s] Range (min … max): 3.360 s … 3.472 s 10 runs Summary ./exe/ruby ../ruby2/test.rb ran 1.11 ± 0.02 times faster than ruby ../ruby2/test.rb ``` --- array.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/array.c b/array.c index 2acaafee03d833..a6aeeeeca156b4 100644 --- a/array.c +++ b/array.c @@ -2917,23 +2917,28 @@ rb_ary_join(VALUE ary, VALUE sep) StringValue(sep); len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1); } - for (i=0; i n) i = n; - result = rb_str_buf_new(len + (n-i)*10); - rb_enc_associate(result, rb_usascii_encoding()); - i = ary_join_0(ary, sep, i, result); - first = i == 0; - ary_join_1(ary, ary, sep, i, result, &first); - return result; + if (RB_UNLIKELY(!RB_TYPE_P(val, T_STRING))) { + tmp = rb_check_string_type(val); + if (NIL_P(tmp) || tmp != val) { + int first; + long n = RARRAY_LEN(ary); + if (i > n) i = n; + result = rb_str_buf_new(len + (n-i)*10); + rb_enc_associate(result, rb_usascii_encoding()); + i = ary_join_0(ary, sep, i, result); + first = i == 0; + ary_join_1(ary, ary, sep, i, result, &first); + return result; + } + len += RSTRING_LEN(tmp); + len_memo = RARRAY_LEN(ary); + } + else { + len += RSTRING_LEN(val); } - - len += RSTRING_LEN(tmp); } result = rb_str_new(0, len); From dfdc5d40ec25e42ff63982958c43d425bee909fb Mon Sep 17 00:00:00 2001 From: yui-knk Date: Wed, 26 Nov 2025 14:11:23 +0900 Subject: [PATCH 102/367] Check and raise semantics errors on nested variables captures in patterns This commit makes these codes to be invalid. ```ruby case 0 in [a] | 1 end case 0 in { a: b } | 1 end case 0 in [{ a: [{ b: [{ c: }] }] }] | 1 end ``` --- parse.y | 6 +----- test/.excludes-parsey/TestPatternMatching.rb | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 test/.excludes-parsey/TestPatternMatching.rb diff --git a/parse.y b/parse.y index f6222ea52ea1b7..a9fccdd5f721e0 100644 --- a/parse.y +++ b/parse.y @@ -5468,11 +5468,6 @@ p_top_expr_body : p_expr ; p_expr : p_as - { - p->ctxt.in_alt_pattern = 0; - p->ctxt.capture_in_pattern = 0; - $$ = $1; - } ; p_as : p_expr tASSOC p_variable @@ -5494,6 +5489,7 @@ p_alt : p_alt[left] '|'[alt] if (p->ctxt.capture_in_pattern) { yyerror1(&@alt, "alternative pattern after variable capture"); } + p->ctxt.in_alt_pattern = 0; $$ = NEW_OR($left, $right, &@$, &@alt); /*% ripper: binary!($:left, ID2VAL(idOr), $:right) %*/ } diff --git a/test/.excludes-parsey/TestPatternMatching.rb b/test/.excludes-parsey/TestPatternMatching.rb deleted file mode 100644 index 20a1868d0c5c9e..00000000000000 --- a/test/.excludes-parsey/TestPatternMatching.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_alternative_pattern_nested, "Deeply nested captures variables are missing a syntax error") From 28d9493b98c7fef39845393ea00ef5eb6947091b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 3 Dec 2025 10:56:53 +0900 Subject: [PATCH 103/367] [ruby/json] Fix macro arguments `ALWAYS_INLINE()` and `NOINLINE()` are defined with one argument. https://github.com/ruby/json/commit/8fb727901e --- ext/json/generator/generator.c | 15 +++++++-------- ext/json/parser/parser.c | 14 +++++++------- ext/json/simd/simd.h | 10 +++++----- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 6ece9f05385491..35d543a7a768fa 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -128,7 +128,7 @@ typedef struct _search_state { #endif /* HAVE_SIMD */ } search_state; -static ALWAYS_INLINE() void search_flush(search_state *search) +ALWAYS_INLINE(static) void search_flush(search_state *search) { // Do not remove this conditional without profiling, specifically escape-heavy text. // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush). @@ -171,7 +171,7 @@ static inline unsigned char search_escape_basic(search_state *search) return 0; } -static ALWAYS_INLINE() void escape_UTF8_char_basic(search_state *search) +ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search) { const unsigned char ch = (unsigned char)*search->ptr; switch (ch) { @@ -258,7 +258,7 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) #ifdef HAVE_SIMD -static ALWAYS_INLINE() char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len) +ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len) { // Flush the buffer so everything up until the last 'len' characters are unflushed. search_flush(search); @@ -281,7 +281,7 @@ static ALWAYS_INLINE() char *copy_remaining_bytes(search_state *search, unsigned #ifdef HAVE_SIMD_NEON -static ALWAYS_INLINE() unsigned char neon_next_match(search_state *search) +ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search) { uint64_t mask = search->matches_mask; uint32_t index = trailing_zeros64(mask) >> 2; @@ -395,7 +395,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search) #ifdef HAVE_SIMD_SSE2 -static ALWAYS_INLINE() unsigned char sse2_next_match(search_state *search) +ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search) { int mask = search->matches_mask; int index = trailing_zeros(mask); @@ -419,7 +419,7 @@ static ALWAYS_INLINE() unsigned char sse2_next_match(search_state *search) #define TARGET_SSE2 #endif -static TARGET_SSE2 ALWAYS_INLINE() unsigned char search_escape_basic_sse2(search_state *search) +ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search) { if (RB_UNLIKELY(search->has_matches)) { // There are more matches if search->matches_mask > 0. @@ -1123,8 +1123,7 @@ struct hash_foreach_arg { bool mixed_keys_encountered; }; -NOINLINE() -static void +NOINLINE(static) void json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg) { if (arg->mixed_keys_encountered) { diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 0216eef987e3f3..5b7cd835cd7d1e 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -88,7 +88,7 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring #if JSON_CPU_LITTLE_ENDIAN_64BITS #if __has_builtin(__builtin_bswap64) #undef rstring_cache_memcmp -static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rptr, const long length) +ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr, const long length) { // The libc memcmp has numerous complex optimizations, but in this particular case, // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to @@ -117,7 +117,7 @@ static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rpt #endif #endif -static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring) { const char *rstring_ptr; long rstring_length; @@ -131,7 +131,7 @@ static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, } } -static ALWAYS_INLINE() VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) +ALWAYS_INLINE(static) VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) { int low = 0; int high = cache->length - 1; @@ -540,7 +540,7 @@ json_eat_comments(JSON_ParserState *state) } } -static ALWAYS_INLINE() void +ALWAYS_INLINE(static) void json_eat_whitespace(JSON_ParserState *state) { while (true) { @@ -661,7 +661,7 @@ static inline const char *json_next_backslash(const char *pe, const char *string return NULL; } -static NOINLINE() VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions) +NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions) { bool intern = is_name || config->freeze; bool symbolize = is_name && config->symbolize_names; @@ -946,7 +946,7 @@ static const bool string_scan_table[256] = { static SIMD_Implementation simd_impl = SIMD_NONE; #endif /* HAVE_SIMD */ -static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state) +ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state) { #ifdef HAVE_SIMD #if defined(HAVE_SIMD_NEON) @@ -1015,7 +1015,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi return Qfalse; } -static ALWAYS_INLINE() VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name) +ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name) { state->cursor++; const char *start = state->cursor; diff --git a/ext/json/simd/simd.h b/ext/json/simd/simd.h index c9e3b3ec7c9f28..f8e5ee1880e5b1 100644 --- a/ext/json/simd/simd.h +++ b/ext/json/simd/simd.h @@ -73,14 +73,14 @@ static inline SIMD_Implementation find_simd_implementation(void) #define HAVE_SIMD_NEON 1 // See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon -static ALWAYS_INLINE() uint64_t neon_match_mask(uint8x16_t matches) +ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches) { const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4); const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0); return mask & 0x8888888888888888ull; } -static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr) +ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr) { uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr); @@ -93,7 +93,7 @@ static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr) return neon_match_mask(needs_escape); } -static ALWAYS_INLINE() int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask) +ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask) { while (*ptr + sizeof(uint8x16_t) <= end) { uint64_t chunk_mask = compute_chunk_mask_neon(*ptr); @@ -140,7 +140,7 @@ static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table) #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) -static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr) +ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr) { __m128i chunk = _mm_loadu_si128((__m128i const*)ptr); // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33 @@ -151,7 +151,7 @@ static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr) return _mm_movemask_epi8(needs_escape); } -static TARGET_SSE2 ALWAYS_INLINE() int string_scan_simd_sse2(const char **ptr, const char *end, int *mask) +ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask) { while (*ptr + sizeof(__m128i) <= end) { int chunk_mask = compute_chunk_mask_sse2(*ptr); From bf144d8c1040d997ccbd79640f81ff6bb31ed64b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 02:03:21 +0000 Subject: [PATCH 104/367] Bump actions/checkout from 6.0.0 to 6.0.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v6...v6.0.1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/annocheck.yml | 2 +- .github/workflows/auto_review_pr.yml | 2 +- .github/workflows/baseruby.yml | 2 +- .github/workflows/bundled_gems.yml | 2 +- .github/workflows/check_dependencies.yml | 2 +- .github/workflows/check_misc.yml | 4 ++-- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/compilers.yml | 26 ++++++++++++------------ .github/workflows/cygwin.yml | 2 +- .github/workflows/default_gems_list.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/mingw.yml | 2 +- .github/workflows/modgc.yml | 2 +- .github/workflows/parse_y.yml | 2 +- .github/workflows/post_push.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/rust-warnings.yml | 2 +- .github/workflows/scorecards.yml | 2 +- .github/workflows/spec_guards.yml | 2 +- .github/workflows/sync_default_gems.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/wasm.yml | 2 +- .github/workflows/windows.yml | 2 +- .github/workflows/yjit-macos.yml | 4 ++-- .github/workflows/yjit-ubuntu.yml | 6 +++--- .github/workflows/zjit-macos.yml | 6 +++--- .github/workflows/zjit-ubuntu.yml | 8 ++++---- 27 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml index c1d227181a7c6b..a2958fc00971dc 100644 --- a/.github/workflows/annocheck.yml +++ b/.github/workflows/annocheck.yml @@ -61,7 +61,7 @@ jobs: - run: id working-directory: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/auto_review_pr.yml b/.github/workflows/auto_review_pr.yml index 34081cfdf7bc7f..1351e78b98abef 100644 --- a/.github/workflows/auto_review_pr.yml +++ b/.github/workflows/auto_review_pr.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6.0.0 + uses: actions/checkout@v6.0.1 - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 68d65dd71cbb3a..9ad8978c3e6e51 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -53,7 +53,7 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler: none - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: ./.github/actions/setup/ubuntu diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml index 58742c1ce18134..ad2e3915199102 100644 --- a/.github/workflows/bundled_gems.yml +++ b/.github/workflows/bundled_gems.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index ee17217f274bcc..218bef28186e2d 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -30,7 +30,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: ./.github/actions/setup/ubuntu if: ${{ contains(matrix.os, 'ubuntu') }} diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml index d181759fc1320a..d7b096a96d58ec 100644 --- a/.github/workflows/check_misc.yml +++ b/.github/workflows/check_misc.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }} @@ -65,7 +65,7 @@ jobs: echo RDOC='ruby -W0 --disable-gems tool/rdoc-srcdir -q' >> $GITHUB_ENV - name: Checkout rdoc - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: repository: ruby/rdoc ref: ${{ steps.rdoc.outputs.ref }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 62fc319b19d1dd..a92c93b476d3af 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install libraries if: ${{ contains(matrix.os, 'macos') }} diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 3ecc749c0456a0..97a698613a2f10 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -51,7 +51,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } # Set fetch-depth: 10 so that Launchable can receive commits information. - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } @@ -74,7 +74,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - name: 'GCC 15 LTO' @@ -104,7 +104,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'clang 22', with: { tag: 'clang-22' }, timeout-minutes: 5 } @@ -125,7 +125,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'clang 13', with: { tag: 'clang-13' }, timeout-minutes: 5 } @@ -146,7 +146,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } # -Wno-strict-prototypes is necessary with current clang-15 since @@ -172,7 +172,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'C++20', with: { CXXFLAGS: '-std=c++20 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' }, timeout-minutes: 5 } @@ -192,7 +192,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'disable-jit', with: { append_configure: '--disable-yjit --disable-zjit' }, timeout-minutes: 5 } @@ -214,7 +214,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'NDEBUG', with: { cppflags: '-DNDEBUG' }, timeout-minutes: 5 } @@ -234,7 +234,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'HASH_DEBUG', with: { cppflags: '-DHASH_DEBUG' }, timeout-minutes: 5 } @@ -254,7 +254,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'USE_LAZY_LOAD', with: { cppflags: '-DUSE_LAZY_LOAD' }, timeout-minutes: 5 } @@ -274,7 +274,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'GC_DEBUG_STRESS_TO_CLASS', with: { cppflags: '-DGC_DEBUG_STRESS_TO_CLASS' }, timeout-minutes: 5 } @@ -293,7 +293,7 @@ jobs: timeout-minutes: 60 services: { docuum: { image: 'stephanmisc/docuum', options: '--init', volumes: [ '/root', '/var/run/docker.sock:/var/run/docker.sock' ] } } steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } } - { uses: './.github/actions/compilers', name: 'VM_DEBUG_BP_CHECK', with: { cppflags: '-DVM_DEBUG_BP_CHECK' }, timeout-minutes: 5 } @@ -319,7 +319,7 @@ jobs: - 'compileB' - 'compileC' steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github } - uses: ./.github/actions/slack with: diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 8b97cc195ff8c8..ac73991fe80b55 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -40,7 +40,7 @@ jobs: steps: - run: git config --global core.autocrlf input - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Cygwin uses: cygwin/cygwin-install-action@master diff --git a/.github/workflows/default_gems_list.yml b/.github/workflows/default_gems_list.yml index 63671382f94941..420228f3997d45 100644 --- a/.github/workflows/default_gems_list.yml +++ b/.github/workflows/default_gems_list.yml @@ -20,7 +20,7 @@ jobs: if: ${{ github.repository == 'ruby/ruby' }} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: token: ${{ (github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull')) && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9a1feb06358463..29adcab39af018 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -61,7 +61,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 192f71649a9a35..1a709d8344a290 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -166,7 +166,7 @@ jobs: [ ${#failed[@]} -eq 0 ] shell: sh - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/modgc.yml b/.github/workflows/modgc.yml index 8b62ccd111b72a..7851005e5133e1 100644 --- a/.github/workflows/modgc.yml +++ b/.github/workflows/modgc.yml @@ -48,7 +48,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/parse_y.yml b/.github/workflows/parse_y.yml index 337973a54d07a6..a1035ef451dd82 100644 --- a/.github/workflows/parse_y.yml +++ b/.github/workflows/parse_y.yml @@ -51,7 +51,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/post_push.yml b/.github/workflows/post_push.yml index 188b4d94d0056e..318444c0a291bf 100644 --- a/.github/workflows/post_push.yml +++ b/.github/workflows/post_push.yml @@ -28,7 +28,7 @@ jobs: REDMINE_SYS_API_KEY: ${{ secrets.REDMINE_SYS_API_KEY }} if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/ruby_') }} - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 500 # for notify-slack-commits token: ${{ secrets.MATZBOT_AUTO_UPDATE_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24bdec5eb9d257..03227bcd7234fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.0 + - uses: actions/checkout@v6.0.1 - uses: ruby/setup-ruby@v1 with: diff --git a/.github/workflows/rust-warnings.yml b/.github/workflows/rust-warnings.yml index f05b35346e8703..a2e3208e5294f4 100644 --- a/.github/workflows/rust-warnings.yml +++ b/.github/workflows/rust-warnings.yml @@ -36,7 +36,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install Rust run: rustup default beta diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 6233f06661d557..a321d14010cd69 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index 20b3367c832a75..8ccb954d84548d 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -45,7 +45,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: diff --git a/.github/workflows/sync_default_gems.yml b/.github/workflows/sync_default_gems.yml index 26144cc49689eb..907dc0b4f025fe 100644 --- a/.github/workflows/sync_default_gems.yml +++ b/.github/workflows/sync_default_gems.yml @@ -27,7 +27,7 @@ jobs: if: ${{ github.repository == 'ruby/ruby' }} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 name: Check out ruby/ruby with: token: ${{ github.repository == 'ruby/ruby' && secrets.MATZBOT_AUTO_UPDATE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 9ec59324900094..9f3ad412f7ba6c 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -60,7 +60,7 @@ jobs: )}} steps: &make-steps - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 082d1984e50620..b59e1d4c931ef7 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -59,7 +59,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index dd4bf0de968108..67ea1cacae2cf1 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -66,7 +66,7 @@ jobs: bundler: none windows-toolchain: none - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml index 148dd7a4a3268d..a59b4d650855ef 100644 --- a/.github/workflows/yjit-macos.yml +++ b/.github/workflows/yjit-macos.yml @@ -41,7 +41,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - run: RUST_BACKTRACE=1 cargo test working-directory: yjit @@ -83,7 +83,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 5303ab1c794f09..00214709b93e65 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -36,7 +36,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 # For now we can't run cargo test --offline because it complains about the # capstone dependency, even though the dependency is optional @@ -68,7 +68,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 # Check that we don't have linting errors in release mode, too - run: cargo clippy --all-targets --all-features @@ -120,7 +120,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index fc7bf459d89de5..ed559d64e10d05 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -68,7 +68,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -172,7 +172,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: ./.github/actions/setup/macos @@ -192,7 +192,7 @@ jobs: run: echo "MAKEFLAGS=" >> "$GITHUB_ENV" - name: Checkout ruby-bench - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: repository: ruby/ruby-bench path: ruby-bench diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index ac400d410d6cf6..5d281eab0b9947 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -41,7 +41,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - run: cargo clippy --all-targets --all-features working-directory: zjit @@ -103,7 +103,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: sparse-checkout-cone-mode: false sparse-checkout: /.github @@ -228,7 +228,7 @@ jobs: )}} steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: ./.github/actions/setup/ubuntu @@ -244,7 +244,7 @@ jobs: - run: make install - name: Checkout ruby-bench - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: repository: ruby/ruby-bench path: ruby-bench From f4466ec8cb1e11192fd9e967bf564c2b4c37d55d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 02:06:58 +0000 Subject: [PATCH 105/367] Bump actions/checkout in /.github/actions/setup/directories Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/1af3b93b6815bc44a9784bd300feb67ff0d1eeb3...8e8c483db84b4bee98b60c0593521ed34d9990e8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/actions/setup/directories/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup/directories/action.yml b/.github/actions/setup/directories/action.yml index 8389ef54b78e5e..916e8b5acd0c4c 100644 --- a/.github/actions/setup/directories/action.yml +++ b/.github/actions/setup/directories/action.yml @@ -95,7 +95,7 @@ runs: git config --global init.defaultBranch garbage - if: inputs.checkout - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: path: ${{ inputs.srcdir }} fetch-depth: ${{ inputs.fetch-depth }} From b2f110651c0ab1c3991dd89dc2529a6f21e17170 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 2 Dec 2025 20:39:25 -0600 Subject: [PATCH 106/367] [DOC] Harmonize #+ methods --- complex.c | 25 +++++++++++++++++-------- numeric.c | 39 ++++++++++++++++++++++++--------------- rational.c | 27 +++++++++++++++++++-------- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/complex.c b/complex.c index d69b0a65817497..12dec4d23b04c8 100644 --- a/complex.c +++ b/complex.c @@ -816,17 +816,26 @@ rb_complex_uminus(VALUE self) } /* - * call-seq: - * complex + numeric -> new_complex + * call-seq: + * self + other -> numeric + * + * Returns the sum of +self+ and +other+: + * + * Complex(1, 2) + 0 # => (1+2i) + * Complex(1, 2) + 1 # => (2+2i) + * Complex(1, 2) + -1 # => (0+2i) + * + * Complex(1, 2) + 1.0 # => (2.0+2i) + * + * Complex(1, 2) + Complex(2, 1) # => (3+3i) + * Complex(1, 2) + Complex(2.0, 1.0) # => (3.0+3.0i) * - * Returns the sum of +self+ and +numeric+: + * Complex(1, 2) + Rational(1, 1) # => ((2/1)+2i) + * Complex(1, 2) + Rational(1, 2) # => ((3/2)+2i) * - * Complex.rect(2, 3) + Complex.rect(2, 3) # => (4+6i) - * Complex.rect(900) + Complex.rect(1) # => (901+0i) - * Complex.rect(-2, 9) + Complex.rect(-9, 2) # => (-11+11i) - * Complex.rect(9, 8) + 4 # => (13+8i) - * Complex.rect(20, 9) + 9.8 # => (29.8+9i) + * For a computation involving Floats, the result may be inexact (see Float#+): * + * Complex(1, 2) + 3.14 # => (4.140000000000001+2i) */ VALUE rb_complex_plus(VALUE self, VALUE other) diff --git a/numeric.c b/numeric.c index 54c9649a87fa55..dd0797b9c58d8f 100644 --- a/numeric.c +++ b/numeric.c @@ -1132,15 +1132,21 @@ rb_float_uminus(VALUE flt) /* * call-seq: - * self + other -> numeric + * self + other -> float or complex * - * Returns a new \Float which is the sum of +self+ and +other+: + * Returns the sum of +self+ and +other+; + * the result may be inexact (see Float): * - * f = 3.14 - * f + 1 # => 4.140000000000001 - * f + 1.0 # => 4.140000000000001 - * f + Rational(1, 1) # => 4.140000000000001 - * f + Complex(1, 0) # => (4.140000000000001+0i) + * 3.14 + 0 # => 3.14 + * 3.14 + 1 # => 4.140000000000001 + * -3.14 + 0 # => -3.14 + * -3.14 + 1 # => -2.14 + + * 3.14 + -3.14 # => 0.0 + * -3.14 + -3.14 # => -6.28 + * + * 3.14 + Complex(1, 0) # => (4.140000000000001+0i) + * 3.14 + Rational(1, 1) # => 4.140000000000001 * */ @@ -3999,17 +4005,20 @@ rb_fix_plus(VALUE x, VALUE y) /* * call-seq: - * self + numeric -> numeric_result + * self + other -> numeric + * + * Returns the sum of +self+ and +other+: * - * Performs addition: + * 1 + 1 # => 2 + * 1 + -1 # => 0 + * 1 + 0 # => 1 + * 1 + -2 # => -1 + * 1 + Complex(1, 0) # => (2+0i) + * 1 + Rational(1, 1) # => (2/1) * - * 2 + 2 # => 4 - * -2 + 2 # => 0 - * -2 + -2 # => -4 - * 2 + 2.0 # => 4.0 - * 2 + Rational(2, 1) # => (4/1) - * 2 + Complex(2, 0) # => (4+0i) + * For a computation involving Floats, the result may be inexact (see Float#+): * + * 1 + 3.14 # => 4.140000000000001 */ VALUE diff --git a/rational.c b/rational.c index 77b1b175d08185..7c2bd0b1eb7f00 100644 --- a/rational.c +++ b/rational.c @@ -715,16 +715,27 @@ f_addsub(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) static double nurat_to_double(VALUE self); /* - * call-seq: - * rat + numeric -> numeric + * call-seq: + * self + other -> numeric + * + * Returns the sum of +self+ and +other+: + * + * Rational(2, 3) + 0 # => (2/3) + * Rational(2, 3) + 1 # => (5/3) + * Rational(2, 3) + -1 # => (-1/3) + * + * Rational(2, 3) + Complex(1, 0) # => ((5/3)+0i) + * + * Rational(2, 3) + Rational(1, 1) # => (5/3) + * Rational(2, 3) + Rational(3, 2) # => (13/6) + * Rational(2, 3) + Rational(3.0, 2.0) # => (13/6) + * Rational(2, 3) + Rational(3.1, 2.1) # => (30399297484750849/14186338826217063) + * + * For a computation involving Floats, the result may be inexact (see Float#+): * - * Performs addition. + * Rational(2, 3) + 1.0 # => 1.6666666666666665 + * Rational(2, 3) + Complex(1.0, 0.0) # => (1.6666666666666665+0.0i) * - * Rational(2, 3) + Rational(2, 3) #=> (4/3) - * Rational(900) + Rational(1) #=> (901/1) - * Rational(-2, 9) + Rational(-9, 2) #=> (-85/18) - * Rational(9, 8) + 4 #=> (41/8) - * Rational(20, 9) + 9.8 #=> 12.022222222222222 */ VALUE rb_rational_plus(VALUE self, VALUE other) From 6e723bee45fedaffa5ea382c9ace628b58c0c70d Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 2 Dec 2025 20:41:21 -0600 Subject: [PATCH 107/367] [DOC] About Float Imprecision (#15293) --- doc/float.rb | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++ numeric.c | 82 --------------------------------- 2 files changed, 128 insertions(+), 82 deletions(-) create mode 100644 doc/float.rb diff --git a/doc/float.rb b/doc/float.rb new file mode 100644 index 00000000000000..01668bfc6dacf1 --- /dev/null +++ b/doc/float.rb @@ -0,0 +1,128 @@ +# A \Float object stores a real number +# using the native architecture's double-precision floating-point representation. +# +# == \Float Imprecisions +# +# Some real numbers can be represented precisely as \Float objects: +# +# 37.5 # => 37.5 +# 98.75 # => 98.75 +# 12.3125 # => 12.3125 +# +# Others cannot; among these are the transcendental numbers, including: +# +# - Pi, π: in mathematics, a number of infinite precision: +# 3.1415926535897932384626433... (to 25 places); +# in Ruby, it is of limited precision (in this case, to 16 decimal places): +# +# Math::PI # => 3.141592653589793 +# +# - Euler's number, e: in mathematics, a number of infinite precision: +# 2.7182818284590452353602874... (to 25 places); +# in Ruby, it is of limited precision (in this case, to 15 decimal places): +# +# Math::E # => 2.718281828459045 +# +# Some floating-point computations in Ruby give precise results: +# +# 1.0/2 # => 0.5 +# 100.0/8 # => 12.5 +# +# Others do not: +# +# - In mathematics, 2/3 as a decimal number is an infinitely-repeating decimal: +# 0.666... (forever); +# in Ruby, +2.0/3+ is of limited precision (in this case, to 16 decimal places): +# +# 2.0/3 # => 0.6666666666666666 +# +# - In mathematics, the square root of 2 is an irrational number of infinite precision: +# 1.4142135623730950488016887... (to 25 decimal places); +# in Ruby, it is of limited precision (in this case, to 16 decimal places): +# +# Math.sqrt(2.0) # => 1.4142135623730951 +# +# - Even a simple computation can introduce imprecision: +# +# x = 0.1 + 0.2 # => 0.30000000000000004 +# y = 0.3 # => 0.3 +# x == y # => false +# +# See: +# +# - https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html +# - https://github.com/rdp/ruby_tutorials_core/wiki/Ruby-Talk-FAQ#-why-are-rubys-floats-imprecise +# - https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems +# +# Note that precise storage and computation of rational numbers +# is possible using Rational objects. +# +# == Creating a \Float +# +# You can create a \Float object explicitly with: +# +# - A {floating-point literal}[rdoc-ref:syntax/literals.rdoc@Float+Literals]. +# +# You can convert certain objects to Floats with: +# +# - Method #Float. +# +# == What's Here +# +# First, what's elsewhere. Class \Float: +# +# - Inherits from +# {class Numeric}[rdoc-ref:Numeric@What-27s+Here] +# and {class Object}[rdoc-ref:Object@What-27s+Here]. +# - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here]. +# +# Here, class \Float provides methods for: +# +# - {Querying}[rdoc-ref:Float@Querying] +# - {Comparing}[rdoc-ref:Float@Comparing] +# - {Converting}[rdoc-ref:Float@Converting] +# +# === Querying +# +# - #finite?: Returns whether +self+ is finite. +# - #hash: Returns the integer hash code for +self+. +# - #infinite?: Returns whether +self+ is infinite. +# - #nan?: Returns whether +self+ is a NaN (not-a-number). +# +# === Comparing +# +# - #<: Returns whether +self+ is less than the given value. +# - #<=: Returns whether +self+ is less than or equal to the given value. +# - #<=>: Returns a number indicating whether +self+ is less than, equal +# to, or greater than the given value. +# - #== (aliased as #=== and #eql?): Returns whether +self+ is equal to +# the given value. +# - #>: Returns whether +self+ is greater than the given value. +# - #>=: Returns whether +self+ is greater than or equal to the given value. +# +# === Converting +# +# - #% (aliased as #modulo): Returns +self+ modulo the given value. +# - #*: Returns the product of +self+ and the given value. +# - #**: Returns the value of +self+ raised to the power of the given value. +# - #+: Returns the sum of +self+ and the given value. +# - #-: Returns the difference of +self+ and the given value. +# - #/: Returns the quotient of +self+ and the given value. +# - #ceil: Returns the smallest number greater than or equal to +self+. +# - #coerce: Returns a 2-element array containing the given value converted to a \Float +# and +self+ +# - #divmod: Returns a 2-element array containing the quotient and remainder +# results of dividing +self+ by the given value. +# - #fdiv: Returns the \Float result of dividing +self+ by the given value. +# - #floor: Returns the greatest number smaller than or equal to +self+. +# - #next_float: Returns the next-larger representable \Float. +# - #prev_float: Returns the next-smaller representable \Float. +# - #quo: Returns the quotient from dividing +self+ by the given value. +# - #round: Returns +self+ rounded to the nearest value, to a given precision. +# - #to_i (aliased as #to_int): Returns +self+ truncated to an Integer. +# - #to_s (aliased as #inspect): Returns a string containing the place-value +# representation of +self+ in the given radix. +# - #truncate: Returns +self+ truncated to a given precision. +# + + class Float; end diff --git a/numeric.c b/numeric.c index dd0797b9c58d8f..bc0edd6abe91f9 100644 --- a/numeric.c +++ b/numeric.c @@ -906,88 +906,6 @@ num_negative_p(VALUE num) return RBOOL(rb_num_negative_int_p(num)); } - -/******************************************************************** - * - * Document-class: Float - * - * A \Float object represents a sometimes-inexact real number using the native - * architecture's double-precision floating point representation. - * - * Floating point has a different arithmetic and is an inexact number. - * So you should know its esoteric system. See following: - * - * - https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html - * - https://github.com/rdp/ruby_tutorials_core/wiki/Ruby-Talk-FAQ#-why-are-rubys-floats-imprecise - * - https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems - * - * You can create a \Float object explicitly with: - * - * - A {floating-point literal}[rdoc-ref:syntax/literals.rdoc@Float+Literals]. - * - * You can convert certain objects to Floats with: - * - * - Method #Float. - * - * == What's Here - * - * First, what's elsewhere. Class \Float: - * - * - Inherits from - * {class Numeric}[rdoc-ref:Numeric@What-27s+Here] - * and {class Object}[rdoc-ref:Object@What-27s+Here]. - * - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here]. - * - * Here, class \Float provides methods for: - * - * - {Querying}[rdoc-ref:Float@Querying] - * - {Comparing}[rdoc-ref:Float@Comparing] - * - {Converting}[rdoc-ref:Float@Converting] - * - * === Querying - * - * - #finite?: Returns whether +self+ is finite. - * - #hash: Returns the integer hash code for +self+. - * - #infinite?: Returns whether +self+ is infinite. - * - #nan?: Returns whether +self+ is a NaN (not-a-number). - * - * === Comparing - * - * - #<: Returns whether +self+ is less than the given value. - * - #<=: Returns whether +self+ is less than or equal to the given value. - * - #<=>: Returns a number indicating whether +self+ is less than, equal - * to, or greater than the given value. - * - #== (aliased as #=== and #eql?): Returns whether +self+ is equal to - * the given value. - * - #>: Returns whether +self+ is greater than the given value. - * - #>=: Returns whether +self+ is greater than or equal to the given value. - * - * === Converting - * - * - #% (aliased as #modulo): Returns +self+ modulo the given value. - * - #*: Returns the product of +self+ and the given value. - * - #**: Returns the value of +self+ raised to the power of the given value. - * - #+: Returns the sum of +self+ and the given value. - * - #-: Returns the difference of +self+ and the given value. - * - #/: Returns the quotient of +self+ and the given value. - * - #ceil: Returns the smallest number greater than or equal to +self+. - * - #coerce: Returns a 2-element array containing the given value converted to a \Float - * and +self+ - * - #divmod: Returns a 2-element array containing the quotient and remainder - * results of dividing +self+ by the given value. - * - #fdiv: Returns the \Float result of dividing +self+ by the given value. - * - #floor: Returns the greatest number smaller than or equal to +self+. - * - #next_float: Returns the next-larger representable \Float. - * - #prev_float: Returns the next-smaller representable \Float. - * - #quo: Returns the quotient from dividing +self+ by the given value. - * - #round: Returns +self+ rounded to the nearest value, to a given precision. - * - #to_i (aliased as #to_int): Returns +self+ truncated to an Integer. - * - #to_s (aliased as #inspect): Returns a string containing the place-value - * representation of +self+ in the given radix. - * - #truncate: Returns +self+ truncated to a given precision. - * - */ - VALUE rb_float_new_in_heap(double d) { From d6107f4ae96ef526918847c566eef8f59cfc0bdd Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 3 Dec 2025 08:11:21 +0900 Subject: [PATCH 108/367] [ruby/rubygems] Bump Bundler version to 4.0.0 https://github.com/ruby/rubygems/commit/a55c485226 --- lib/bundler/version.rb | 2 +- spec/bundler/realworld/fixtures/tapioca/Gemfile.lock | 2 +- spec/bundler/realworld/fixtures/warbler/Gemfile.lock | 2 +- tool/bundler/dev_gems.rb.lock | 2 +- tool/bundler/rubocop_gems.rb.lock | 2 +- tool/bundler/standard_gems.rb.lock | 2 +- tool/bundler/test_gems.rb.lock | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 3a5b5fd86bdabc..8cdc3067366a8e 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "4.0.0.beta2".freeze + VERSION = "4.0.0".freeze def self.bundler_major_version @bundler_major_version ||= gem_version.segments.first diff --git a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock index 6f86f3192ec3a6..92b16c76064450 100644 --- a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock @@ -46,4 +46,4 @@ DEPENDENCIES tapioca BUNDLED WITH - 4.0.0.beta2 + 4.0.0 diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock index 42036fc23d17b4..90090e5fbfdc7e 100644 --- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock @@ -36,4 +36,4 @@ DEPENDENCIES warbler! BUNDLED WITH - 4.0.0.beta2 + 4.0.0 diff --git a/tool/bundler/dev_gems.rb.lock b/tool/bundler/dev_gems.rb.lock index cb2d6b4106f453..4cc6655e43d631 100644 --- a/tool/bundler/dev_gems.rb.lock +++ b/tool/bundler/dev_gems.rb.lock @@ -129,4 +129,4 @@ CHECKSUMS turbo_tests (2.2.5) sha256=3fa31497d12976d11ccc298add29107b92bda94a90d8a0a5783f06f05102509f BUNDLED WITH - 4.0.0.beta2 + 4.0.0 diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index 5cd23d7ef8bb62..77bdc4b6f90a2a 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -156,4 +156,4 @@ CHECKSUMS unicode-emoji (4.1.0) sha256=4997d2d5df1ed4252f4830a9b6e86f932e2013fbff2182a9ce9ccabda4f325a5 BUNDLED WITH - 4.0.0.beta2 + 4.0.0 diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index 20b7d153929917..354a57d660e99b 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -176,4 +176,4 @@ CHECKSUMS unicode-emoji (4.1.0) sha256=4997d2d5df1ed4252f4830a9b6e86f932e2013fbff2182a9ce9ccabda4f325a5 BUNDLED WITH - 4.0.0.beta2 + 4.0.0 diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index 44a2cdcd969d3c..bc103af8605282 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -100,4 +100,4 @@ CHECKSUMS tilt (2.6.1) sha256=35a99bba2adf7c1e362f5b48f9b581cce4edfba98117e34696dde6d308d84770 BUNDLED WITH - 4.0.0.beta2 + 4.0.0 From 1af7550114a0401229eda42de24a829611ffacec Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 3 Dec 2025 08:11:22 +0900 Subject: [PATCH 109/367] [ruby/rubygems] Bump Rubygems version to 4.0.0 https://github.com/ruby/rubygems/commit/9d744beb56 --- lib/rubygems.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 52ccf10159506b..eaeffc7a54d00b 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,7 +9,7 @@ require "rbconfig" module Gem - VERSION = "4.0.0.beta2" + VERSION = "4.0.0" end require_relative "rubygems/defaults" From 65cfd5e13beca200e834d92132a3c8aa86578c98 Mon Sep 17 00:00:00 2001 From: git Date: Wed, 3 Dec 2025 02:43:39 +0000 Subject: [PATCH 110/367] Update default gems list at 1af7550114a0401229eda42de24a82 [ci skip] --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 18265f7c7cada5..bec7ee9db45036 100644 --- a/NEWS.md +++ b/NEWS.md @@ -186,8 +186,8 @@ The following default gem is added. The following default gems are updated. -* RubyGems 4.0.0.beta2 -* bundler 4.0.0.beta2 +* RubyGems 4.0.0 +* bundler 4.0.0 * date 3.5.0 * digest 3.2.1 * english 0.8.1 From b8a7988478bfa8faff7a6f36e5dd22f6f98872b7 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Tue, 2 Dec 2025 16:37:43 -0800 Subject: [PATCH 111/367] Avoid leaking fd in uminus_no_embed test --- test/ruby/test_string.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index e2384e15007d2b..50dc5ec2f95875 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -3443,9 +3443,11 @@ def test_uminus_no_freeze_not_bare def test_uminus_no_embed_gc pad = "a"*2048 - ("aa".."zz").each do |c| - fstr = -(c + pad).freeze - File.open(IO::NULL, "w").write(fstr) + File.open(IO::NULL, "w") do |dev_null| + ("aa".."zz").each do |c| + fstr = -(c + pad).freeze + dev_null.write(fstr) + end end GC.start end From 4762f429f4c26ce838bd3810dc709d08a207716a Mon Sep 17 00:00:00 2001 From: "B. Burt" <162539390+beeburrt@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:02:06 -0700 Subject: [PATCH 112/367] [DOC] typo fix in ruby/file.c --- file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file.c b/file.c index 2c8e87b768e4db..0fdccb7d149be4 100644 --- a/file.c +++ b/file.c @@ -2172,7 +2172,7 @@ rb_file_size_p(VALUE obj, VALUE fname) * File.owned?(file_name) -> true or false * * Returns true if the named file exists and the - * effective used id of the calling process is the owner of + * effective user id of the calling process is the owner of * the file. * * _file_name_ can be an IO object. From 8c3909935e2ba9f79bf3492772c77c305a0d370b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 25 Nov 2025 15:06:33 +0100 Subject: [PATCH 113/367] Handle NEWOBJ tracepoints settings fields [Bug #21710] - struct.c: `struct_alloc` It is possible for a `NEWOBJ` tracepoint call back to write fields into a newly allocated object before `struct_alloc` had the time to set the `RSTRUCT_GEN_FIELDS` flags and such. Hence we can't blindly initialize the `fields_obj` reference to `0` we first need to check no fields were added yet. - object.c: `rb_class_allocate_instance` Similarly, if a `NEWOBJ` tracepoint tries to set fields on the object, the `shape_id` must already be set, as it's required on T_OBJECT to know where to write fields. `NEWOBJ_OF` had to be refactored to accept a `shape_id`. --- ext/-test-/tracepoint/tracepoint.c | 24 ++++++++++++++++++++++++ gc.c | 15 ++++++++------- internal/gc.h | 12 +++++++----- object.c | 13 ++++++------- shape.c | 4 ++++ shape.h | 14 +++++++++++--- struct.c | 13 +++++++++---- test/-ext-/tracepoint/test_tracepoint.rb | 18 ++++++++++++++++++ 8 files changed, 87 insertions(+), 26 deletions(-) diff --git a/ext/-test-/tracepoint/tracepoint.c b/ext/-test-/tracepoint/tracepoint.c index 001d9513b29fb2..e0bd182d18278b 100644 --- a/ext/-test-/tracepoint/tracepoint.c +++ b/ext/-test-/tracepoint/tracepoint.c @@ -86,6 +86,29 @@ tracepoint_specify_normal_and_internal_events(VALUE self) return Qnil; /* should not be reached */ } +int rb_objspace_internal_object_p(VALUE obj); + +static void +on_newobj_event(VALUE tpval, void *data) +{ + VALUE obj = rb_tracearg_object(rb_tracearg_from_tracepoint(tpval)); + if (RB_TYPE_P(obj, T_STRING)) { + // Would fail !rb_obj_exivar_p(str) assertion in fstring_concurrent_set_create + return; + } + if (!rb_objspace_internal_object_p(obj)) rb_obj_id(obj); +} + +static VALUE +add_object_id(RB_UNUSED_VAR(VALUE _)) +{ + VALUE tp = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, on_newobj_event, NULL); + rb_tracepoint_enable(tp); + rb_yield(Qnil); + rb_tracepoint_disable(tp); + return Qnil; +} + void Init_gc_hook(VALUE); void @@ -95,4 +118,5 @@ Init_tracepoint(void) Init_gc_hook(tp_mBug); rb_define_module_function(tp_mBug, "tracepoint_track_objspace_events", tracepoint_track_objspace_events, 0); rb_define_module_function(tp_mBug, "tracepoint_specify_normal_and_internal_events", tracepoint_specify_normal_and_internal_events, 0); + rb_define_singleton_method(tp_mBug, "tracepoint_add_object_id", add_object_id, 0); } diff --git a/gc.c b/gc.c index 97b7362c9fbb85..557a3cbff4017d 100644 --- a/gc.c +++ b/gc.c @@ -991,9 +991,10 @@ gc_validate_pc(VALUE obj) } static inline VALUE -newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, bool wb_protected, size_t size) +newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t size) { VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size); + RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); gc_validate_pc(obj); @@ -1032,17 +1033,17 @@ newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, bool wb_protected, size_t s } VALUE -rb_wb_unprotected_newobj_of(VALUE klass, VALUE flags, size_t size) +rb_wb_unprotected_newobj_of(VALUE klass, VALUE flags, shape_id_t shape_id, size_t size) { GC_ASSERT((flags & FL_WB_PROTECTED) == 0); - return newobj_of(GET_RACTOR(), klass, flags, FALSE, size); + return newobj_of(GET_RACTOR(), klass, flags, shape_id, FALSE, size); } VALUE -rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags, size_t size) +rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape_id, size_t size) { GC_ASSERT((flags & FL_WB_PROTECTED) == 0); - return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, TRUE, size); + return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, shape_id, TRUE, size); } #define UNEXPECTED_NODE(func) \ @@ -1063,7 +1064,7 @@ rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FU { RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1); if (klass) rb_data_object_check(klass); - VALUE obj = newobj_of(GET_RACTOR(), klass, T_DATA, !dmark, sizeof(struct RTypedData)); + VALUE obj = newobj_of(GET_RACTOR(), klass, T_DATA, ROOT_SHAPE_ID, !dmark, sizeof(struct RTypedData)); struct RData *data = (struct RData *)obj; data->dmark = dmark; @@ -1087,7 +1088,7 @@ typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_ RBIMPL_NONNULL_ARG(type); if (klass) rb_data_object_check(klass); bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark; - VALUE obj = newobj_of(GET_RACTOR(), klass, T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, wb_protected, size); + VALUE obj = newobj_of(GET_RACTOR(), klass, T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, ROOT_SHAPE_ID, wb_protected, size); struct RTypedData *data = (struct RTypedData *)obj; data->fields_obj = 0; diff --git a/internal/gc.h b/internal/gc.h index ec408d7fac53b9..ea001449a0030c 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -122,10 +122,12 @@ const char *rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj) struct rb_execution_context_struct; /* in vm_core.h */ struct rb_objspace; /* in vm_core.h */ -#define NEWOBJ_OF(var, T, c, f, s, ec) \ +#define NEWOBJ_OF_WITH_SHAPE(var, T, c, f, shape_id, s, ec) \ T *(var) = (T *)(((f) & FL_WB_PROTECTED) ? \ - rb_wb_protected_newobj_of((ec ? ec : GET_EC()), (c), (f) & ~FL_WB_PROTECTED, s) : \ - rb_wb_unprotected_newobj_of((c), (f), s)) + rb_wb_protected_newobj_of((ec ? ec : GET_EC()), (c), (f) & ~FL_WB_PROTECTED, shape_id, s) : \ + rb_wb_unprotected_newobj_of((c), (f), shape_id, s)) + +#define NEWOBJ_OF(var, T, c, f, s, ec) NEWOBJ_OF_WITH_SHAPE(var, T, c, f, 0 /* ROOT_SHAPE_ID */, s, ec) #ifndef RB_GC_OBJECT_METADATA_ENTRY_DEFINED # define RB_GC_OBJECT_METADATA_ENTRY_DEFINED @@ -248,8 +250,8 @@ VALUE rb_gc_disable_no_rest(void); /* gc.c (export) */ const char *rb_objspace_data_type_name(VALUE obj); -VALUE rb_wb_protected_newobj_of(struct rb_execution_context_struct *, VALUE, VALUE, size_t); -VALUE rb_wb_unprotected_newobj_of(VALUE, VALUE, size_t); +VALUE rb_wb_protected_newobj_of(struct rb_execution_context_struct *, VALUE, VALUE, uint32_t /* shape_id_t */, size_t); +VALUE rb_wb_unprotected_newobj_of(VALUE, VALUE, uint32_t /* shape_id_t */, size_t); size_t rb_obj_memsize_of(VALUE); struct rb_gc_object_metadata_entry *rb_gc_object_metadata(VALUE obj); void rb_gc_mark_values(long n, const VALUE *values); diff --git a/object.c b/object.c index e960f1855b0c80..bcafab3c3d4bf0 100644 --- a/object.c +++ b/object.c @@ -124,18 +124,17 @@ rb_class_allocate_instance(VALUE klass) size = sizeof(struct RObject); } - NEWOBJ_OF(o, struct RObject, klass, - T_OBJECT | (RGENGC_WB_PROTECTED_OBJECT ? FL_WB_PROTECTED : 0), size, 0); + // There might be a NEWOBJ tracepoint callback, and it may set fields. + // So the shape must be passed to `NEWOBJ_OF`. + VALUE flags = T_OBJECT | (RGENGC_WB_PROTECTED_OBJECT ? FL_WB_PROTECTED : 0); + NEWOBJ_OF_WITH_SHAPE(o, struct RObject, klass, flags, rb_shape_root(rb_gc_heap_id_for_size(size)), size, 0); VALUE obj = (VALUE)o; - RUBY_ASSERT(RSHAPE_TYPE_P(RBASIC_SHAPE_ID(obj), SHAPE_ROOT)); - - RBASIC_SET_SHAPE_ID(obj, rb_shape_root(rb_gc_heap_id_for_size(size))); - #if RUBY_DEBUG RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); VALUE *ptr = ROBJECT_FIELDS(obj); - for (size_t i = 0; i < ROBJECT_FIELDS_CAPACITY(obj); i++) { + size_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); + for (size_t i = fields_count; i < ROBJECT_FIELDS_CAPACITY(obj); i++) { ptr[i] = Qundef; } if (rb_obj_class(obj) != rb_class_real(klass)) { diff --git a/shape.c b/shape.c index 7acfe72930a09f..754be1cfd64e65 100644 --- a/shape.c +++ b/shape.c @@ -1240,6 +1240,10 @@ rb_shape_foreach_field(shape_id_t initial_shape_id, rb_shape_foreach_transition_ bool rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) { + if (shape_id == ROOT_SHAPE_ID) { + return true; + } + if (shape_id == INVALID_SHAPE_ID) { rb_bug("Can't set INVALID_SHAPE_ID on an object"); } diff --git a/shape.h b/shape.h index d9cfe48759c333..b0bb4db0bfce1b 100644 --- a/shape.h +++ b/shape.h @@ -163,10 +163,8 @@ bool rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id); #endif static inline void -RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) { - RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); - RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_fields)); #if RBASIC_SHAPE_ID_FIELD RBASIC(obj)->shape_id = (VALUE)shape_id; #else @@ -174,6 +172,16 @@ RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) RBASIC(obj)->flags &= SHAPE_FLAG_MASK; RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); #endif +} + +static inline void +RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id) +{ + RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj)); + RUBY_ASSERT(!RB_TYPE_P(obj, T_IMEMO) || IMEMO_TYPE_P(obj, imemo_fields)); + + RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); + RUBY_ASSERT(rb_shape_verify_consistency(obj, shape_id)); } diff --git a/struct.c b/struct.c index 9da9bbdfe369c8..a6155d4684249f 100644 --- a/struct.c +++ b/struct.c @@ -819,12 +819,17 @@ struct_alloc(VALUE klass) if (n > 0 && rb_gc_size_allocatable_p(embedded_size)) { flags |= n << RSTRUCT_EMBED_LEN_SHIFT; + if (RCLASS_MAX_IV_COUNT(klass) == 0) { + // We set the flag before calling `NEWOBJ_OF` in case a NEWOBJ tracepoint does + // attempt to write fields. We'll remove it later if no fields was written to. + flags |= RSTRUCT_GEN_FIELDS; + } NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0); - if (RCLASS_MAX_IV_COUNT(klass) == 0 && embedded_size == rb_gc_obj_slot_size((VALUE)st)) { - FL_SET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS); - } - else { + if (RCLASS_MAX_IV_COUNT(klass) == 0 + && !rb_shape_obj_has_fields((VALUE)st) + && embedded_size < rb_gc_obj_slot_size((VALUE)st)) { + FL_UNSET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS); RSTRUCT_SET_FIELDS_OBJ((VALUE)st, 0); } rb_mem_clear((VALUE *)st->as.ary, n); diff --git a/test/-ext-/tracepoint/test_tracepoint.rb b/test/-ext-/tracepoint/test_tracepoint.rb index debddd83d043fe..2256f58bc710b0 100644 --- a/test/-ext-/tracepoint/test_tracepoint.rb +++ b/test/-ext-/tracepoint/test_tracepoint.rb @@ -82,6 +82,24 @@ def run(hook) end end + def test_tracepoint_add_object_id + Bug.tracepoint_add_object_id do + klass = Struct.new + 2.times { klass.new } + + klass = Struct.new(:a) + 2.times { klass.new } + + klass = Struct.new(:a, :b, :c) + 2.times { klass.new } + + 2.times { Set.new } # To test T_DATA / TypedData RUBY_TYPED_EMBEDDABLE + 2.times { Proc.new { } } # To test T_DATA / TypedData non embeddable + + 2.times { Object.new } + end + end + def test_teardown_with_active_GC_end_hook assert_separately([], 'require("-test-/tracepoint"); Bug.after_gc_exit_hook = proc {}; GC.start') end From 706d80830b9f0a5a2eac66251d1417abb2ff143c Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 3 Dec 2025 17:38:23 +0900 Subject: [PATCH 114/367] [ruby/rubygems] Silence Bundler UI in plugin installer specs https://github.com/ruby/rubygems/commit/90a0af8204 --- spec/bundler/bundler/plugin/installer_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb index 8e1879395a6320..c200a98afaf264 100644 --- a/spec/bundler/bundler/plugin/installer_spec.rb +++ b/spec/bundler/bundler/plugin/installer_spec.rb @@ -47,6 +47,13 @@ build_plugin "re-plugin" build_plugin "ma-plugin" end + + @previous_ui = Bundler.ui + Bundler.ui = Bundler::UI::Silent.new + end + + after do + Bundler.ui = @previous_ui end context "git plugins" do From 4d377c8c2eedc42f274b3b2a841fa24d5b4c5541 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 14:41:35 +0100 Subject: [PATCH 115/367] [ruby/json] Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument Otherwise it's very error prone. https://github.com/ruby/json/commit/c54de70f90 --- ext/json/lib/json/common.rb | 21 +++++++++++++++++++-- test/json/json_common_interface_test.rb | 7 +++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 233b8c7e62d628..fcdc0d9f0be3e1 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -550,6 +550,7 @@ def pretty_generate(obj, opts = nil) :create_additions => nil, } # :call-seq: + # JSON.unsafe_load(source, options = {}) -> object # JSON.unsafe_load(source, proc = nil, options = {}) -> object # # Returns the Ruby objects created by parsing the given +source+. @@ -681,7 +682,12 @@ def pretty_generate(obj, opts = nil) # def unsafe_load(source, proc = nil, options = nil) opts = if options.nil? - _unsafe_load_default_options + if proc && proc.is_a?(Hash) + options, proc = proc, nil + options + else + _unsafe_load_default_options + end else _unsafe_load_default_options.merge(options) end @@ -709,6 +715,7 @@ def unsafe_load(source, proc = nil, options = nil) end # :call-seq: + # JSON.load(source, options = {}) -> object # JSON.load(source, proc = nil, options = {}) -> object # # Returns the Ruby objects created by parsing the given +source+. @@ -845,8 +852,18 @@ def unsafe_load(source, proc = nil, options = nil) # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>} # def load(source, proc = nil, options = nil) + if proc && options.nil? && proc.is_a?(Hash) + options = proc + proc = nil + end + opts = if options.nil? - _load_default_options + if proc && proc.is_a?(Hash) + options, proc = proc, nil + options + else + _load_default_options + end else _load_default_options.merge(options) end diff --git a/test/json/json_common_interface_test.rb b/test/json/json_common_interface_test.rb index 37fa439575cd87..13e2ca062a92bf 100644 --- a/test/json/json_common_interface_test.rb +++ b/test/json/json_common_interface_test.rb @@ -149,6 +149,7 @@ def test_load_with_proc def test_load_with_options json = '{ "foo": NaN }' assert JSON.load(json, nil, :allow_nan => true)['foo'].nan? + assert JSON.load(json, :allow_nan => true)['foo'].nan? end def test_load_null @@ -215,6 +216,12 @@ def test_unsafe_load_with_proc assert_equal expected, visited end + def test_unsafe_load_with_options + json = '{ "foo": NaN }' + assert JSON.unsafe_load(json, nil, :allow_nan => true)['foo'].nan? + assert JSON.unsafe_load(json, :allow_nan => true)['foo'].nan? + end + def test_unsafe_load_default_options too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' assert JSON.unsafe_load(too_deep, nil).is_a?(Array) From 54a73a57a292d8d7e88cadd3fd8454a3084a60e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Thu, 27 Nov 2025 16:12:17 +0100 Subject: [PATCH 116/367] [ruby/json] Test and restore behavior around to_json changing depth When serializing an Array, and one of the elements of the Array requires calling `to_json`, if the depth is changed, it will be used for the next entries, which wasn't the case before https://github.com/ruby/json/commit/5abd43490714, and is not the case with TruffleRuby and JRuby. Additionally, with TruffleRuby and JRuby the state's depth after the `to_json` call is used to close the Array, which isn't the case with CRuby. https://github.com/ruby/json/commit/386b36fde5 --- ext/json/generator/generator.c | 2 ++ test/json/json_generator_test.rb | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 35d543a7a768fa..9e6e617a59a7a8 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -1294,6 +1294,8 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d VALUE tmp; if (rb_respond_to(obj, i_to_json)) { tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data)); + GET_STATE(data->vstate); + data->depth = state->depth; Check_Type(tmp, T_STRING); fbuffer_append_str(buffer, tmp); } else { diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 9600f4be8d15d0..ab3a6807d65fec 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -321,6 +321,30 @@ def test_allow_nan end end + def test_depth_bad_to_json + obj = Object.new + def obj.to_json(state) + state.depth += 1 + "{#{state.object_nl}"\ + "#{state.indent * state.depth}\"foo\":#{state.space}1#{state.object_nl}"\ + "#{state.indent * (state.depth - 1)}}" + end + indent = " " * 2 if RUBY_ENGINE != "ruby" + assert_equal <<~JSON.chomp, JSON.pretty_generate([obj] * 2) + [ + { + "foo": 1 + }, + { + "foo": 1 + } + #{indent}] + JSON + state = JSON::State.new(object_nl: "\n", array_nl: "\n", space: " ", indent: " ") + state.generate(obj) + assert_equal 1, state.depth + end + def test_depth pretty = { object_nl: "\n", array_nl: "\n", space: " ", indent: " " } state = JSON.state.new(**pretty) From 32c7c3c19aa7c9c3fda10a9520d29e244baeaa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Fri, 28 Nov 2025 15:53:58 +0100 Subject: [PATCH 117/367] [ruby/json] Reproduce C ext behavior of ignoring mutated depth in arrays https://github.com/ruby/json/commit/e0257b9f82 --- test/json/json_generator_test.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index ab3a6807d65fec..e623e05409ee6c 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -329,7 +329,6 @@ def obj.to_json(state) "#{state.indent * state.depth}\"foo\":#{state.space}1#{state.object_nl}"\ "#{state.indent * (state.depth - 1)}}" end - indent = " " * 2 if RUBY_ENGINE != "ruby" assert_equal <<~JSON.chomp, JSON.pretty_generate([obj] * 2) [ { @@ -338,11 +337,14 @@ def obj.to_json(state) { "foo": 1 } - #{indent}] + ] JSON state = JSON::State.new(object_nl: "\n", array_nl: "\n", space: " ", indent: " ") state.generate(obj) - assert_equal 1, state.depth + assert_equal 1, state.depth # FIXME + state.depth = 0 + state.generate([obj]) + assert_equal 0, state.depth end def test_depth From 05383a1de2f2afe263ab894e851eca51e40bb543 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 15:13:00 +0100 Subject: [PATCH 118/367] [ruby/json] Fix duplicated test_unsafe_load_with_options test case https://github.com/ruby/json/commit/7b62fac525 --- test/json/json_common_interface_test.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/json/json_common_interface_test.rb b/test/json/json_common_interface_test.rb index 13e2ca062a92bf..3dfd0623cd98bc 100644 --- a/test/json/json_common_interface_test.rb +++ b/test/json/json_common_interface_test.rb @@ -216,12 +216,6 @@ def test_unsafe_load_with_proc assert_equal expected, visited end - def test_unsafe_load_with_options - json = '{ "foo": NaN }' - assert JSON.unsafe_load(json, nil, :allow_nan => true)['foo'].nan? - assert JSON.unsafe_load(json, :allow_nan => true)['foo'].nan? - end - def test_unsafe_load_default_options too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' assert JSON.unsafe_load(too_deep, nil).is_a?(Array) @@ -237,6 +231,7 @@ def test_unsafe_load_with_options assert_raise(JSON::ParserError) { JSON.unsafe_load(nan_json, nil, :allow_nan => false)['foo'].nan? } # make sure it still uses the defaults when something is provided assert JSON.unsafe_load(nan_json, nil, :allow_blank => true)['foo'].nan? + assert JSON.unsafe_load(nan_json, :allow_nan => true)['foo'].nan? end def test_unsafe_load_null From 5770c186d1e9d8e7202c83763c9619faa1f4c97c Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 10:42:50 +0100 Subject: [PATCH 119/367] Rename `rb_obj_exivar_p` -> `rb_obj_gen_fields_p` The "EXIVAR" terminology has been replaced by "gen fields" AKA "generic fields". Exivar implies variable, but generic fields include more than just variables, e.g. `object_id`. --- ext/-test-/tracepoint/tracepoint.c | 2 +- gc.c | 4 ++-- hash.c | 4 ++-- ractor.c | 6 +++--- shape.h | 2 +- string.c | 2 +- variable.c | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ext/-test-/tracepoint/tracepoint.c b/ext/-test-/tracepoint/tracepoint.c index e0bd182d18278b..03887b1d5b6549 100644 --- a/ext/-test-/tracepoint/tracepoint.c +++ b/ext/-test-/tracepoint/tracepoint.c @@ -93,7 +93,7 @@ on_newobj_event(VALUE tpval, void *data) { VALUE obj = rb_tracearg_object(rb_tracearg_from_tracepoint(tpval)); if (RB_TYPE_P(obj, T_STRING)) { - // Would fail !rb_obj_exivar_p(str) assertion in fstring_concurrent_set_create + // Would fail !rb_obj_gen_fields_p(str) assertion in fstring_concurrent_set_create return; } if (!rb_objspace_internal_object_p(obj)) rb_obj_id(obj); diff --git a/gc.c b/gc.c index 557a3cbff4017d..9ccaffdc0b371c 100644 --- a/gc.c +++ b/gc.c @@ -2061,7 +2061,7 @@ rb_gc_obj_free_vm_weak_references(VALUE obj) { obj_free_object_id(obj); - if (rb_obj_exivar_p(obj)) { + if (rb_obj_gen_fields_p(obj)) { rb_free_generic_ivar(obj); } @@ -3116,7 +3116,7 @@ rb_gc_mark_children(void *objspace, VALUE obj) { struct gc_mark_classext_foreach_arg foreach_args; - if (rb_obj_exivar_p(obj)) { + if (rb_obj_gen_fields_p(obj)) { rb_mark_generic_ivar(obj); } diff --git a/hash.c b/hash.c index 0b98b68d169b44..ac9a71794c8430 100644 --- a/hash.c +++ b/hash.c @@ -1554,7 +1554,7 @@ rb_hash_dup(VALUE hash) const VALUE flags = RBASIC(hash)->flags; VALUE ret = hash_dup(hash, rb_obj_class(hash), flags & RHASH_PROC_DEFAULT); - if (rb_obj_exivar_p(hash)) { + if (rb_obj_gen_fields_p(hash)) { rb_copy_generic_ivar(ret, hash); } return ret; @@ -2876,7 +2876,7 @@ hash_aset(st_data_t *key, st_data_t *val, struct update_arg *arg, int existing) VALUE rb_hash_key_str(VALUE key) { - if (!rb_obj_exivar_p(key) && RBASIC_CLASS(key) == rb_cString) { + if (!rb_obj_gen_fields_p(key) && RBASIC_CLASS(key) == rb_cString) { return rb_fstring(key); } else { diff --git a/ractor.c b/ractor.c index 8238d9a456e233..ea09583c5f7f7c 100644 --- a/ractor.c +++ b/ractor.c @@ -1137,7 +1137,7 @@ rb_obj_set_shareable_no_assert(VALUE obj) { FL_SET_RAW(obj, FL_SHAREABLE); - if (rb_obj_exivar_p(obj)) { + if (rb_obj_gen_fields_p(obj)) { VALUE fields = rb_obj_fields_no_ractor_check(obj); if (imemo_type_p(fields, imemo_fields)) { // no recursive mark @@ -1750,7 +1750,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) else if (data->replacement != _val) { RB_OBJ_WRITE(parent_obj, &v, data->replacement); } \ } while (0) - if (UNLIKELY(rb_obj_exivar_p(obj))) { + if (UNLIKELY(rb_obj_gen_fields_p(obj))) { VALUE fields_obj = rb_obj_fields_no_ractor_check(obj); if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) { @@ -1984,7 +1984,7 @@ move_leave(VALUE obj, struct obj_traverse_replace_data *data) rb_gc_writebarrier_remember(data->replacement); void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c - if (UNLIKELY(rb_obj_exivar_p(obj))) { + if (UNLIKELY(rb_obj_gen_fields_p(obj))) { rb_replace_generic_ivar(data->replacement, obj); } diff --git a/shape.h b/shape.h index b0bb4db0bfce1b..9478d4b3a95dc6 100644 --- a/shape.h +++ b/shape.h @@ -437,7 +437,7 @@ rb_shape_obj_has_fields(VALUE obj) } static inline bool -rb_obj_exivar_p(VALUE obj) +rb_obj_gen_fields_p(VALUE obj) { switch (TYPE(obj)) { case T_NONE: diff --git a/string.c b/string.c index c794b36748e6e6..56c83ca2d53cd6 100644 --- a/string.c +++ b/string.c @@ -549,7 +549,7 @@ fstring_concurrent_set_create(VALUE str, void *data) RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); RUBY_ASSERT(OBJ_FROZEN(str)); RUBY_ASSERT(!FL_TEST_RAW(str, STR_FAKESTR)); - RUBY_ASSERT(!rb_obj_exivar_p(str)); + RUBY_ASSERT(!rb_obj_gen_fields_p(str)); RUBY_ASSERT(RBASIC_CLASS(str) == rb_cString); RUBY_ASSERT(!rb_objspace_garbage_object_p(str)); diff --git a/variable.c b/variable.c index d4c5d91e25d6dc..9a7e11850dcef5 100644 --- a/variable.c +++ b/variable.c @@ -1291,7 +1291,7 @@ rb_obj_fields(VALUE obj, ID field_name) void rb_free_generic_ivar(VALUE obj) { - if (rb_obj_exivar_p(obj)) { + if (rb_obj_gen_fields_p(obj)) { st_data_t key = (st_data_t)obj, value; switch (BUILTIN_TYPE(obj)) { case T_DATA: @@ -2218,7 +2218,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) rb_check_frozen(dest); - if (!rb_obj_exivar_p(obj)) { + if (!rb_obj_gen_fields_p(obj)) { return; } From b78db63be4d078b7ac29c8e9fcb40cb20d232265 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 10:47:28 +0100 Subject: [PATCH 120/367] fstring_concurrent_set_create: only assert the string has no ivars The NEWOBJ tracepoint can generate an object_id, that's alright, what we don't want is actual instance variables. --- ext/-test-/tracepoint/tracepoint.c | 4 ---- string.c | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/ext/-test-/tracepoint/tracepoint.c b/ext/-test-/tracepoint/tracepoint.c index 03887b1d5b6549..7f7aa246628858 100644 --- a/ext/-test-/tracepoint/tracepoint.c +++ b/ext/-test-/tracepoint/tracepoint.c @@ -92,10 +92,6 @@ static void on_newobj_event(VALUE tpval, void *data) { VALUE obj = rb_tracearg_object(rb_tracearg_from_tracepoint(tpval)); - if (RB_TYPE_P(obj, T_STRING)) { - // Would fail !rb_obj_gen_fields_p(str) assertion in fstring_concurrent_set_create - return; - } if (!rb_objspace_internal_object_p(obj)) rb_obj_id(obj); } diff --git a/string.c b/string.c index 56c83ca2d53cd6..0370fc5d1e02e6 100644 --- a/string.c +++ b/string.c @@ -549,7 +549,7 @@ fstring_concurrent_set_create(VALUE str, void *data) RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); RUBY_ASSERT(OBJ_FROZEN(str)); RUBY_ASSERT(!FL_TEST_RAW(str, STR_FAKESTR)); - RUBY_ASSERT(!rb_obj_gen_fields_p(str)); + RUBY_ASSERT(!rb_shape_obj_has_ivars(str)); RUBY_ASSERT(RBASIC_CLASS(str) == rb_cString); RUBY_ASSERT(!rb_objspace_garbage_object_p(str)); From 208271e3723653cd4cb9cd2eb4a6c631eee0b09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Wed, 3 Dec 2025 15:43:28 +0100 Subject: [PATCH 121/367] [ruby/json] Fix handling of depth https://github.com/ruby/json/commit/ccca602274 --- ext/json/generator/generator.c | 50 +++------------------ ext/json/lib/json/common.rb | 2 +- test/json/json_generator_test.rb | 77 ++++++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 9e6e617a59a7a8..d202e97ea18156 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -968,14 +968,16 @@ static void vstate_spill(struct generate_json_data *data) RB_OBJ_WRITTEN(vstate, Qundef, state->as_json); } -static inline VALUE vstate_get(struct generate_json_data *data) +static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj) { if (RB_UNLIKELY(!data->vstate)) { vstate_spill(data); } GET_STATE(data->vstate); state->depth = data->depth; - return data->vstate; + VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate); + // no need to restore state->depth, vstate is just a temporary State + return tmp; } static VALUE @@ -1293,9 +1295,7 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d { VALUE tmp; if (rb_respond_to(obj, i_to_json)) { - tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data)); - GET_STATE(data->vstate); - data->depth = state->depth; + tmp = json_call_to_json(data, obj); Check_Type(tmp, T_STRING); fbuffer_append_str(buffer, tmp); } else { @@ -1477,16 +1477,6 @@ static VALUE generate_json_try(VALUE d) return fbuffer_finalize(data->buffer); } -// Preserves the deprecated behavior of State#depth being set. -static VALUE generate_json_ensure_deprecated(VALUE d) -{ - struct generate_json_data *data = (struct generate_json_data *)d; - fbuffer_free(data->buffer); - data->state->depth = data->depth; - - return Qundef; -} - static VALUE generate_json_ensure(VALUE d) { struct generate_json_data *data = (struct generate_json_data *)d; @@ -1507,13 +1497,13 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, struct generate_json_data data = { .buffer = &buffer, - .vstate = self, + .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json .state = state, .depth = state->depth, .obj = obj, .func = func }; - return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure_deprecated, (VALUE)&data); + return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); } /* call-seq: @@ -1532,31 +1522,6 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self) return cState_partial_generate(self, obj, generate_json, io); } -static VALUE cState_generate_new(int argc, VALUE *argv, VALUE self) -{ - rb_check_arity(argc, 1, 2); - VALUE obj = argv[0]; - VALUE io = argc > 1 ? argv[1] : Qnil; - - GET_STATE(self); - - char stack_buffer[FBUFFER_STACK_SIZE]; - FBuffer buffer = { - .io = RTEST(io) ? io : Qfalse, - }; - fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); - - struct generate_json_data data = { - .buffer = &buffer, - .vstate = Qfalse, - .state = state, - .depth = state->depth, - .obj = obj, - .func = generate_json - }; - return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); -} - static VALUE cState_initialize(int argc, VALUE *argv, VALUE self) { rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`"); @@ -2145,7 +2110,6 @@ void Init_generator(void) rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0); rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1); rb_define_method(cState, "generate", cState_generate, -1); - rb_define_method(cState, "generate_new", cState_generate_new, -1); // :nodoc: rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0); diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index fcdc0d9f0be3e1..877b96814e8bca 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -1074,7 +1074,7 @@ def initialize(options = nil, &as_json) # # Serialize the given object into a \JSON document. def dump(object, io = nil) - @state.generate_new(object, io) + @state.generate(object, io) end alias_method :generate, :dump diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index e623e05409ee6c..9f8b35de093271 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -321,7 +321,8 @@ def test_allow_nan end end - def test_depth_bad_to_json + # An object that changes state.depth when it receives to_json(state) + def bad_to_json obj = Object.new def obj.to_json(state) state.depth += 1 @@ -329,21 +330,44 @@ def obj.to_json(state) "#{state.indent * state.depth}\"foo\":#{state.space}1#{state.object_nl}"\ "#{state.indent * (state.depth - 1)}}" end - assert_equal <<~JSON.chomp, JSON.pretty_generate([obj] * 2) + obj + end + + def test_depth_restored_bad_to_json + state = JSON::State.new + state.generate(bad_to_json) + assert_equal 0, state.depth + end + + def test_depth_restored_bad_to_json_in_Array + assert_equal <<~JSON.chomp, JSON.pretty_generate([bad_to_json] * 2) [ { "foo": 1 }, { - "foo": 1 - } + "foo": 1 + } ] JSON - state = JSON::State.new(object_nl: "\n", array_nl: "\n", space: " ", indent: " ") - state.generate(obj) - assert_equal 1, state.depth # FIXME - state.depth = 0 - state.generate([obj]) + state = JSON::State.new + state.generate([bad_to_json]) + assert_equal 0, state.depth + end + + def test_depth_restored_bad_to_json_in_Hash + assert_equal <<~JSON.chomp, JSON.pretty_generate(a: bad_to_json, b: bad_to_json) + { + "a": { + "foo": 1 + }, + "b": { + "foo": 1 + } + } + JSON + state = JSON::State.new + state.generate(a: bad_to_json) assert_equal 0, state.depth end @@ -361,10 +385,36 @@ def test_depth_nesting_error ary = []; ary << ary assert_raise(JSON::NestingError) { generate(ary) } assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) } - s = JSON.state.new - assert_equal 0, s.depth + end + + def test_depth_nesting_error_to_json + ary = []; ary << ary + s = JSON.state.new(depth: 1) assert_raise(JSON::NestingError) { ary.to_json(s) } - assert_equal 100, s.depth + assert_equal 1, s.depth + end + + def test_depth_nesting_error_Hash_to_json + hash = {}; hash[:a] = hash + s = JSON.state.new(depth: 1) + assert_raise(JSON::NestingError) { hash.to_json(s) } + assert_equal 1, s.depth + end + + def test_depth_nesting_error_generate + ary = []; ary << ary + s = JSON.state.new(depth: 1) + assert_raise(JSON::NestingError) { s.generate(ary) } + assert_equal 1, s.depth + end + + def test_depth_exception_calling_to_json + def (obj = Object.new).to_json(*) + raise + end + s = JSON.state.new(depth: 1).freeze + assert_raise(RuntimeError) { s.generate([{ hash: obj }]) } + assert_equal 1, s.depth end def test_buffer_initial_length @@ -1006,7 +1056,8 @@ def test_nesting_recovery state = JSON::State.new ary = [] ary << ary - assert_raise(JSON::NestingError) { state.generate_new(ary) } + assert_raise(JSON::NestingError) { state.generate(ary) } + assert_equal 0, state.depth assert_equal '{"a":1}', state.generate({ a: 1 }) end end From 94581b1ffde5e2afeba4631152955c18ec52ccf0 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 16:23:14 +0100 Subject: [PATCH 122/367] [ruby/json] Release 2.17.0 https://github.com/ruby/json/commit/4bdb2d14fe --- ext/json/lib/json/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index cc25a0453e20c9..b7de7c27e21446 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module JSON - VERSION = '2.16.0' + VERSION = '2.17.0' end From 20fc8aff05ce857f3d3b759d92f1941132398b65 Mon Sep 17 00:00:00 2001 From: git Date: Wed, 3 Dec 2025 15:27:19 +0000 Subject: [PATCH 123/367] Update default gems list at 94581b1ffde5e2afeba4631152955c [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index bec7ee9db45036..f3e7f1ae0e3a6e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -198,7 +198,7 @@ The following default gems are updated. * io-console 0.8.1 * io-nonblock 0.3.2 * io-wait 0.4.0.dev -* json 2.16.0 +* json 2.17.0 * net-http 0.8.0 * openssl 4.0.0.pre * optparse 0.8.0 From d7dffcdbeeee81bb3bbe63b86620cb682eb3ab23 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 3 Dec 2025 15:41:23 +0100 Subject: [PATCH 124/367] [ruby/prism] Follow repo move from oracle/truffleruby to truffleruby/truffleruby https://github.com/ruby/prism/commit/c8e1b11120 --- prism/prism.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/prism.h b/prism/prism.h index dc31f26e786a5c..c468db18bef3c2 100644 --- a/prism/prism.h +++ b/prism/prism.h @@ -314,7 +314,7 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * dependencies. It is currently being integrated into * [CRuby](https://github.com/ruby/ruby), * [JRuby](https://github.com/jruby/jruby), - * [TruffleRuby](https://github.com/oracle/truffleruby), + * [TruffleRuby](https://github.com/truffleruby/truffleruby), * [Sorbet](https://github.com/sorbet/sorbet), and * [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree). * From fcf3939780972d587b18afc26c4abd2da2c0b7ec Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 14:50:35 +0100 Subject: [PATCH 125/367] Speedup TypedData_Get_Struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While profiling `Monitor#synchronize` and `Mutex#synchronize` I noticed a fairly significant amount of time spent in `rb_check_typeddata`. By implementing a fast path that assumes the object is valid and that can be inlined, it does make a significant difference: Before: ``` Mutex 13.548M (± 3.6%) i/s (73.81 ns/i) - 68.566M in 5.067444 Monitor 10.497M (± 6.5%) i/s (95.27 ns/i) - 52.529M in 5.032698s ``` After: ``` Mutex 20.887M (± 0.3%) i/s (47.88 ns/i) - 106.021M in 5.075989s Monitor 16.245M (±13.3%) i/s (61.56 ns/i) - 80.705M in 5.099680s ``` ```ruby require 'bundler/inline' gemfile do gem "benchmark-ips" end mutex = Mutex.new require "monitor" monitor = Monitor.new Benchmark.ips do |x| x.report("Mutex") { mutex.synchronize { } } x.report("Monitor") { monitor.synchronize { } } end ``` --- include/ruby/internal/core/rtypeddata.h | 45 ++++++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index 24e87e63f979f9..aaf8f7997c8459 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -507,19 +507,6 @@ RBIMPL_SYMBOL_EXPORT_END() sizeof(type)) #endif -/** - * Obtains a C struct from inside of a wrapper Ruby object. - * - * @param obj An instance of ::RTypedData. - * @param type Type name of the C struct. - * @param data_type The data type describing `type`. - * @param sval Variable name of obtained C struct. - * @exception rb_eTypeError `obj` is not a kind of `data_type`. - * @return Unwrapped C struct that `obj` holds. - */ -#define TypedData_Get_Struct(obj,type,data_type,sval) \ - ((sval) = RBIMPL_CAST((type *)rb_check_typeddata((obj), (data_type)))) - static inline bool RTYPEDDATA_EMBEDDED_P(VALUE obj) { @@ -614,6 +601,38 @@ RTYPEDDATA_TYPE(VALUE obj) return (const struct rb_data_type_struct *)(RTYPEDDATA(obj)->type & TYPED_DATA_PTR_MASK); } +RBIMPL_ATTR_PURE_UNLESS_DEBUG() +RBIMPL_ATTR_ARTIFICIAL() +/** + * @private + * + * This is an implementation detail of TypedData_Get_Struct(). Don't use it + * directly. + */ +static inline void * +rbimpl_check_typeddata(VALUE obj, const rb_data_type_t *type) +{ + if (RB_LIKELY(RB_TYPE_P(obj, T_DATA) && RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj) == type)) { + return RTYPEDDATA_GET_DATA(obj); + } + + return rb_check_typeddata(obj, type); +} + + +/** + * Obtains a C struct from inside of a wrapper Ruby object. + * + * @param obj An instance of ::RTypedData. + * @param type Type name of the C struct. + * @param data_type The data type describing `type`. + * @param sval Variable name of obtained C struct. + * @exception rb_eTypeError `obj` is not a kind of `data_type`. + * @return Unwrapped C struct that `obj` holds. + */ +#define TypedData_Get_Struct(obj,type,data_type,sval) \ + ((sval) = RBIMPL_CAST((type *)rbimpl_check_typeddata((obj), (data_type)))) + /** * While we don't stop you from using this function, it seems to be an * implementation detail of #TypedData_Make_Struct, which is preferred over From f9cd94f17d6fef49f1ee5cbb8f66839f0d7a5db9 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 1 Dec 2025 15:15:42 -0800 Subject: [PATCH 126/367] wb-protect autoload_const --- variable.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/variable.c b/variable.c index 9a7e11850dcef5..faeffec660a9f2 100644 --- a/variable.c +++ b/variable.c @@ -2734,7 +2734,7 @@ autoload_const_free(void *ptr) static const rb_data_type_t autoload_const_type = { "autoload_const", {autoload_const_mark_and_move, autoload_const_free, autoload_const_memsize, autoload_const_mark_and_move,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; static struct autoload_data * @@ -2778,12 +2778,12 @@ autoload_copy_table_for_box_i(st_data_t key, st_data_t value, st_data_t arg) struct autoload_data *autoload_data = rb_check_typeddata(autoload_data_value, &autoload_data_type); VALUE new_value = TypedData_Make_Struct(0, struct autoload_const, &autoload_const_type, autoload_const); - autoload_const->box_value = rb_get_box_object((rb_box_t *)box); - autoload_const->module = src_const->module; + RB_OBJ_WRITE(new_value, &autoload_const->box_value, rb_get_box_object((rb_box_t *)box)); + RB_OBJ_WRITE(new_value, &autoload_const->module, src_const->module); autoload_const->name = src_const->name; - autoload_const->value = src_const->value; + RB_OBJ_WRITE(new_value, &autoload_const->value, src_const->value); autoload_const->flag = src_const->flag; - autoload_const->autoload_data_value = autoload_data_value; + RB_OBJ_WRITE(new_value, &autoload_const->autoload_data_value, autoload_data_value); ccan_list_add_tail(&autoload_data->constants, &autoload_const->cnode); st_insert(tbl, (st_data_t)autoload_const->name, (st_data_t)new_value); @@ -2907,12 +2907,12 @@ autoload_synchronized(VALUE _arguments) { struct autoload_const *autoload_const; VALUE autoload_const_value = TypedData_Make_Struct(0, struct autoload_const, &autoload_const_type, autoload_const); - autoload_const->box_value = arguments->box_value; - autoload_const->module = arguments->module; + RB_OBJ_WRITE(autoload_const_value, &autoload_const->box_value, arguments->box_value); + RB_OBJ_WRITE(autoload_const_value, &autoload_const->module, arguments->module); autoload_const->name = arguments->name; autoload_const->value = Qundef; autoload_const->flag = CONST_PUBLIC; - autoload_const->autoload_data_value = autoload_data_value; + RB_OBJ_WRITE(autoload_const_value, &autoload_const->autoload_data_value, autoload_data_value); ccan_list_add_tail(&autoload_data->constants, &autoload_const->cnode); st_insert(autoload_table, (st_data_t)arguments->name, (st_data_t)autoload_const_value); RB_OBJ_WRITTEN(autoload_table_value, Qundef, autoload_const_value); @@ -3920,21 +3920,21 @@ rb_const_set(VALUE klass, ID id, VALUE val) const_added(klass, id); } -static struct autoload_data * -autoload_data_for_named_constant(VALUE module, ID name, struct autoload_const **autoload_const_pointer) +static VALUE +autoload_const_value_for_named_constant(VALUE module, ID name, struct autoload_const **autoload_const_pointer) { - VALUE autoload_data_value = autoload_data(module, name); - if (!autoload_data_value) return 0; + VALUE autoload_const_value = autoload_data(module, name); + if (!autoload_const_value) return Qfalse; - struct autoload_data *autoload_data = get_autoload_data(autoload_data_value, autoload_const_pointer); - if (!autoload_data) return 0; + struct autoload_data *autoload_data = get_autoload_data(autoload_const_value, autoload_const_pointer); + if (!autoload_data) return Qfalse; /* for autoloading thread, keep the defined value to autoloading storage */ if (autoload_by_current(autoload_data)) { - return autoload_data; + return autoload_const_value; } - return 0; + return Qfalse; } static void @@ -3954,13 +3954,13 @@ const_tbl_update(struct autoload_const *ac, int autoload_force) RUBY_ASSERT_CRITICAL_SECTION_ENTER(); VALUE file = ac->file; int line = ac->line; - struct autoload_data *ele = autoload_data_for_named_constant(klass, id, &ac); + VALUE autoload_const_value = autoload_const_value_for_named_constant(klass, id, &ac); - if (!autoload_force && ele) { + if (!autoload_force && autoload_const_value) { rb_clear_constant_cache_for_id(id); - ac->value = val; /* autoload_data is non-WB-protected */ - ac->file = rb_source_location(&ac->line); + RB_OBJ_WRITE(autoload_const_value, &ac->value, val); + RB_OBJ_WRITE(autoload_const_value, &ac->file, rb_source_location(&ac->line)); } else { /* otherwise autoloaded constant, allow to override */ @@ -4054,10 +4054,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, ce->flag &= ~mask; ce->flag |= flag; if (UNDEF_P(ce->value)) { - struct autoload_data *ele; - - ele = autoload_data_for_named_constant(mod, id, &ac); - if (ele) { + if (autoload_const_value_for_named_constant(mod, id, &ac)) { ac->flag &= ~mask; ac->flag |= flag; } From ed31a0caa88006afa507fd387e3f84ad8b8ddb00 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:05:13 +0100 Subject: [PATCH 127/367] [ruby/prism] Correctly handle line continuations in %w/i% interrupted by heredocs See https://bugs.ruby-lang.org/issues/21756. Ripper fails to parse this, but prism actually also doesn't handle it correctly. When heredocs are used, even in lowercase percent arays there can be multiple `STRING_CONTENT` tokens. We need to concat them. Luckily we don't need to handle as many cases as in uppercase arrays where interpolation is allowed. https://github.com/ruby/prism/commit/211677000e --- prism/prism.c | 71 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index cd4d166a124ef1..291d1d85218c1e 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -19299,18 +19299,52 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); pm_token_t opening = parser->previous; pm_array_node_t *array = pm_array_node_create(parser, &opening); + pm_node_t *current = NULL; while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { accept1(parser, PM_TOKEN_WORDS_SEP); if (match1(parser, PM_TOKEN_STRING_END)) break; - if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + // Interpolation is not possible but nested heredocs can still lead to + // consecutive (disjoint) string tokens when the final newline is escaped. + while (match1(parser, PM_TOKEN_STRING_CONTENT)) { pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); - pm_array_node_elements_append(array, UP(pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing))); + + // Record the string node, moving to interpolation if needed. + if (current == NULL) { + current = UP(pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing)); + parser_lex(parser); + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) { + pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); + parser_lex(parser); + pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string); + } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) { + pm_symbol_node_t *cast = (pm_symbol_node_t *) current; + pm_token_t bounds = not_provided(parser); + + pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = cast->value_loc.start, .end = cast->value_loc.end }; + pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped)); + pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing)); + parser_lex(parser); + + pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + pm_interpolated_symbol_node_append(interpolated, first_string); + pm_interpolated_symbol_node_append(interpolated, second_string); + + xfree(current); + current = UP(interpolated); + } else { + assert(false && "unreachable"); + } } - expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); + if (current) { + pm_array_node_elements_append(array, current); + current = NULL; + } else { + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + } } pm_token_t closing = parser->current; @@ -19489,23 +19523,42 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); pm_token_t opening = parser->previous; pm_array_node_t *array = pm_array_node_create(parser, &opening); - - // skip all leading whitespaces - accept1(parser, PM_TOKEN_WORDS_SEP); + pm_node_t *current = NULL; while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { accept1(parser, PM_TOKEN_WORDS_SEP); if (match1(parser, PM_TOKEN_STRING_END)) break; - if (match1(parser, PM_TOKEN_STRING_CONTENT)) { + // Interpolation is not possible but nested heredocs can still lead to + // consecutive (disjoint) string tokens when the final newline is escaped. + while (match1(parser, PM_TOKEN_STRING_CONTENT)) { pm_token_t opening = not_provided(parser); pm_token_t closing = not_provided(parser); pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing)); - pm_array_node_elements_append(array, string); + + // Record the string node, moving to interpolation if needed. + if (current == NULL) { + current = string; + } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) { + pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string); + } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) { + pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing); + pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(interpolated, string); + current = UP(interpolated); + } else { + assert(false && "unreachable"); + } + parser_lex(parser); } - expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + if (current) { + pm_array_node_elements_append(array, current); + current = NULL; + } else { + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + } } pm_token_t closing = parser->current; From d5c7cf0a1a1d2a72421b9a166e19442f89b99868 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:06:22 +0100 Subject: [PATCH 128/367] [ruby/prism] Fix wrong error message for lower percent i arrays Not so sure how to trigger it but this is definitly more correct. https://github.com/ruby/prism/commit/1bc8ec5e5d --- prism/prism.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index 291d1d85218c1e..02247734e2ace7 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -19343,7 +19343,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_array_node_elements_append(array, current); current = NULL; } else { - expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); + expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); } } From fd02356e36198b5bb0bb64f303a716a4ada9ed15 Mon Sep 17 00:00:00 2001 From: Goshanraj Govindaraj Date: Wed, 3 Dec 2025 13:37:43 -0500 Subject: [PATCH 129/367] ZJIT: Optimize NewArray to use rb_ec_ary_new_from_values (#15391) --- zjit/src/codegen.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index bb19d7d8209ff8..57ee65e91b754b 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -1451,15 +1451,16 @@ fn gen_new_array( ) -> lir::Opnd { gen_prepare_leaf_call_with_gc(asm, state); - let length: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long"); + let num: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long"); - let new_array = asm_ccall!(asm, rb_ary_new_capa, length.into()); - - for val in elements { - asm_ccall!(asm, rb_ary_push, new_array, val); + if elements.is_empty() { + asm_ccall!(asm, rb_ec_ary_new_from_values, EC, 0i64.into(), Opnd::UImm(0)) + } else { + let argv = gen_push_opnds(asm, &elements); + let new_array = asm_ccall!(asm, rb_ec_ary_new_from_values, EC, num.into(), argv); + gen_pop_opnds(asm, &elements); + new_array } - - new_array } /// Compile array access (`array[index]`) From 228d13f6ed914d1e7f6bd2416e3f5be8283be865 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 18:08:14 +0100 Subject: [PATCH 130/367] gc.c: Pass shape_id to `newobj_init` Attempt to fix the following SEGV: ``` ruby(gc_mark) ../src/gc/default/default.c:4429 ruby(gc_mark_children+0x45) [0x560b380bf8b5] ../src/gc/default/default.c:4625 ruby(gc_mark_stacked_objects) ../src/gc/default/default.c:4647 ruby(gc_mark_stacked_objects_all) ../src/gc/default/default.c:4685 ruby(gc_marks_rest) ../src/gc/default/default.c:5707 ruby(gc_marks+0x4e7) [0x560b380c41c1] ../src/gc/default/default.c:5821 ruby(gc_start) ../src/gc/default/default.c:6502 ruby(heap_prepare+0xa4) [0x560b380c4efc] ../src/gc/default/default.c:2074 ruby(heap_next_free_page) ../src/gc/default/default.c:2289 ruby(newobj_cache_miss) ../src/gc/default/default.c:2396 ruby(RB_SPECIAL_CONST_P+0x0) [0x560b380c5df4] ../src/gc/default/default.c:2420 ruby(RB_BUILTIN_TYPE) ../src/include/ruby/internal/value_type.h:184 ruby(newobj_init) ../src/gc/default/default.c:2136 ruby(rb_gc_impl_new_obj) ../src/gc/default/default.c:2500 ruby(newobj_of) ../src/gc.c:996 ruby(rb_imemo_new+0x37) [0x560b380d8bed] ../src/imemo.c:46 ruby(imemo_fields_new) ../src/imemo.c:105 ruby(rb_imemo_fields_new) ../src/imemo.c:120 ``` I have no reproduction, but my understanding based on the backtrace and error is that GC is triggered inside `newobj_init` causing the new object to be marked while in a incomplete state. I believe the fix is to pass the `shape_id` down to `newobj_init` so it can be set before the GC has a chance to trigger. --- gc.c | 5 ++--- gc/default/default.c | 33 ++++++++++++++++----------------- gc/gc_impl.h | 2 +- gc/mmtk/mmtk.c | 5 +++-- shape.h | 9 +++++++-- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/gc.c b/gc.c index 9ccaffdc0b371c..1020a461dce11f 100644 --- a/gc.c +++ b/gc.c @@ -622,7 +622,7 @@ typedef struct gc_function_map { void (*stress_set)(void *objspace_ptr, VALUE flag); VALUE (*stress_get)(void *objspace_ptr); // Object allocation - VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size); + VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t alloc_size); size_t (*obj_slot_size)(VALUE obj); size_t (*heap_id_for_size)(void *objspace_ptr, size_t size); bool (*size_allocatable_p)(size_t size); @@ -993,8 +993,7 @@ gc_validate_pc(VALUE obj) static inline VALUE newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t size) { - VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size); - RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); + VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, shape_id, wb_protected, size); gc_validate_pc(obj); diff --git a/gc/default/default.c b/gc/default/default.c index 54b93dc8d07cf6..2cacb2cfd9e26b 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -32,6 +32,7 @@ #include "darray.h" #include "gc/gc.h" #include "gc/gc_impl.h" +#include "shape.h" #ifndef BUILDING_MODULAR_GC # include "probes.h" @@ -2131,15 +2132,13 @@ rb_gc_impl_source_location_cstr(int *ptr) #endif static inline VALUE -newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, VALUE obj) +newobj_init(VALUE klass, VALUE flags, shape_id_t shape_id, int wb_protected, rb_objspace_t *objspace, VALUE obj) { GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); GC_ASSERT((flags & FL_WB_PROTECTED) == 0); RBASIC(obj)->flags = flags; *((VALUE *)&RBASIC(obj)->klass) = klass; -#if RBASIC_SHAPE_ID_FIELD - RBASIC(obj)->shape_id = 0; -#endif + RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); int t = flags & RUBY_T_MASK; if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) { @@ -2423,10 +2422,10 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t he return obj; } -ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx)); +ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx)); static inline VALUE -newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx) +newobj_slowpath(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx) { VALUE obj; unsigned int lev; @@ -2451,32 +2450,32 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_new } obj = newobj_alloc(objspace, cache, heap_idx, true); - newobj_init(klass, flags, wb_protected, objspace, obj); + newobj_init(klass, flags, shape_id, wb_protected, objspace, obj); } RB_GC_CR_UNLOCK(lev); return obj; } -NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, +NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx)); -NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, +NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx)); static VALUE -newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) +newobj_slowpath_wb_protected(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) { - return newobj_slowpath(klass, flags, objspace, cache, TRUE, heap_idx); + return newobj_slowpath(klass, flags, shape_id, objspace, cache, TRUE, heap_idx); } static VALUE -newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) +newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) { - return newobj_slowpath(klass, flags, objspace, cache, FALSE, heap_idx); + return newobj_slowpath(klass, flags, shape_id, objspace, cache, FALSE, heap_idx); } VALUE -rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size) +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t alloc_size) { VALUE obj; rb_objspace_t *objspace = objspace_ptr; @@ -2497,14 +2496,14 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags if (!RB_UNLIKELY(during_gc || ruby_gc_stressful) && wb_protected) { obj = newobj_alloc(objspace, cache, heap_idx, false); - newobj_init(klass, flags, wb_protected, objspace, obj); + newobj_init(klass, flags, shape_id, wb_protected, objspace, obj); } else { RB_DEBUG_COUNTER_INC(obj_newobj_slowpath); obj = wb_protected ? - newobj_slowpath_wb_protected(klass, flags, objspace, cache, heap_idx) : - newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, heap_idx); + newobj_slowpath_wb_protected(klass, flags, shape_id, objspace, cache, heap_idx) : + newobj_slowpath_wb_unprotected(klass, flags, shape_id, objspace, cache, heap_idx); } return obj; diff --git a/gc/gc_impl.h b/gc/gc_impl.h index 3250483639e775..65c0586d46bfad 100644 --- a/gc/gc_impl.h +++ b/gc/gc_impl.h @@ -55,7 +55,7 @@ GC_IMPL_FN VALUE rb_gc_impl_stress_get(void *objspace_ptr); GC_IMPL_FN VALUE rb_gc_impl_config_get(void *objspace_ptr); GC_IMPL_FN void rb_gc_impl_config_set(void *objspace_ptr, VALUE hash); // Object allocation -GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size); +GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, uint32_t /* shape_id_t */ shape_id, bool wb_protected, size_t alloc_size); GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj); GC_IMPL_FN size_t rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size); GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size); diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index e1678dcf6ab0b4..a3bdf1cd4a9a42 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -7,6 +7,7 @@ #include "gc/gc.h" #include "gc/gc_impl.h" +#include "shape.h" #include "gc/mmtk/mmtk.h" #include "ccan/list/list.h" @@ -603,7 +604,7 @@ rb_gc_impl_config_set(void *objspace_ptr, VALUE hash) // Object allocation VALUE -rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size) +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t alloc_size) { #define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0 struct objspace *objspace = objspace_ptr; @@ -625,7 +626,7 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags VALUE *alloc_obj = mmtk_alloc(ractor_cache->mutator, alloc_size + 8, MMTk_MIN_OBJ_ALIGN, 0, MMTK_ALLOCATION_SEMANTICS_DEFAULT); alloc_obj++; alloc_obj[-1] = alloc_size; - alloc_obj[0] = flags; + alloc_obj[0] = RSHAPE_COMBINE_IN_FLAGS(flags, shape_id);; alloc_obj[1] = klass; mmtk_post_alloc(ractor_cache->mutator, (void*)alloc_obj, alloc_size + 8, MMTK_ALLOCATION_SEMANTICS_DEFAULT); diff --git a/shape.h b/shape.h index 9478d4b3a95dc6..28c2a7eef9421a 100644 --- a/shape.h +++ b/shape.h @@ -162,6 +162,12 @@ RBASIC_SHAPE_ID_FOR_READ(VALUE obj) bool rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id); #endif +static inline VALUE +RSHAPE_COMBINE_IN_FLAGS(VALUE flags, shape_id_t shape_id) +{ + return (flags &SHAPE_FLAG_MASK) | (((VALUE)shape_id) << SHAPE_FLAG_SHIFT); +} + static inline void RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) { @@ -169,8 +175,7 @@ RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) RBASIC(obj)->shape_id = (VALUE)shape_id; #else // Object shapes are occupying top bits - RBASIC(obj)->flags &= SHAPE_FLAG_MASK; - RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); + RBASIC(obj)->flags = RSHAPE_COMBINE_IN_FLAGS(RBASIC(obj)->flags, shape_id); #endif } From 8d1a6bc48b1a45cacd8b1f1c42f71d5967a27bba Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 20:40:22 +0100 Subject: [PATCH 131/367] gc.c: check if the struct has fields before marking the fields_obj If GC trigger in the middle of `struct_alloc`, and the struct has more than 3 elements, then `fields_obj` reference is garbage. We must first check the shape to know if it was actually initialized. --- gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gc.c b/gc.c index 1020a461dce11f..7b4547e2ea0bca 100644 --- a/gc.c +++ b/gc.c @@ -3303,7 +3303,7 @@ rb_gc_mark_children(void *objspace, VALUE obj) gc_mark_internal(ptr[i]); } - if (!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) { + if (rb_shape_obj_has_fields(obj) && !FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) { gc_mark_internal(RSTRUCT_FIELDS_OBJ(obj)); } From 9913d8da1ffcd00e8c648ac52abefdc086662797 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 1 Dec 2025 23:19:14 -0800 Subject: [PATCH 132/367] Group malloc counters together --- gc/default/default.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/gc/default/default.c b/gc/default/default.c index 2cacb2cfd9e26b..1d50172739fdfd 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -468,8 +468,14 @@ enum gc_mode { typedef struct rb_objspace { struct { - size_t limit; size_t increase; +#if RGENGC_ESTIMATE_OLDMALLOC + size_t oldmalloc_increase; +#endif + } malloc_counters; + + struct { + size_t limit; #if MALLOC_ALLOCATED_SIZE size_t allocated_size; size_t allocations; @@ -590,7 +596,6 @@ typedef struct rb_objspace { size_t old_objects_limit; #if RGENGC_ESTIMATE_OLDMALLOC - size_t oldmalloc_increase; size_t oldmalloc_increase_limit; #endif @@ -864,7 +869,7 @@ RVALUE_AGE_SET(VALUE obj, int age) } #define malloc_limit objspace->malloc_params.limit -#define malloc_increase objspace->malloc_params.increase +#define malloc_increase objspace->malloc_counters.increase #define malloc_allocated_size objspace->malloc_params.allocated_size #define heap_pages_lomem objspace->heap_pages.range[0] #define heap_pages_himem objspace->heap_pages.range[1] @@ -4910,7 +4915,7 @@ gc_marks_check(rb_objspace_t *objspace, st_foreach_callback_func *checker_func, { size_t saved_malloc_increase = objspace->malloc_params.increase; #if RGENGC_ESTIMATE_OLDMALLOC - size_t saved_oldmalloc_increase = objspace->rgengc.oldmalloc_increase; + size_t saved_oldmalloc_increase = objspace->malloc_counters.oldmalloc_increase; #endif VALUE already_disabled = rb_objspace_gc_disable(objspace); @@ -4933,7 +4938,7 @@ gc_marks_check(rb_objspace_t *objspace, st_foreach_callback_func *checker_func, if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace); objspace->malloc_params.increase = saved_malloc_increase; #if RGENGC_ESTIMATE_OLDMALLOC - objspace->rgengc.oldmalloc_increase = saved_oldmalloc_increase; + objspace->malloc_counters.oldmalloc_increase = saved_oldmalloc_increase; #endif } #endif /* RGENGC_CHECK_MODE >= 4 */ @@ -6331,7 +6336,7 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) /* reset oldmalloc info */ #if RGENGC_ESTIMATE_OLDMALLOC if (!full_mark) { - if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) { + if (objspace->malloc_counters.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) { gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_OLDMALLOC; objspace->rgengc.oldmalloc_increase_limit = (size_t)(objspace->rgengc.oldmalloc_increase_limit * gc_params.oldmalloc_limit_growth_factor); @@ -6344,13 +6349,13 @@ gc_reset_malloc_info(rb_objspace_t *objspace, bool full_mark) if (0) fprintf(stderr, "%"PRIdSIZE"\t%d\t%"PRIuSIZE"\t%"PRIuSIZE"\t%"PRIdSIZE"\n", rb_gc_count(), gc_needs_major_flags, - objspace->rgengc.oldmalloc_increase, + objspace->malloc_counters.oldmalloc_increase, objspace->rgengc.oldmalloc_increase_limit, gc_params.oldmalloc_limit_max); } else { /* major GC */ - objspace->rgengc.oldmalloc_increase = 0; + objspace->malloc_counters.oldmalloc_increase = 0; if ((objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_BY_OLDMALLOC) == 0) { objspace->rgengc.oldmalloc_increase_limit = @@ -7583,7 +7588,7 @@ rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym) SET(old_objects, objspace->rgengc.old_objects); SET(old_objects_limit, objspace->rgengc.old_objects_limit); #if RGENGC_ESTIMATE_OLDMALLOC - SET(oldmalloc_increase_bytes, objspace->rgengc.oldmalloc_increase); + SET(oldmalloc_increase_bytes, objspace->malloc_counters.oldmalloc_increase); SET(oldmalloc_increase_bytes_limit, objspace->rgengc.oldmalloc_increase_limit); #endif @@ -8040,13 +8045,13 @@ objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_siz if (new_size > old_size) { RUBY_ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size); #if RGENGC_ESTIMATE_OLDMALLOC - RUBY_ATOMIC_SIZE_ADD(objspace->rgengc.oldmalloc_increase, new_size - old_size); + RUBY_ATOMIC_SIZE_ADD(objspace->malloc_counters.oldmalloc_increase, new_size - old_size); #endif } else { atomic_sub_nounderflow(&malloc_increase, old_size - new_size); #if RGENGC_ESTIMATE_OLDMALLOC - atomic_sub_nounderflow(&objspace->rgengc.oldmalloc_increase, old_size - new_size); + atomic_sub_nounderflow(&objspace->malloc_counters.oldmalloc_increase, old_size - new_size); #endif } From a773bbf0cc35cd4b73509edd58a0757d06abaca6 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Tue, 2 Dec 2025 09:46:10 -0800 Subject: [PATCH 133/367] Track small malloc/free changes in thread local --- gc/default/default.c | 76 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/gc/default/default.c b/gc/default/default.c index 1d50172739fdfd..f8fd40b44fd8b4 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -159,6 +159,17 @@ #define GC_OLDMALLOC_LIMIT_MAX (128 * 1024 * 1024 /* 128MB */) #endif +#ifndef GC_MALLOC_INCREASE_LOCAL_THRESHOLD +#define GC_MALLOC_INCREASE_LOCAL_THRESHOLD (8 * 1024 /* 8KB */) +#endif + +#ifdef RB_THREAD_LOCAL_SPECIFIER +#define USE_MALLOC_INCREASE_LOCAL 1 +static RB_THREAD_LOCAL_SPECIFIER int malloc_increase_local; +#else +#define USE_MALLOC_INCREASE_LOCAL 0 +#endif + #ifndef GC_CAN_COMPILE_COMPACTION #if defined(__wasi__) /* WebAssembly doesn't support signals */ # define GC_CAN_COMPILE_COMPACTION 0 @@ -7531,6 +7542,8 @@ ns_to_ms(uint64_t ns) return ns / (1000 * 1000); } +static void malloc_increase_local_flush(rb_objspace_t *objspace); + VALUE rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym) { @@ -7540,6 +7553,7 @@ rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym) setup_gc_stat_symbols(); ractor_cache_flush_count(objspace, rb_gc_get_ractor_newobj_cache()); + malloc_increase_local_flush(objspace); if (RB_TYPE_P(hash_or_sym, T_HASH)) { hash = hash_or_sym; @@ -8027,6 +8041,45 @@ objspace_malloc_gc_stress(rb_objspace_t *objspace) } } +static void +malloc_increase_commit(rb_objspace_t *objspace, size_t new_size, size_t old_size) +{ + if (new_size > old_size) { + RUBY_ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size); +#if RGENGC_ESTIMATE_OLDMALLOC + RUBY_ATOMIC_SIZE_ADD(objspace->malloc_counters.oldmalloc_increase, new_size - old_size); +#endif + } + else { + atomic_sub_nounderflow(&malloc_increase, old_size - new_size); +#if RGENGC_ESTIMATE_OLDMALLOC + atomic_sub_nounderflow(&objspace->malloc_counters.oldmalloc_increase, old_size - new_size); +#endif + } +} + +#if USE_MALLOC_INCREASE_LOCAL +static void +malloc_increase_local_flush(rb_objspace_t *objspace) +{ + int delta = malloc_increase_local; + if (delta == 0) return; + + malloc_increase_local = 0; + if (delta > 0) { + malloc_increase_commit(objspace, (size_t)delta, 0); + } + else { + malloc_increase_commit(objspace, 0, (size_t)(-delta)); + } +} +#else +static void +malloc_increase_local_flush(rb_objspace_t *objspace) +{ +} +#endif + static inline bool objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type, bool gc_allowed) { @@ -8042,18 +8095,23 @@ objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_s static bool objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type, bool gc_allowed) { - if (new_size > old_size) { - RUBY_ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size); -#if RGENGC_ESTIMATE_OLDMALLOC - RUBY_ATOMIC_SIZE_ADD(objspace->malloc_counters.oldmalloc_increase, new_size - old_size); -#endif +#if USE_MALLOC_INCREASE_LOCAL + if (new_size < GC_MALLOC_INCREASE_LOCAL_THRESHOLD && + old_size < GC_MALLOC_INCREASE_LOCAL_THRESHOLD) { + malloc_increase_local += (int)new_size - (int)old_size; + + if (malloc_increase_local >= GC_MALLOC_INCREASE_LOCAL_THRESHOLD || + malloc_increase_local <= -GC_MALLOC_INCREASE_LOCAL_THRESHOLD) { + malloc_increase_local_flush(objspace); + } } else { - atomic_sub_nounderflow(&malloc_increase, old_size - new_size); -#if RGENGC_ESTIMATE_OLDMALLOC - atomic_sub_nounderflow(&objspace->malloc_counters.oldmalloc_increase, old_size - new_size); -#endif + malloc_increase_local_flush(objspace); + malloc_increase_commit(objspace, new_size, old_size); } +#else + malloc_increase_commit(objspace, new_size, old_size); +#endif if (type == MEMOP_TYPE_MALLOC && gc_allowed) { retry: From 2b23b05bf2c0f30f2c4ee9bb3030fa58f2cba3a6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 24 Nov 2025 16:16:08 -0800 Subject: [PATCH 134/367] ZJIT: Add a specialized instruction iterator to the assembler This commit adds a specialized instruction iterator to the assembler with a custom "peek" method. The reason is that we want to add basic blocks to LIR. When we split instructions, we need to add any new instructions to the correct basic block. The custom iterator will maintain the correct basic block inside the assembler, that way when we push any new instructions they will be appended to the correct place. --- zjit/src/backend/lir.rs | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index 3c9bf72023d90b..d7c6740d13a1e4 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -1399,6 +1399,15 @@ impl Assembler } } + pub fn instruction_iterator(&mut self) -> InsnIter { + let insns = take(&mut self.insns); + InsnIter { + old_insns_iter: insns.into_iter(), + peeked: None, + index: 0, + } + } + pub fn expect_leaf_ccall(&mut self, stack_size: usize) { self.leaf_ccall_stack_size = Some(stack_size); } @@ -1998,6 +2007,44 @@ impl fmt::Debug for Assembler { } } +pub struct InsnIter { + old_insns_iter: std::vec::IntoIter, + peeked: Option<(usize, Insn)>, + index: usize, +} + +impl InsnIter { + // We're implementing our own peek() because we don't want peek to + // cross basic blocks as we're iterating. + pub fn peek(&mut self) -> Option<&(usize, Insn)> { + // If we don't have a peeked value, get one + if self.peeked.is_none() { + let insn = self.old_insns_iter.next()?; + let idx = self.index; + self.index += 1; + self.peeked = Some((idx, insn)); + } + // Return a reference to the peeked value + self.peeked.as_ref() + } + + // Get the next instruction. Right now we're passing the "new" assembler + // (the assembler we're copying in to) as a parameter. Once we've + // introduced basic blocks to LIR, we'll use the to set the correct BB + // on the new assembler, but for now it is unused. + pub fn next(&mut self, _new_asm: &mut Assembler) -> Option<(usize, Insn)> { + // If we have a peeked value, return it + if let Some(item) = self.peeked.take() { + return Some(item); + } + // Otherwise get the next from underlying iterator + let insn = self.old_insns_iter.next()?; + let idx = self.index; + self.index += 1; + Some((idx, insn)) + } +} + impl Assembler { #[must_use] pub fn add(&mut self, left: Opnd, right: Opnd) -> Opnd { From d7e55f84f2bd62d302b29513d4c4dc8ae9aef96f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 25 Nov 2025 16:51:54 -0800 Subject: [PATCH 135/367] ZJIT: Use the custom iterator This commit uses the custom instruction iterator in arm64 / x86_64 instruction splitting. Once we introduce basic blocks to LIR, the custom iterator will ensure that instructions are added to the correct place. --- zjit/src/backend/arm64/mod.rs | 25 +++++++++++++------------ zjit/src/backend/x86_64/mod.rs | 21 +++++++++++---------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs index 013fd315833409..78fb69b0b04d5b 100644 --- a/zjit/src/backend/arm64/mod.rs +++ b/zjit/src/backend/arm64/mod.rs @@ -391,10 +391,10 @@ impl Assembler { let mut asm_local = Assembler::new_with_asm(&self); let live_ranges: Vec = take(&mut self.live_ranges); - let mut iterator = self.insns.into_iter().enumerate().peekable(); + let mut iterator = self.instruction_iterator(); let asm = &mut asm_local; - while let Some((index, mut insn)) = iterator.next() { + while let Some((index, mut insn)) = iterator.next(asm) { // Here we're going to map the operands of the instruction to load // any Opnd::Value operands into registers if they are heap objects // such that only the Op::Load instruction needs to handle that @@ -428,13 +428,13 @@ impl Assembler { *right = split_shifted_immediate(asm, other_opnd); // Now `right` is either a register or an immediate, both can try to // merge with a subsequent mov. - merge_three_reg_mov(&live_ranges, &mut iterator, left, left, out); + merge_three_reg_mov(&live_ranges, &mut iterator, asm, left, left, out); asm.push_insn(insn); } _ => { *left = split_load_operand(asm, *left); *right = split_shifted_immediate(asm, *right); - merge_three_reg_mov(&live_ranges, &mut iterator, left, right, out); + merge_three_reg_mov(&live_ranges, &mut iterator, asm, left, right, out); asm.push_insn(insn); } } @@ -444,7 +444,7 @@ impl Assembler { *right = split_shifted_immediate(asm, *right); // Now `right` is either a register or an immediate, // both can try to merge with a subsequent mov. - merge_three_reg_mov(&live_ranges, &mut iterator, left, left, out); + merge_three_reg_mov(&live_ranges, &mut iterator, asm, left, left, out); asm.push_insn(insn); } Insn::And { left, right, out } | @@ -454,7 +454,7 @@ impl Assembler { *left = opnd0; *right = opnd1; - merge_three_reg_mov(&live_ranges, &mut iterator, left, right, out); + merge_three_reg_mov(&live_ranges, &mut iterator, asm, left, right, out); asm.push_insn(insn); } @@ -567,7 +567,7 @@ impl Assembler { if matches!(out, Opnd::VReg { .. }) && *out == *src && live_ranges[out.vreg_idx()].end() == index + 1 => { *out = Opnd::Reg(*reg); asm.push_insn(insn); - iterator.next(); // Pop merged Insn::Mov + iterator.next(asm); // Pop merged Insn::Mov } _ => { asm.push_insn(insn); @@ -694,7 +694,7 @@ impl Assembler { /// VRegs, most splits should happen in [`Self::arm64_split`]. However, some instructions /// need to be split with registers after `alloc_regs`, e.g. for `compile_exits`, so this /// splits them and uses scratch registers for it. - fn arm64_scratch_split(self) -> Assembler { + fn arm64_scratch_split(mut self) -> Assembler { /// If opnd is Opnd::Mem with a too large disp, make the disp smaller using lea. fn split_large_disp(asm: &mut Assembler, opnd: Opnd, scratch_opnd: Opnd) -> Opnd { match opnd { @@ -753,9 +753,9 @@ impl Assembler { let mut asm_local = Assembler::new_with_asm(&self); let asm = &mut asm_local; asm.accept_scratch_reg = true; - let mut iterator = self.insns.into_iter().enumerate().peekable(); + let iterator = &mut self.instruction_iterator(); - while let Some((_, mut insn)) = iterator.next() { + while let Some((_, mut insn)) = iterator.next(asm) { match &mut insn { Insn::Add { left, right, out } | Insn::Sub { left, right, out } | @@ -1660,7 +1660,8 @@ impl Assembler { /// If a, b, and c are all registers. fn merge_three_reg_mov( live_ranges: &[LiveRange], - iterator: &mut std::iter::Peekable>, + iterator: &mut InsnIter, + asm: &mut Assembler, left: &Opnd, right: &Opnd, out: &mut Opnd, @@ -1671,7 +1672,7 @@ fn merge_three_reg_mov( = (left, right, iterator.peek()) { if out == src && live_ranges[out.vreg_idx()].end() == *mov_idx && matches!(*dest, Opnd::Reg(_) | Opnd::VReg{..}) { *out = *dest; - iterator.next(); // Pop merged Insn::Mov + iterator.next(asm); // Pop merged Insn::Mov } } } diff --git a/zjit/src/backend/x86_64/mod.rs b/zjit/src/backend/x86_64/mod.rs index a9bd57f368c59b..5e975e1bd03ccf 100644 --- a/zjit/src/backend/x86_64/mod.rs +++ b/zjit/src/backend/x86_64/mod.rs @@ -138,11 +138,12 @@ impl Assembler { /// Split IR instructions for the x86 platform fn x86_split(mut self) -> Assembler { - let mut asm = Assembler::new_with_asm(&self); + let mut asm_local = Assembler::new_with_asm(&self); + let asm = &mut asm_local; let live_ranges: Vec = take(&mut self.live_ranges); - let mut iterator = self.insns.into_iter().enumerate().peekable(); + let mut iterator = self.instruction_iterator(); - while let Some((index, mut insn)) = iterator.next() { + while let Some((index, mut insn)) = iterator.next(asm) { let is_load = matches!(insn, Insn::Load { .. } | Insn::LoadInto { .. }); let mut opnd_iter = insn.opnd_iter_mut(); @@ -187,13 +188,13 @@ impl Assembler { if out == src && left == dest && live_ranges[out.vreg_idx()].end() == index + 1 && uimm_num_bits(*value) <= 32 => { *out = *dest; asm.push_insn(insn); - iterator.next(); // Pop merged Insn::Mov + iterator.next(asm); // Pop merged Insn::Mov } (Opnd::Reg(_), Opnd::Reg(_), Some(Insn::Mov { dest, src })) if out == src && live_ranges[out.vreg_idx()].end() == index + 1 && *dest == *left => { *out = *dest; asm.push_insn(insn); - iterator.next(); // Pop merged Insn::Mov + iterator.next(asm); // Pop merged Insn::Mov } _ => { match (*left, *right) { @@ -373,7 +374,7 @@ impl Assembler { (Insn::Lea { opnd, out }, Some(Insn::Mov { dest: Opnd::Reg(reg), src })) if matches!(out, Opnd::VReg { .. }) && out == src && live_ranges[out.vreg_idx()].end() == index + 1 => { asm.push_insn(Insn::Lea { opnd: *opnd, out: Opnd::Reg(*reg) }); - iterator.next(); // Pop merged Insn::Mov + iterator.next(asm); // Pop merged Insn::Mov } _ => asm.push_insn(insn), } @@ -384,14 +385,14 @@ impl Assembler { } } - asm + asm_local } /// Split instructions using scratch registers. To maximize the use of the register pool /// for VRegs, most splits should happen in [`Self::x86_split`]. However, some instructions /// need to be split with registers after `alloc_regs`, e.g. for `compile_exits`, so /// this splits them and uses scratch registers for it. - pub fn x86_scratch_split(self) -> Assembler { + pub fn x86_scratch_split(mut self) -> Assembler { /// For some instructions, we want to be able to lower a 64-bit operand /// without requiring more registers to be available in the register /// allocator. So we just use the SCRATCH0_OPND register temporarily to hold @@ -470,9 +471,9 @@ impl Assembler { let mut asm_local = Assembler::new_with_asm(&self); let asm = &mut asm_local; asm.accept_scratch_reg = true; - let mut iterator = self.insns.into_iter().enumerate().peekable(); + let mut iterator = self.instruction_iterator(); - while let Some((_, mut insn)) = iterator.next() { + while let Some((_, mut insn)) = iterator.next(asm) { match &mut insn { Insn::Add { left, right, out } | Insn::Sub { left, right, out } | From 612a66805f35b2c732ec843d78f1dcad5c6456cd Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 16:02:31 -0500 Subject: [PATCH 136/367] Remove spurious obj != klass check in shape_get_next This should never be true. I added an `rb_bug` in case it was and it wasn't true in any of btest or test-all. Co-authored-by: Aaron Patterson --- shape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shape.c b/shape.c index 754be1cfd64e65..c6f5af2519323e 100644 --- a/shape.c +++ b/shape.c @@ -839,7 +839,7 @@ shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE obj, ID id, } // Check if we should update max_iv_count on the object's class - if (obj != klass && new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass)) { + if (new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass)) { RCLASS_SET_MAX_IV_COUNT(klass, new_shape->next_field_index); } From f1670733249fb30d755bad1f88c0e54b26bdf49e Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 16:06:41 -0500 Subject: [PATCH 137/367] Move imemo fields check out of shape_get_next Not every caller (for example, YJIT) actually needs to pass the object. YJIT (and, in the future, ZJIT) only need to pass the class. --- shape.c | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/shape.c b/shape.c index c6f5af2519323e..13d1edf36c928a 100644 --- a/shape.c +++ b/shape.c @@ -801,7 +801,7 @@ shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value) } static inline rb_shape_t * -shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE obj, ID id, bool emit_warnings) +shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE klass, ID id, bool emit_warnings) { RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id)))); @@ -812,23 +812,6 @@ shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE obj, ID id, } #endif - VALUE klass; - if (IMEMO_TYPE_P(obj, imemo_fields)) { - VALUE owner = rb_imemo_fields_owner(obj); - switch (BUILTIN_TYPE(owner)) { - case T_CLASS: - case T_MODULE: - klass = rb_singleton_class(owner); - break; - default: - klass = rb_obj_class(owner); - break; - } - } - else { - klass = rb_obj_class(obj); - } - bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS; bool variation_created = false; rb_shape_t *new_shape = get_next_shape_internal(shape, id, shape_type, &variation_created, allow_new_shape); @@ -862,6 +845,28 @@ shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE obj, ID id, return new_shape; } +static VALUE +obj_get_owner_class(VALUE obj) +{ + VALUE klass; + if (IMEMO_TYPE_P(obj, imemo_fields)) { + VALUE owner = rb_imemo_fields_owner(obj); + switch (BUILTIN_TYPE(owner)) { + case T_CLASS: + case T_MODULE: + klass = rb_singleton_class(owner); + break; + default: + klass = rb_obj_class(owner); + break; + } + } + else { + klass = rb_obj_class(obj); + } + return klass; +} + static rb_shape_t * remove_shape_recursive(VALUE obj, rb_shape_t *shape, ID id, rb_shape_t **removed_shape) { @@ -884,7 +889,8 @@ remove_shape_recursive(VALUE obj, rb_shape_t *shape, ID id, rb_shape_t **removed // We found a new parent. Create a child of the new parent that // has the same attributes as this shape. if (new_parent) { - rb_shape_t *new_child = shape_get_next(new_parent, shape->type, obj, shape->edge_name, true); + VALUE klass = obj_get_owner_class(obj); + rb_shape_t *new_child = shape_get_next(new_parent, shape->type, klass, shape->edge_name, true); RUBY_ASSERT(!new_child || new_child->capacity <= shape->capacity); return new_child; } @@ -933,7 +939,8 @@ rb_shape_transition_add_ivar(VALUE obj, ID id) shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj); RUBY_ASSERT(!shape_frozen_p(original_shape_id)); - rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, obj, id, true); + VALUE klass = obj_get_owner_class(obj); + rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass, id, true); if (next_shape) { return shape_id(next_shape, original_shape_id); } @@ -948,7 +955,8 @@ rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id) shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj); RUBY_ASSERT(!shape_frozen_p(original_shape_id)); - rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, obj, id, false); + VALUE klass = obj_get_owner_class(obj); + rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass, id, false); if (next_shape) { return shape_id(next_shape, original_shape_id); } From b43e66d3b37d4bd029a90dbee376e475aed79d2a Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 16:11:30 -0500 Subject: [PATCH 138/367] YJIT: Pass class and shape ID directly instead of object --- shape.c | 4 +--- shape.h | 2 +- yjit/src/codegen.rs | 4 +++- yjit/src/cruby_bindings.inc.rs | 6 +++++- zjit/src/cruby_bindings.inc.rs | 6 +++++- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/shape.c b/shape.c index 13d1edf36c928a..90036722f10026 100644 --- a/shape.c +++ b/shape.c @@ -950,12 +950,10 @@ rb_shape_transition_add_ivar(VALUE obj, ID id) } shape_id_t -rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id) +rb_shape_transition_add_ivar_no_warnings(VALUE klass, shape_id_t original_shape_id, ID id) { - shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj); RUBY_ASSERT(!shape_frozen_p(original_shape_id)); - VALUE klass = obj_get_owner_class(obj); rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass, id, false); if (next_shape) { return shape_id(next_shape, original_shape_id); diff --git a/shape.h b/shape.h index 28c2a7eef9421a..bebfaba608c7fa 100644 --- a/shape.h +++ b/shape.h @@ -230,7 +230,7 @@ shape_id_t rb_shape_transition_frozen(VALUE obj); shape_id_t rb_shape_transition_complex(VALUE obj); shape_id_t rb_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_id); shape_id_t rb_shape_transition_add_ivar(VALUE obj, ID id); -shape_id_t rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id); +shape_id_t rb_shape_transition_add_ivar_no_warnings(VALUE klass, shape_id_t original_shape_id, ID id); shape_id_t rb_shape_transition_object_id(VALUE obj); shape_id_t rb_shape_transition_heap(VALUE obj, size_t heap_index); shape_id_t rb_shape_object_id(shape_id_t original_shape_id); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 50762c64d3eceb..865f80ca4f0cab 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3101,7 +3101,9 @@ fn gen_set_ivar( let mut new_shape_too_complex = false; let new_shape = if !shape_too_complex && receiver_t_object && ivar_index.is_none() { let current_shape_id = comptime_receiver.shape_id_of(); - let next_shape_id = unsafe { rb_shape_transition_add_ivar_no_warnings(comptime_receiver, ivar_name) }; + // We don't need to check about imemo_fields here because we're definitely looking at a T_OBJECT. + let klass = unsafe { rb_obj_class(comptime_receiver) }; + let next_shape_id = unsafe { rb_shape_transition_add_ivar_no_warnings(klass, current_shape_id, ivar_name) }; // If the VM ran out of shapes, or this class generated too many leaf, // it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table). diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 6efef9c55c39c4..b9a8197184a21a 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1064,7 +1064,11 @@ extern "C" { pub fn rb_shape_id_offset() -> i32; pub fn rb_obj_shape_id(obj: VALUE) -> shape_id_t; pub fn rb_shape_get_iv_index(shape_id: shape_id_t, id: ID, value: *mut attr_index_t) -> bool; - pub fn rb_shape_transition_add_ivar_no_warnings(obj: VALUE, id: ID) -> shape_id_t; + pub fn rb_shape_transition_add_ivar_no_warnings( + klass: VALUE, + original_shape_id: shape_id_t, + id: ID, + ) -> shape_id_t; pub fn rb_ivar_get_at(obj: VALUE, index: attr_index_t, id: ID) -> VALUE; pub fn rb_ivar_get_at_no_ractor_check(obj: VALUE, index: attr_index_t) -> VALUE; pub fn rb_gvar_get(arg1: ID) -> VALUE; diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 7bd392563987a3..0048fd6daece27 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -2033,7 +2033,11 @@ unsafe extern "C" { pub fn rb_shape_id_offset() -> i32; pub fn rb_obj_shape_id(obj: VALUE) -> shape_id_t; pub fn rb_shape_get_iv_index(shape_id: shape_id_t, id: ID, value: *mut attr_index_t) -> bool; - pub fn rb_shape_transition_add_ivar_no_warnings(obj: VALUE, id: ID) -> shape_id_t; + pub fn rb_shape_transition_add_ivar_no_warnings( + klass: VALUE, + original_shape_id: shape_id_t, + id: ID, + ) -> shape_id_t; pub fn rb_ivar_get_at_no_ractor_check(obj: VALUE, index: attr_index_t) -> VALUE; pub fn rb_gvar_get(arg1: ID) -> VALUE; pub fn rb_gvar_set(arg1: ID, arg2: VALUE) -> VALUE; From b79ef73a3bb911c8d5ac9016092eab08b146fb3a Mon Sep 17 00:00:00 2001 From: yui-knk Date: Wed, 3 Dec 2025 11:27:06 +0900 Subject: [PATCH 139/367] Remove needless parse.y `value_expr` macro In the past parse.y and ripper had different `value_expr` definition so that `value_expr` does nothing for ripper. ```c // parse.y #define value_expr(node) value_expr_gen(p, (node)) // ripper #define value_expr(node) ((void)(node)) ``` However Rearchitect Ripper (89cfc1520717257073012ec07105c551e4b8af7c) removed `value_expr` definition for ripper then this commit removes needless parse.y macro and uses `value_expr_gen` directly. --- parse.y | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/parse.y b/parse.y index a9fccdd5f721e0..e65159257632c0 100644 --- a/parse.y +++ b/parse.y @@ -1402,10 +1402,9 @@ static NODE *logop(struct parser_params*,ID,NODE*,NODE*,const YYLTYPE*,const YYL static NODE *newline_node(NODE*); static void fixpos(NODE*,NODE*); -static int value_expr_gen(struct parser_params*,NODE*); +static int value_expr(struct parser_params*,NODE*); static void void_expr(struct parser_params*,NODE*); static NODE *remove_begin(NODE*); -#define value_expr(node) value_expr_gen(p, (node)) static NODE *void_stmts(struct parser_params*,NODE*); static void reduce_nodes(struct parser_params*,NODE**); static void block_dup_check(struct parser_params*,NODE*,NODE*); @@ -3103,39 +3102,39 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) %rule range_expr(range) : range tDOT2 range { - value_expr($1); - value_expr($3); + value_expr(p, $1); + value_expr(p, $3); $$ = NEW_DOT2($1, $3, &@$, &@2); /*% ripper: dot2!($:1, $:3) %*/ } | range tDOT3 range { - value_expr($1); - value_expr($3); + value_expr(p, $1); + value_expr(p, $3); $$ = NEW_DOT3($1, $3, &@$, &@2); /*% ripper: dot3!($:1, $:3) %*/ } | range tDOT2 { - value_expr($1); + value_expr(p, $1); $$ = NEW_DOT2($1, new_nil_at(p, &@2.end_pos), &@$, &@2); /*% ripper: dot2!($:1, Qnil) %*/ } | range tDOT3 { - value_expr($1); + value_expr(p, $1); $$ = NEW_DOT3($1, new_nil_at(p, &@2.end_pos), &@$, &@2); /*% ripper: dot3!($:1, Qnil) %*/ } | tBDOT2 range { - value_expr($2); + value_expr(p, $2); $$ = NEW_DOT2(new_nil_at(p, &@1.beg_pos), $2, &@$, &@1); /*% ripper: dot2!(Qnil, $:2) %*/ } | tBDOT3 range { - value_expr($2); + value_expr(p, $2); $$ = NEW_DOT3(new_nil_at(p, &@1.beg_pos), $2, &@$, &@1); /*% ripper: dot3!(Qnil, $:2) %*/ } @@ -3144,7 +3143,7 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary) %rule value_expr(value) : value { - value_expr($1); + value_expr(p, $1); $$ = $1; } ; @@ -3467,7 +3466,7 @@ expr : command_call } | arg tASSOC { - value_expr($arg); + value_expr(p, $arg); } p_in_kwarg[ctxt] p_pvtbl p_pktbl p_top_expr_body[body] @@ -3482,7 +3481,7 @@ expr : command_call } | arg keyword_in { - value_expr($arg); + value_expr(p, $arg); } p_in_kwarg[ctxt] p_pvtbl p_pktbl p_top_expr_body[body] @@ -4059,7 +4058,7 @@ arg : asgn(arg_rhs) ternary : arg '?' arg '\n'? ':' arg { - value_expr($1); + value_expr(p, $1); $$ = new_if(p, $1, $3, $6, &@$, &NULL_LOC, &@5, &NULL_LOC); fixpos($$, $1); /*% ripper: ifop!($:1, $:3, $:6) %*/ @@ -4138,13 +4137,13 @@ aref_args : none arg_rhs : arg %prec tOP_ASGN { - value_expr($1); + value_expr(p, $1); $$ = $1; } | arg modifier_rescue after_rescue arg { p->ctxt.in_rescue = $3.in_rescue; - value_expr($1); + value_expr(p, $1); $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4); /*% ripper: rescue_mod!($:1, $:4) %*/ } @@ -12820,8 +12819,8 @@ call_bin_op(struct parser_params *p, NODE *recv, ID id, NODE *arg1, const YYLTYPE *op_loc, const YYLTYPE *loc) { NODE *expr; - value_expr(recv); - value_expr(arg1); + value_expr(p, recv); + value_expr(p, arg1); expr = NEW_OPCALL(recv, id, NEW_LIST(arg1, &arg1->nd_loc), loc); nd_set_line(expr, op_loc->beg_pos.lineno); return expr; @@ -12831,7 +12830,7 @@ static NODE * call_uni_op(struct parser_params *p, NODE *recv, ID id, const YYLTYPE *op_loc, const YYLTYPE *loc) { NODE *opcall; - value_expr(recv); + value_expr(p, recv); opcall = NEW_OPCALL(recv, id, 0, loc); nd_set_line(opcall, op_loc->beg_pos.lineno); return opcall; @@ -12881,8 +12880,8 @@ match_op(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *op_lo NODE *n; int line = op_loc->beg_pos.lineno; - value_expr(node1); - value_expr(node2); + value_expr(p, node1); + value_expr(p, node2); if ((n = last_expr_once_body(node1)) != 0) { switch (nd_type(n)) { @@ -13922,7 +13921,7 @@ value_expr_check(struct parser_params *p, NODE *node) } static int -value_expr_gen(struct parser_params *p, NODE *node) +value_expr(struct parser_params *p, NODE *node) { NODE *void_node = value_expr_check(p, node); if (void_node) { @@ -14191,7 +14190,7 @@ range_op(struct parser_params *p, NODE *node, const YYLTYPE *loc) if (node == 0) return 0; type = nd_type(node); - value_expr(node); + value_expr(p, node); if (type == NODE_INTEGER) { if (!e_option_supplied(p)) rb_warn0L(nd_line(node), "integer literal in flip-flop"); ID lineno = rb_intern("$."); @@ -14328,7 +14327,7 @@ logop(struct parser_params *p, ID id, NODE *left, NODE *right, { enum node_type type = id == idAND || id == idANDOP ? NODE_AND : NODE_OR; NODE *op; - value_expr(left); + value_expr(p, left); if (left && nd_type_p(left, type)) { NODE *node = left, *second; while ((second = RNODE_AND(node)->nd_2nd) != 0 && nd_type_p(second, type)) { From e96bbd718e4aef81c749080016a44364a2b8e8c9 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Wed, 3 Dec 2025 15:52:30 +0900 Subject: [PATCH 140/367] Remove needless parse.y `new_nil` macro In the past parse.y and ripper had different `new_nil` definition so that `new_nil` returns `nil` for ripper. ```c // parse.y #define new_nil(loc) NEW_NIL(loc) // ripper #define new_nil(loc) Qnil ``` However Rearchitect Ripper (89cfc1520717257073012ec07105c551e4b8af7c) removed `new_nil` definition for ripper then this commit removes needless parse.y macro and uses `NEW_NIL` directly. --- parse.y | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/parse.y b/parse.y index e65159257632c0..32431434de9734 100644 --- a/parse.y +++ b/parse.y @@ -1393,7 +1393,6 @@ last_expr_node(NODE *expr) static NODE* cond(struct parser_params *p, NODE *node, const YYLTYPE *loc); static NODE* method_cond(struct parser_params *p, NODE *node, const YYLTYPE *loc); -#define new_nil(loc) NEW_NIL(loc) static NODE *new_nil_at(struct parser_params *p, const rb_code_position_t *pos); static NODE *new_if(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*); static NODE *new_unless(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*); @@ -4451,7 +4450,7 @@ primary : inline_primary } | keyword_not '(' rparen { - $$ = call_uni_op(p, method_cond(p, new_nil(&@2), &@2), METHOD_NOT, &@1, &@$); + $$ = call_uni_op(p, method_cond(p, NEW_NIL(&@2), &@2), METHOD_NOT, &@1, &@$); /*% ripper: unary!(ID2VAL(idNOT), Qnil) %*/ } | fcall brace_block From 8f7d821dbae8bba00e1e905bbda1b639998288aa Mon Sep 17 00:00:00 2001 From: Rune Philosof Date: Tue, 28 Oct 2025 13:55:59 +0100 Subject: [PATCH 141/367] [ruby/psych] Remove y Object extension in IRB Fixes: ruby#685 This feature can easily break how you use other gems like factory_bot or prawn. https://github.com/ruby/psych/pull/747#issuecomment-3413139525 > But I kind of think we should leave `psych/y` around. If people really want to use it they could require the file. If you miss the function in Kernel, you can require it interactively or add it to `.irbrc`: ```ruby require 'psych/y' ``` https://github.com/ruby/psych/commit/f1610b3f05 --- ext/psych/lib/psych/core_ext.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/psych/lib/psych/core_ext.rb b/ext/psych/lib/psych/core_ext.rb index 78fe262239a391..6dfd0f16962896 100644 --- a/ext/psych/lib/psych/core_ext.rb +++ b/ext/psych/lib/psych/core_ext.rb @@ -14,10 +14,6 @@ def to_yaml options = {} end end -if defined?(::IRB) - require_relative 'y' -end - # Up to Ruby 3.4, Set was a regular object and was dumped as such # by Pysch. # Starting from Ruby 4.0 it's a core class written in C, so we have to implement From 0e7e6858e3a3689708371028cb1553317b905bb0 Mon Sep 17 00:00:00 2001 From: Caleb Stewart Date: Mon, 4 Aug 2025 20:52:52 -0500 Subject: [PATCH 142/367] [ruby/psych] Add option to disable symbol parsing https://github.com/ruby/psych/commit/4e9d08c285 --- ext/psych/lib/psych.rb | 13 +++++++------ ext/psych/lib/psych/nodes/node.rb | 4 ++-- ext/psych/lib/psych/scalar_scanner.rb | 5 +++-- ext/psych/lib/psych/visitors/to_ruby.rb | 4 ++-- test/psych/test_scalar_scanner.rb | 5 +++++ 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb index 0c158c9ff329b1..850a6d19374d87 100644 --- a/ext/psych/lib/psych.rb +++ b/ext/psych/lib/psych.rb @@ -269,10 +269,10 @@ module Psych # YAML documents that are supplied via user input. Instead, please use the # load method or the safe_load method. # - def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false + def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true result = parse(yaml, filename: filename) return fallback unless result - result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer) + result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols) end ### @@ -320,13 +320,13 @@ def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: fals # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"} # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"} # - def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false + def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true result = parse(yaml, filename: filename) return fallback unless result class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s), permitted_symbols.map(&:to_s)) - scanner = ScalarScanner.new class_loader, strict_integer: strict_integer + scanner = ScalarScanner.new class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols visitor = if aliases Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze else @@ -366,7 +366,7 @@ def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: # Raises a TypeError when `yaml` parameter is NilClass. This method is # similar to `safe_load` except that `Symbol` objects are allowed by default. # - def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false + def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true safe_load yaml, permitted_classes: permitted_classes, permitted_symbols: permitted_symbols, aliases: aliases, @@ -374,7 +374,8 @@ def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: fallback: fallback, symbolize_names: symbolize_names, freeze: freeze, - strict_integer: strict_integer + strict_integer: strict_integer, + parse_symbols: parse_symbols end ### diff --git a/ext/psych/lib/psych/nodes/node.rb b/ext/psych/lib/psych/nodes/node.rb index 6ae5c591484539..f89c82b7f76df8 100644 --- a/ext/psych/lib/psych/nodes/node.rb +++ b/ext/psych/lib/psych/nodes/node.rb @@ -45,8 +45,8 @@ def each &block # Convert this node to Ruby. # # See also Psych::Visitors::ToRuby - def to_ruby(symbolize_names: false, freeze: false, strict_integer: false) - Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer).accept(self) + def to_ruby(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true) + Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: true).accept(self) end alias :transform :to_ruby diff --git a/ext/psych/lib/psych/scalar_scanner.rb b/ext/psych/lib/psych/scalar_scanner.rb index 8cf868f8638e9e..6a556fb3b89bc2 100644 --- a/ext/psych/lib/psych/scalar_scanner.rb +++ b/ext/psych/lib/psych/scalar_scanner.rb @@ -27,10 +27,11 @@ class ScalarScanner attr_reader :class_loader # Create a new scanner - def initialize class_loader, strict_integer: false + def initialize class_loader, strict_integer: false, parse_symbols: true @symbol_cache = {} @class_loader = class_loader @strict_integer = strict_integer + @parse_symbols = parse_symbols end # Tokenize +string+ returning the Ruby object @@ -72,7 +73,7 @@ def tokenize string -Float::INFINITY elsif string.match?(/^\.nan$/i) Float::NAN - elsif string.match?(/^:./) + elsif @parse_symbols && string.match?(/^:./) if string =~ /^:(["'])(.*)\1/ @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, '')) else diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index 2814ce1a695df0..e62311ae12d177 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -12,9 +12,9 @@ module Visitors ### # This class walks a YAML AST, converting each node to Ruby class ToRuby < Psych::Visitors::Visitor - def self.create(symbolize_names: false, freeze: false, strict_integer: false) + def self.create(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true) class_loader = ClassLoader.new - scanner = ScalarScanner.new class_loader, strict_integer: strict_integer + scanner = ScalarScanner.new class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze) end diff --git a/test/psych/test_scalar_scanner.rb b/test/psych/test_scalar_scanner.rb index 2637a74df82b31..bc6a74ad8b2a98 100644 --- a/test/psych/test_scalar_scanner.rb +++ b/test/psych/test_scalar_scanner.rb @@ -138,6 +138,11 @@ def test_scan_strings_with_strict_int_delimiters assert_equal '-0b___', scanner.tokenize('-0b___') end + def test_scan_without_parse_symbols + scanner = Psych::ScalarScanner.new ClassLoader.new, parse_symbols: false + assert_equal ':foo', scanner.tokenize(':foo') + end + def test_scan_int_commas_and_underscores # NB: This test is to ensure backward compatibility with prior Psych versions, # not to test against any actual YAML specification. From c764269fbc4e2e58cea7c3880fa1485c7d2db123 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Tue, 2 Dec 2025 10:11:09 -0800 Subject: [PATCH 143/367] ZJIT: Only use make_equal_to for instructions with output It's used as an alternative to find-and-replace, so we should have nothing to replace. --- zjit/src/hir.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 5ede64d42c2d80..b51a55db93c463 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1995,6 +1995,10 @@ impl Function { /// Replace `insn` with the new instruction `replacement`, which will get appended to `insns`. fn make_equal_to(&mut self, insn: InsnId, replacement: InsnId) { + assert!(self.insns[insn.0].has_output(), + "Don't use make_equal_to for instruction with no output"); + assert!(self.insns[replacement.0].has_output(), + "Can't replace instruction that has output with instruction that has no output"); // Don't push it to the block self.union_find.borrow_mut().make_equal_to(insn, replacement); } @@ -2960,9 +2964,8 @@ impl Function { let offset = SIZEOF_VALUE_I32 * ivar_index as i32; (as_heap, offset) }; - let replacement = self.push_insn(block, Insn::StoreField { recv: ivar_storage, id, offset, val }); + self.push_insn(block, Insn::StoreField { recv: ivar_storage, id, offset, val }); self.push_insn(block, Insn::WriteBarrier { recv: self_val, val }); - self.make_equal_to(insn_id, replacement); } _ => { self.push_insn_id(block, insn_id); } } @@ -3520,11 +3523,9 @@ impl Function { }; // If we're adding a new instruction, mark the two equivalent in the union-find and // do an incremental flow typing of the new instruction. - if insn_id != replacement_id { + if insn_id != replacement_id && self.insns[replacement_id.0].has_output() { self.make_equal_to(insn_id, replacement_id); - if self.insns[replacement_id.0].has_output() { - self.insn_types[replacement_id.0] = self.infer_type(replacement_id); - } + self.insn_types[replacement_id.0] = self.infer_type(replacement_id); } new_insns.push(replacement_id); // If we've just folded an IfTrue into a Jump, for example, don't bother copying From 19f0df04ff733f687bf33ebd9c85285c5e733253 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Tue, 2 Dec 2025 10:28:41 -0800 Subject: [PATCH 144/367] ZJIT: Fix definite assignment to work with multiple entry blocks --- zjit/src/hir.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index b51a55db93c463..9d38336f8918fd 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4165,11 +4165,13 @@ impl Function { // missing location. let mut assigned_in = vec![None; self.num_blocks()]; let rpo = self.rpo(); - // Begin with every block having every variable defined, except for the entry block, which - // starts with nothing defined. - assigned_in[self.entry_block.0] = Some(InsnSet::with_capacity(self.insns.len())); + // Begin with every block having every variable defined, except for the entry blocks, which + // start with nothing defined. + let entry_blocks = self.entry_blocks(); for &block in &rpo { - if block != self.entry_block { + if entry_blocks.contains(&block) { + assigned_in[block.0] = Some(InsnSet::with_capacity(self.insns.len())); + } else { let mut all_ones = InsnSet::with_capacity(self.insns.len()); all_ones.insert_all(); assigned_in[block.0] = Some(all_ones); From 3efd8c6764a5b324b7d04a12443c2f18dd74181b Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 20:25:52 -0500 Subject: [PATCH 145/367] ZJIT: Inline Kernel#class (#15397) We generally know the receiver's class from profile info. I see 600k of these when running lobsters. --- zjit/bindgen/src/main.rs | 1 + zjit/src/cruby_bindings.inc.rs | 1 + zjit/src/cruby_methods.rs | 18 +++++- zjit/src/hir.rs | 33 +++++++++-- zjit/src/hir/opt_tests.rs | 101 ++++++++++++++++++++++++++++++--- 5 files changed, 138 insertions(+), 16 deletions(-) diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 0860c073cd17ed..2bae33713b274d 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -107,6 +107,7 @@ fn main() { .allowlist_function("rb_obj_is_kind_of") .allowlist_function("rb_obj_frozen_p") .allowlist_function("rb_class_inherited_p") + .allowlist_function("rb_class_real") .allowlist_type("ruby_encoding_consts") .allowlist_function("rb_hash_new") .allowlist_function("rb_hash_new_with_size") diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 0048fd6daece27..5b083e56a8d432 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1971,6 +1971,7 @@ unsafe extern "C" { pub fn rb_obj_is_kind_of(obj: VALUE, klass: VALUE) -> VALUE; pub fn rb_obj_alloc(klass: VALUE) -> VALUE; pub fn rb_obj_frozen_p(obj: VALUE) -> VALUE; + pub fn rb_class_real(klass: VALUE) -> VALUE; pub fn rb_class_inherited_p(scion: VALUE, ascendant: VALUE) -> VALUE; pub fn rb_backref_get() -> VALUE; pub fn rb_range_new(beg: VALUE, end: VALUE, excl: ::std::os::raw::c_int) -> VALUE; diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 0f5c992b88f892..71bf6ab83df0ad 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -186,11 +186,18 @@ pub fn init() -> Annotations { ($module:ident, $method_name:literal, $return_type:expr) => { annotate_builtin!($module, $method_name, $return_type, no_gc, leaf, elidable) }; - ($module:ident, $method_name:literal, $return_type:expr, $($properties:ident),+) => { + ($module:ident, $method_name:literal, $return_type:expr $(, $properties:ident)*) => { let mut props = FnProperties::default(); props.return_type = $return_type; $(props.$properties = true;)+ annotate_builtin_method(builtin_funcs, unsafe { $module }, $method_name, props); + }; + ($module:ident, $method_name:literal, $inline:ident, $return_type:expr $(, $properties:ident)*) => { + let mut props = FnProperties::default(); + props.return_type = $return_type; + props.inline = $inline; + $(props.$properties = true;)+ + annotate_builtin_method(builtin_funcs, unsafe { $module }, $method_name, props); } } @@ -256,7 +263,7 @@ pub fn init() -> Annotations { annotate_builtin!(rb_mKernel, "Integer", types::Integer); // TODO(max): Annotate rb_mKernel#class as returning types::Class. Right now there is a subtle // type system bug that causes an issue if we make it return types::Class. - annotate_builtin!(rb_mKernel, "class", types::HeapObject, leaf); + annotate_builtin!(rb_mKernel, "class", inline_kernel_class, types::HeapObject, leaf); annotate_builtin!(rb_mKernel, "frozen?", types::BoolExact); annotate_builtin!(rb_cSymbol, "name", types::StringExact); annotate_builtin!(rb_cSymbol, "to_s", types::StringExact); @@ -785,3 +792,10 @@ fn inline_kernel_respond_to_p( } Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(result) })) } + +fn inline_kernel_class(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { + let &[recv] = args else { return None; }; + let recv_class = fun.type_of(recv).runtime_exact_ruby_class()?; + let real_class = unsafe { rb_class_real(recv_class) }; + Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(real_class) })) +} diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 9d38336f8918fd..797cc167fc4513 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -862,6 +862,7 @@ pub enum Insn { // Invoke a builtin function InvokeBuiltin { bf: rb_builtin_function, + recv: InsnId, args: Vec, state: InsnId, leaf: bool, @@ -1922,7 +1923,7 @@ impl Function { state, reason, }, - &InvokeBuiltin { bf, ref args, state, leaf, return_type } => InvokeBuiltin { bf, args: find_vec!(args), state, leaf, return_type }, + &InvokeBuiltin { bf, recv, ref args, state, leaf, return_type } => InvokeBuiltin { bf, recv: find!(recv), args: find_vec!(args), state, leaf, return_type }, &ArrayDup { val, state } => ArrayDup { val: find!(val), state }, &HashDup { val, state } => HashDup { val: find!(val), state }, &HashAref { hash, key, state } => HashAref { hash: find!(hash), key: find!(key), state }, @@ -2811,6 +2812,7 @@ impl Function { self.push_insn(block, Insn::IncrCounter(Counter::inline_iseq_optimized_send_count)); let replacement = self.push_insn(block, Insn::InvokeBuiltin { bf, + recv, args: vec![recv], state, leaf: true, @@ -3388,6 +3390,25 @@ impl Function { continue; } } + Insn::InvokeBuiltin { bf, recv, args, state, .. } => { + let props = ZJITState::get_method_annotations().get_builtin_properties(&bf).unwrap_or_default(); + // Try inlining the cfunc into HIR + let tmp_block = self.new_block(u32::MAX); + if let Some(replacement) = (props.inline)(self, tmp_block, recv, &args, state) { + // Copy contents of tmp_block to block + assert_ne!(block, tmp_block); + let insns = std::mem::take(&mut self.blocks[tmp_block.0].insns); + self.blocks[block.0].insns.extend(insns); + self.push_insn(block, Insn::IncrCounter(Counter::inline_cfunc_optimized_send_count)); + self.make_equal_to(insn_id, replacement); + if self.type_of(replacement).bit_equal(types::Any) { + // Not set yet; infer type + self.insn_types[replacement.0] = self.infer_type(replacement); + } + self.remove_block(tmp_block); + continue; + } + } _ => {} } self.push_insn_id(block, insn_id); @@ -3704,13 +3725,13 @@ impl Function { | &Insn::CCallVariadic { recv, ref args, state, .. } | &Insn::CCallWithFrame { recv, ref args, state, .. } | &Insn::SendWithoutBlockDirect { recv, ref args, state, .. } + | &Insn::InvokeBuiltin { recv, ref args, state, .. } | &Insn::InvokeSuper { recv, ref args, state, .. } => { worklist.push_back(recv); worklist.extend(args); worklist.push_back(state); } - &Insn::InvokeBuiltin { ref args, state, .. } - | &Insn::InvokeBlock { ref args, state, .. } => { + &Insn::InvokeBlock { ref args, state, .. } => { worklist.extend(args); worklist.push_back(state) } @@ -4323,6 +4344,7 @@ impl Function { | Insn::InvokeSuper { recv, ref args, .. } | Insn::CCallWithFrame { recv, ref args, .. } | Insn::CCallVariadic { recv, ref args, .. } + | Insn::InvokeBuiltin { recv, ref args, .. } | Insn::ArrayInclude { target: recv, elements: ref args, .. } => { self.assert_subtype(insn_id, recv, types::BasicObject)?; for &arg in args { @@ -4331,8 +4353,7 @@ impl Function { Ok(()) } // Instructions with a Vec of Ruby objects - Insn::InvokeBuiltin { ref args, .. } - | Insn::InvokeBlock { ref args, .. } + Insn::InvokeBlock { ref args, .. } | Insn::NewArray { elements: ref args, .. } | Insn::ArrayHash { elements: ref args, .. } | Insn::ArrayMax { elements: ref args, .. } => { @@ -5785,6 +5806,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let insn_id = fun.push_insn(block, Insn::InvokeBuiltin { bf, + recv: self_param, args, state: exit_id, leaf, @@ -5813,6 +5835,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let insn_id = fun.push_insn(block, Insn::InvokeBuiltin { bf, + recv: self_param, args, state: exit_id, leaf, diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 9809c8bffbee01..ab9bc1151381d7 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -2611,9 +2611,10 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) IncrCounter inline_iseq_optimized_send_count - v25:HeapObject = InvokeBuiltin leaf _bi20, v20 + v26:Class[Module@0x1010] = Const Value(VALUE(0x1010)) + IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v25 + Return v26 "); } @@ -8937,15 +8938,15 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1000) v40:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count - v43:HeapObject = InvokeBuiltin leaf _bi20, v40 + v44:Class[C@0x1000] = Const Value(VALUE(0x1000)) + IncrCounter inline_cfunc_optimized_send_count v13:StaticSymbol[:_lex_actions] = Const Value(VALUE(0x1038)) v15:TrueClass = Const Value(true) PatchPoint MethodRedefined(Class@0x1040, respond_to?@0x1048, cme:0x1050) PatchPoint NoSingletonClass(Class@0x1040) - v47:ModuleSubclass[class_exact*:Class@VALUE(0x1040)] = GuardType v43, ModuleSubclass[class_exact*:Class@VALUE(0x1040)] PatchPoint MethodRedefined(Class@0x1040, _lex_actions@0x1078, cme:0x1080) PatchPoint NoSingletonClass(Class@0x1040) - v51:TrueClass = Const Value(true) + v52:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts v24:StaticSymbol[:CORRECT] = Const Value(VALUE(0x10a8)) @@ -8976,14 +8977,96 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1000) v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count - v26:HeapObject = InvokeBuiltin leaf _bi20, v23 + v27:Class[C@0x1000] = Const Value(VALUE(0x1000)) + IncrCounter inline_cfunc_optimized_send_count PatchPoint MethodRedefined(Class@0x1038, name@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Class@0x1038) - v30:ModuleSubclass[class_exact*:Class@VALUE(0x1038)] = GuardType v26, ModuleSubclass[class_exact*:Class@VALUE(0x1038)] IncrCounter inline_cfunc_optimized_send_count - v32:StringExact|NilClass = CCall v30, :Module#name@0x1070 + v33:StringExact|NilClass = CCall v27, :Module#name@0x1070 CheckInterrupts - Return v32 + Return v33 + "); + } + + #[test] + fn test_fold_kernel_class() { + eval(r#" + class C; end + def test(o) = o.class + test(C.new) + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + IncrCounter inline_iseq_optimized_send_count + v25:Class[C@0x1000] = Const Value(VALUE(0x1000)) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_fold_fixnum_class() { + eval(r#" + def test = 5.class + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[5] = Const Value(5) + PatchPoint MethodRedefined(Integer@0x1000, class@0x1008, cme:0x1010) + IncrCounter inline_iseq_optimized_send_count + v21:Class[Integer@0x1000] = Const Value(VALUE(0x1000)) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v21 + "); + } + + #[test] + fn test_fold_singleton_class() { + eval(r#" + def test = self.class + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + IncrCounter inline_iseq_optimized_send_count + v22:Class[Object@0x1038] = Const Value(VALUE(0x1038)) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v22 "); } From 0af85a1fe291856d6b3d2b3bb68e6edb8cb44b4f Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 21:27:56 -0500 Subject: [PATCH 146/367] ZJIT: Optimize setivar with shape transition (#15375) Since we do a decent job of pre-sizing objects, don't handle the case where we would need to re-size an object. Also don't handle too-complex shapes. lobsters stats before: ``` Top-20 calls to C functions from JIT code (79.4% of total 90,051,140): rb_vm_opt_send_without_block: 19,762,433 (21.9%) rb_vm_setinstancevariable: 7,698,314 ( 8.5%) rb_hash_aref: 6,767,461 ( 7.5%) rb_vm_env_write: 5,373,080 ( 6.0%) rb_vm_send: 5,049,229 ( 5.6%) rb_vm_getinstancevariable: 4,535,259 ( 5.0%) rb_obj_is_kind_of: 3,746,306 ( 4.2%) rb_ivar_get_at_no_ractor_check: 3,745,237 ( 4.2%) rb_vm_invokesuper: 3,037,467 ( 3.4%) rb_ary_entry: 2,351,983 ( 2.6%) rb_vm_opt_getconstant_path: 1,344,740 ( 1.5%) rb_vm_invokeblock: 1,184,474 ( 1.3%) Hash#[]=: 1,064,288 ( 1.2%) rb_gc_writebarrier: 1,006,972 ( 1.1%) rb_ec_ary_new_from_values: 902,687 ( 1.0%) fetch: 898,667 ( 1.0%) rb_str_buf_append: 833,787 ( 0.9%) rb_class_allocate_instance: 822,024 ( 0.9%) Hash#fetch: 699,580 ( 0.8%) _bi20: 682,068 ( 0.8%) Top-4 setivar fallback reasons (100.0% of total 7,732,326): shape_transition: 6,032,109 (78.0%) not_monomorphic: 1,469,300 (19.0%) not_t_object: 172,636 ( 2.2%) too_complex: 58,281 ( 0.8%) ``` lobsters stats after: ``` Top-20 calls to C functions from JIT code (79.0% of total 88,322,656): rb_vm_opt_send_without_block: 19,777,880 (22.4%) rb_hash_aref: 6,771,589 ( 7.7%) rb_vm_env_write: 5,372,789 ( 6.1%) rb_gc_writebarrier: 5,195,527 ( 5.9%) rb_vm_send: 5,049,145 ( 5.7%) rb_vm_getinstancevariable: 4,538,485 ( 5.1%) rb_obj_is_kind_of: 3,746,241 ( 4.2%) rb_ivar_get_at_no_ractor_check: 3,745,172 ( 4.2%) rb_vm_invokesuper: 3,037,157 ( 3.4%) rb_ary_entry: 2,351,968 ( 2.7%) rb_vm_setinstancevariable: 1,703,337 ( 1.9%) rb_vm_opt_getconstant_path: 1,344,730 ( 1.5%) rb_vm_invokeblock: 1,184,290 ( 1.3%) Hash#[]=: 1,061,868 ( 1.2%) rb_ec_ary_new_from_values: 902,666 ( 1.0%) fetch: 898,666 ( 1.0%) rb_str_buf_append: 833,784 ( 0.9%) rb_class_allocate_instance: 821,778 ( 0.9%) Hash#fetch: 755,913 ( 0.9%) Top-4 setivar fallback reasons (100.0% of total 1,703,337): not_monomorphic: 1,472,405 (86.4%) not_t_object: 172,629 (10.1%) too_complex: 58,281 ( 3.4%) new_shape_needs_extension: 22 ( 0.0%) ``` I also noticed that primitive printing in HIR was broken so I fixed that. Co-authored-by: Aaron Patterson --- jit.c | 6 ++ zjit/bindgen/src/main.rs | 1 + zjit/src/codegen.rs | 11 ++- zjit/src/cruby.rs | 4 +- zjit/src/cruby_bindings.inc.rs | 1 + zjit/src/hir.rs | 37 +++++++- zjit/src/hir/opt_tests.rs | 157 +++++++++++++++++++++++++++++---- zjit/src/hir_type/mod.rs | 51 +++++++++-- zjit/src/stats.rs | 2 + 9 files changed, 242 insertions(+), 28 deletions(-) diff --git a/jit.c b/jit.c index dd28bd6ad02aef..a886b66278c2aa 100644 --- a/jit.c +++ b/jit.c @@ -767,3 +767,9 @@ rb_yarv_str_eql_internal(VALUE str1, VALUE str2) } void rb_jit_str_concat_codepoint(VALUE str, VALUE codepoint); + +attr_index_t +rb_jit_shape_capacity(shape_id_t shape_id) +{ + return RSHAPE_CAPACITY(shape_id); +} diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 2bae33713b274d..e07953ffd5df08 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -100,6 +100,7 @@ fn main() { .allowlist_function("rb_shape_id_offset") .allowlist_function("rb_shape_get_iv_index") .allowlist_function("rb_shape_transition_add_ivar_no_warnings") + .allowlist_function("rb_jit_shape_capacity") .allowlist_var("rb_invalid_shape_id") .allowlist_type("shape_id_fl_type") .allowlist_var("VM_KW_SPECIFIED_BITS_MAX") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 57ee65e91b754b..8c5fdc816d6450 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -350,6 +350,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::Const { val: Const::CPtr(val) } => gen_const_cptr(val), &Insn::Const { val: Const::CInt64(val) } => gen_const_long(val), &Insn::Const { val: Const::CUInt16(val) } => gen_const_uint16(val), + &Insn::Const { val: Const::CUInt32(val) } => gen_const_uint32(val), Insn::Const { .. } => panic!("Unexpected Const in gen_insn: {insn}"), Insn::NewArray { elements, state } => gen_new_array(asm, opnds!(elements), &function.frame_state(*state)), Insn::NewHash { elements, state } => gen_new_hash(jit, asm, opnds!(elements), &function.frame_state(*state)), @@ -475,7 +476,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::LoadEC => gen_load_ec(), Insn::LoadSelf => gen_load_self(), &Insn::LoadField { recv, id, offset, return_type: _ } => gen_load_field(asm, opnd!(recv), id, offset), - &Insn::StoreField { recv, id, offset, val } => no_output!(gen_store_field(asm, opnd!(recv), id, offset, opnd!(val))), + &Insn::StoreField { recv, id, offset, val } => no_output!(gen_store_field(asm, opnd!(recv), id, offset, opnd!(val), function.type_of(val))), &Insn::WriteBarrier { recv, val } => no_output!(gen_write_barrier(asm, opnd!(recv), opnd!(val), function.type_of(val))), &Insn::IsBlockGiven => gen_is_block_given(jit, asm), Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)), @@ -1099,10 +1100,10 @@ fn gen_load_field(asm: &mut Assembler, recv: Opnd, id: ID, offset: i32) -> Opnd asm.load(Opnd::mem(64, recv, offset)) } -fn gen_store_field(asm: &mut Assembler, recv: Opnd, id: ID, offset: i32, val: Opnd) { +fn gen_store_field(asm: &mut Assembler, recv: Opnd, id: ID, offset: i32, val: Opnd, val_type: Type) { asm_comment!(asm, "Store field id={} offset={}", id.contents_lossy(), offset); let recv = asm.load(recv); - asm.store(Opnd::mem(64, recv, offset), val); + asm.store(Opnd::mem(val_type.num_bits(), recv, offset), val); } fn gen_write_barrier(asm: &mut Assembler, recv: Opnd, val: Opnd, val_type: Type) { @@ -1159,6 +1160,10 @@ fn gen_const_uint16(val: u16) -> lir::Opnd { Opnd::UImm(val as u64) } +fn gen_const_uint32(val: u32) -> lir::Opnd { + Opnd::UImm(val as u64) +} + /// Compile a basic block argument fn gen_param(asm: &mut Assembler, idx: usize) -> lir::Opnd { // Allocate a register or a stack slot diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 9ed3a1abf79ca2..b255c28e52cd17 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -999,8 +999,9 @@ mod manual_defs { use super::*; pub const SIZEOF_VALUE: usize = 8; + pub const BITS_PER_BYTE: usize = 8; pub const SIZEOF_VALUE_I32: i32 = SIZEOF_VALUE as i32; - pub const VALUE_BITS: u8 = 8 * SIZEOF_VALUE as u8; + pub const VALUE_BITS: u8 = BITS_PER_BYTE as u8 * SIZEOF_VALUE as u8; pub const RUBY_LONG_MIN: isize = std::os::raw::c_long::MIN as isize; pub const RUBY_LONG_MAX: isize = std::os::raw::c_long::MAX as isize; @@ -1382,6 +1383,7 @@ pub(crate) mod ids { name: thread_ptr name: self_ content: b"self" name: rb_ivar_get_at_no_ractor_check + name: _shape_id } /// Get an CRuby `ID` to an interned string, e.g. a particular method name. diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 5b083e56a8d432..a80f3d83c5f414 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -2232,4 +2232,5 @@ unsafe extern "C" { ); pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; pub fn rb_jit_str_concat_codepoint(str_: VALUE, codepoint: VALUE); + pub fn rb_jit_shape_capacity(shape_id: shape_id_t) -> attr_index_t; } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 797cc167fc4513..32d6f63b9ed74c 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2949,10 +2949,34 @@ impl Function { self.push_insn_id(block, insn_id); continue; } let mut ivar_index: u16 = 0; + let mut next_shape_id = recv_type.shape(); if !unsafe { rb_shape_get_iv_index(recv_type.shape().0, id, &mut ivar_index) } { - // TODO(max): Shape transition - self.push_insn(block, Insn::IncrCounter(Counter::setivar_fallback_shape_transition)); - self.push_insn_id(block, insn_id); continue; + // Current shape does not contain this ivar; do a shape transition. + let current_shape_id = recv_type.shape(); + let class = recv_type.class(); + // We're only looking at T_OBJECT so ignore all of the imemo stuff. + assert!(recv_type.flags().is_t_object()); + next_shape_id = ShapeId(unsafe { rb_shape_transition_add_ivar_no_warnings(class, current_shape_id.0, id) }); + let ivar_result = unsafe { rb_shape_get_iv_index(next_shape_id.0, id, &mut ivar_index) }; + assert!(ivar_result, "New shape must have the ivar index"); + // If the VM ran out of shapes, or this class generated too many leaf, + // it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table). + let new_shape_too_complex = unsafe { rb_jit_shape_too_complex_p(next_shape_id.0) }; + // TODO(max): Is it OK to bail out here after making a shape transition? + if new_shape_too_complex { + self.push_insn(block, Insn::IncrCounter(Counter::setivar_fallback_new_shape_too_complex)); + self.push_insn_id(block, insn_id); continue; + } + let current_capacity = unsafe { rb_jit_shape_capacity(current_shape_id.0) }; + let next_capacity = unsafe { rb_jit_shape_capacity(next_shape_id.0) }; + // If the new shape has a different capacity, or is TOO_COMPLEX, we'll have to + // reallocate it. + let needs_extension = next_capacity != current_capacity; + if needs_extension { + self.push_insn(block, Insn::IncrCounter(Counter::setivar_fallback_new_shape_needs_extension)); + self.push_insn_id(block, insn_id); continue; + } + // Fall through to emitting the ivar write } let self_val = self.push_insn(block, Insn::GuardType { val: self_val, guard_type: types::HeapBasicObject, state }); let self_val = self.push_insn(block, Insn::GuardShape { val: self_val, shape: recv_type.shape(), state }); @@ -2968,6 +2992,13 @@ impl Function { }; self.push_insn(block, Insn::StoreField { recv: ivar_storage, id, offset, val }); self.push_insn(block, Insn::WriteBarrier { recv: self_val, val }); + if next_shape_id != recv_type.shape() { + // Write the new shape ID + assert_eq!(SHAPE_ID_NUM_BITS, 32); + let shape_id = self.push_insn(block, Insn::Const { val: Const::CUInt32(next_shape_id.0) }); + let shape_id_offset = unsafe { rb_shape_id_offset() }; + self.push_insn(block, Insn::StoreField { recv: self_val, id: ID!(_shape_id), offset: shape_id_offset, val: shape_id }); + } } _ => { self.push_insn_id(block, insn_id); } } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index ab9bc1151381d7..a174ac1b69280e 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -3484,6 +3484,39 @@ mod hir_opt_tests { "); } + #[test] + fn test_dont_specialize_complex_shape_definedivar() { + eval(r#" + class C + def test = defined?(@a) + end + obj = C.new + (0..1000).each do |i| + obj.instance_variable_set(:"@v#{i}", i) + end + (0..1000).each do |i| + obj.remove_instance_variable(:"@v#{i}") + end + obj.test + TEST = C.instance_method(:test) + "#); + assert_snapshot!(hir_string_proc("TEST"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + IncrCounter definedivar_fallback_too_complex + v10:StringExact|NilClass = DefinedIvar v6, :@a + CheckInterrupts + Return v10 + "); + } + #[test] fn test_specialize_monomorphic_setivar_already_in_shape() { eval(" @@ -3513,7 +3546,7 @@ mod hir_opt_tests { } #[test] - fn test_dont_specialize_monomorphic_setivar_with_shape_transition() { + fn test_specialize_monomorphic_setivar_with_shape_transition() { eval(" def test = @foo = 5 test @@ -3530,13 +3563,57 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:Fixnum[5] = Const Value(5) PatchPoint SingleRactorMode - IncrCounter setivar_fallback_shape_transition - SetIvar v6, :@foo, v10 + v19:HeapBasicObject = GuardType v6, HeapBasicObject + v20:HeapBasicObject = GuardShape v19, 0x1000 + StoreField v20, :@foo@0x1001, v10 + WriteBarrier v20, v10 + v23:CUInt32[4194311] = Const CUInt32(4194311) + StoreField v20, :_shape_id@0x1002, v23 CheckInterrupts Return v10 "); } + #[test] + fn test_specialize_multiple_monomorphic_setivar_with_shape_transition() { + eval(" + def test + @foo = 1 + @bar = 2 + end + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[1] = Const Value(1) + PatchPoint SingleRactorMode + v25:HeapBasicObject = GuardType v6, HeapBasicObject + v26:HeapBasicObject = GuardShape v25, 0x1000 + StoreField v26, :@foo@0x1001, v10 + WriteBarrier v26, v10 + v29:CUInt32[4194311] = Const CUInt32(4194311) + StoreField v26, :_shape_id@0x1002, v29 + v16:Fixnum[2] = Const Value(2) + PatchPoint SingleRactorMode + v31:HeapBasicObject = GuardType v6, HeapBasicObject + v32:HeapBasicObject = GuardShape v31, 0x1003 + StoreField v32, :@bar@0x1004, v16 + WriteBarrier v32, v16 + v35:CUInt32[4194312] = Const CUInt32(4194312) + StoreField v32, :_shape_id@0x1002, v35 + CheckInterrupts + Return v16 + "); + } + #[test] fn test_dont_specialize_setivar_with_t_data() { eval(" @@ -3611,6 +3688,9 @@ mod hir_opt_tests { (0..1000).each do |i| obj.instance_variable_set(:"@v#{i}", i) end + (0..1000).each do |i| + obj.remove_instance_variable(:"@v#{i}") + end obj.test TEST = C.instance_method(:test) "#); @@ -3626,7 +3706,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:Fixnum[5] = Const Value(5) PatchPoint SingleRactorMode - IncrCounter setivar_fallback_shape_transition + IncrCounter setivar_fallback_too_complex SetIvar v6, :@a, v10 CheckInterrupts Return v10 @@ -5417,6 +5497,43 @@ mod hir_opt_tests { "); } + #[test] + fn test_dont_optimize_getivar_with_too_complex_shape() { + eval(r#" + class C + attr_accessor :foo + end + obj = C.new + (0..1000).each do |i| + obj.instance_variable_set(:"@v#{i}", i) + end + (0..1000).each do |i| + obj.remove_instance_variable(:"@v#{i}") + end + def test(o) = o.foo + test obj + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(C@0x1000) + v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + IncrCounter getivar_fallback_too_complex + v22:BasicObject = GetIvar v21, :@foo + CheckInterrupts + Return v22 + "); + } + #[test] fn test_optimize_send_with_block() { eval(r#" @@ -5697,8 +5814,11 @@ mod hir_opt_tests { v16:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - IncrCounter setivar_fallback_shape_transition - SetIvar v26, :@foo, v16 + v29:HeapObject[class_exact:C] = GuardShape v26, 0x1038 + StoreField v29, :@foo@0x1039, v16 + WriteBarrier v29, v16 + v32:CUInt32[4194311] = Const CUInt32(4194311) + StoreField v29, :_shape_id@0x103a, v32 CheckInterrupts Return v16 "); @@ -5729,8 +5849,11 @@ mod hir_opt_tests { v16:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - IncrCounter setivar_fallback_shape_transition - SetIvar v26, :@foo, v16 + v29:HeapObject[class_exact:C] = GuardShape v26, 0x1038 + StoreField v29, :@foo@0x1039, v16 + WriteBarrier v29, v16 + v32:CUInt32[4194311] = Const CUInt32(4194311) + StoreField v29, :_shape_id@0x103a, v32 CheckInterrupts Return v16 "); @@ -9109,18 +9232,22 @@ mod hir_opt_tests { SetLocal l0, EP@3, v27 v39:BasicObject = GetLocal l0, EP@3 PatchPoint SingleRactorMode - IncrCounter setivar_fallback_shape_transition - SetIvar v14, :@formatted, v39 - v45:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(Class@0x1008, lambda@0x1010, cme:0x1018) - PatchPoint NoSingletonClass(Class@0x1008) - v60:BasicObject = CCallWithFrame v45, :RubyVM::FrozenCore.lambda@0x1040, block=0x1048 + v56:HeapBasicObject = GuardType v14, HeapBasicObject + v57:HeapBasicObject = GuardShape v56, 0x1000 + StoreField v57, :@formatted@0x1001, v39 + WriteBarrier v57, v39 + v60:CUInt32[4194311] = Const CUInt32(4194311) + StoreField v57, :_shape_id@0x1002, v60 + v45:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(Class@0x1010) + v65:BasicObject = CCallWithFrame v45, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 v48:BasicObject = GetLocal l0, EP@6 v49:BasicObject = GetLocal l0, EP@5 v50:BasicObject = GetLocal l0, EP@4 v51:BasicObject = GetLocal l0, EP@3 CheckInterrupts - Return v60 + Return v65 "); } } diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index 8e862d74e71aba..a7ffcd1b77e882 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -89,13 +89,13 @@ fn write_spec(f: &mut std::fmt::Formatter, printer: &TypePrinter) -> std::fmt::R Specialization::TypeExact(val) => write!(f, "[class_exact:{}]", get_class_name(val)), Specialization::Int(val) if ty.is_subtype(types::CBool) => write!(f, "[{}]", val != 0), - Specialization::Int(val) if ty.is_subtype(types::CInt8) => write!(f, "[{}]", (val as i64) >> 56), - Specialization::Int(val) if ty.is_subtype(types::CInt16) => write!(f, "[{}]", (val as i64) >> 48), - Specialization::Int(val) if ty.is_subtype(types::CInt32) => write!(f, "[{}]", (val as i64) >> 32), + Specialization::Int(val) if ty.is_subtype(types::CInt8) => write!(f, "[{}]", (val & u8::MAX as u64) as i8), + Specialization::Int(val) if ty.is_subtype(types::CInt16) => write!(f, "[{}]", (val & u16::MAX as u64) as i16), + Specialization::Int(val) if ty.is_subtype(types::CInt32) => write!(f, "[{}]", (val & u32::MAX as u64) as i32), Specialization::Int(val) if ty.is_subtype(types::CInt64) => write!(f, "[{}]", val as i64), - Specialization::Int(val) if ty.is_subtype(types::CUInt8) => write!(f, "[{}]", val >> 56), - Specialization::Int(val) if ty.is_subtype(types::CUInt16) => write!(f, "[{}]", val >> 48), - Specialization::Int(val) if ty.is_subtype(types::CUInt32) => write!(f, "[{}]", val >> 32), + Specialization::Int(val) if ty.is_subtype(types::CUInt8) => write!(f, "[{}]", val & u8::MAX as u64), + Specialization::Int(val) if ty.is_subtype(types::CUInt16) => write!(f, "[{}]", val & u16::MAX as u64), + Specialization::Int(val) if ty.is_subtype(types::CUInt32) => write!(f, "[{}]", val & u32::MAX as u64), Specialization::Int(val) if ty.is_subtype(types::CUInt64) => write!(f, "[{}]", val), Specialization::Int(val) if ty.is_subtype(types::CPtr) => write!(f, "[{}]", Const::CPtr(val as *const u8).print(printer.ptr_map)), Specialization::Int(val) => write!(f, "[{val}]"), @@ -517,6 +517,18 @@ impl Type { pub fn print(self, ptr_map: &PtrPrintMap) -> TypePrinter<'_> { TypePrinter { inner: self, ptr_map } } + + pub fn num_bits(&self) -> u8 { + self.num_bytes() * crate::cruby::BITS_PER_BYTE as u8 + } + + pub fn num_bytes(&self) -> u8 { + if self.is_subtype(types::CUInt8) || self.is_subtype(types::CInt8) { return 1; } + if self.is_subtype(types::CUInt16) || self.is_subtype(types::CInt16) { return 2; } + if self.is_subtype(types::CUInt32) || self.is_subtype(types::CInt32) { return 4; } + // CUInt64, CInt64, CPtr, CNull, CDouble, or anything else defaults to 8 bytes + crate::cruby::SIZEOF_VALUE as u8 + } } #[cfg(test)] @@ -574,6 +586,33 @@ mod tests { assert_subtype(types::Any, types::Any); } + #[test] + fn from_const() { + let cint32 = Type::from_const(Const::CInt32(12)); + assert_subtype(cint32, types::CInt32); + assert_eq!(cint32.spec, Specialization::Int(12)); + assert_eq!(format!("{}", cint32), "CInt32[12]"); + + let cint32 = Type::from_const(Const::CInt32(-12)); + assert_subtype(cint32, types::CInt32); + assert_eq!(cint32.spec, Specialization::Int((-12i64) as u64)); + assert_eq!(format!("{}", cint32), "CInt32[-12]"); + + let cuint32 = Type::from_const(Const::CInt32(12)); + assert_subtype(cuint32, types::CInt32); + assert_eq!(cuint32.spec, Specialization::Int(12)); + + let cuint32 = Type::from_const(Const::CUInt32(0xffffffff)); + assert_subtype(cuint32, types::CUInt32); + assert_eq!(cuint32.spec, Specialization::Int(0xffffffff)); + assert_eq!(format!("{}", cuint32), "CUInt32[4294967295]"); + + let cuint32 = Type::from_const(Const::CUInt32(0xc00087)); + assert_subtype(cuint32, types::CUInt32); + assert_eq!(cuint32.spec, Specialization::Int(0xc00087)); + assert_eq!(format!("{}", cuint32), "CUInt32[12583047]"); + } + #[test] fn integer() { assert_subtype(Type::fixnum(123), types::Fixnum); diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 890d92bc569a28..7a076b7cda2550 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -255,6 +255,8 @@ make_counters! { setivar_fallback_too_complex, setivar_fallback_frozen, setivar_fallback_shape_transition, + setivar_fallback_new_shape_too_complex, + setivar_fallback_new_shape_needs_extension, } // Ivar fallback counters that are summed as dynamic_getivar_count From 932762f29457ad1def6fbab7eca7bcbeeb58ea5c Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Mon, 17 Nov 2025 00:18:33 +0100 Subject: [PATCH 147/367] [ruby/rubygems] Increase connection pool to allow for up to 70% speed increase: - ### TL;DR Bundler is heavily limited by the connection pool which manages a single connection. By increasing the number of connection, we can drastiscally speed up the installation process when many gems need to be downloaded and installed. ### Benchmark There are various factors that are hard to control such as compilation time and network speed but after dozens of tests I can consistently get aroud 70% speed increase when downloading and installing 472 gems, most having no native extensions (on purpose). ``` # Before bundle install 28.60s user 12.70s system 179% cpu 23.014 total # After bundle install 30.09s user 15.90s system 281% cpu 16.317 total ``` You can find on this gist how this was benchmarked and the Gemfile used https://gist.github.com/Edouard-chin/c8e39148c0cdf324dae827716fbe24a0 ### Context A while ago in #869, Aaron introduced a connection pool which greatly improved Bundler speed. It was noted in the PR description that managing one connection was already good enough and it wasn't clear whether we needed more connections. Aaron also had the intuition that we may need to increase the pool for downloading gems and he was right. > We need to study how RubyGems uses connections and make a decision > based on request usage (e.g. only use one connection for many small > requests like bundler API, and maybe many connections for > downloading gems) When bundler downloads and installs gem in parallel https://github.com/ruby/rubygems/blob/4f85e02fdd89ee28852722dfed42a13c9f5c9193/bundler/lib/bundler/installer/parallel_installer.rb#L128 most threads have to wait for the only connection in the pool to be available which is not efficient. ### Solution This commit modifies the pool size for the fetcher that Bundler uses. RubyGems fetcher will continue to use a single connection. The bundler fetcher is used in 2 places. 1. When downloading gems https://github.com/ruby/rubygems/blob/4f85e02fdd89ee28852722dfed42a13c9f5c9193/bundler/lib/bundler/source/rubygems.rb#L481-L484 2. When grabing the index (not the compact index) using the `bundle install --full-index` flag. https://github.com/ruby/rubygems/blob/4f85e02fdd89ee28852722dfed42a13c9f5c9193/bundler/lib/bundler/fetcher/index.rb#L9 Having more connections in 2) is not any useful but tweaking the size based on where the fetcher is used is a bit tricky so I opted to modify it at the class level. I fiddle with the pool size and found that 5 seems to be the sweet spot at least for my environment. https://github.com/ruby/rubygems/commit/6063fd9963 --- lib/bundler/fetcher/gem_remote_fetcher.rb | 6 ++ lib/rubygems/remote_fetcher.rb | 3 +- lib/rubygems/request/connection_pools.rb | 7 ++- lib/rubygems/request/http_pool.rb | 15 +++-- .../fetcher/gem_remote_fetcher_spec.rb | 60 +++++++++++++++++++ .../test_gem_request_connection_pools.rb | 12 ++++ tool/bundler/test_gems.rb | 1 + tool/bundler/test_gems.rb.lock | 3 + 8 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb diff --git a/lib/bundler/fetcher/gem_remote_fetcher.rb b/lib/bundler/fetcher/gem_remote_fetcher.rb index 3fc7b682632b85..3c3c1826a1b1e2 100644 --- a/lib/bundler/fetcher/gem_remote_fetcher.rb +++ b/lib/bundler/fetcher/gem_remote_fetcher.rb @@ -5,6 +5,12 @@ module Bundler class Fetcher class GemRemoteFetcher < Gem::RemoteFetcher + def initialize(*) + super + + @pool_size = 5 + end + def request(*args) super do |req| req.delete("User-Agent") if headers["User-Agent"] diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 01788a6a5f17d5..805f7aaf82ed1a 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -82,6 +82,7 @@ def initialize(proxy = nil, dns = nil, headers = {}) @proxy = proxy @pools = {} @pool_lock = Thread::Mutex.new + @pool_size = 1 @cert_files = Gem::Request.get_cert_files @headers = headers @@ -338,7 +339,7 @@ def proxy_for(proxy, uri) def pools_for(proxy) @pool_lock.synchronize do - @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files + @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files, @pool_size end end end diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb index 6c1b04ab6567d2..01e7e0629a903f 100644 --- a/lib/rubygems/request/connection_pools.rb +++ b/lib/rubygems/request/connection_pools.rb @@ -7,11 +7,12 @@ class << self attr_accessor :client end - def initialize(proxy_uri, cert_files) + def initialize(proxy_uri, cert_files, pool_size = 1) @proxy_uri = proxy_uri @cert_files = cert_files @pools = {} @pool_mutex = Thread::Mutex.new + @pool_size = pool_size end def pool_for(uri) @@ -20,9 +21,9 @@ def pool_for(uri) @pool_mutex.synchronize do @pools[key] ||= if https? uri - Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri) + Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri, @pool_size) else - Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri) + Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri, @pool_size) end end end diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb index 52543de41fdeab..468502ca6b64d6 100644 --- a/lib/rubygems/request/http_pool.rb +++ b/lib/rubygems/request/http_pool.rb @@ -9,12 +9,14 @@ class Gem::Request::HTTPPool # :nodoc: attr_reader :cert_files, :proxy_uri - def initialize(http_args, cert_files, proxy_uri) + def initialize(http_args, cert_files, proxy_uri, pool_size) @http_args = http_args @cert_files = cert_files @proxy_uri = proxy_uri - @queue = Thread::SizedQueue.new 1 - @queue << nil + @pool_size = pool_size + + @queue = Thread::SizedQueue.new @pool_size + setup_queue end def checkout @@ -31,7 +33,8 @@ def close_all connection.finish end end - @queue.push(nil) + + setup_queue end private @@ -44,4 +47,8 @@ def setup_connection(connection) connection.start connection end + + def setup_queue + @pool_size.times { @queue.push(nil) } + end end diff --git a/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb b/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb new file mode 100644 index 00000000000000..df1a58d843623e --- /dev/null +++ b/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "rubygems/remote_fetcher" +require "bundler/fetcher/gem_remote_fetcher" +require_relative "../../support/artifice/helpers/artifice" +require "bundler/vendored_persistent.rb" + +RSpec.describe Bundler::Fetcher::GemRemoteFetcher do + describe "Parallel download" do + it "download using multiple connections from the pool" do + unless Bundler.rubygems.provides?(">= 4.0.0.dev") + skip "This example can only run when RubyGems supports multiple http connection pool" + end + + require_relative "../../support/artifice/helpers/endpoint" + concurrent_ruby_path = Dir[scoped_base_system_gem_path.join("gems/concurrent-ruby-*/lib/concurrent-ruby")].first + $LOAD_PATH.unshift(concurrent_ruby_path) + require "concurrent-ruby" + + require_rack_test + responses = [] + + latch1 = Concurrent::CountDownLatch.new + latch2 = Concurrent::CountDownLatch.new + previous_client = Gem::Request::ConnectionPools.client + dummy_endpoint = Class.new(Endpoint) do + get "/foo" do + latch2.count_down + latch1.wait + + responses << "foo" + end + + get "/bar" do + responses << "bar" + + latch1.count_down + end + end + + Artifice.activate_with(dummy_endpoint) + Gem::Request::ConnectionPools.client = Gem::Net::HTTP + + first_request = Thread.new do + subject.fetch_path("https://example.org/foo") + end + second_request = Thread.new do + latch2.wait + subject.fetch_path("https://example.org/bar") + end + + [first_request, second_request].each(&:join) + + expect(responses).to eq(["bar", "foo"]) + ensure + Artifice.deactivate + Gem::Request::ConnectionPools.client = previous_client + end + end +end diff --git a/test/rubygems/test_gem_request_connection_pools.rb b/test/rubygems/test_gem_request_connection_pools.rb index 966447bff64192..2860deabf7132d 100644 --- a/test/rubygems/test_gem_request_connection_pools.rb +++ b/test/rubygems/test_gem_request_connection_pools.rb @@ -148,4 +148,16 @@ def test_thread_waits_for_connection end end.join end + + def test_checkouts_multiple_connections_from_the_pool + uri = Gem::URI.parse("http://example/some_endpoint") + pools = Gem::Request::ConnectionPools.new nil, [], 2 + pool = pools.pool_for uri + + pool.checkout + + Thread.new do + assert_not_nil(pool.checkout) + end.join + end end diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb index 9776a96b90eded..ddc19e2939d447 100644 --- a/tool/bundler/test_gems.rb +++ b/tool/bundler/test_gems.rb @@ -11,6 +11,7 @@ gem "rb_sys" gem "fiddle" gem "rubygems-generate_index", "~> 1.1" +gem "concurrent-ruby" gem "psych" gem "etc", platforms: [:ruby, :windows] gem "open3" diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index bc103af8605282..b11a9607255aa1 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -4,6 +4,7 @@ GEM base64 (0.3.0) builder (3.3.0) compact_index (0.15.0) + concurrent-ruby (1.3.5) date (3.5.0) date (3.5.0-java) etc (1.4.6) @@ -59,6 +60,7 @@ PLATFORMS DEPENDENCIES builder (~> 3.2) compact_index (~> 0.15.0) + concurrent-ruby etc fiddle open3 @@ -75,6 +77,7 @@ CHECKSUMS base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f compact_index (0.15.0) sha256=5c6c404afca8928a7d9f4dde9524f6e1610db17e675330803055db282da84a8b + concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6 date (3.5.0) sha256=5e74fd6c04b0e65d97ad4f3bb5cb2d8efb37f386cc848f46310b4593ffc46ee5 date (3.5.0-java) sha256=d6876651299185b935e1b834a353e3a1d1db054be478967e8104e30a9a8f1127 etc (1.4.6) sha256=0f7e9e7842ea5e3c3bd9bc81746ebb8c65ea29e4c42a93520a0d638129c7de01 From d58a45d32ffe8afed1685e54017fb81cea898867 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 4 Dec 2025 09:06:10 +0100 Subject: [PATCH 148/367] [ruby/json] Fix a regression in parsing of unicode surogate pairs Fix: https://github.com/ruby/json/issues/912 In the case of surogate pairs we consume two backslashes, so `json_next_backslash` need to ensure it's not sending us back in the stream. https://github.com/ruby/json/commit/0fce370c41 --- ext/json/parser/parser.c | 4 +++- test/json/json_parser_test.rb | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 5b7cd835cd7d1e..c84c7ed660a06d 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -651,7 +651,9 @@ static inline const char *json_next_backslash(const char *pe, const char *string positions->size--; const char *next_position = positions->positions[0]; positions->positions++; - return next_position; + if (next_position >= pe) { + return next_position; + } } if (positions->has_more) { diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 3e662bda324ba5..257e4f1736a2d0 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -325,6 +325,13 @@ def test_invalid_unicode_escape assert_raise(JSON::ParserError) { parse('"\u111___"') } end + def test_unicode_followed_by_newline + # Ref: https://github.com/ruby/json/issues/912 + assert_equal "🌌\n".bytes, JSON.parse('"\ud83c\udf0c\n"').bytes + assert_equal "🌌\n", JSON.parse('"\ud83c\udf0c\n"') + assert_predicate JSON.parse('"\ud83c\udf0c\n"'), :valid_encoding? + end + def test_invalid_surogates assert_raise(JSON::ParserError) { parse('"\\uD800"') } assert_raise(JSON::ParserError) { parse('"\\uD800_________________"') } From d343968ec3a899a29ff4e330dc914d67972ebe85 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 4 Dec 2025 09:12:10 +0100 Subject: [PATCH 149/367] [ruby/json] Release 2.17.1 https://github.com/ruby/json/commit/e5e4fd558e --- ext/json/lib/json/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index b7de7c27e21446..4ed61c43c8b187 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module JSON - VERSION = '2.17.0' + VERSION = '2.17.1' end From 6917321686ad99551925d87ef353ddb21149d3db Mon Sep 17 00:00:00 2001 From: git Date: Thu, 4 Dec 2025 08:14:02 +0000 Subject: [PATCH 150/367] Update default gems list at d343968ec3a899a29ff4e330dc914d [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index f3e7f1ae0e3a6e..89f45deb99cf7b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -198,7 +198,7 @@ The following default gems are updated. * io-console 0.8.1 * io-nonblock 0.3.2 * io-wait 0.4.0.dev -* json 2.17.0 +* json 2.17.1 * net-http 0.8.0 * openssl 4.0.0.pre * optparse 0.8.0 From f8231dacd570e8c96c385fb9691719756b1dbc7d Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Thu, 4 Dec 2025 14:29:35 +0900 Subject: [PATCH 151/367] Ractor.store_if_absent should not warn ```ruby $VERBOSE = true Ractor.store_if_absent :key do end #=> warning: the block passed to 'Ractor.store_if_absent' defined at :474 may be ignored ``` --- ractor.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ractor.rb b/ractor.rb index 7c00e148a120aa..3c0c6744e6148e 100644 --- a/ractor.rb +++ b/ractor.rb @@ -472,6 +472,7 @@ def self.[]=(sym, val) # }.map(&:value).uniq.size #=> 1 and f() is called only once # def self.store_if_absent(sym) + Primitive.attr! :use_block Primitive.ractor_local_value_store_if_absent(sym) end From 7f41c3e7b1373f35cc073241b3773289be9be03e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 4 Dec 2025 17:35:38 +0900 Subject: [PATCH 152/367] Add `rb_eval_cmd_call_kw` to shortcut --- internal/vm.h | 1 + signal.c | 2 +- variable.c | 2 +- vm_eval.c | 14 ++++++++++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/internal/vm.h b/internal/vm.h index e5ed47afae0e29..7fae590d198fdd 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -78,6 +78,7 @@ void rb_check_stack_overflow(void); VALUE rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, VALUE data2, long flags); struct vm_ifunc *rb_current_ifunc(void); VALUE rb_gccct_clear_table(VALUE); +VALUE rb_eval_cmd_call_kw(VALUE cmd, int argc, const VALUE *argv, int kw_splat); #if USE_YJIT || USE_ZJIT /* vm_exec.c */ diff --git a/signal.c b/signal.c index 5971e12ee9b00f..12af9058a25439 100644 --- a/signal.c +++ b/signal.c @@ -1066,7 +1066,7 @@ signal_exec(VALUE cmd, int sig) EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { VALUE signum = INT2NUM(sig); - rb_eval_cmd_kw(cmd, rb_ary_new3(1, signum), RB_NO_KEYWORDS); + rb_eval_cmd_call_kw(cmd, 1, &signum, RB_NO_KEYWORDS); } EC_POP_TAG(); ec = GET_EC(); diff --git a/variable.c b/variable.c index faeffec660a9f2..47b521856676dd 100644 --- a/variable.c +++ b/variable.c @@ -862,7 +862,7 @@ rb_define_virtual_variable( static void rb_trace_eval(VALUE cmd, VALUE val) { - rb_eval_cmd_kw(cmd, rb_ary_new3(1, val), RB_NO_KEYWORDS); + rb_eval_cmd_call_kw(cmd, 1, &val, RB_NO_KEYWORDS); } VALUE diff --git a/vm_eval.c b/vm_eval.c index 281d63a1b8268a..12bdabc3302f68 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -2147,6 +2147,17 @@ rb_eval_string_wrap(const char *str, int *pstate) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat) +{ + Check_Type(arg, T_ARRAY); + int argc = RARRAY_LENINT(arg); + const VALUE *argv = RARRAY_CONST_PTR(arg); + VALUE val = rb_eval_cmd_call_kw(cmd, argc, argv, kw_splat); + RB_GC_GUARD(arg); + return val; +} + +VALUE +rb_eval_cmd_call_kw(VALUE cmd, int argc, const VALUE *argv, int kw_splat) { enum ruby_tag_type state; volatile VALUE val = Qnil; /* OK */ @@ -2155,8 +2166,7 @@ rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat) EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { if (!RB_TYPE_P(cmd, T_STRING)) { - val = rb_funcallv_kw(cmd, idCall, RARRAY_LENINT(arg), - RARRAY_CONST_PTR(arg), kw_splat); + val = rb_funcallv_kw(cmd, idCall, argc, argv, kw_splat); } else { val = eval_string_with_cref(rb_vm_top_self(), cmd, NULL, 0, 0); From b872fbe3dd3fe2601e4cddfd3179b5166dc121d4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 4 Dec 2025 18:07:32 +0900 Subject: [PATCH 153/367] Deprecate `rb_eval_cmd_kw` --- include/ruby/internal/intern/vm.h | 3 +++ spec/ruby/optional/capi/ext/kernel_spec.c | 4 +++ spec/ruby/optional/capi/kernel_spec.rb | 30 ++++++++++++----------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h index 29e0c7f534acb1..779f77fe91fcb5 100644 --- a/include/ruby/internal/intern/vm.h +++ b/include/ruby/internal/intern/vm.h @@ -95,6 +95,7 @@ VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv); */ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat); +#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL /** * This API is practically a variant of rb_proc_call_kw() now. Historically * when there still was a concept called `$SAFE`, this was an API for that. @@ -109,7 +110,9 @@ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int k * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. * @return What the command evaluates to. */ +RBIMPL_ATTR_DEPRECATED_INTERNAL(4.0) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat); +#endif /** * Identical to rb_funcallv(), except it takes Ruby's array instead of C's. diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index ff71a7e5892219..a8fed21b5900b6 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -117,9 +117,11 @@ VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) { return rb_eval_string(RSTRING_PTR(str)); } +#ifndef RUBY_VERSION_IS_4_0 VALUE kernel_spec_rb_eval_cmd_kw(VALUE self, VALUE cmd, VALUE args, VALUE kw_splat) { return rb_eval_cmd_kw(cmd, args, NUM2INT(kw_splat)); } +#endif VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) { rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before"))); @@ -403,7 +405,9 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_category_warn_deprecated_with_integer_extra_value", kernel_spec_rb_category_warn_deprecated_with_integer_extra_value, 1); rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4); rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1); +#ifndef RUBY_VERSION_IS_4_0 rb_define_method(cls, "rb_eval_cmd_kw", kernel_spec_rb_eval_cmd_kw, 3); +#endif rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1); rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1); rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2); diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb index d915a72c22af75..6633ee50c1f8ac 100644 --- a/spec/ruby/optional/capi/kernel_spec.rb +++ b/spec/ruby/optional/capi/kernel_spec.rb @@ -635,22 +635,24 @@ def proc_caller end end - describe "rb_eval_cmd_kw" do - it "evaluates a string of ruby code" do - @s.rb_eval_cmd_kw("1+1", [], 0).should == 2 - end + ruby_version_is ""..."4.0" do + describe "rb_eval_cmd_kw" do + it "evaluates a string of ruby code" do + @s.rb_eval_cmd_kw("1+1", [], 0).should == 2 + end - it "calls a proc with the supplied arguments" do - @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8] - end + it "calls a proc with the supplied arguments" do + @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8] + end - it "calls a proc with keyword arguments if kw_splat is non zero" do - a_proc = -> *x, **y { - res = x.map { |i| i + 1 } - y.each { |k, v| res << k; res << v } - res - } - @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3] + it "calls a proc with keyword arguments if kw_splat is non zero" do + a_proc = -> *x, **y { + res = x.map { |i| i + 1 } + y.each { |k, v| res << k; res << v } + res + } + @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3] + end end end From 8099e9d2d1b47ef4ce2fa184bea079a270ceb466 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 4 Dec 2025 18:08:23 +0900 Subject: [PATCH 154/367] [DOC] Fix a macro name --- doc/extension.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/extension.rdoc b/doc/extension.rdoc index ba59d107ab4c84..6cf4c4926c93fc 100644 --- a/doc/extension.rdoc +++ b/doc/extension.rdoc @@ -2047,7 +2047,7 @@ the *_kw functions introduced in Ruby 2.7. #define rb_proc_call_with_block_kw(p, c, v, b, kw) rb_proc_call_with_block(p, c, v, b) #define rb_method_call_kw(c, v, m, kw) rb_method_call(c, v, m) #define rb_method_call_with_block_kw(c, v, m, b, kw) rb_method_call_with_block(c, v, m, b) - #define rb_eval_cmd_kwd(c, a, kw) rb_eval_cmd(c, a, 0) + #define rb_eval_cmd_kw(c, a, kw) rb_eval_cmd(c, a, 0) #endif == Appendix C. Functions available for use in extconf.rb From 465a86c3417d2936c311d9571aa9b6494a83eed8 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:58:51 +0100 Subject: [PATCH 155/367] [ruby/prism] Fix `%Q` with newline delimiter and heredoc interpolation The lexer did not jump to the `heredoc_end`, causing the heredoc end delimiter to be parsed twice. Normally the heredocs get flushed when a newline is encountered. But because the newline is part of the string delimiter, that codepath is not taken. Fixes [Bug #21758] https://github.com/ruby/prism/commit/7440eb4b11 --- prism/prism.c | 7 ++++++ .../heredoc_percent_q_newline_delimiter.txt | 11 ++++++++++ .../heredoc_percent_q_newline_delimiter.txt | 22 +++++++++++++++++++ test/prism/ruby/parser_test.rb | 3 +++ test/prism/ruby/ruby_parser_test.rb | 1 + 5 files changed, 44 insertions(+) create mode 100644 test/prism/errors/heredoc_percent_q_newline_delimiter.txt create mode 100644 test/prism/fixtures/heredoc_percent_q_newline_delimiter.txt diff --git a/prism/prism.c b/prism/prism.c index 02247734e2ace7..77ac74192e6f66 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -11612,6 +11612,13 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_LABEL_END); } + // When the delimiter itself is a newline, we won't + // get a chance to flush heredocs in the usual places since + // the newline is already consumed. + if (term == '\n' && parser->heredoc_end) { + parser_flush_heredoc_end(parser); + } + lex_state_set(parser, PM_LEX_STATE_END); lex_mode_pop(parser); LEX(PM_TOKEN_STRING_END); diff --git a/test/prism/errors/heredoc_percent_q_newline_delimiter.txt b/test/prism/errors/heredoc_percent_q_newline_delimiter.txt new file mode 100644 index 00000000000000..73664c071f6208 --- /dev/null +++ b/test/prism/errors/heredoc_percent_q_newline_delimiter.txt @@ -0,0 +1,11 @@ +%q +#{< Date: Fri, 5 Dec 2025 00:27:45 +0900 Subject: [PATCH 156/367] [Bug #21764] Propagate the encoding of ID to warning --- io.c | 20 ++++---------------- spec/ruby/language/predefined_spec.rb | 16 ++++++++++++++-- string.c | 17 ++++++++++++++++- test/ruby/test_string.rb | 14 +++++++++++++- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/io.c b/io.c index a5a8139f2914c9..549e7e1317ef30 100644 --- a/io.c +++ b/io.c @@ -8641,31 +8641,19 @@ rb_f_printf(int argc, VALUE *argv, VALUE _) return Qnil; } -static void -deprecated_str_setter(VALUE val, ID id, VALUE *var) -{ - rb_str_setter(val, id, &val); - if (!NIL_P(val)) { - rb_warn_deprecated("'%s'", NULL, rb_id2name(id)); - } - *var = val; -} +extern void rb_deprecated_str_setter(VALUE val, ID id, VALUE *var); static void deprecated_rs_setter(VALUE val, ID id, VALUE *var) { + rb_deprecated_str_setter(val, id, &val); if (!NIL_P(val)) { - if (!RB_TYPE_P(val, T_STRING)) { - rb_raise(rb_eTypeError, "value of %"PRIsVALUE" must be String", rb_id2str(id)); - } if (rb_str_equal(val, rb_default_rs)) { val = rb_default_rs; } else { val = rb_str_frozen_bare_string(val); } - rb_enc_str_coderange(val); - rb_warn_deprecated("'%s'", NULL, rb_id2name(id)); } *var = val; } @@ -15730,7 +15718,7 @@ Init_IO(void) rb_define_method(rb_cIO, "initialize", rb_io_initialize, -1); rb_output_fs = Qnil; - rb_define_hooked_variable("$,", &rb_output_fs, 0, deprecated_str_setter); + rb_define_hooked_variable("$,", &rb_output_fs, 0, rb_deprecated_str_setter); rb_default_rs = rb_fstring_lit("\n"); /* avoid modifying RS_default */ rb_vm_register_global_object(rb_default_rs); @@ -15740,7 +15728,7 @@ Init_IO(void) rb_gvar_ractor_local("$/"); // not local but ractor safe rb_define_hooked_variable("$-0", &rb_rs, 0, deprecated_rs_setter); rb_gvar_ractor_local("$-0"); // not local but ractor safe - rb_define_hooked_variable("$\\", &rb_output_rs, 0, deprecated_str_setter); + rb_define_hooked_variable("$\\", &rb_output_rs, 0, rb_deprecated_str_setter); rb_define_virtual_variable("$_", get_LAST_READ_LINE, set_LAST_READ_LINE); rb_gvar_ractor_local("$_"); diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb index f2488615aaec37..fc1667a38fe8d0 100644 --- a/spec/ruby/language/predefined_spec.rb +++ b/spec/ruby/language/predefined_spec.rb @@ -748,6 +748,10 @@ def foo it "raises a TypeError if assigned a boolean" do -> { $/ = true }.should raise_error(TypeError, 'value of $/ must be String') end + + it "warns if assigned non-nil" do + -> { $/ = "_" }.should complain(/warning: (?:non-nil )?[`']\$\/' is deprecated/) + end end describe "Predefined global $-0" do @@ -825,6 +829,10 @@ def foo it "raises a TypeError if assigned a boolean" do -> { $-0 = true }.should raise_error(TypeError, 'value of $-0 must be String') end + + it "warns if assigned non-nil" do + -> { $-0 = "_" }.should complain(/warning: (?:non-nil )?[`']\$-0' is deprecated/) + end end describe "Predefined global $\\" do @@ -864,6 +872,10 @@ def foo -> { $\ = 1 }.should raise_error(TypeError, 'value of $\ must be String') -> { $\ = true }.should raise_error(TypeError, 'value of $\ must be String') end + + it "warns if assigned non-nil" do + -> { $\ = "_" }.should complain(/warning: (?:non-nil )?[`']\$\\' is deprecated/) + end end describe "Predefined global $," do @@ -880,7 +892,7 @@ def foo end it "warns if assigned non-nil" do - -> { $, = "_" }.should complain(/warning: [`']\$,' is deprecated/) + -> { $, = "_" }.should complain(/warning: (?:non-nil )?[`']\$,' is deprecated/) end end @@ -917,7 +929,7 @@ def foo end it "warns if assigned non-nil" do - -> { $; = "_" }.should complain(/warning: [`']\$;' is deprecated/) + -> { $; = "_" }.should complain(/warning: (?:non-nil )?[`']\$;' is deprecated/) end end diff --git a/string.c b/string.c index 0370fc5d1e02e6..065e8c4d8c94dc 100644 --- a/string.c +++ b/string.c @@ -11375,6 +11375,21 @@ rb_str_setter(VALUE val, ID id, VALUE *var) *var = val; } +static void +nil_setter_warning(ID id) +{ + rb_warn_deprecated("'%"PRIsVALUE"'", NULL, rb_id2str(id)); +} + +void +rb_deprecated_str_setter(VALUE val, ID id, VALUE *var) +{ + rb_str_setter(val, id, var); + if (!NIL_P(*var)) { + nil_setter_warning(id); + } +} + static void rb_fs_setter(VALUE val, ID id, VALUE *var) { @@ -11385,7 +11400,7 @@ rb_fs_setter(VALUE val, ID id, VALUE *var) rb_id2str(id)); } if (!NIL_P(val)) { - rb_warn_deprecated("'$;'", NULL); + nil_setter_warning(id); } *var = val; } diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 50dc5ec2f95875..6fdc2bdaa8cdfb 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -1886,6 +1886,17 @@ def test_fs assert_raise_with_message(TypeError, /\$;/) { $; = [] } + name = "\u{5206 5217}" + assert_separately([], "#{<<~"do;"}\n#{<<~"end;"}") + do; + alias $#{name} $; + assert_deprecated_warning(/\\$#{name}/) { $#{name} = "" } + assert_raise_with_message(TypeError, /\\$#{name}/) { $#{name} = 1 } + end; + end + + def test_fs_gc + return unless @cls == String assert_separately(%W[-W0], "#{<<~"begin;"}\n#{<<~'end;'}") bug = '[ruby-core:79582] $; must not be GCed' @@ -2761,7 +2772,7 @@ def (hyphen = Object.new).to_str; "-"; end assert_equal([S("abcdb"), S("c"), S("e")], S("abcdbce").rpartition(/b\Kc/)) end - def test_fs_setter + def test_rs return unless @cls == String assert_raise(TypeError) { $/ = 1 } @@ -2769,6 +2780,7 @@ def test_fs_setter assert_separately([], "#{<<~"do;"}\n#{<<~"end;"}") do; alias $#{name} $/ + assert_deprecated_warning(/\\$#{name}/) { $#{name} = "" } assert_raise_with_message(TypeError, /\\$#{name}/) { $#{name} = 1 } end; end From 29a12297c3594ed24102d62e16dfd6d4e5e328f3 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 01:04:08 +0900 Subject: [PATCH 157/367] Refine non-nil warnings for the deprecated variables --- string.c | 2 +- test/ruby/test_io.rb | 8 ++++---- test/ruby/test_string.rb | 18 ++++++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/string.c b/string.c index 065e8c4d8c94dc..34c79eca8e5cff 100644 --- a/string.c +++ b/string.c @@ -11378,7 +11378,7 @@ rb_str_setter(VALUE val, ID id, VALUE *var) static void nil_setter_warning(ID id) { - rb_warn_deprecated("'%"PRIsVALUE"'", NULL, rb_id2str(id)); + rb_warn_deprecated("non-nil '%"PRIsVALUE"'", NULL, rb_id2str(id)); } void diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index dd8ccbc96a63a5..1adf47ac51a2d6 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -2907,10 +2907,10 @@ def test_print end def test_print_separators - EnvUtil.suppress_warning { - $, = ':' - $\ = "\n" - } + assert_deprecated_warning(/non-nil '\$,'/) {$, = ":"} + assert_raise(TypeError) {$, = 1} + assert_deprecated_warning(/non-nil '\$\\'/) {$\ = "\n"} + assert_raise(TypeError) {$/ = 1} pipe(proc do |w| w.print('a') EnvUtil.suppress_warning {w.print('a','b','c')} diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 6fdc2bdaa8cdfb..1fe0629331ec30 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -1883,9 +1883,13 @@ def test_split_with_block def test_fs return unless @cls == String - assert_raise_with_message(TypeError, /\$;/) { - $; = [] - } + begin + fs = $; + assert_deprecated_warning(/non-nil '\$;'/) {$; = "x"} + assert_raise_with_message(TypeError, /\$;/) {$; = []} + ensure + EnvUtil.suppress_warning {$; = fs} + end name = "\u{5206 5217}" assert_separately([], "#{<<~"do;"}\n#{<<~"end;"}") do; @@ -2775,7 +2779,13 @@ def (hyphen = Object.new).to_str; "-"; end def test_rs return unless @cls == String - assert_raise(TypeError) { $/ = 1 } + begin + rs = $/ + assert_deprecated_warning(/non-nil '\$\/'/) { $/ = "" } + assert_raise(TypeError) { $/ = 1 } + ensure + EnvUtil.suppress_warning { $/ = rs } + end name = "\u{5206 884c}" assert_separately([], "#{<<~"do;"}\n#{<<~"end;"}") do; From 3730022787086852fa2fbc94ffda6ec8c8fbc0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Thu, 4 Dec 2025 17:25:45 +0100 Subject: [PATCH 158/367] Remove mismatched indentations warning (#15410) --- test/ruby/test_zjit.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 6609bb2461c31d..00550bbe2058e2 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -1081,20 +1081,20 @@ def test(x) end def test_opt_duparray_send_include_p_redefined - assert_compiles '[:true, :false]', %q{ - class Array - alias_method :old_include?, :include? - def include?(x) - old_include?(x) ? :true : :false - end + assert_compiles '[:true, :false]', %q{ + class Array + alias_method :old_include?, :include? + def include?(x) + old_include?(x) ? :true : :false end + end - def test(x) - [:y, 1].include?(x) - end - [test(1), test("n")] - }, insns: [:opt_duparray_send], call_threshold: 1 - end + def test(x) + [:y, 1].include?(x) + end + [test(1), test("n")] + }, insns: [:opt_duparray_send], call_threshold: 1 + end def test_opt_newarray_send_hash assert_compiles 'Integer', %q{ From f2cd772329b8d07e29ed114480ff99ad36acbd75 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 5 Dec 2025 01:13:55 +0900 Subject: [PATCH 159/367] (experimental) RUBY_TYPED_FROZEN_SHAREABLE_NO_REC `T_DATA` has a flag `RUBY_TYPED_FROZEN_SHAREABLE` which means if the `T_DATA` object is frozen, it can be sharable. On the `Ractor.make_sharable(obj)`, rechable objects from the `T_DATA` object will be apply `Ractor.make_shareable` recursively. `RUBY_TYPED_FROZEN_SHAREABLE_NO_REC` is similar to the `RUBY_TYPED_FROZEN_SHAREABLE`, but doesn't apply `Ractor.make_sharable` recursively for children. If it refers to unshareable objects, it will simply raise an error. I'm not sure this pattern is common or not, so it is not in public. If we find more cases, we can discuss publication. --- include/ruby/internal/core/rtypeddata.h | 6 +++ ractor.c | 50 ++++++++++++++++++------- ractor_core.h | 3 ++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index aaf8f7997c8459..ee79c2e2a90a18 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -157,6 +157,12 @@ rbimpl_typeddata_flags { */ RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE, + // experimental flag + // Similar to RUBY_TYPED_FROZEN_SHAREABLE, but doesn't make shareable + // reachable objects from this T_DATA object on the Ractor.make_shareable. + // If it refers to unsharable objects, simply raise an error. + // RUBY_TYPED_FROZEN_SHAREABLE_NO_REC = RUBY_FL_FINALIZE, + /** * This flag has something to do with our garbage collector. These days * ruby objects are "generational". There are those who are young and diff --git a/ractor.c b/ractor.c index ea09583c5f7f7c..6bd38b6ab875f0 100644 --- a/ractor.c +++ b/ractor.c @@ -1427,6 +1427,26 @@ allow_frozen_shareable_p(VALUE obj) return false; } +static enum obj_traverse_iterator_result +make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_result result) +{ + if (!RB_OBJ_FROZEN_RAW(obj)) { + rb_funcall(obj, idFreeze, 0); + + if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) { + rb_raise(rb_eRactorError, "#freeze does not freeze object correctly"); + } + + if (RB_OBJ_SHAREABLE_P(obj)) { + return traverse_skip; + } + } + + return result; +} + +static int obj_refer_only_shareables_p(VALUE obj); + static enum obj_traverse_iterator_result make_shareable_check_shareable(VALUE obj) { @@ -1436,7 +1456,21 @@ make_shareable_check_shareable(VALUE obj) return traverse_skip; } else if (!allow_frozen_shareable_p(obj)) { - if (rb_obj_is_proc(obj)) { + VM_ASSERT(RB_TYPE_P(obj, T_DATA)); + const rb_data_type_t *type = RTYPEDDATA_TYPE(obj); + + if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC) { + if (obj_refer_only_shareables_p(obj)) { + make_shareable_check_shareable_freeze(obj, traverse_skip); + RB_OBJ_SET_SHAREABLE(obj); + return traverse_skip; + } + else { + rb_raise(rb_eRactorError, + "can not make shareable object for %+"PRIsVALUE" because it refers unshareable objects", obj); + } + } + else if (rb_obj_is_proc(obj)) { rb_proc_ractor_make_shareable(obj, Qundef); return traverse_cont; } @@ -1466,19 +1500,7 @@ make_shareable_check_shareable(VALUE obj) break; } - if (!RB_OBJ_FROZEN_RAW(obj)) { - rb_funcall(obj, idFreeze, 0); - - if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) { - rb_raise(rb_eRactorError, "#freeze does not freeze object correctly"); - } - - if (RB_OBJ_SHAREABLE_P(obj)) { - return traverse_skip; - } - } - - return traverse_cont; + return make_shareable_check_shareable_freeze(obj, traverse_cont); } static enum obj_traverse_iterator_result diff --git a/ractor_core.h b/ractor_core.h index 81374c0769f6ca..d6f9d4eda0fd4b 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -9,6 +9,9 @@ #define RACTOR_CHECK_MODE (VM_CHECK_MODE || RUBY_DEBUG) && (SIZEOF_UINT64_T == SIZEOF_VALUE) #endif +// experimental flag because it is not sure it is the common pattern +#define RUBY_TYPED_FROZEN_SHAREABLE_NO_REC RUBY_FL_FINALIZE + struct rb_ractor_sync { // ractor lock rb_nativethread_lock_t lock; From 4c893e2ff1077b977483da14c0540e9247b87c7e Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 5 Dec 2025 01:19:55 +0900 Subject: [PATCH 160/367] Method and UnboundMethod can be sharable with `RUBY_TYPED_FROZEN_SHAREABLE_NO_REC`, if the receiver object is shareable on Method objects. --- bootstraptest/test_ractor.rb | 22 ++++++++++++++++++++++ proc.c | 2 +- test/ruby/test_ractor.rb | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 6c3a45148c23e1..cc00fa3586af93 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1203,6 +1203,28 @@ def /(other) end } +# Ractor.make_sharable(Method/UnboundMethod) +assert_equal 'true', %q{ + # raise because receiver is unsharable + begin + _m0 = Ractor.make_shareable(self.method(:__id__)) + rescue => e + raise e unless e.message =~ /can not make shareable object/ + else + raise "no error" + end + + # Method with sharable receiver + M1 = Ractor.make_shareable(Object.method(:__id__)) + + # UnboundMethod + M2 = Ractor.make_shareable(Object.instance_method(:__id__)) + + Ractor.new do + Object.__id__ == M1.call && M1.call == M2.bind_call(Object) + end.value +} + # Ractor.shareable?(recursive_objects) assert_equal '[false, false]', %q{ y = [] diff --git a/proc.c b/proc.c index 2b14c38938b8f0..2df8fbee5e23a9 100644 --- a/proc.c +++ b/proc.c @@ -1668,7 +1668,7 @@ static const rb_data_type_t method_data_type = { NULL, // No external memory to report, bm_mark_and_move, }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE | RUBY_TYPED_FROZEN_SHAREABLE_NO_REC }; VALUE diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index efc67338e4c0dd..09c470911a5aaa 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -47,7 +47,7 @@ def test_shareability_error_uses_inspect def x.to_s raise "this should not be called" end - assert_unshareable(x, "can not make shareable object for #", exception: Ractor::Error) + assert_unshareable(x, "can not make shareable object for # because it refers unshareable objects", exception: Ractor::Error) end def test_default_thread_group From 6fe1c1591106ef428f42cd5601be1cab994dae9a Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Tue, 18 Nov 2025 22:40:42 +0900 Subject: [PATCH 161/367] [ruby/openssl] Revert "rewriting most of the asn1 init code in ruby" This reverts commit https://github.com/ruby/openssl/commit/830505172882. The commit is part of the bigger effort to rewrite OpenSSL::ASN1 in Ruby. OpenSSL::ASN1 is relatively isolated from the rest of ruby/openssl and is not tightly bound to the OpenSSL API. The current implementation also needs a major refactor for several reasons, so this remains a long-term goal. However, the work is not yet complete. We are close to releasing v4.0.0, and we want to avoid shipping fragmented code in a stable branch. The changes can be reapplied when the rest is ready. https://github.com/ruby/openssl/commit/362942dcbf --- ext/openssl/lib/openssl.rb | 1 - ext/openssl/lib/openssl/asn1.rb | 188 ------------------------- ext/openssl/ossl_asn1.c | 238 +++++++++++++++++++++++++++++--- 3 files changed, 216 insertions(+), 211 deletions(-) delete mode 100644 ext/openssl/lib/openssl/asn1.rb diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb index 48f88dcbba9a6c..41927f32ae8238 100644 --- a/ext/openssl/lib/openssl.rb +++ b/ext/openssl/lib/openssl.rb @@ -12,7 +12,6 @@ require 'openssl.so' -require_relative 'openssl/asn1' require_relative 'openssl/bn' require_relative 'openssl/cipher' require_relative 'openssl/digest' diff --git a/ext/openssl/lib/openssl/asn1.rb b/ext/openssl/lib/openssl/asn1.rb deleted file mode 100644 index 89fa28e1fb0b4a..00000000000000 --- a/ext/openssl/lib/openssl/asn1.rb +++ /dev/null @@ -1,188 +0,0 @@ -# frozen_string_literal: true -#-- -# -# = Ruby-space definitions that completes C-space funcs for ASN.1 -# -# = Licence -# This program is licensed under the same licence as Ruby. -# (See the file 'COPYING'.) -#++ - -module OpenSSL - module ASN1 - class ASN1Data - # - # Carries the value of a ASN.1 type. - # Please confer Constructive and Primitive for the mappings between - # ASN.1 data types and Ruby classes. - # - attr_accessor :value - - # An Integer representing the tag number of this ASN1Data. Never +nil+. - attr_accessor :tag - - # A Symbol representing the tag class of this ASN1Data. Never +nil+. - # See ASN1Data for possible values. - attr_accessor :tag_class - - # - # Never +nil+. A boolean value indicating whether the encoding uses - # indefinite length (in the case of parsing) or whether an indefinite - # length form shall be used (in the encoding case). - # In DER, every value uses definite length form. But in scenarios where - # large amounts of data need to be transferred it might be desirable to - # have some kind of streaming support available. - # For example, huge OCTET STRINGs are preferably sent in smaller-sized - # chunks, each at a time. - # This is possible in BER by setting the length bytes of an encoding - # to zero and by this indicating that the following value will be - # sent in chunks. Indefinite length encodings are always constructed. - # The end of such a stream of chunks is indicated by sending a EOC - # (End of Content) tag. SETs and SEQUENCEs may use an indefinite length - # encoding, but also primitive types such as e.g. OCTET STRINGS or - # BIT STRINGS may leverage this functionality (cf. ITU-T X.690). - # - attr_accessor :indefinite_length - - alias infinite_length indefinite_length - alias infinite_length= indefinite_length= - - # - # :call-seq: - # OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data - # - # _value_: Please have a look at Constructive and Primitive to see how Ruby - # types are mapped to ASN.1 types and vice versa. - # - # _tag_: An Integer indicating the tag number. - # - # _tag_class_: A Symbol indicating the tag class. Please cf. ASN1 for - # possible values. - # - # == Example - # asn1_int = OpenSSL::ASN1Data.new(42, 2, :UNIVERSAL) # => Same as OpenSSL::ASN1::Integer.new(42) - # tagged_int = OpenSSL::ASN1Data.new(42, 0, :CONTEXT_SPECIFIC) # implicitly 0-tagged INTEGER - # - def initialize(value, tag, tag_class) - raise ASN1Error, "invalid tag class" unless tag_class.is_a?(Symbol) - - @tag = tag - @value = value - @tag_class = tag_class - @indefinite_length = false - end - end - - module TaggedASN1Data - # - # May be used as a hint for encoding a value either implicitly or - # explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. - # _tagging_ is not set when a ASN.1 structure is parsed using - # OpenSSL::ASN1.decode. - # - attr_accessor :tagging - - # :call-seq: - # OpenSSL::ASN1::Primitive.new(value [, tag, tagging, tag_class ]) => Primitive - # - # _value_: is mandatory. - # - # _tag_: optional, may be specified for tagged values. If no _tag_ is - # specified, the UNIVERSAL tag corresponding to the Primitive sub-class - # is used by default. - # - # _tagging_: may be used as an encoding hint to encode a value either - # explicitly or implicitly, see ASN1 for possible values. - # - # _tag_class_: if _tag_ and _tagging_ are +nil+ then this is set to - # +:UNIVERSAL+ by default. If either _tag_ or _tagging_ are set then - # +:CONTEXT_SPECIFIC+ is used as the default. For possible values please - # cf. ASN1. - # - # == Example - # int = OpenSSL::ASN1::Integer.new(42) - # zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :IMPLICIT) - # private_explicit_zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :EXPLICIT, :PRIVATE) - # - def initialize(value, tag = nil, tagging = nil, tag_class = nil) - tag ||= ASN1.take_default_tag(self.class) - - raise ASN1Error, "must specify tag number" unless tag - - if tagging - raise ASN1Error, "invalid tagging method" unless tagging.is_a?(Symbol) - end - - tag_class ||= tagging ? :CONTEXT_SPECIFIC : :UNIVERSAL - - raise ASN1Error, "invalid tag class" unless tag_class.is_a?(Symbol) - - @tagging = tagging - super(value ,tag, tag_class) - end - end - - class Primitive < ASN1Data - include TaggedASN1Data - - undef_method :indefinite_length= - undef_method :infinite_length= - end - - class Constructive < ASN1Data - include TaggedASN1Data - include Enumerable - - # :call-seq: - # asn1_ary.each { |asn1| block } => asn1_ary - # - # Calls the given block once for each element in self, passing that element - # as parameter _asn1_. If no block is given, an enumerator is returned - # instead. - # - # == Example - # asn1_ary.each do |asn1| - # puts asn1 - # end - # - def each(&blk) - @value.each(&blk) - - self - end - end - - class Boolean < Primitive ; end - class Integer < Primitive ; end - class Enumerated < Primitive ; end - - class BitString < Primitive - attr_accessor :unused_bits - - def initialize(*) - super - - @unused_bits = 0 - end - end - - class EndOfContent < ASN1Data - def initialize - super("", 0, :UNIVERSAL) - end - end - - # :nodoc: - def self.take_default_tag(klass) - tag = CLASS_TAG_MAP[klass] - - return tag if tag - - sklass = klass.superclass - - return unless sklass - - take_default_tag(sklass) - end - end -end diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 38397ebda87c86..17cecd0e96cb56 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -11,6 +11,7 @@ static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, int yield, long *num_read); +static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); /* * DATE conversion @@ -199,6 +200,10 @@ ossl_asn1obj_to_string_long_name(const ASN1_OBJECT *obj) #define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) #define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) +#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) +#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) +#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) +#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) #define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) VALUE mASN1; @@ -226,6 +231,7 @@ static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ static VALUE sym_IMPLICIT, sym_EXPLICIT; static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; +static ID id_each; /* * Ruby to ASN1 converters @@ -669,6 +675,35 @@ ossl_asn1_class2sym(int tc) return sym_UNIVERSAL; } +/* + * call-seq: + * OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data + * + * _value_: Please have a look at Constructive and Primitive to see how Ruby + * types are mapped to ASN.1 types and vice versa. + * + * _tag_: An Integer indicating the tag number. + * + * _tag_class_: A Symbol indicating the tag class. Please cf. ASN1 for + * possible values. + * + * == Example + * asn1_int = OpenSSL::ASN1Data.new(42, 2, :UNIVERSAL) # => Same as OpenSSL::ASN1::Integer.new(42) + * tagged_int = OpenSSL::ASN1Data.new(42, 0, :CONTEXT_SPECIFIC) # implicitly 0-tagged INTEGER + */ +static VALUE +ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) +{ + if(!SYMBOL_P(tag_class)) + ossl_raise(eASN1Error, "invalid tag class"); + ossl_asn1_set_tag(self, tag); + ossl_asn1_set_value(self, value); + ossl_asn1_set_tag_class(self, tag_class); + ossl_asn1_set_indefinite_length(self, Qfalse); + + return self; +} + static VALUE to_der_internal(VALUE self, int constructed, int indef_len, VALUE body) { @@ -797,19 +832,20 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, if (tc == sym_UNIVERSAL && tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { VALUE klass = *ossl_asn1_info[tag].klass; - if (tag == V_ASN1_EOC) - asn1data = rb_funcall(cASN1EndOfContent, rb_intern("new"), 0); - else { - VALUE args[4] = { value, INT2NUM(tag), Qnil, tc }; - asn1data = rb_funcallv_public(klass, rb_intern("new"), 4, args); - } + VALUE args[4]; + args[0] = value; + args[1] = INT2NUM(tag); + args[2] = Qnil; + args[3] = tc; + asn1data = rb_obj_alloc(klass); + ossl_asn1_initialize(4, args, asn1data); if(tag == V_ASN1_BIT_STRING){ rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); } } else { - VALUE args[3] = { value, INT2NUM(tag), tc }; - asn1data = rb_funcallv_public(cASN1Data, rb_intern("new"), 3, args); + asn1data = rb_obj_alloc(cASN1Data); + ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); } return asn1data; @@ -845,20 +881,20 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, } if (tc == sym_UNIVERSAL) { - if (tag == V_ASN1_SEQUENCE) { - VALUE args[4] = { ary, INT2NUM(tag), Qnil, tc }; - asn1data = rb_funcallv_public(cASN1Sequence, rb_intern("new"), 4, args); - } else if (tag == V_ASN1_SET) { - VALUE args[4] = { ary, INT2NUM(tag), Qnil, tc }; - asn1data = rb_funcallv_public(cASN1Set, rb_intern("new"), 4, args); - } else { - VALUE args[4] = { ary, INT2NUM(tag), Qnil, tc }; - asn1data = rb_funcallv_public(cASN1Constructive, rb_intern("new"), 4, args); - } + VALUE args[4]; + if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET) + asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass); + else + asn1data = rb_obj_alloc(cASN1Constructive); + args[0] = ary; + args[1] = INT2NUM(tag); + args[2] = Qnil; + args[3] = tc; + ossl_asn1_initialize(4, args, asn1data); } else { - VALUE args[3] = {ary, INT2NUM(tag), tc}; - asn1data = rb_funcallv_public(cASN1Data, rb_intern("new"), 3, args); + asn1data = rb_obj_alloc(cASN1Data); + ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); } if (indefinite) @@ -1051,6 +1087,83 @@ ossl_asn1_decode_all(VALUE self, VALUE obj) return ary; } +/* + * call-seq: + * OpenSSL::ASN1::Primitive.new(value [, tag, tagging, tag_class ]) => Primitive + * + * _value_: is mandatory. + * + * _tag_: optional, may be specified for tagged values. If no _tag_ is + * specified, the UNIVERSAL tag corresponding to the Primitive sub-class + * is used by default. + * + * _tagging_: may be used as an encoding hint to encode a value either + * explicitly or implicitly, see ASN1 for possible values. + * + * _tag_class_: if _tag_ and _tagging_ are +nil+ then this is set to + * +:UNIVERSAL+ by default. If either _tag_ or _tagging_ are set then + * +:CONTEXT_SPECIFIC+ is used as the default. For possible values please + * cf. ASN1. + * + * == Example + * int = OpenSSL::ASN1::Integer.new(42) + * zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :IMPLICIT) + * private_explicit_zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :EXPLICIT, :PRIVATE) + */ +static VALUE +ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE value, tag, tagging, tag_class; + int default_tag; + + rb_scan_args(argc, argv, "13", &value, &tag, &tagging, &tag_class); + default_tag = ossl_asn1_default_tag(self); + + if (default_tag == -1 || argc > 1) { + if(NIL_P(tag)) + ossl_raise(eASN1Error, "must specify tag number"); + if(!NIL_P(tagging) && !SYMBOL_P(tagging)) + ossl_raise(eASN1Error, "invalid tagging method"); + if(NIL_P(tag_class)) { + if (NIL_P(tagging)) + tag_class = sym_UNIVERSAL; + else + tag_class = sym_CONTEXT_SPECIFIC; + } + if(!SYMBOL_P(tag_class)) + ossl_raise(eASN1Error, "invalid tag class"); + } + else{ + tag = INT2NUM(default_tag); + tagging = Qnil; + tag_class = sym_UNIVERSAL; + } + ossl_asn1_set_tag(self, tag); + ossl_asn1_set_value(self, value); + ossl_asn1_set_tagging(self, tagging); + ossl_asn1_set_tag_class(self, tag_class); + ossl_asn1_set_indefinite_length(self, Qfalse); + if (default_tag == V_ASN1_BIT_STRING) + rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0)); + + return self; +} + +static VALUE +ossl_asn1eoc_initialize(VALUE self) { + VALUE tag, tagging, tag_class, value; + tag = INT2FIX(0); + tagging = Qnil; + tag_class = sym_UNIVERSAL; + value = rb_str_new("", 0); + ossl_asn1_set_tag(self, tag); + ossl_asn1_set_value(self, value); + ossl_asn1_set_tagging(self, tagging); + ossl_asn1_set_tag_class(self, tag_class); + ossl_asn1_set_indefinite_length(self, Qfalse); + return self; +} + static VALUE ossl_asn1eoc_to_der(VALUE self) { @@ -1142,6 +1255,27 @@ ossl_asn1cons_to_der(VALUE self) return to_der_internal(self, 1, indef_len, str); } +/* + * call-seq: + * asn1_ary.each { |asn1| block } => asn1_ary + * + * Calls the given block once for each element in self, passing that element + * as parameter _asn1_. If no block is given, an enumerator is returned + * instead. + * + * == Example + * asn1_ary.each do |asn1| + * puts asn1 + * end + */ +static VALUE +ossl_asn1cons_each(VALUE self) +{ + rb_block_call(ossl_asn1_get_value(self), id_each, 0, 0, 0, 0); + + return self; +} + /* * call-seq: * OpenSSL::ASN1::ObjectId.register(object_id, short_name, long_name) @@ -1255,7 +1389,7 @@ ossl_asn1obj_eq(VALUE self, VALUE other) #define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \ static VALUE ossl_asn1_##klass(int argc, VALUE *argv, VALUE self)\ -{ return rb_funcallv_public(cASN1##klass, rb_intern("new"), argc, argv); } +{ return rb_funcall3(cASN1##klass, rb_intern("new"), argc, argv); } OSSL_ASN1_IMPL_FACTORY_METHOD(Boolean) OSSL_ASN1_IMPL_FACTORY_METHOD(Integer) @@ -1536,6 +1670,42 @@ Init_ossl_asn1(void) * puts int2.value # => 1 */ cASN1Data = rb_define_class_under(mASN1, "ASN1Data", rb_cObject); + /* + * Carries the value of a ASN.1 type. + * Please confer Constructive and Primitive for the mappings between + * ASN.1 data types and Ruby classes. + */ + rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0); + /* + * An Integer representing the tag number of this ASN1Data. Never +nil+. + */ + rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0); + /* + * A Symbol representing the tag class of this ASN1Data. Never +nil+. + * See ASN1Data for possible values. + */ + rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0); + /* + * Never +nil+. A boolean value indicating whether the encoding uses + * indefinite length (in the case of parsing) or whether an indefinite + * length form shall be used (in the encoding case). + * In DER, every value uses definite length form. But in scenarios where + * large amounts of data need to be transferred it might be desirable to + * have some kind of streaming support available. + * For example, huge OCTET STRINGs are preferably sent in smaller-sized + * chunks, each at a time. + * This is possible in BER by setting the length bytes of an encoding + * to zero and by this indicating that the following value will be + * sent in chunks. Indefinite length encodings are always constructed. + * The end of such a stream of chunks is indicated by sending a EOC + * (End of Content) tag. SETs and SEQUENCEs may use an indefinite length + * encoding, but also primitive types such as e.g. OCTET STRINGS or + * BIT STRINGS may leverage this functionality (cf. ITU-T X.690). + */ + rb_attr(cASN1Data, rb_intern("indefinite_length"), 1, 1, 0); + rb_define_alias(cASN1Data, "infinite_length", "indefinite_length"); + rb_define_alias(cASN1Data, "infinite_length=", "indefinite_length="); + rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3); rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0); /* Document-class: OpenSSL::ASN1::Primitive @@ -1603,6 +1773,16 @@ Init_ossl_asn1(void) * prim_zero_tagged_explicit = .new(value, 0, :EXPLICIT) */ cASN1Primitive = rb_define_class_under(mASN1, "Primitive", cASN1Data); + /* + * May be used as a hint for encoding a value either implicitly or + * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. + * _tagging_ is not set when a ASN.1 structure is parsed using + * OpenSSL::ASN1.decode. + */ + rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue); + rb_undef_method(cASN1Primitive, "indefinite_length="); + rb_undef_method(cASN1Primitive, "infinite_length="); + rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1); rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0); /* Document-class: OpenSSL::ASN1::Constructive @@ -1633,7 +1813,17 @@ Init_ossl_asn1(void) * set = OpenSSL::ASN1::Set.new( [ int, str ] ) */ cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data); + rb_include_module(cASN1Constructive, rb_mEnumerable); + /* + * May be used as a hint for encoding a value either implicitly or + * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. + * _tagging_ is not set when a ASN.1 structure is parsed using + * OpenSSL::ASN1.decode. + */ + rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue); + rb_define_method(cASN1Constructive, "initialize", ossl_asn1_initialize, -1); rb_define_method(cASN1Constructive, "to_der", ossl_asn1cons_to_der, 0); + rb_define_method(cASN1Constructive, "each", ossl_asn1cons_each, 0); #define OSSL_ASN1_DEFINE_CLASS(name, super) \ do{\ @@ -1682,10 +1872,13 @@ do{\ rb_define_alias(cASN1ObjectId, "short_name", "sn"); rb_define_alias(cASN1ObjectId, "long_name", "ln"); rb_define_method(cASN1ObjectId, "==", ossl_asn1obj_eq, 1); + rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0); + rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0); rb_define_method(cASN1EndOfContent, "to_der", ossl_asn1eoc_to_der, 0); class_tag_map = rb_hash_new(); + rb_gc_register_mark_object(class_tag_map); rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC)); rb_hash_aset(class_tag_map, cASN1Boolean, INT2NUM(V_ASN1_BOOLEAN)); rb_hash_aset(class_tag_map, cASN1Integer, INT2NUM(V_ASN1_INTEGER)); @@ -1709,5 +1902,6 @@ do{\ rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING)); rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING)); rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING)); - rb_define_const(mASN1, "CLASS_TAG_MAP", class_tag_map); + + id_each = rb_intern_const("each"); } From 5062c0c621d887367af8a054e5e5d83d7ec57dd3 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 30 Jul 2025 03:40:32 +0900 Subject: [PATCH 162/367] [ruby/openssl] Expand tabs in C source files Since around 2018, we have been using spaces for indentation for newly added code[1]. The mixed use of tabs and spaces has repeatedly confused new contributors who configured their editors to use a different tab size than 8. Since git blame can now skip specific commits, ruby/ruby did a mass reformatting of tabs in 2022[2]. Do the same in ruby/openssl. While at it, fix a few indentation issues, mainly in switch-case labels and in ossl_ssl_session.c, which used doubled indentation size. This patch contains white-space changes only. git diff -w output should be empty. [1] https://bugs.ruby-lang.org/issues/14246 [2] https://bugs.ruby-lang.org/issues/18891 https://github.com/ruby/openssl/commit/4d6214f507 --- ext/openssl/ossl.c | 250 ++++++------- ext/openssl/ossl.h | 18 +- ext/openssl/ossl_asn1.c | 650 ++++++++++++++++----------------- ext/openssl/ossl_bio.c | 6 +- ext/openssl/ossl_bn.c | 564 ++++++++++++++-------------- ext/openssl/ossl_cipher.c | 98 ++--- ext/openssl/ossl_digest.c | 34 +- ext/openssl/ossl_engine.c | 74 ++-- ext/openssl/ossl_hmac.c | 42 +-- ext/openssl/ossl_kdf.c | 74 ++-- ext/openssl/ossl_ns_spki.c | 50 +-- ext/openssl/ossl_ocsp.c | 272 +++++++------- ext/openssl/ossl_pkcs12.c | 26 +- ext/openssl/ossl_pkcs7.c | 144 ++++---- ext/openssl/ossl_pkey.c | 132 +++---- ext/openssl/ossl_pkey.h | 190 +++++----- ext/openssl/ossl_pkey_dh.c | 28 +- ext/openssl/ossl_pkey_dsa.c | 4 +- ext/openssl/ossl_pkey_ec.c | 196 +++++----- ext/openssl/ossl_pkey_rsa.c | 62 ++-- ext/openssl/ossl_rand.c | 14 +- ext/openssl/ossl_ssl.c | 300 +++++++-------- ext/openssl/ossl_ssl.h | 16 +- ext/openssl/ossl_ssl_session.c | 184 +++++----- ext/openssl/ossl_ts.c | 6 +- ext/openssl/ossl_x509.c | 2 +- ext/openssl/ossl_x509attr.c | 48 +-- ext/openssl/ossl_x509cert.c | 100 ++--- ext/openssl/ossl_x509crl.c | 96 ++--- ext/openssl/ossl_x509ext.c | 64 ++-- ext/openssl/ossl_x509name.c | 90 ++--- ext/openssl/ossl_x509req.c | 72 ++-- ext/openssl/ossl_x509revoked.c | 46 +-- ext/openssl/ossl_x509store.c | 62 ++-- 34 files changed, 2007 insertions(+), 2007 deletions(-) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 60780790b02d1d..a19ff23b107c88 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -13,71 +13,71 @@ /* * Data Conversion */ -#define OSSL_IMPL_ARY2SK(name, type, expected_class, dup) \ -VALUE \ -ossl_##name##_ary2sk0(VALUE ary) \ -{ \ - STACK_OF(type) *sk; \ - VALUE val; \ - type *x; \ - int i; \ - \ - Check_Type(ary, T_ARRAY); \ - sk = sk_##type##_new_null(); \ - if (!sk) ossl_raise(eOSSLError, NULL); \ - \ - for (i = 0; i < RARRAY_LEN(ary); i++) { \ - val = rb_ary_entry(ary, i); \ - if (!rb_obj_is_kind_of(val, expected_class)) { \ - sk_##type##_pop_free(sk, type##_free); \ - ossl_raise(eOSSLError, "object in array not" \ - " of class ##type##"); \ - } \ - x = dup(val); /* NEED TO DUP */ \ - sk_##type##_push(sk, x); \ - } \ - return (VALUE)sk; \ -} \ - \ -STACK_OF(type) * \ -ossl_protect_##name##_ary2sk(VALUE ary, int *status) \ -{ \ - return (STACK_OF(type)*)rb_protect( \ - (VALUE (*)(VALUE))ossl_##name##_ary2sk0, \ - ary, \ - status); \ -} \ - \ -STACK_OF(type) * \ -ossl_##name##_ary2sk(VALUE ary) \ -{ \ - STACK_OF(type) *sk; \ - int status = 0; \ - \ - sk = ossl_protect_##name##_ary2sk(ary, &status); \ - if (status) rb_jump_tag(status); \ - \ - return sk; \ +#define OSSL_IMPL_ARY2SK(name, type, expected_class, dup) \ +VALUE \ +ossl_##name##_ary2sk0(VALUE ary) \ +{ \ + STACK_OF(type) *sk; \ + VALUE val; \ + type *x; \ + int i; \ + \ + Check_Type(ary, T_ARRAY); \ + sk = sk_##type##_new_null(); \ + if (!sk) ossl_raise(eOSSLError, NULL); \ + \ + for (i = 0; i < RARRAY_LEN(ary); i++) { \ + val = rb_ary_entry(ary, i); \ + if (!rb_obj_is_kind_of(val, expected_class)) { \ + sk_##type##_pop_free(sk, type##_free); \ + ossl_raise(eOSSLError, "object in array not" \ + " of class ##type##"); \ + } \ + x = dup(val); /* NEED TO DUP */ \ + sk_##type##_push(sk, x); \ + } \ + return (VALUE)sk; \ +} \ + \ +STACK_OF(type) * \ +ossl_protect_##name##_ary2sk(VALUE ary, int *status) \ +{ \ + return (STACK_OF(type)*)rb_protect( \ + (VALUE (*)(VALUE))ossl_##name##_ary2sk0, \ + ary, \ + status); \ +} \ + \ +STACK_OF(type) * \ +ossl_##name##_ary2sk(VALUE ary) \ +{ \ + STACK_OF(type) *sk; \ + int status = 0; \ + \ + sk = ossl_protect_##name##_ary2sk(ary, &status); \ + if (status) rb_jump_tag(status); \ + \ + return sk; \ } OSSL_IMPL_ARY2SK(x509, X509, cX509Cert, DupX509CertPtr) -#define OSSL_IMPL_SK2ARY(name, type) \ -VALUE \ -ossl_##name##_sk2ary(const STACK_OF(type) *sk) \ -{ \ - type *t; \ - int i, num; \ - VALUE ary; \ - \ - RUBY_ASSERT(sk != NULL); \ - num = sk_##type##_num(sk); \ - ary = rb_ary_new_capa(num); \ - \ - for (i=0; i> 4]; - out[i * 2 + 1] = hex[p & 0x0f]; + out[i * 2 + 0] = hex[p >> 4]; + out[i * 2 + 1] = hex[p & 0x0f]; } } @@ -143,14 +143,14 @@ VALUE ossl_pem_passwd_value(VALUE pass) { if (NIL_P(pass)) - return Qnil; + return Qnil; StringValue(pass); /* PEM_BUFSIZE is currently used as the second argument of pem_password_cb, * that is +max_len+ of ossl_pem_passwd_cb() */ if (RSTRING_LEN(pass) > PEM_BUFSIZE) - ossl_raise(eOSSLError, "password must not be longer than %d bytes", PEM_BUFSIZE); + ossl_raise(eOSSLError, "password must not be longer than %d bytes", PEM_BUFSIZE); return pass; } @@ -160,7 +160,7 @@ ossl_pem_passwd_cb0(VALUE flag) { VALUE pass = rb_yield(flag); if (NIL_P(pass)) - return Qnil; + return Qnil; StringValue(pass); return pass; } @@ -173,46 +173,46 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_) VALUE rflag, pass = (VALUE)pwd_; if (RTEST(pass)) { - /* PEM_def_callback(buf, max_len, flag, StringValueCStr(pass)) does not - * work because it does not allow NUL characters and truncates to 1024 - * bytes silently if the input is over 1024 bytes */ - if (RB_TYPE_P(pass, T_STRING)) { - len = RSTRING_LEN(pass); - if (len <= max_len) { - memcpy(buf, RSTRING_PTR(pass), len); - return (int)len; - } - } - OSSL_Debug("passed data is not valid String???"); - return -1; + /* PEM_def_callback(buf, max_len, flag, StringValueCStr(pass)) does not + * work because it does not allow NUL characters and truncates to 1024 + * bytes silently if the input is over 1024 bytes */ + if (RB_TYPE_P(pass, T_STRING)) { + len = RSTRING_LEN(pass); + if (len <= max_len) { + memcpy(buf, RSTRING_PTR(pass), len); + return (int)len; + } + } + OSSL_Debug("passed data is not valid String???"); + return -1; } if (!rb_block_given_p()) { - return PEM_def_callback(buf, max_len, flag, NULL); + return PEM_def_callback(buf, max_len, flag, NULL); } while (1) { - /* - * when the flag is nonzero, this password - * will be used to perform encryption; otherwise it will - * be used to perform decryption. - */ - rflag = flag ? Qtrue : Qfalse; - pass = rb_protect(ossl_pem_passwd_cb0, rflag, &status); - if (status) { - /* ignore an exception raised. */ - rb_set_errinfo(Qnil); - return -1; - } - if (NIL_P(pass)) - return -1; - len = RSTRING_LEN(pass); - if (len > max_len) { - rb_warning("password must not be longer than %d bytes", max_len); - continue; - } - memcpy(buf, RSTRING_PTR(pass), len); - break; + /* + * when the flag is nonzero, this password + * will be used to perform encryption; otherwise it will + * be used to perform decryption. + */ + rflag = flag ? Qtrue : Qfalse; + pass = rb_protect(ossl_pem_passwd_cb0, rflag, &status); + if (status) { + /* ignore an exception raised. */ + rb_set_errinfo(Qnil); + return -1; + } + if (NIL_P(pass)) + return -1; + len = RSTRING_LEN(pass); + if (len > max_len) { + rb_warning("password must not be longer than %d bytes", max_len); + continue; + } + memcpy(buf, RSTRING_PTR(pass), len); + break; } return (int)len; } @@ -247,7 +247,7 @@ VALUE ossl_to_der_if_possible(VALUE obj) { if(rb_respond_to(obj, ossl_s_to_der)) - return ossl_to_der(obj); + return ossl_to_der(obj); return obj; } @@ -289,12 +289,12 @@ ossl_raise(VALUE exc, const char *fmt, ...) VALUE err; if (fmt) { - va_start(args, fmt); - err = rb_vsprintf(fmt, args); - va_end(args); + va_start(args, fmt); + err = rb_vsprintf(fmt, args); + va_end(args); } else { - err = Qnil; + err = Qnil; } rb_exc_raise(ossl_make_error(exc, err)); @@ -434,17 +434,17 @@ ossl_fips_mode_set(VALUE self, VALUE enabled) return enabled; #elif defined(OPENSSL_FIPS) || defined(OPENSSL_IS_AWSLC) if (RTEST(enabled)) { - int mode = FIPS_mode(); - if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */ - ossl_raise(eOSSLError, "Turning on FIPS mode failed"); + int mode = FIPS_mode(); + if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */ + ossl_raise(eOSSLError, "Turning on FIPS mode failed"); } else { - if(!FIPS_mode_set(0)) /* turning off twice is OK */ - ossl_raise(eOSSLError, "Turning off FIPS mode failed"); + if(!FIPS_mode_set(0)) /* turning off twice is OK */ + ossl_raise(eOSSLError, "Turning off FIPS mode failed"); } return enabled; #else if (RTEST(enabled)) - ossl_raise(eOSSLError, "This version of OpenSSL does not support FIPS mode"); + ossl_raise(eOSSLError, "This version of OpenSSL does not support FIPS mode"); return enabled; #endif } @@ -473,8 +473,8 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) } switch (CRYPTO_memcmp(p1, p2, len1)) { - case 0: return Qtrue; - default: return Qfalse; + case 0: return Qtrue; + default: return Qfalse; } } @@ -996,13 +996,13 @@ Init_openssl(void) #if OSSL_OPENSSL_PREREQ(3, 0, 0) Qtrue #elif defined(OPENSSL_FIPS) - Qtrue + Qtrue #elif defined(OPENSSL_IS_AWSLC) // AWS-LC FIPS can only be enabled during compile time. - FIPS_mode() ? Qtrue : Qfalse + FIPS_mode() ? Qtrue : Qfalse #else - Qfalse + Qfalse #endif - ); + ); rb_define_module_function(mOSSL, "fips_mode", ossl_fips_mode_get, 0); rb_define_module_function(mOSSL, "fips_mode=", ossl_fips_mode_set, 1); diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index d519c96cd6d313..93deafb4b68363 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -92,10 +92,10 @@ extern VALUE eOSSLError; * CheckTypes */ #define OSSL_Check_Kind(obj, klass) do {\ - if (!rb_obj_is_kind_of((obj), (klass))) {\ - ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected kind of %"PRIsVALUE")",\ - rb_obj_class(obj), (klass));\ - }\ + if (!rb_obj_is_kind_of((obj), (klass))) {\ + ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected kind of %"PRIsVALUE")",\ + rb_obj_class(obj), (klass));\ + }\ } while (0) /* @@ -174,11 +174,11 @@ VALUE ossl_to_der_if_possible(VALUE); extern VALUE dOSSL; #define OSSL_Debug(...) do { \ - if (dOSSL == Qtrue) { \ - fprintf(stderr, "OSSL_DEBUG: "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ - } \ + if (dOSSL == Qtrue) { \ + fprintf(stderr, "OSSL_DEBUG: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ + } \ } while (0) /* diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 17cecd0e96cb56..cb13ac6ecf876d 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -10,7 +10,7 @@ #include "ossl.h" static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, - int depth, int yield, long *num_read); + int depth, int yield, long *num_read); static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); /* @@ -26,37 +26,37 @@ asn1time_to_time(const ASN1_TIME *time) memset(&tm, 0, sizeof(struct tm)); switch (time->type) { - case V_ASN1_UTCTIME: - count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - - if (count == 5) { - tm.tm_sec = 0; - } else if (count != 6) { - ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"", - time->data); - } - if (tm.tm_year < 50) { - tm.tm_year += 2000; - } else { - tm.tm_year += 1900; - } - break; - case V_ASN1_GENERALIZEDTIME: - count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - if (count == 5) { - tm.tm_sec = 0; - } - else if (count != 6) { - ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"", - time->data); - } - break; - default: - rb_warning("unknown time format"); + case V_ASN1_UTCTIME: + count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, + &tm.tm_sec); + + if (count == 5) { + tm.tm_sec = 0; + } else if (count != 6) { + ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"", + time->data); + } + if (tm.tm_year < 50) { + tm.tm_year += 2000; + } else { + tm.tm_year += 1900; + } + break; + case V_ASN1_GENERALIZEDTIME: + count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, + &tm.tm_sec); + if (count == 5) { + tm.tm_sec = 0; + } + else if (count != 6) { + ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"", + time->data); + } + break; + default: + rb_warning("unknown time format"); return Qnil; } argv[0] = INT2NUM(tm.tm_year); @@ -81,13 +81,13 @@ ossl_time_split(VALUE time, time_t *sec, int *days) VALUE num = rb_Integer(time); if (FIXNUM_P(num)) { - time_t t = FIX2LONG(num); - *sec = t % 86400; - *days = rb_long2int(t / 86400); + time_t t = FIX2LONG(num); + *sec = t % 86400; + *days = rb_long2int(t / 86400); } else { - *days = NUM2INT(rb_funcall(num, rb_intern("/"), 1, INT2FIX(86400))); - *sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400))); + *days = NUM2INT(rb_funcall(num, rb_intern("/"), 1, INT2FIX(86400))); + *sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400))); } } @@ -110,16 +110,16 @@ asn1integer_to_num(const ASN1_INTEGER *ai) VALUE num; if (!ai) { - ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); + ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); } if (ai->type == V_ASN1_ENUMERATED) - /* const_cast: workaround for old OpenSSL */ - bn = ASN1_ENUMERATED_to_BN((ASN1_ENUMERATED *)ai, NULL); + /* const_cast: workaround for old OpenSSL */ + bn = ASN1_ENUMERATED_to_BN((ASN1_ENUMERATED *)ai, NULL); else - bn = ASN1_INTEGER_to_BN(ai, NULL); + bn = ASN1_INTEGER_to_BN(ai, NULL); if (!bn) - ossl_raise(eOSSLError, NULL); + ossl_raise(eOSSLError, NULL); num = ossl_bn_new(bn); BN_free(bn); @@ -132,12 +132,12 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) BIGNUM *bn; if (NIL_P(obj)) - ossl_raise(rb_eTypeError, "Can't convert nil into Integer"); + ossl_raise(rb_eTypeError, "Can't convert nil into Integer"); bn = GetBNPtr(obj); if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) - ossl_raise(eOSSLError, NULL); + ossl_raise(eOSSLError, NULL); return ai; } @@ -160,13 +160,13 @@ ossl_asn1obj_to_string_oid(const ASN1_OBJECT *a1obj) str = rb_usascii_str_new(NULL, 127); len = OBJ_obj2txt(RSTRING_PTR(str), RSTRING_LENINT(str), a1obj, 1); if (len <= 0 || len == INT_MAX) - ossl_raise(eOSSLError, "OBJ_obj2txt"); + ossl_raise(eOSSLError, "OBJ_obj2txt"); if (len > RSTRING_LEN(str)) { - /* +1 is for the \0 terminator added by OBJ_obj2txt() */ - rb_str_resize(str, len + 1); - len = OBJ_obj2txt(RSTRING_PTR(str), len + 1, a1obj, 1); - if (len <= 0) - ossl_raise(eOSSLError, "OBJ_obj2txt"); + /* +1 is for the \0 terminator added by OBJ_obj2txt() */ + rb_str_resize(str, len + 1); + len = OBJ_obj2txt(RSTRING_PTR(str), len + 1, a1obj, 1); + if (len <= 0) + ossl_raise(eOSSLError, "OBJ_obj2txt"); } rb_str_set_len(str, len); return str; @@ -240,9 +240,9 @@ static ASN1_BOOLEAN obj_to_asn1bool(VALUE obj) { if (NIL_P(obj)) - ossl_raise(rb_eTypeError, "Can't convert nil into Boolean"); + ossl_raise(rb_eTypeError, "Can't convert nil into Boolean"); - return RTEST(obj) ? 0xff : 0x0; + return RTEST(obj) ? 0xff : 0x0; } static ASN1_INTEGER* @@ -257,11 +257,11 @@ obj_to_asn1bstr(VALUE obj, long unused_bits) ASN1_BIT_STRING *bstr; if (unused_bits < 0 || unused_bits > 7) - ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\ - "the range 0 to 7"); + ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\ + "the range 0 to 7"); StringValue(obj); if(!(bstr = ASN1_BIT_STRING_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_BIT_STRING_set(bstr, (unsigned char *)RSTRING_PTR(obj), RSTRING_LENINT(obj)); bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */ bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits; @@ -276,7 +276,7 @@ obj_to_asn1str(VALUE obj) StringValue(obj); if(!(str = ASN1_STRING_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_STRING_set(str, RSTRING_PTR(obj), RSTRING_LENINT(obj)); return str; @@ -288,9 +288,9 @@ obj_to_asn1null(VALUE obj) ASN1_NULL *null; if(!NIL_P(obj)) - ossl_raise(eASN1Error, "nil expected"); + ossl_raise(eASN1Error, "nil expected"); if(!(null = ASN1_NULL_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return null; } @@ -318,7 +318,7 @@ obj_to_asn1utime(VALUE time) ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_UTCTIME_adj(NULL, sec, off_days, 0))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return t; } @@ -333,7 +333,7 @@ obj_to_asn1gtime(VALUE time) ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_GENERALIZEDTIME_adj(NULL, sec, off_days, 0))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return t; } @@ -346,7 +346,7 @@ obj_to_asn1derstr(VALUE obj) str = ossl_to_der(obj); if(!(a1str = ASN1_STRING_new())) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_STRING_set(a1str, RSTRING_PTR(str), RSTRING_LENINT(str)); return a1str; @@ -361,9 +361,9 @@ decode_bool(unsigned char* der, long length) const unsigned char *p = der; if (length != 3) - ossl_raise(eASN1Error, "invalid length for BOOLEAN"); + ossl_raise(eASN1Error, "invalid length for BOOLEAN"); if (p[0] != 1 || p[1] != 1) - ossl_raise(eASN1Error, "invalid BOOLEAN"); + ossl_raise(eASN1Error, "invalid BOOLEAN"); return p[2] ? Qtrue : Qfalse; } @@ -378,9 +378,9 @@ decode_int(unsigned char* der, long length) p = der; if(!(ai = d2i_ASN1_INTEGER(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ret = rb_protect(asn1integer_to_num_i, - (VALUE)ai, &status); + (VALUE)ai, &status); ASN1_INTEGER_free(ai); if(status) rb_jump_tag(status); @@ -397,11 +397,11 @@ decode_bstr(unsigned char* der, long length, long *unused_bits) p = der; if(!(bstr = d2i_ASN1_BIT_STRING(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); len = bstr->length; *unused_bits = 0; if(bstr->flags & ASN1_STRING_FLAG_BITS_LEFT) - *unused_bits = bstr->flags & 0x07; + *unused_bits = bstr->flags & 0x07; ret = rb_str_new((const char *)bstr->data, len); ASN1_BIT_STRING_free(bstr); @@ -418,9 +418,9 @@ decode_enum(unsigned char* der, long length) p = der; if(!(ai = d2i_ASN1_ENUMERATED(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ret = rb_protect(asn1integer_to_num_i, - (VALUE)ai, &status); + (VALUE)ai, &status); ASN1_ENUMERATED_free(ai); if(status) rb_jump_tag(status); @@ -435,7 +435,7 @@ decode_null(unsigned char* der, long length) p = der; if(!(null = d2i_ASN1_NULL(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ASN1_NULL_free(null); return Qnil; @@ -475,9 +475,9 @@ decode_time(unsigned char* der, long length) p = der; if(!(time = d2i_ASN1_TIME(NULL, &p, length))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); ret = rb_protect(asn1time_to_time_i, - (VALUE)time, &status); + (VALUE)time, &status); ASN1_TIME_free(time); if(status) rb_jump_tag(status); @@ -488,7 +488,7 @@ static VALUE decode_eoc(unsigned char *der, long length) { if (length != 2 || !(der[0] == 0x00 && der[1] == 0x00)) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return rb_str_new("", 0); } @@ -553,62 +553,62 @@ ossl_asn1_get_asn1type(VALUE obj) tag = ossl_asn1_default_tag(obj); value = ossl_asn1_get_value(obj); switch(tag){ - case V_ASN1_BOOLEAN: - ptr = (void*)(VALUE)obj_to_asn1bool(value); - free_func = NULL; - break; - case V_ASN1_INTEGER: /* FALLTHROUGH */ - case V_ASN1_ENUMERATED: - ptr = obj_to_asn1int(value); - free_func = (free_func_type *)ASN1_INTEGER_free; - break; - case V_ASN1_BIT_STRING: + case V_ASN1_BOOLEAN: + ptr = (void*)(VALUE)obj_to_asn1bool(value); + free_func = NULL; + break; + case V_ASN1_INTEGER: /* FALLTHROUGH */ + case V_ASN1_ENUMERATED: + ptr = obj_to_asn1int(value); + free_func = (free_func_type *)ASN1_INTEGER_free; + break; + case V_ASN1_BIT_STRING: rflag = rb_attr_get(obj, sivUNUSED_BITS); - ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); - free_func = (free_func_type *)ASN1_BIT_STRING_free; - break; - case V_ASN1_NULL: - ptr = obj_to_asn1null(value); - free_func = (free_func_type *)ASN1_NULL_free; - break; - case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ - case V_ASN1_UTF8STRING: /* FALLTHROUGH */ - case V_ASN1_NUMERICSTRING: /* FALLTHROUGH */ - case V_ASN1_PRINTABLESTRING: /* FALLTHROUGH */ - case V_ASN1_T61STRING: /* FALLTHROUGH */ - case V_ASN1_VIDEOTEXSTRING: /* FALLTHROUGH */ - case V_ASN1_IA5STRING: /* FALLTHROUGH */ - case V_ASN1_GRAPHICSTRING: /* FALLTHROUGH */ - case V_ASN1_ISO64STRING: /* FALLTHROUGH */ - case V_ASN1_GENERALSTRING: /* FALLTHROUGH */ - case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ - case V_ASN1_BMPSTRING: - ptr = obj_to_asn1str(value); - free_func = (free_func_type *)ASN1_STRING_free; - break; - case V_ASN1_OBJECT: - ptr = ossl_to_asn1obj(value); - free_func = (free_func_type *)ASN1_OBJECT_free; - break; - case V_ASN1_UTCTIME: - ptr = obj_to_asn1utime(value); - free_func = (free_func_type *)ASN1_TIME_free; - break; - case V_ASN1_GENERALIZEDTIME: - ptr = obj_to_asn1gtime(value); - free_func = (free_func_type *)ASN1_TIME_free; - break; - case V_ASN1_SET: /* FALLTHROUGH */ - case V_ASN1_SEQUENCE: - ptr = obj_to_asn1derstr(obj); - free_func = (free_func_type *)ASN1_STRING_free; - break; - default: - ossl_raise(eASN1Error, "unsupported ASN.1 type"); + ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); + free_func = (free_func_type *)ASN1_BIT_STRING_free; + break; + case V_ASN1_NULL: + ptr = obj_to_asn1null(value); + free_func = (free_func_type *)ASN1_NULL_free; + break; + case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ + case V_ASN1_UTF8STRING: /* FALLTHROUGH */ + case V_ASN1_NUMERICSTRING: /* FALLTHROUGH */ + case V_ASN1_PRINTABLESTRING: /* FALLTHROUGH */ + case V_ASN1_T61STRING: /* FALLTHROUGH */ + case V_ASN1_VIDEOTEXSTRING: /* FALLTHROUGH */ + case V_ASN1_IA5STRING: /* FALLTHROUGH */ + case V_ASN1_GRAPHICSTRING: /* FALLTHROUGH */ + case V_ASN1_ISO64STRING: /* FALLTHROUGH */ + case V_ASN1_GENERALSTRING: /* FALLTHROUGH */ + case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ + case V_ASN1_BMPSTRING: + ptr = obj_to_asn1str(value); + free_func = (free_func_type *)ASN1_STRING_free; + break; + case V_ASN1_OBJECT: + ptr = ossl_to_asn1obj(value); + free_func = (free_func_type *)ASN1_OBJECT_free; + break; + case V_ASN1_UTCTIME: + ptr = obj_to_asn1utime(value); + free_func = (free_func_type *)ASN1_TIME_free; + break; + case V_ASN1_GENERALIZEDTIME: + ptr = obj_to_asn1gtime(value); + free_func = (free_func_type *)ASN1_TIME_free; + break; + case V_ASN1_SET: /* FALLTHROUGH */ + case V_ASN1_SEQUENCE: + ptr = obj_to_asn1derstr(obj); + free_func = (free_func_type *)ASN1_STRING_free; + break; + default: + ossl_raise(eASN1Error, "unsupported ASN.1 type"); } if(!(ret = OPENSSL_malloc(sizeof(ASN1_TYPE)))){ - if(free_func) free_func(ptr); - ossl_raise(eASN1Error, "ASN1_TYPE alloc failure"); + if(free_func) free_func(ptr); + ossl_raise(eASN1Error, "ASN1_TYPE alloc failure"); } memset(ret, 0, sizeof(ASN1_TYPE)); ASN1_TYPE_set(ret, tag, ptr); @@ -623,10 +623,10 @@ ossl_asn1_default_tag(VALUE obj) tmp_class = CLASS_OF(obj); while (!NIL_P(tmp_class)) { - tag = rb_hash_lookup(class_tag_map, tmp_class); - if (tag != Qnil) - return NUM2INT(tag); - tmp_class = rb_class_superclass(tmp_class); + tag = rb_hash_lookup(class_tag_map, tmp_class); + if (tag != Qnil) + return NUM2INT(tag); + tmp_class = rb_class_superclass(tmp_class); } return -1; @@ -639,7 +639,7 @@ ossl_asn1_tag(VALUE obj) tag = ossl_asn1_get_tag(obj); if(NIL_P(tag)) - ossl_raise(eASN1Error, "tag number not specified"); + ossl_raise(eASN1Error, "tag number not specified"); return NUM2INT(tag); } @@ -651,28 +651,28 @@ ossl_asn1_tag_class(VALUE obj) s = ossl_asn1_get_tag_class(obj); if (NIL_P(s) || s == sym_UNIVERSAL) - return V_ASN1_UNIVERSAL; + return V_ASN1_UNIVERSAL; else if (s == sym_APPLICATION) - return V_ASN1_APPLICATION; + return V_ASN1_APPLICATION; else if (s == sym_CONTEXT_SPECIFIC) - return V_ASN1_CONTEXT_SPECIFIC; + return V_ASN1_CONTEXT_SPECIFIC; else if (s == sym_PRIVATE) - return V_ASN1_PRIVATE; + return V_ASN1_PRIVATE; else - ossl_raise(eASN1Error, "invalid tag class"); + ossl_raise(eASN1Error, "invalid tag class"); } static VALUE ossl_asn1_class2sym(int tc) { if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) - return sym_PRIVATE; + return sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) - return sym_CONTEXT_SPECIFIC; + return sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) - return sym_APPLICATION; + return sym_APPLICATION; else - return sym_UNIVERSAL; + return sym_UNIVERSAL; } /* @@ -695,7 +695,7 @@ static VALUE ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) { if(!SYMBOL_P(tag_class)) - ossl_raise(eASN1Error, "invalid tag class"); + ossl_raise(eASN1Error, "invalid tag class"); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); ossl_asn1_set_tag_class(self, tag_class); @@ -717,35 +717,35 @@ to_der_internal(VALUE self, int constructed, int indef_len, VALUE body) body_length = RSTRING_LENINT(body); if (ossl_asn1_get_tagging(self) == sym_EXPLICIT) { - int inner_length, e_encoding = indef_len ? 2 : 1; - - if (default_tag_number == -1) - ossl_raise(eASN1Error, "explicit tagging of unknown tag"); - - inner_length = ASN1_object_size(encoding, body_length, default_tag_number); - total_length = ASN1_object_size(e_encoding, inner_length, tag_number); - str = rb_str_new(NULL, total_length); - p = (unsigned char *)RSTRING_PTR(str); - /* Put explicit tag */ - ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class); - /* Append inner object */ - ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL); - memcpy(p, RSTRING_PTR(body), body_length); - p += body_length; - if (indef_len) { - ASN1_put_eoc(&p); /* For inner object */ - ASN1_put_eoc(&p); /* For wrapper object */ - } + int inner_length, e_encoding = indef_len ? 2 : 1; + + if (default_tag_number == -1) + ossl_raise(eASN1Error, "explicit tagging of unknown tag"); + + inner_length = ASN1_object_size(encoding, body_length, default_tag_number); + total_length = ASN1_object_size(e_encoding, inner_length, tag_number); + str = rb_str_new(NULL, total_length); + p = (unsigned char *)RSTRING_PTR(str); + /* Put explicit tag */ + ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class); + /* Append inner object */ + ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL); + memcpy(p, RSTRING_PTR(body), body_length); + p += body_length; + if (indef_len) { + ASN1_put_eoc(&p); /* For inner object */ + ASN1_put_eoc(&p); /* For wrapper object */ + } } else { - total_length = ASN1_object_size(encoding, body_length, tag_number); - str = rb_str_new(NULL, total_length); - p = (unsigned char *)RSTRING_PTR(str); - ASN1_put_object(&p, encoding, body_length, tag_number, tag_class); - memcpy(p, RSTRING_PTR(body), body_length); - p += body_length; - if (indef_len) - ASN1_put_eoc(&p); + total_length = ASN1_object_size(encoding, body_length, tag_number); + str = rb_str_new(NULL, total_length); + p = (unsigned char *)RSTRING_PTR(str); + ASN1_put_object(&p, encoding, body_length, tag_number, tag_class); + memcpy(p, RSTRING_PTR(body), body_length); + p += body_length; + if (indef_len) + ASN1_put_eoc(&p); } assert(p - (unsigned char *)RSTRING_PTR(str) == total_length); return str; @@ -768,18 +768,18 @@ ossl_asn1data_to_der(VALUE self) VALUE value = ossl_asn1_get_value(self); if (rb_obj_is_kind_of(value, rb_cArray)) - return ossl_asn1cons_to_der(self); + return ossl_asn1cons_to_der(self); else { - if (RTEST(ossl_asn1_get_indefinite_length(self))) - ossl_raise(eASN1Error, "indefinite length form cannot be used " \ - "with primitive encoding"); - return ossl_asn1prim_to_der(self); + if (RTEST(ossl_asn1_get_indefinite_length(self))) + ossl_raise(eASN1Error, "indefinite length form cannot be used " \ + "with primitive encoding"); + return ossl_asn1prim_to_der(self); } } static VALUE int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, - VALUE tc, long *num_read) + VALUE tc, long *num_read) { VALUE value, asn1data; unsigned char *p; @@ -788,64 +788,64 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, p = *pp; if(tc == sym_UNIVERSAL && tag < ossl_asn1_info_size) { - switch(tag){ - case V_ASN1_EOC: - value = decode_eoc(p, hlen+length); - break; - case V_ASN1_BOOLEAN: - value = decode_bool(p, hlen+length); - break; - case V_ASN1_INTEGER: - value = decode_int(p, hlen+length); - break; - case V_ASN1_BIT_STRING: - value = decode_bstr(p, hlen+length, &flag); - break; - case V_ASN1_NULL: - value = decode_null(p, hlen+length); - break; - case V_ASN1_ENUMERATED: - value = decode_enum(p, hlen+length); - break; - case V_ASN1_OBJECT: - value = decode_obj(p, hlen+length); - break; - case V_ASN1_UTCTIME: /* FALLTHROUGH */ - case V_ASN1_GENERALIZEDTIME: - value = decode_time(p, hlen+length); - break; - default: - /* use original value */ - p += hlen; - value = rb_str_new((const char *)p, length); - break; - } + switch(tag){ + case V_ASN1_EOC: + value = decode_eoc(p, hlen+length); + break; + case V_ASN1_BOOLEAN: + value = decode_bool(p, hlen+length); + break; + case V_ASN1_INTEGER: + value = decode_int(p, hlen+length); + break; + case V_ASN1_BIT_STRING: + value = decode_bstr(p, hlen+length, &flag); + break; + case V_ASN1_NULL: + value = decode_null(p, hlen+length); + break; + case V_ASN1_ENUMERATED: + value = decode_enum(p, hlen+length); + break; + case V_ASN1_OBJECT: + value = decode_obj(p, hlen+length); + break; + case V_ASN1_UTCTIME: /* FALLTHROUGH */ + case V_ASN1_GENERALIZEDTIME: + value = decode_time(p, hlen+length); + break; + default: + /* use original value */ + p += hlen; + value = rb_str_new((const char *)p, length); + break; + } } else { - p += hlen; - value = rb_str_new((const char *)p, length); + p += hlen; + value = rb_str_new((const char *)p, length); } *pp += hlen + length; *num_read = hlen + length; if (tc == sym_UNIVERSAL && - tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { - VALUE klass = *ossl_asn1_info[tag].klass; - VALUE args[4]; - args[0] = value; - args[1] = INT2NUM(tag); - args[2] = Qnil; - args[3] = tc; - asn1data = rb_obj_alloc(klass); - ossl_asn1_initialize(4, args, asn1data); - if(tag == V_ASN1_BIT_STRING){ - rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); - } + tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { + VALUE klass = *ossl_asn1_info[tag].klass; + VALUE args[4]; + args[0] = value; + args[1] = INT2NUM(tag); + args[2] = Qnil; + args[3] = tc; + asn1data = rb_obj_alloc(klass); + ossl_asn1_initialize(4, args, asn1data); + if(tag == V_ASN1_BIT_STRING){ + rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); + } } else { - asn1data = rb_obj_alloc(cASN1Data); - ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); + asn1data = rb_obj_alloc(cASN1Data); + ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); } return asn1data; @@ -853,8 +853,8 @@ int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, static VALUE int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, - long *offset, int depth, int yield, int j, - int tag, VALUE tc, long *num_read) + long *offset, int depth, int yield, int j, + int tag, VALUE tc, long *num_read) { VALUE value, asn1data, ary; int indefinite; @@ -865,42 +865,42 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, available_len = indefinite ? max_len : length; while (available_len > 0) { - long inner_read = 0; - value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); - *num_read += inner_read; - available_len -= inner_read; + long inner_read = 0; + value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); + *num_read += inner_read; + available_len -= inner_read; - if (indefinite) { + if (indefinite) { if (ossl_asn1_tag(value) == V_ASN1_EOC && ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) break; if (available_len == 0) ossl_raise(eASN1Error, "EOC missing in indefinite length encoding"); - } - rb_ary_push(ary, value); + } + rb_ary_push(ary, value); } if (tc == sym_UNIVERSAL) { - VALUE args[4]; - if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET) - asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass); - else - asn1data = rb_obj_alloc(cASN1Constructive); - args[0] = ary; - args[1] = INT2NUM(tag); - args[2] = Qnil; - args[3] = tc; - ossl_asn1_initialize(4, args, asn1data); + VALUE args[4]; + if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET) + asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass); + else + asn1data = rb_obj_alloc(cASN1Constructive); + args[0] = ary; + args[1] = INT2NUM(tag); + args[2] = Qnil; + args[3] = tc; + ossl_asn1_initialize(4, args, asn1data); } else { - asn1data = rb_obj_alloc(cASN1Data); - ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); + asn1data = rb_obj_alloc(cASN1Data); + ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); } if (indefinite) - ossl_asn1_set_indefinite_length(asn1data, Qtrue); + ossl_asn1_set_indefinite_length(asn1data, Qtrue); else - ossl_asn1_set_indefinite_length(asn1data, Qfalse); + ossl_asn1_set_indefinite_length(asn1data, Qfalse); *offset = off; return asn1data; @@ -908,7 +908,7 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, - int yield, long *num_read) + int yield, long *num_read) { unsigned char *start, *p; const unsigned char *p0; @@ -924,46 +924,46 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, if(j & 0x80) ossl_raise(eASN1Error, NULL); if(len > length) ossl_raise(eASN1Error, "value is too short"); if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) - tag_class = sym_PRIVATE; + tag_class = sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) - tag_class = sym_CONTEXT_SPECIFIC; + tag_class = sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) - tag_class = sym_APPLICATION; + tag_class = sym_APPLICATION; else - tag_class = sym_UNIVERSAL; + tag_class = sym_UNIVERSAL; hlen = p - start; if(yield) { - VALUE arg = rb_ary_new(); - rb_ary_push(arg, LONG2NUM(depth)); - rb_ary_push(arg, LONG2NUM(*offset)); - rb_ary_push(arg, LONG2NUM(hlen)); - rb_ary_push(arg, LONG2NUM(len)); - rb_ary_push(arg, (j & V_ASN1_CONSTRUCTED) ? Qtrue : Qfalse); - rb_ary_push(arg, ossl_asn1_class2sym(tc)); - rb_ary_push(arg, INT2NUM(tag)); - rb_yield(arg); + VALUE arg = rb_ary_new(); + rb_ary_push(arg, LONG2NUM(depth)); + rb_ary_push(arg, LONG2NUM(*offset)); + rb_ary_push(arg, LONG2NUM(hlen)); + rb_ary_push(arg, LONG2NUM(len)); + rb_ary_push(arg, (j & V_ASN1_CONSTRUCTED) ? Qtrue : Qfalse); + rb_ary_push(arg, ossl_asn1_class2sym(tc)); + rb_ary_push(arg, INT2NUM(tag)); + rb_yield(arg); } if(j & V_ASN1_CONSTRUCTED) { - *pp += hlen; - off += hlen; - asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); - inner_read += hlen; + *pp += hlen; + off += hlen; + asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); + inner_read += hlen; } else { - if ((j & 0x01) && (len == 0)) - ossl_raise(eASN1Error, "indefinite length for primitive value"); - asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read); - off += hlen + len; + if ((j & 0x01) && (len == 0)) + ossl_raise(eASN1Error, "indefinite length for primitive value"); + asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read); + off += hlen + len; } if (num_read) - *num_read = inner_read; + *num_read = inner_read; if (len != 0 && inner_read != hlen + len) { - ossl_raise(eASN1Error, - "Type mismatch. Bytes read: %ld Bytes available: %ld", - inner_read, hlen + len); + ossl_raise(eASN1Error, + "Type mismatch. Bytes read: %ld Bytes available: %ld", + inner_read, hlen + len); } *offset = off; @@ -974,9 +974,9 @@ static void int_ossl_decode_sanity_check(long len, long read, long offset) { if (len != 0 && (read != len || offset != len)) { - ossl_raise(eASN1Error, - "Type mismatch. Total bytes read: %ld Bytes available: %ld Offset: %ld", - read, len, offset); + ossl_raise(eASN1Error, + "Type mismatch. Total bytes read: %ld Bytes available: %ld Offset: %ld", + read, len, offset); } } @@ -1076,11 +1076,11 @@ ossl_asn1_decode_all(VALUE self, VALUE obj) tmp_len = len; ary = rb_ary_new(); while (tmp_len > 0) { - long tmp_read = 0; - val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read); - rb_ary_push(ary, val); - read += tmp_read; - tmp_len -= tmp_read; + long tmp_read = 0; + val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read); + rb_ary_push(ary, val); + read += tmp_read; + tmp_len -= tmp_read; } RB_GC_GUARD(tmp); int_ossl_decode_sanity_check(len, read, offset); @@ -1120,23 +1120,23 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) default_tag = ossl_asn1_default_tag(self); if (default_tag == -1 || argc > 1) { - if(NIL_P(tag)) - ossl_raise(eASN1Error, "must specify tag number"); - if(!NIL_P(tagging) && !SYMBOL_P(tagging)) - ossl_raise(eASN1Error, "invalid tagging method"); - if(NIL_P(tag_class)) { - if (NIL_P(tagging)) - tag_class = sym_UNIVERSAL; - else - tag_class = sym_CONTEXT_SPECIFIC; - } - if(!SYMBOL_P(tag_class)) - ossl_raise(eASN1Error, "invalid tag class"); + if(NIL_P(tag)) + ossl_raise(eASN1Error, "must specify tag number"); + if(!NIL_P(tagging) && !SYMBOL_P(tagging)) + ossl_raise(eASN1Error, "invalid tagging method"); + if(NIL_P(tag_class)) { + if (NIL_P(tagging)) + tag_class = sym_UNIVERSAL; + else + tag_class = sym_CONTEXT_SPECIFIC; + } + if(!SYMBOL_P(tag_class)) + ossl_raise(eASN1Error, "invalid tag class"); } else{ - tag = INT2NUM(default_tag); - tagging = Qnil; - tag_class = sym_UNIVERSAL; + tag = INT2NUM(default_tag); + tagging = Qnil; + tag_class = sym_UNIVERSAL; } ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); @@ -1144,7 +1144,7 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) ossl_asn1_set_tag_class(self, tag_class); ossl_asn1_set_indefinite_length(self, Qfalse); if (default_tag == V_ASN1_BIT_STRING) - rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0)); + rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0)); return self; } @@ -1186,20 +1186,20 @@ ossl_asn1prim_to_der(VALUE self) VALUE str; if (ossl_asn1_default_tag(self) == -1) { - str = ossl_asn1_get_value(self); - return to_der_internal(self, 0, 0, StringValue(str)); + str = ossl_asn1_get_value(self); + return to_der_internal(self, 0, 0, StringValue(str)); } asn1 = ossl_asn1_get_asn1type(self); alllen = i2d_ASN1_TYPE(asn1, NULL); if (alllen < 0) { - ASN1_TYPE_free(asn1); - ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); + ASN1_TYPE_free(asn1); + ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); } str = ossl_str_new(NULL, alllen, &state); if (state) { - ASN1_TYPE_free(asn1); - rb_jump_tag(state); + ASN1_TYPE_free(asn1); + rb_jump_tag(state); } p0 = p1 = (unsigned char *)RSTRING_PTR(str); if (i2d_ASN1_TYPE(asn1, &p0) < 0) { @@ -1212,7 +1212,7 @@ ossl_asn1prim_to_der(VALUE self) /* Strip header since to_der_internal() wants only the payload */ j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen); if (j & 0x80) - ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */ + ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */ return to_der_internal(self, 0, 0, rb_str_drop_bytes(str, alllen - bodylen)); } @@ -1234,22 +1234,22 @@ ossl_asn1cons_to_der(VALUE self) ary = rb_convert_type(ossl_asn1_get_value(self), T_ARRAY, "Array", "to_a"); str = rb_str_new(NULL, 0); for (i = 0; i < RARRAY_LEN(ary); i++) { - VALUE item = RARRAY_AREF(ary, i); - - if (indef_len && rb_obj_is_kind_of(item, cASN1EndOfContent)) { - if (i != RARRAY_LEN(ary) - 1) - ossl_raise(eASN1Error, "illegal EOC octets in value"); - - /* - * EOC is not really part of the content, but we required to add one - * at the end in the past. - */ - break; - } - - item = ossl_to_der_if_possible(item); - StringValue(item); - rb_str_append(str, item); + VALUE item = RARRAY_AREF(ary, i); + + if (indef_len && rb_obj_is_kind_of(item, cASN1EndOfContent)) { + if (i != RARRAY_LEN(ary) - 1) + ossl_raise(eASN1Error, "illegal EOC octets in value"); + + /* + * EOC is not really part of the content, but we required to add one + * at the end in the past. + */ + break; + } + + item = ossl_to_der_if_possible(item); + StringValue(item); + rb_str_append(str, item); } return to_der_internal(self, 1, indef_len, str); @@ -1295,7 +1295,7 @@ ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln) StringValueCStr(ln); if(!OBJ_create(RSTRING_PTR(oid), RSTRING_PTR(sn), RSTRING_PTR(ln))) - ossl_raise(eASN1Error, NULL); + ossl_raise(eASN1Error, NULL); return Qtrue; } @@ -1315,7 +1315,7 @@ ossl_asn1obj_get_sn(VALUE self) val = ossl_asn1_get_value(self); if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) - ret = rb_str_new2(OBJ_nid2sn(nid)); + ret = rb_str_new2(OBJ_nid2sn(nid)); return ret; } @@ -1335,7 +1335,7 @@ ossl_asn1obj_get_ln(VALUE self) val = ossl_asn1_get_value(self); if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) - ret = rb_str_new2(OBJ_nid2ln(nid)); + ret = rb_str_new2(OBJ_nid2ln(nid)); return ret; } @@ -1364,7 +1364,7 @@ ossl_asn1obj_get_oid(VALUE self) str = rb_protect(asn1obj_get_oid_i, (VALUE)a1obj, &state); ASN1_OBJECT_free(a1obj); if (state) - rb_jump_tag(state); + rb_jump_tag(state); return str; } @@ -1577,9 +1577,9 @@ Init_ossl_asn1(void) */ rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary); for(i = 0; i < ossl_asn1_info_size; i++){ - if(ossl_asn1_info[i].name[0] == '[') continue; - rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i)); - rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name)); + if(ossl_asn1_info[i].name[0] == '[') continue; + rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i)); + rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name)); } /* Document-class: OpenSSL::ASN1::ASN1Data diff --git a/ext/openssl/ossl_bio.c b/ext/openssl/ossl_bio.c index 2ef2080507b709..4edde5091d0933 100644 --- a/ext/openssl/ossl_bio.c +++ b/ext/openssl/ossl_bio.c @@ -16,11 +16,11 @@ ossl_obj2bio(volatile VALUE *pobj) BIO *bio; if (RB_TYPE_P(obj, T_FILE)) - obj = rb_funcallv(obj, rb_intern("read"), 0, NULL); + obj = rb_funcallv(obj, rb_intern("read"), 0, NULL); StringValue(obj); bio = BIO_new_mem_buf(RSTRING_PTR(obj), RSTRING_LENINT(obj)); if (!bio) - ossl_raise(eOSSLError, "BIO_new_mem_buf"); + ossl_raise(eOSSLError, "BIO_new_mem_buf"); *pobj = obj; return bio; } @@ -36,7 +36,7 @@ ossl_membio2str(BIO *bio) ret = ossl_str_new(buf->data, buf->length, &state); BIO_free(bio); if (state) - rb_jump_tag(state); + rb_jump_tag(state); return ret; } diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 0ac8ae248ef9e4..9014f2df2b9df9 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -11,19 +11,19 @@ #include "ossl.h" #define NewBN(klass) \ - TypedData_Wrap_Struct((klass), &ossl_bn_type, 0) + TypedData_Wrap_Struct((klass), &ossl_bn_type, 0) #define SetBN(obj, bn) do { \ - if (!(bn)) { \ - ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ - } \ - RTYPEDDATA_DATA(obj) = (bn); \ + if (!(bn)) { \ + ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ + RTYPEDDATA_DATA(obj) = (bn); \ } while (0) #define GetBN(obj, bn) do { \ - TypedData_Get_Struct((obj), BIGNUM, &ossl_bn_type, (bn)); \ - if (!(bn)) { \ - ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ - } \ + TypedData_Get_Struct((obj), BIGNUM, &ossl_bn_type, (bn)); \ + if (!(bn)) { \ + ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ } while (0) static void @@ -35,7 +35,7 @@ ossl_bn_free(void *ptr) static const rb_data_type_t ossl_bn_type = { "OpenSSL/BN", { - 0, ossl_bn_free, + 0, ossl_bn_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, }; @@ -75,40 +75,40 @@ integer_to_bnptr(VALUE obj, BIGNUM *orig) BIGNUM *bn; if (FIXNUM_P(obj)) { - long i; - unsigned char bin[sizeof(long)]; - long n = FIX2LONG(obj); - unsigned long un = labs(n); - - for (i = sizeof(long) - 1; 0 <= i; i--) { - bin[i] = un & 0xff; - un >>= 8; - } - - bn = BN_bin2bn(bin, sizeof(bin), orig); - if (!bn) - ossl_raise(eBNError, "BN_bin2bn"); - if (n < 0) - BN_set_negative(bn, 1); + long i; + unsigned char bin[sizeof(long)]; + long n = FIX2LONG(obj); + unsigned long un = labs(n); + + for (i = sizeof(long) - 1; 0 <= i; i--) { + bin[i] = un & 0xff; + un >>= 8; + } + + bn = BN_bin2bn(bin, sizeof(bin), orig); + if (!bn) + ossl_raise(eBNError, "BN_bin2bn"); + if (n < 0) + BN_set_negative(bn, 1); } else { /* assuming Bignum */ - size_t len = rb_absint_size(obj, NULL); - unsigned char *bin; - VALUE buf; - int sign; - - if (INT_MAX < len) { - rb_raise(eBNError, "bignum too long"); - } - bin = (unsigned char*)ALLOCV_N(unsigned char, buf, len); - sign = rb_integer_pack(obj, bin, len, 1, 0, INTEGER_PACK_BIG_ENDIAN); - - bn = BN_bin2bn(bin, (int)len, orig); - ALLOCV_END(buf); - if (!bn) - ossl_raise(eBNError, "BN_bin2bn"); - if (sign < 0) - BN_set_negative(bn, 1); + size_t len = rb_absint_size(obj, NULL); + unsigned char *bin; + VALUE buf; + int sign; + + if (INT_MAX < len) { + rb_raise(eBNError, "bignum too long"); + } + bin = (unsigned char*)ALLOCV_N(unsigned char, buf, len); + sign = rb_integer_pack(obj, bin, len, 1, 0, INTEGER_PACK_BIG_ENDIAN); + + bn = BN_bin2bn(bin, (int)len, orig); + ALLOCV_END(buf); + if (!bn) + ossl_raise(eBNError, "BN_bin2bn"); + if (sign < 0) + BN_set_negative(bn, 1); } return bn; @@ -121,11 +121,11 @@ try_convert_to_bn(VALUE obj) VALUE newobj = Qnil; if (rb_obj_is_kind_of(obj, cBN)) - return obj; + return obj; if (RB_INTEGER_TYPE_P(obj)) { - newobj = NewBN(cBN); /* Handle potential mem leaks */ - bn = integer_to_bnptr(obj, NULL); - SetBN(newobj, bn); + newobj = NewBN(cBN); /* Handle potential mem leaks */ + bn = integer_to_bnptr(obj, NULL); + SetBN(newobj, bn); } return newobj; @@ -139,7 +139,7 @@ ossl_bn_value_ptr(volatile VALUE *ptr) tmp = try_convert_to_bn(*ptr); if (NIL_P(tmp)) - ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); + ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); GetBN(tmp, bn); *ptr = tmp; @@ -209,7 +209,7 @@ ossl_bn_alloc(VALUE klass) VALUE obj = NewBN(klass); if (!(bn = BN_new())) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } SetBN(obj, bn); @@ -251,7 +251,7 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self) char *ptr; if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) { - base = NUM2INT(bs); + base = NUM2INT(bs); } if (NIL_P(str)) { @@ -260,49 +260,49 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (RB_INTEGER_TYPE_P(str)) { - GetBN(self, bn); - integer_to_bnptr(str, bn); + GetBN(self, bn); + integer_to_bnptr(str, bn); - return self; + return self; } if (RTEST(rb_obj_is_kind_of(str, cBN))) { - BIGNUM *other; - - GetBN(self, bn); - GetBN(str, other); /* Safe - we checked kind_of? above */ - if (!BN_copy(bn, other)) { - ossl_raise(eBNError, NULL); - } - return self; + BIGNUM *other; + + GetBN(self, bn); + GetBN(str, other); /* Safe - we checked kind_of? above */ + if (!BN_copy(bn, other)) { + ossl_raise(eBNError, NULL); + } + return self; } GetBN(self, bn); switch (base) { - case 0: + case 0: ptr = StringValuePtr(str); if (!BN_mpi2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) { - ossl_raise(eBNError, NULL); - } - break; - case 2: + ossl_raise(eBNError, NULL); + } + break; + case 2: ptr = StringValuePtr(str); if (!BN_bin2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) { - ossl_raise(eBNError, NULL); - } - break; - case 10: - if (!BN_dec2bn(&bn, StringValueCStr(str))) { - ossl_raise(eBNError, NULL); - } - break; - case 16: - if (!BN_hex2bn(&bn, StringValueCStr(str))) { - ossl_raise(eBNError, NULL); - } - break; - default: - ossl_raise(rb_eArgError, "invalid radix %d", base); + ossl_raise(eBNError, NULL); + } + break; + case 10: + if (!BN_dec2bn(&bn, StringValueCStr(str))) { + ossl_raise(eBNError, NULL); + } + break; + case 16: + if (!BN_hex2bn(&bn, StringValueCStr(str))) { + ossl_raise(eBNError, NULL); + } + break; + default: + ossl_raise(rb_eArgError, "invalid radix %d", base); } return self; } @@ -334,32 +334,32 @@ ossl_bn_to_s(int argc, VALUE *argv, VALUE self) char *buf; if (rb_scan_args(argc, argv, "01", &bs) == 1) { - base = NUM2INT(bs); + base = NUM2INT(bs); } GetBN(self, bn); switch (base) { - case 0: - len = BN_bn2mpi(bn, NULL); + case 0: + len = BN_bn2mpi(bn, NULL); str = rb_str_new(0, len); - if (BN_bn2mpi(bn, (unsigned char *)RSTRING_PTR(str)) != len) - ossl_raise(eBNError, NULL); - break; - case 2: - len = BN_num_bytes(bn); + if (BN_bn2mpi(bn, (unsigned char *)RSTRING_PTR(str)) != len) + ossl_raise(eBNError, NULL); + break; + case 2: + len = BN_num_bytes(bn); str = rb_str_new(0, len); - if (BN_bn2bin(bn, (unsigned char *)RSTRING_PTR(str)) != len) - ossl_raise(eBNError, NULL); - break; - case 10: - if (!(buf = BN_bn2dec(bn))) ossl_raise(eBNError, NULL); - str = ossl_buf2str(buf, rb_long2int(strlen(buf))); - break; - case 16: - if (!(buf = BN_bn2hex(bn))) ossl_raise(eBNError, NULL); - str = ossl_buf2str(buf, rb_long2int(strlen(buf))); - break; - default: - ossl_raise(rb_eArgError, "invalid radix %d", base); + if (BN_bn2bin(bn, (unsigned char *)RSTRING_PTR(str)) != len) + ossl_raise(eBNError, NULL); + break; + case 10: + if (!(buf = BN_bn2dec(bn))) ossl_raise(eBNError, NULL); + str = ossl_buf2str(buf, rb_long2int(strlen(buf))); + break; + case 16: + if (!(buf = BN_bn2hex(bn))) ossl_raise(eBNError, NULL); + str = ossl_buf2str(buf, rb_long2int(strlen(buf))); + break; + default: + ossl_raise(rb_eArgError, "invalid radix %d", base); } return str; @@ -379,7 +379,7 @@ ossl_bn_to_i(VALUE self) GetBN(self, bn); if (!(txt = BN_bn2hex(bn))) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } num = rb_cstr_to_inum(txt, 16, Qtrue); OPENSSL_free(txt); @@ -397,31 +397,31 @@ static VALUE ossl_bn_coerce(VALUE self, VALUE other) { switch(TYPE(other)) { - case T_STRING: - self = ossl_bn_to_s(0, NULL, self); - break; - case T_FIXNUM: - case T_BIGNUM: - self = ossl_bn_to_i(self); - break; - default: - if (!RTEST(rb_obj_is_kind_of(other, cBN))) { - ossl_raise(rb_eTypeError, "Don't know how to coerce"); - } + case T_STRING: + self = ossl_bn_to_s(0, NULL, self); + break; + case T_FIXNUM: + case T_BIGNUM: + self = ossl_bn_to_i(self); + break; + default: + if (!RTEST(rb_obj_is_kind_of(other, cBN))) { + ossl_raise(rb_eTypeError, "Don't know how to coerce"); + } } return rb_assoc_new(other, self); } -#define BIGNUM_BOOL1(func) \ - static VALUE \ - ossl_bn_##func(VALUE self) \ - { \ - BIGNUM *bn; \ - GetBN(self, bn); \ - if (BN_##func(bn)) { \ - return Qtrue; \ - } \ - return Qfalse; \ +#define BIGNUM_BOOL1(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + if (BN_##func(bn)) { \ + return Qtrue; \ + } \ + return Qfalse; \ } /* @@ -456,27 +456,27 @@ ossl_bn_is_negative(VALUE self) GetBN(self, bn); if (BN_is_zero(bn)) - return Qfalse; + return Qfalse; return BN_is_negative(bn) ? Qtrue : Qfalse; } -#define BIGNUM_1c(func) \ - static VALUE \ - ossl_bn_##func(VALUE self) \ - { \ - BIGNUM *bn, *result; \ - VALUE obj; \ - GetBN(self, bn); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn, ossl_bn_ctx) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_1c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn, *result; \ + VALUE obj; \ + GetBN(self, bn); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn, ossl_bn_ctx) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -486,23 +486,23 @@ ossl_bn_is_negative(VALUE self) */ BIGNUM_1c(sqr) -#define BIGNUM_2(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn1, bn2) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_2(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn1, bn2) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -519,23 +519,23 @@ BIGNUM_2(add) */ BIGNUM_2(sub) -#define BIGNUM_2c(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_2c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -573,18 +573,18 @@ BIGNUM_2c(gcd) */ BIGNUM_2c(mod_sqr) -#define BIGNUM_2cr(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx))) \ - ossl_raise(eBNError, NULL); \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_2cr(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx))) \ + ossl_raise(eBNError, NULL); \ + SetBN(obj, result); \ + return obj; \ } /* @@ -619,16 +619,16 @@ ossl_bn_div(VALUE self, VALUE other) obj1 = NewBN(klass); obj2 = NewBN(klass); if (!(r1 = BN_new())) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } if (!(r2 = BN_new())) { - BN_free(r1); - ossl_raise(eBNError, NULL); + BN_free(r1); + ossl_raise(eBNError, NULL); } if (!BN_div(r1, r2, bn1, bn2, ossl_bn_ctx)) { - BN_free(r1); - BN_free(r2); - ossl_raise(eBNError, NULL); + BN_free(r1); + BN_free(r2); + ossl_raise(eBNError, NULL); } SetBN(obj1, r1); SetBN(obj2, r2); @@ -636,24 +636,24 @@ ossl_bn_div(VALUE self, VALUE other) return rb_ary_new3(2, obj1, obj2); } -#define BIGNUM_3c(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other1); \ - BIGNUM *bn3 = GetBNPtr(other2), *result; \ - VALUE obj; \ - GetBN(self, bn1); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_3c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other1); \ + BIGNUM *bn3 = GetBNPtr(other2), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -684,17 +684,17 @@ BIGNUM_3c(mod_mul) */ BIGNUM_3c(mod_exp) -#define BIGNUM_BIT(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE bit) \ - { \ - BIGNUM *bn; \ - rb_check_frozen(self); \ - GetBN(self, bn); \ - if (BN_##func(bn, NUM2INT(bit)) <= 0) { \ - ossl_raise(eBNError, NULL); \ - } \ - return self; \ +#define BIGNUM_BIT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bit) \ + { \ + BIGNUM *bn; \ + rb_check_frozen(self); \ + GetBN(self, bn); \ + if (BN_##func(bn, NUM2INT(bit)) <= 0) { \ + ossl_raise(eBNError, NULL); \ + } \ + return self; \ } /* @@ -733,30 +733,30 @@ ossl_bn_is_bit_set(VALUE self, VALUE bit) b = NUM2INT(bit); GetBN(self, bn); if (BN_is_bit_set(bn, b)) { - return Qtrue; + return Qtrue; } return Qfalse; } -#define BIGNUM_SHIFT(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE bits) \ - { \ - BIGNUM *bn, *result; \ - int b; \ - VALUE obj; \ - b = NUM2INT(bits); \ - GetBN(self, bn); \ - obj = NewBN(rb_obj_class(self)); \ - if (!(result = BN_new())) { \ - ossl_raise(eBNError, NULL); \ - } \ - if (BN_##func(result, bn, b) <= 0) { \ - BN_free(result); \ - ossl_raise(eBNError, NULL); \ - } \ - SetBN(obj, result); \ - return obj; \ +#define BIGNUM_SHIFT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bits) \ + { \ + BIGNUM *bn, *result; \ + int b; \ + VALUE obj; \ + b = NUM2INT(bits); \ + GetBN(self, bn); \ + obj = NewBN(rb_obj_class(self)); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (BN_##func(result, bn, b) <= 0) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + SetBN(obj, result); \ + return obj; \ } /* @@ -773,18 +773,18 @@ BIGNUM_SHIFT(lshift) */ BIGNUM_SHIFT(rshift) -#define BIGNUM_SELF_SHIFT(func) \ - static VALUE \ - ossl_bn_self_##func(VALUE self, VALUE bits) \ - { \ - BIGNUM *bn; \ - int b; \ - rb_check_frozen(self); \ - b = NUM2INT(bits); \ - GetBN(self, bn); \ - if (BN_##func(bn, bn, b) <= 0) \ - ossl_raise(eBNError, NULL); \ - return self; \ +#define BIGNUM_SELF_SHIFT(func) \ + static VALUE \ + ossl_bn_self_##func(VALUE self, VALUE bits) \ + { \ + BIGNUM *bn; \ + int b; \ + rb_check_frozen(self); \ + b = NUM2INT(bits); \ + GetBN(self, bn); \ + if (BN_##func(bn, bn, b) <= 0) \ + ossl_raise(eBNError, NULL); \ + return self; \ } /* @@ -886,32 +886,32 @@ ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass) num = NUM2INT(vnum); if (vsafe == Qfalse) { - safe = 0; + safe = 0; } if (!NIL_P(vadd)) { - add = GetBNPtr(vadd); - rem = NIL_P(vrem) ? NULL : GetBNPtr(vrem); + add = GetBNPtr(vadd); + rem = NIL_P(vrem) ? NULL : GetBNPtr(vrem); } obj = NewBN(klass); if (!(result = BN_new())) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } if (!BN_generate_prime_ex(result, num, safe, add, rem, NULL)) { - BN_free(result); - ossl_raise(eBNError, NULL); + BN_free(result); + ossl_raise(eBNError, NULL); } SetBN(obj, result); return obj; } -#define BIGNUM_NUM(func) \ - static VALUE \ - ossl_bn_##func(VALUE self) \ - { \ - BIGNUM *bn; \ - GetBN(self, bn); \ - return INT2NUM(BN_##func(bn)); \ +#define BIGNUM_NUM(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + return INT2NUM(BN_##func(bn)); \ } /* @@ -942,7 +942,7 @@ ossl_bn_copy(VALUE self, VALUE other) bn2 = GetBNPtr(other); if (!BN_copy(bn1, bn2)) { - ossl_raise(eBNError, NULL); + ossl_raise(eBNError, NULL); } return self; } @@ -961,7 +961,7 @@ ossl_bn_uplus(VALUE self) obj = NewBN(cBN); bn2 = BN_dup(bn1); if (!bn2) - ossl_raise(eBNError, "BN_dup"); + ossl_raise(eBNError, "BN_dup"); SetBN(obj, bn2); return obj; @@ -981,7 +981,7 @@ ossl_bn_uminus(VALUE self) obj = NewBN(cBN); bn2 = BN_dup(bn1); if (!bn2) - ossl_raise(eBNError, "BN_dup"); + ossl_raise(eBNError, "BN_dup"); SetBN(obj, bn2); BN_set_negative(bn2, !BN_is_negative(bn2)); @@ -1006,13 +1006,13 @@ ossl_bn_abs(VALUE self) } } -#define BIGNUM_CMP(func) \ - static VALUE \ - ossl_bn_##func(VALUE self, VALUE other) \ - { \ - BIGNUM *bn1, *bn2 = GetBNPtr(other); \ - GetBN(self, bn1); \ - return INT2NUM(BN_##func(bn1, bn2)); \ +#define BIGNUM_CMP(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other); \ + GetBN(self, bn1); \ + return INT2NUM(BN_##func(bn1, bn2)); \ } /* @@ -1049,11 +1049,11 @@ ossl_bn_eq(VALUE self, VALUE other) GetBN(self, bn1); other = try_convert_to_bn(other); if (NIL_P(other)) - return Qfalse; + return Qfalse; GetBN(other, bn2); if (!BN_cmp(bn1, bn2)) { - return Qtrue; + return Qtrue; } return Qfalse; } @@ -1072,7 +1072,7 @@ ossl_bn_eql(VALUE self, VALUE other) BIGNUM *bn1, *bn2; if (!rb_obj_is_kind_of(other, cBN)) - return Qfalse; + return Qfalse; GetBN(self, bn1); GetBN(other, bn2); @@ -1099,8 +1099,8 @@ ossl_bn_hash(VALUE self) len = BN_num_bytes(bn); buf = ALLOCV(tmp, len); if (BN_bn2bin(bn, buf) != len) { - ALLOCV_END(tmp); - ossl_raise(eBNError, "BN_bn2bin"); + ALLOCV_END(tmp); + ossl_raise(eBNError, "BN_bn2bin"); } hash = ST2FIX(rb_memhash(buf, len)); diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index f7b2c01a36d933..db65e99888d3bf 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -14,7 +14,7 @@ #define AllocCipher(obj, ctx) do { \ (ctx) = EVP_CIPHER_CTX_new(); \ if (!(ctx)) \ - ossl_raise(rb_eRuntimeError, NULL); \ + ossl_raise(rb_eRuntimeError, NULL); \ RTYPEDDATA_DATA(obj) = (ctx); \ } while (0) #define GetCipherInit(obj, ctx) do { \ @@ -23,7 +23,7 @@ #define GetCipher(obj, ctx) do { \ GetCipherInit((obj), (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \ + ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \ } \ } while (0) @@ -41,7 +41,7 @@ static void ossl_cipher_free(void *ptr); static const rb_data_type_t ossl_cipher_type = { "OpenSSL/Cipher", { - 0, ossl_cipher_free, + 0, ossl_cipher_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -112,7 +112,7 @@ ossl_cipher_new(const EVP_CIPHER *cipher) ret = ossl_cipher_alloc(cCipher); AllocCipher(ret, ctx); if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return ret; } @@ -149,7 +149,7 @@ ossl_cipher_initialize(VALUE self, VALUE str) GetCipherInit(self, ctx); if (ctx) { - ossl_raise(rb_eRuntimeError, "Cipher already initialized!"); + ossl_raise(rb_eRuntimeError, "Cipher already initialized!"); } cipher = ossl_evp_cipher_fetch(str, &cipher_holder); AllocCipher(self, ctx); @@ -171,11 +171,11 @@ ossl_cipher_copy(VALUE self, VALUE other) GetCipherInit(self, ctx1); if (!ctx1) { - AllocCipher(self, ctx1); + AllocCipher(self, ctx1); } GetCipher(other, ctx2); if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return self; } @@ -200,8 +200,8 @@ ossl_s_ciphers(VALUE self) ary = rb_ary_new(); OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, - add_cipher_name_to_ary, - (void*)ary); + add_cipher_name_to_ary, + (void*)ary); return ary; } @@ -222,7 +222,7 @@ ossl_cipher_reset(VALUE self) GetCipher(self, ctx); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return self; } @@ -304,20 +304,20 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest); StringValue(vpass); if(!NIL_P(vsalt)){ - StringValue(vsalt); - if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN) - ossl_raise(eCipherError, "salt must be an 8-octet string"); - salt = (unsigned char *)RSTRING_PTR(vsalt); + StringValue(vsalt); + if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN) + ossl_raise(eCipherError, "salt must be an 8-octet string"); + salt = (unsigned char *)RSTRING_PTR(vsalt); } iter = NIL_P(viter) ? 2048 : NUM2INT(viter); if (iter <= 0) - rb_raise(rb_eArgError, "iterations must be a positive integer"); + rb_raise(rb_eArgError, "iterations must be a positive integer"); digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_md_fetch(vdigest, &md_holder); GetCipher(self, ctx); EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt, - (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv); + (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv); if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); OPENSSL_cleanse(key, sizeof key); OPENSSL_cleanse(iv, sizeof iv); @@ -328,25 +328,25 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) static int ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_ptr, - const unsigned char *in, long in_len) + const unsigned char *in, long in_len) { int out_part_len; int limit = INT_MAX / 2 + 1; long out_len = 0; do { - int in_part_len = in_len > limit ? limit : (int)in_len; + int in_part_len = in_len > limit ? limit : (int)in_len; - if (!EVP_CipherUpdate(ctx, out ? (out + out_len) : 0, - &out_part_len, in, in_part_len)) - return 0; + if (!EVP_CipherUpdate(ctx, out ? (out + out_len) : 0, + &out_part_len, in, in_part_len)) + return 0; - out_len += out_part_len; - in += in_part_len; + out_len += out_part_len; + in += in_part_len; } while ((in_len -= limit) > 0); if (out_len_ptr) - *out_len_ptr = out_len; + *out_len_ptr = out_len; return 1; } @@ -377,7 +377,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &data, &str); if (!RTEST(rb_attr_get(self, id_key_set))) - ossl_raise(eCipherError, "key not set"); + ossl_raise(eCipherError, "key not set"); StringValue(data); in = (unsigned char *)RSTRING_PTR(data); @@ -396,8 +396,8 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) * currently implemented in OpenSSL, but this can change in the future. */ if (in_len > LONG_MAX - EVP_MAX_BLOCK_LENGTH) { - ossl_raise(rb_eRangeError, - "data too big to make output buffer: %ld bytes", in_len); + ossl_raise(rb_eRangeError, + "data too big to make output buffer: %ld bytes", in_len); } out_len = in_len + EVP_MAX_BLOCK_LENGTH; @@ -412,7 +412,7 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) } if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len)) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); assert(out_len <= RSTRING_LEN(str)); rb_str_set_len(str, out_len); @@ -503,10 +503,10 @@ ossl_cipher_set_key(VALUE self, VALUE key) key_len = EVP_CIPHER_CTX_key_length(ctx); if (RSTRING_LEN(key) != key_len) - ossl_raise(rb_eArgError, "key must be %d bytes", key_len); + ossl_raise(rb_eArgError, "key must be %d bytes", key_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); rb_ivar_set(self, id_key_set, Qtrue); @@ -541,14 +541,14 @@ ossl_cipher_set_iv(VALUE self, VALUE iv) GetCipher(self, ctx); if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) - iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); + iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); if (!iv_len) - iv_len = EVP_CIPHER_CTX_iv_length(ctx); + iv_len = EVP_CIPHER_CTX_iv_length(ctx); if (RSTRING_LEN(iv) != iv_len) - ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len); + ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return iv; } @@ -603,7 +603,7 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "AEAD not supported by this cipher"); + ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len)) ossl_raise(eCipherError, "couldn't set additional authenticated data"); @@ -636,18 +636,18 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &vtag_len); if (NIL_P(vtag_len)) - vtag_len = rb_attr_get(self, id_auth_tag_len); + vtag_len = rb_attr_get(self, id_auth_tag_len); if (!NIL_P(vtag_len)) - tag_len = NUM2INT(vtag_len); + tag_len = NUM2INT(vtag_len); GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "authentication tag not supported by this cipher"); + ossl_raise(eCipherError, "authentication tag not supported by this cipher"); ret = rb_str_new(NULL, tag_len); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret))) - ossl_raise(eCipherError, "retrieving the authentication tag failed"); + ossl_raise(eCipherError, "retrieving the authentication tag failed"); return ret; } @@ -686,10 +686,10 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "authentication tag not supported by this cipher"); + ossl_raise(eCipherError, "authentication tag not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) - ossl_raise(eCipherError, "unable to set AEAD tag"); + ossl_raise(eCipherError, "unable to set AEAD tag"); return vtag; } @@ -716,10 +716,10 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "AEAD not supported by this cipher"); + ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL)) - ossl_raise(eCipherError, "unable to set authentication tag length"); + ossl_raise(eCipherError, "unable to set authentication tag length"); /* for #auth_tag */ rb_ivar_set(self, id_auth_tag_len, INT2NUM(tag_len)); @@ -748,10 +748,10 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) - ossl_raise(eCipherError, "cipher does not support AEAD"); + ossl_raise(eCipherError, "cipher does not support AEAD"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL)) - ossl_raise(eCipherError, "unable to set IV length"); + ossl_raise(eCipherError, "unable to set IV length"); /* * EVP_CIPHER_CTX_iv_length() returns the default length. So we need to save @@ -809,7 +809,7 @@ ossl_cipher_set_padding(VALUE self, VALUE padding) GetCipher(self, ctx); if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return padding; } @@ -843,9 +843,9 @@ ossl_cipher_iv_length(VALUE self) GetCipher(self, ctx); if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) - len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); + len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); if (!len) - len = EVP_CIPHER_CTX_iv_length(ctx); + len = EVP_CIPHER_CTX_iv_length(ctx); return INT2NUM(len); } diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index 8c1b825cf3909c..e23968b1e325ef 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -12,7 +12,7 @@ #define GetDigest(obj, ctx) do { \ TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_digest_type, (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ } \ } while (0) @@ -34,7 +34,7 @@ ossl_digest_free(void *ctx) static const rb_data_type_t ossl_digest_type = { "OpenSSL/Digest", { - 0, ossl_digest_free, + 0, ossl_digest_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -110,11 +110,11 @@ ossl_digest_new(const EVP_MD *md) ret = ossl_digest_alloc(cDigest); ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(eDigestError, "EVP_MD_CTX_new"); + ossl_raise(eDigestError, "EVP_MD_CTX_new"); RTYPEDDATA_DATA(ret) = ctx; if (!EVP_DigestInit_ex(ctx, md, NULL)) - ossl_raise(eDigestError, "Digest initialization failed"); + ossl_raise(eDigestError, "Digest initialization failed"); return ret; } @@ -161,13 +161,13 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self) TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx); if (!ctx) { - RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new(); - if (!ctx) - ossl_raise(eDigestError, "EVP_MD_CTX_new"); + RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new(); + if (!ctx) + ossl_raise(eDigestError, "EVP_MD_CTX_new"); } if (!EVP_DigestInit_ex(ctx, md, NULL)) - ossl_raise(eDigestError, "Digest initialization failed"); + ossl_raise(eDigestError, "Digest initialization failed"); rb_ivar_set(self, id_md_holder, md_holder); if (!NIL_P(data)) return ossl_digest_update(self, data); @@ -185,14 +185,14 @@ ossl_digest_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx1); if (!ctx1) { - RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new(); - if (!ctx1) - ossl_raise(eDigestError, "EVP_MD_CTX_new"); + RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new(); + if (!ctx1) + ossl_raise(eDigestError, "EVP_MD_CTX_new"); } GetDigest(other, ctx2); if (!EVP_MD_CTX_copy(ctx1, ctx2)) { - ossl_raise(eDigestError, NULL); + ossl_raise(eDigestError, NULL); } return self; } @@ -217,8 +217,8 @@ ossl_s_digests(VALUE self) ary = rb_ary_new(); OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, - add_digest_name_to_ary, - (void*)ary); + add_digest_name_to_ary, + (void*)ary); return ary; } @@ -238,7 +238,7 @@ ossl_digest_reset(VALUE self) GetDigest(self, ctx); if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_get0_md(ctx), NULL) != 1) { - ossl_raise(eDigestError, "Digest initialization failed."); + ossl_raise(eDigestError, "Digest initialization failed."); } return self; @@ -268,7 +268,7 @@ ossl_digest_update(VALUE self, VALUE data) GetDigest(self, ctx); if (!EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) - ossl_raise(eDigestError, "EVP_DigestUpdate"); + ossl_raise(eDigestError, "EVP_DigestUpdate"); return self; } @@ -287,7 +287,7 @@ ossl_digest_finish(VALUE self) GetDigest(self, ctx); str = rb_str_new(NULL, EVP_MD_CTX_size(ctx)); if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL)) - ossl_raise(eDigestError, "EVP_DigestFinal_ex"); + ossl_raise(eDigestError, "EVP_DigestFinal_ex"); return str; } diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index e40f0ff68de37f..a2bcb07ea47d73 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -16,7 +16,7 @@ TypedData_Wrap_Struct((klass), &ossl_engine_type, 0) #define SetEngine(obj, engine) do { \ if (!(engine)) { \ - ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (engine); \ } while(0) @@ -49,12 +49,12 @@ static VALUE eEngineError; */ #define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ do{\ - if(!strcmp(#engine_name, RSTRING_PTR(name))){\ - if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\ - return Qtrue;\ - else\ - ossl_raise(eEngineError, "OPENSSL_init_crypto"); \ - }\ + if(!strcmp(#engine_name, RSTRING_PTR(name))){\ + if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\ + return Qtrue;\ + else\ + ossl_raise(eEngineError, "OPENSSL_init_crypto"); \ + }\ }while(0) static void @@ -66,7 +66,7 @@ ossl_engine_free(void *engine) static const rb_data_type_t ossl_engine_type = { "OpenSSL/Engine", { - 0, ossl_engine_free, + 0, ossl_engine_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -130,12 +130,12 @@ ossl_engine_s_engines(VALUE klass) ary = rb_ary_new(); for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)){ - obj = NewEngine(klass); - /* Need a ref count of two here because of ENGINE_free being - * called internally by OpenSSL when moving to the next ENGINE - * and by us when releasing the ENGINE reference */ - ENGINE_up_ref(e); - SetEngine(obj, e); + obj = NewEngine(klass); + /* Need a ref count of two here because of ENGINE_free being + * called internally by OpenSSL when moving to the next ENGINE + * and by us when releasing the ENGINE reference */ + ENGINE_up_ref(e); + SetEngine(obj, e); rb_ary_push(ary, obj); } @@ -163,13 +163,13 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) ossl_engine_s_load(1, &id, klass); obj = NewEngine(klass); if(!(e = ENGINE_by_id(RSTRING_PTR(id)))) - ossl_raise(eEngineError, NULL); + ossl_raise(eEngineError, NULL); SetEngine(obj, e); if(rb_block_given_p()) rb_yield(obj); if(!ENGINE_init(e)) - ossl_raise(eEngineError, NULL); + ossl_raise(eEngineError, NULL); ENGINE_ctrl(e, ENGINE_CTRL_SET_PASSWORD_CALLBACK, - 0, NULL, (void(*)(void))ossl_pem_passwd_cb); + 0, NULL, (void(*)(void))ossl_pem_passwd_cb); ossl_clear_error(); return obj; @@ -184,7 +184,7 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#, ...] * OpenSSL::Engine.engines.first.id - * #=> "rsax" + * #=> "rsax" */ static VALUE ossl_engine_get_id(VALUE self) @@ -203,7 +203,7 @@ ossl_engine_get_id(VALUE self) * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#, ...] * OpenSSL::Engine.engines.first.name - * #=> "RSAX engine support" + * #=> "RSAX engine support" * */ static VALUE @@ -274,11 +274,11 @@ ossl_engine_get_cipher(VALUE self, VALUE name) * Will raise an EngineError if the digest is unavailable. * * e = OpenSSL::Engine.by_id("openssl") - * #=> # + * #=> # * e.digest("SHA1") - * #=> # + * #=> # * e.digest("zomg") - * #=> OpenSSL::Engine::EngineError: no such digest `zomg' + * #=> OpenSSL::Engine::EngineError: no such digest `zomg' */ static VALUE ossl_engine_get_digest(VALUE self, VALUE name) @@ -365,7 +365,7 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) * your OS. * * [All flags] 0xFFFF - * [No flags] 0x0000 + * [No flags] 0x0000 * * See also */ @@ -399,7 +399,7 @@ ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self) GetEngine(self, e); rb_scan_args(argc, argv, "11", &cmd, &val); ret = ENGINE_ctrl_cmd_string(e, StringValueCStr(cmd), - NIL_P(val) ? NULL : StringValueCStr(val), 0); + NIL_P(val) ? NULL : StringValueCStr(val), 0); if (!ret) ossl_raise(eEngineError, NULL); return self; @@ -409,11 +409,11 @@ static VALUE ossl_engine_cmd_flag_to_name(int flag) { switch(flag){ - case ENGINE_CMD_FLAG_NUMERIC: return rb_str_new2("NUMERIC"); - case ENGINE_CMD_FLAG_STRING: return rb_str_new2("STRING"); - case ENGINE_CMD_FLAG_NO_INPUT: return rb_str_new2("NO_INPUT"); - case ENGINE_CMD_FLAG_INTERNAL: return rb_str_new2("INTERNAL"); - default: return rb_str_new2("UNKNOWN"); + case ENGINE_CMD_FLAG_NUMERIC: return rb_str_new2("NUMERIC"); + case ENGINE_CMD_FLAG_STRING: return rb_str_new2("STRING"); + case ENGINE_CMD_FLAG_NO_INPUT: return rb_str_new2("NO_INPUT"); + case ENGINE_CMD_FLAG_INTERNAL: return rb_str_new2("INTERNAL"); + default: return rb_str_new2("UNKNOWN"); } } @@ -433,13 +433,13 @@ ossl_engine_get_cmds(VALUE self) GetEngine(self, e); ary = rb_ary_new(); if ((defn = ENGINE_get_cmd_defns(e)) != NULL){ - for (p = defn; p->cmd_num > 0; p++){ - tmp = rb_ary_new(); - rb_ary_push(tmp, rb_str_new2(p->cmd_name)); - rb_ary_push(tmp, rb_str_new2(p->cmd_desc)); - rb_ary_push(tmp, ossl_engine_cmd_flag_to_name(p->cmd_flags)); - rb_ary_push(ary, tmp); - } + for (p = defn; p->cmd_num > 0; p++){ + tmp = rb_ary_new(); + rb_ary_push(tmp, rb_str_new2(p->cmd_name)); + rb_ary_push(tmp, rb_str_new2(p->cmd_desc)); + rb_ary_push(tmp, ossl_engine_cmd_flag_to_name(p->cmd_flags)); + rb_ary_push(ary, tmp); + } } return ary; @@ -458,7 +458,7 @@ ossl_engine_inspect(VALUE self) GetEngine(self, e); return rb_sprintf("#<%"PRIsVALUE" id=\"%s\" name=\"%s\">", - rb_obj_class(self), ENGINE_get_id(e), ENGINE_get_name(e)); + rb_obj_class(self), ENGINE_get_id(e), ENGINE_get_name(e)); } #define DefEngineConst(x) rb_define_const(cEngine, #x, INT2NUM(ENGINE_##x)) diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index 8153f64fc57d65..ddcc6a5f8d0d3a 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -14,7 +14,7 @@ #define GetHMAC(obj, ctx) do { \ TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_hmac_type, (ctx)); \ if (!(ctx)) { \ - ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ + ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ } \ } while (0) @@ -41,7 +41,7 @@ ossl_hmac_free(void *ctx) static const rb_data_type_t ossl_hmac_type = { "OpenSSL/HMAC", { - 0, ossl_hmac_free, + 0, ossl_hmac_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -74,17 +74,17 @@ ossl_hmac_alloc(VALUE klass) * * === Example * - * key = 'key' - * instance = OpenSSL::HMAC.new(key, 'SHA1') - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f - * instance.class - * #=> OpenSSL::HMAC + * key = 'key' + * instance = OpenSSL::HMAC.new(key, 'SHA1') + * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f + * instance.class + * #=> OpenSSL::HMAC * * === A note about comparisons * * Two instances can be securely compared with #== in constant time: * - * other_instance = OpenSSL::HMAC.new('key', 'SHA1') + * other_instance = OpenSSL::HMAC.new('key', 'SHA1') * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance == other_instance * #=> true @@ -142,13 +142,13 @@ ossl_hmac_copy(VALUE self, VALUE other) * * === Example * - * first_chunk = 'The quick brown fox jumps ' - * second_chunk = 'over the lazy dog' + * first_chunk = 'The quick brown fox jumps ' + * second_chunk = 'over the lazy dog' * - * instance.update(first_chunk) - * #=> 5b9a8038a65d571076d97fe783989e52278a492a - * instance.update(second_chunk) - * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 + * instance.update(first_chunk) + * #=> 5b9a8038a65d571076d97fe783989e52278a492a + * instance.update(second_chunk) + * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 * */ static VALUE @@ -226,14 +226,14 @@ ossl_hmac_hexdigest(VALUE self) * * === Example * - * data = "The quick brown fox jumps over the lazy dog" - * instance = OpenSSL::HMAC.new('key', 'SHA1') - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f + * data = "The quick brown fox jumps over the lazy dog" + * instance = OpenSSL::HMAC.new('key', 'SHA1') + * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * - * instance.update(data) - * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 - * instance.reset - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f + * instance.update(data) + * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 + * instance.reset + * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * */ static VALUE diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c index 9450612ee2d375..1f280164405aed 100644 --- a/ext/openssl/ossl_kdf.c +++ b/ext/openssl/ossl_kdf.c @@ -41,10 +41,10 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self) const EVP_MD *md; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt"); - kwargs_ids[1] = rb_intern_const("iterations"); - kwargs_ids[2] = rb_intern_const("length"); - kwargs_ids[3] = rb_intern_const("hash"); + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("iterations"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); } rb_scan_args(argc, argv, "1:", &pass, &opts); rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); @@ -57,10 +57,10 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self) str = rb_str_new(0, len); if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass), - (unsigned char *)RSTRING_PTR(salt), - RSTRING_LENINT(salt), iters, md, len, - (unsigned char *)RSTRING_PTR(str))) - ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC"); + (unsigned char *)RSTRING_PTR(salt), + RSTRING_LENINT(salt), iters, md, len, + (unsigned char *)RSTRING_PTR(str))) + ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC"); return str; } @@ -107,11 +107,11 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) uint64_t N, r, p, maxmem; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt"); - kwargs_ids[1] = rb_intern_const("N"); - kwargs_ids[2] = rb_intern_const("r"); - kwargs_ids[3] = rb_intern_const("p"); - kwargs_ids[4] = rb_intern_const("length"); + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("N"); + kwargs_ids[2] = rb_intern_const("r"); + kwargs_ids[3] = rb_intern_const("p"); + kwargs_ids[4] = rb_intern_const("length"); } rb_scan_args(argc, argv, "1:", &pass, &opts); rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs); @@ -131,9 +131,9 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) str = rb_str_new(0, len); if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass), - (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt), - N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len)) - ossl_raise(eKDF, "EVP_PBE_scrypt"); + (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt), + N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len)) + ossl_raise(eKDF, "EVP_PBE_scrypt"); return str; } @@ -180,10 +180,10 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self) EVP_PKEY_CTX *pctx; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt"); - kwargs_ids[1] = rb_intern_const("info"); - kwargs_ids[2] = rb_intern_const("length"); - kwargs_ids[3] = rb_intern_const("hash"); + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("info"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); } rb_scan_args(argc, argv, "1:", &ikm, &opts); rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); @@ -196,39 +196,39 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self) infolen = RSTRING_LENINT(info); len = (size_t)NUM2LONG(kwargs[2]); if (len > LONG_MAX) - rb_raise(rb_eArgError, "length must be non-negative"); + rb_raise(rb_eArgError, "length must be non-negative"); md = ossl_evp_md_fetch(kwargs[3], &md_holder); str = rb_str_new(NULL, (long)len); pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); if (!pctx) - ossl_raise(eKDF, "EVP_PKEY_CTX_new_id"); + ossl_raise(eKDF, "EVP_PKEY_CTX_new_id"); if (EVP_PKEY_derive_init(pctx) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_derive_init"); + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive_init"); } if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md"); + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md"); } if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt), - saltlen) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt"); + saltlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt"); } if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm), - ikmlen) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key"); + ikmlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key"); } if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info), - infolen) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info"); + infolen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info"); } if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) { - EVP_PKEY_CTX_free(pctx); - ossl_raise(eKDF, "EVP_PKEY_derive"); + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive"); } rb_str_set_len(str, (long)len); EVP_PKEY_CTX_free(pctx); diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index e1589fea0e0b24..1d1498824620a9 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_netscape_spki_type, 0) #define SetSPKI(obj, spki) do { \ if (!(spki)) { \ - ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (spki); \ } while (0) #define GetSPKI(obj, spki) do { \ TypedData_Get_Struct((obj), NETSCAPE_SPKI, &ossl_netscape_spki_type, (spki)); \ if (!(spki)) { \ - ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ } \ } while (0) @@ -48,7 +48,7 @@ ossl_netscape_spki_free(void *spki) static const rb_data_type_t ossl_netscape_spki_type = { "OpenSSL/NETSCAPE_SPKI", { - 0, ossl_netscape_spki_free, + 0, ossl_netscape_spki_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -61,7 +61,7 @@ ossl_spki_alloc(VALUE klass) obj = NewSPKI(klass); if (!(spki = NETSCAPE_SPKI_new())) { - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } SetSPKI(obj, spki); @@ -83,15 +83,15 @@ ossl_spki_initialize(int argc, VALUE *argv, VALUE self) const unsigned char *p; if (rb_scan_args(argc, argv, "01", &buffer) == 0) { - return self; + return self; } StringValue(buffer); if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), RSTRING_LENINT(buffer)))) { - ossl_clear_error(); - p = (unsigned char *)RSTRING_PTR(buffer); - if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) { - ossl_raise(eSPKIError, NULL); - } + ossl_clear_error(); + p = (unsigned char *)RSTRING_PTR(buffer); + if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) { + ossl_raise(eSPKIError, NULL); + } } NETSCAPE_SPKI_free(DATA_PTR(self)); SetSPKI(self, spki); @@ -140,7 +140,7 @@ ossl_spki_to_pem(VALUE self) GetSPKI(self, spki); if (!(data = NETSCAPE_SPKI_b64_encode(spki))) { - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } str = ossl_buf2str(data, rb_long2int(strlen(data))); @@ -162,11 +162,11 @@ ossl_spki_print(VALUE self) GetSPKI(self, spki); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } if (!NETSCAPE_SPKI_print(out, spki)) { - BIO_free(out); - ossl_raise(eSPKIError, NULL); + BIO_free(out); + ossl_raise(eSPKIError, NULL); } return ossl_membio2str(out); @@ -187,7 +187,7 @@ ossl_spki_get_public_key(VALUE self) GetSPKI(self, spki); if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */ - ossl_raise(eSPKIError, NULL); + ossl_raise(eSPKIError, NULL); } return ossl_pkey_wrap(pkey); @@ -214,7 +214,7 @@ ossl_spki_set_public_key(VALUE self, VALUE key) pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) - ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey"); + ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey"); return key; } @@ -231,12 +231,12 @@ ossl_spki_get_challenge(VALUE self) GetSPKI(self, spki); if (spki->spkac->challenge->length <= 0) { - OSSL_Debug("Challenge.length <= 0?"); - return rb_str_new(0, 0); + OSSL_Debug("Challenge.length <= 0?"); + return rb_str_new(0, 0); } return rb_str_new((const char *)spki->spkac->challenge->data, - spki->spkac->challenge->length); + spki->spkac->challenge->length); } /* @@ -257,8 +257,8 @@ ossl_spki_set_challenge(VALUE self, VALUE str) StringValue(str); GetSPKI(self, spki); if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING_PTR(str), - RSTRING_LENINT(str))) { - ossl_raise(eSPKIError, NULL); + RSTRING_LENINT(str))) { + ossl_raise(eSPKIError, NULL); } return str; @@ -315,12 +315,12 @@ ossl_spki_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (NETSCAPE_SPKI_verify(spki, pkey)) { case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; case 1: - return Qtrue; + return Qtrue; default: - ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify"); + ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify"); } } diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index b19b51a4ffbb85..97ab24c347b556 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -84,7 +84,7 @@ ossl_ocsp_request_free(void *ptr) static const rb_data_type_t ossl_ocsp_request_type = { "OpenSSL/OCSP/REQUEST", { - 0, ossl_ocsp_request_free, + 0, ossl_ocsp_request_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -98,7 +98,7 @@ ossl_ocsp_response_free(void *ptr) static const rb_data_type_t ossl_ocsp_response_type = { "OpenSSL/OCSP/RESPONSE", { - 0, ossl_ocsp_response_free, + 0, ossl_ocsp_response_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -112,7 +112,7 @@ ossl_ocsp_basicresp_free(void *ptr) static const rb_data_type_t ossl_ocsp_basicresp_type = { "OpenSSL/OCSP/BASICRESP", { - 0, ossl_ocsp_basicresp_free, + 0, ossl_ocsp_basicresp_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -126,7 +126,7 @@ ossl_ocsp_singleresp_free(void *ptr) static const rb_data_type_t ossl_ocsp_singleresp_type = { "OpenSSL/OCSP/SINGLERESP", { - 0, ossl_ocsp_singleresp_free, + 0, ossl_ocsp_singleresp_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -140,7 +140,7 @@ ossl_ocsp_certid_free(void *ptr) static const rb_data_type_t ossl_ocsp_certid_type = { "OpenSSL/OCSP/CERTID", { - 0, ossl_ocsp_certid_free, + 0, ossl_ocsp_certid_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -171,7 +171,7 @@ ossl_ocspreq_alloc(VALUE klass) obj = NewOCSPReq(klass); if (!(req = OCSP_REQUEST_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPReq(obj, req); return obj; @@ -189,7 +189,7 @@ ossl_ocspreq_initialize_copy(VALUE self, VALUE other) req_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_REQUEST), req); if (!req_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPReq(self, req_new); OCSP_REQUEST_free(req_old); @@ -215,15 +215,15 @@ ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ - GetOCSPReq(self, req); - arg = ossl_to_der_if_possible(arg); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - req_new = d2i_OCSP_REQUEST(NULL, &p, RSTRING_LEN(arg)); - if (!req_new) - ossl_raise(eOCSPError, "d2i_OCSP_REQUEST"); - SetOCSPReq(self, req_new); - OCSP_REQUEST_free(req); + GetOCSPReq(self, req); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + req_new = d2i_OCSP_REQUEST(NULL, &p, RSTRING_LEN(arg)); + if (!req_new) + ossl_raise(eOCSPError, "d2i_OCSP_REQUEST"); + SetOCSPReq(self, req_new); + OCSP_REQUEST_free(req); } return self; @@ -249,13 +249,13 @@ ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &val); if(NIL_P(val)) { - GetOCSPReq(self, req); - ret = OCSP_request_add1_nonce(req, NULL, -1); + GetOCSPReq(self, req); + ret = OCSP_request_add1_nonce(req, NULL, -1); } else{ - StringValue(val); - GetOCSPReq(self, req); - ret = OCSP_request_add1_nonce(req, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); + StringValue(val); + GetOCSPReq(self, req); + ret = OCSP_request_add1_nonce(req, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); } if(!ret) ossl_raise(eOCSPError, NULL); @@ -312,10 +312,10 @@ ossl_ocspreq_add_certid(VALUE self, VALUE certid) GetOCSPCertId(certid, id); if (!(id_new = OCSP_CERTID_dup(id))) - ossl_raise(eOCSPError, "OCSP_CERTID_dup"); + ossl_raise(eOCSPError, "OCSP_CERTID_dup"); if (!OCSP_request_add0_id(req, id_new)) { - OCSP_CERTID_free(id_new); - ossl_raise(eOCSPError, "OCSP_request_add0_id"); + OCSP_CERTID_free(id_new); + ossl_raise(eOCSPError, "OCSP_request_add0_id"); } return self; @@ -383,12 +383,12 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); if (!NIL_P(flags)) - flg = NUM2INT(flags); + flg = NUM2INT(flags); md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); if (NIL_P(certs)) - flg |= OCSP_NOCERTS; + flg |= OCSP_NOCERTS; else - x509s = ossl_x509_ary2sk(certs); + x509s = ossl_x509_ary2sk(certs); ret = OCSP_request_sign(req, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); @@ -427,7 +427,7 @@ ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) result = OCSP_request_verify(req, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); if (result <= 0) - ossl_clear_error(); + ossl_clear_error(); return result > 0 ? Qtrue : Qfalse; } @@ -446,11 +446,11 @@ ossl_ocspreq_to_der(VALUE self) GetOCSPReq(self, req); if((len = i2d_OCSP_REQUEST(req, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_OCSP_REQUEST(req, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -494,7 +494,7 @@ ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp) else GetOCSPBasicRes(basic_resp, bs); /* NO NEED TO DUP */ obj = NewOCSPRes(klass); if(!(res = OCSP_response_create(st, bs))) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPRes(obj, res); return obj; @@ -508,7 +508,7 @@ ossl_ocspres_alloc(VALUE klass) obj = NewOCSPRes(klass); if(!(res = OCSP_RESPONSE_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPRes(obj, res); return obj; @@ -526,7 +526,7 @@ ossl_ocspres_initialize_copy(VALUE self, VALUE other) res_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_RESPONSE), res); if (!res_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPRes(self, res_new); OCSP_RESPONSE_free(res_old); @@ -552,15 +552,15 @@ ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ - GetOCSPRes(self, res); - arg = ossl_to_der_if_possible(arg); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - res_new = d2i_OCSP_RESPONSE(NULL, &p, RSTRING_LEN(arg)); - if (!res_new) - ossl_raise(eOCSPError, "d2i_OCSP_RESPONSE"); - SetOCSPRes(self, res_new); - OCSP_RESPONSE_free(res); + GetOCSPRes(self, res); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + res_new = d2i_OCSP_RESPONSE(NULL, &p, RSTRING_LEN(arg)); + if (!res_new) + ossl_raise(eOCSPError, "d2i_OCSP_RESPONSE"); + SetOCSPRes(self, res_new); + OCSP_RESPONSE_free(res); } return self; @@ -621,7 +621,7 @@ ossl_ocspres_get_basic(VALUE self) GetOCSPRes(self, res); ret = NewOCSPBasicRes(cOCSPBasicRes); if(!(bs = OCSP_response_get1_basic(res))) - return Qnil; + return Qnil; SetOCSPBasicRes(ret, bs); return ret; @@ -644,11 +644,11 @@ ossl_ocspres_to_der(VALUE self) GetOCSPRes(self, res); if((len = i2d_OCSP_RESPONSE(res, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_OCSP_RESPONSE(res, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -665,7 +665,7 @@ ossl_ocspbres_alloc(VALUE klass) obj = NewOCSPBasicRes(klass); if(!(bs = OCSP_BASICRESP_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPBasicRes(obj, bs); return obj; @@ -683,7 +683,7 @@ ossl_ocspbres_initialize_copy(VALUE self, VALUE other) bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs); if (!bs_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPBasicRes(self, bs_new); OCSP_BASICRESP_free(bs_old); @@ -708,15 +708,15 @@ ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &arg); if (!NIL_P(arg)) { - GetOCSPBasicRes(self, res); - arg = ossl_to_der_if_possible(arg); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - res_new = d2i_OCSP_BASICRESP(NULL, &p, RSTRING_LEN(arg)); - if (!res_new) - ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP"); - SetOCSPBasicRes(self, res_new); - OCSP_BASICRESP_free(res); + GetOCSPBasicRes(self, res); + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + res_new = d2i_OCSP_BASICRESP(NULL, &p, RSTRING_LEN(arg)); + if (!res_new) + ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP"); + SetOCSPBasicRes(self, res_new); + OCSP_BASICRESP_free(res); } return self; @@ -761,13 +761,13 @@ ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "01", &val); if(NIL_P(val)) { - GetOCSPBasicRes(self, bs); - ret = OCSP_basic_add1_nonce(bs, NULL, -1); + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_add1_nonce(bs, NULL, -1); } else{ - StringValue(val); - GetOCSPBasicRes(self, bs); - ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); + StringValue(val); + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); } if(!ret) ossl_raise(eOCSPError, NULL); @@ -780,12 +780,12 @@ add_status_convert_time(VALUE obj) ASN1_TIME *time; if (RB_INTEGER_TYPE_P(obj)) - time = X509_gmtime_adj(NULL, NUM2INT(obj)); + time = X509_gmtime_adj(NULL, NUM2INT(obj)); else - time = ossl_x509_time_adjust(NULL, obj); + time = ossl_x509_time_adjust(NULL, obj); if (!time) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); return (VALUE)time; } @@ -819,8 +819,8 @@ add_status_convert_time(VALUE obj) */ static VALUE ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, - VALUE reason, VALUE revtime, - VALUE thisupd, VALUE nextupd, VALUE ext) + VALUE reason, VALUE revtime, + VALUE thisupd, VALUE nextupd, VALUE ext) { OCSP_BASICRESP *bs; OCSP_SINGLERESP *single; @@ -834,16 +834,16 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, GetOCSPCertId(cid, id); st = NUM2INT(status); if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */ - ext = rb_check_array_type(ext); - for (i = 0; i < RARRAY_LEN(ext); i++) - OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext); + ext = rb_check_array_type(ext); + for (i = 0; i < RARRAY_LEN(ext); i++) + OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext); } if (st == V_OCSP_CERTSTATUS_REVOKED) { - rsn = NUM2INT(reason); - tmp = rb_protect(add_status_convert_time, revtime, &rstatus); - if (rstatus) goto err; - rev = (ASN1_TIME *)tmp; + rsn = NUM2INT(reason); + tmp = rb_protect(add_status_convert_time, revtime, &rstatus); + if (rstatus) goto err; + rev = (ASN1_TIME *)tmp; } tmp = rb_protect(add_status_convert_time, thisupd, &rstatus); @@ -851,29 +851,29 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, ths = (ASN1_TIME *)tmp; if (!NIL_P(nextupd)) { - tmp = rb_protect(add_status_convert_time, nextupd, &rstatus); - if (rstatus) goto err; - nxt = (ASN1_TIME *)tmp; + tmp = rb_protect(add_status_convert_time, nextupd, &rstatus); + if (rstatus) goto err; + nxt = (ASN1_TIME *)tmp; } if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){ - error = 1; - goto err; + error = 1; + goto err; } if(!NIL_P(ext)){ - X509_EXTENSION *x509ext; - - for(i = 0; i < RARRAY_LEN(ext); i++){ - x509ext = GetX509ExtPtr(RARRAY_AREF(ext, i)); - if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ - error = 1; - goto err; - } - } + X509_EXTENSION *x509ext; + + for(i = 0; i < RARRAY_LEN(ext); i++){ + x509ext = GetX509ExtPtr(RARRAY_AREF(ext, i)); + if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ + error = 1; + goto err; + } + } } - err: + err: ASN1_TIME_free(ths); ASN1_TIME_free(nxt); ASN1_TIME_free(rev); @@ -978,7 +978,7 @@ ossl_ocspbres_find_response(VALUE self, VALUE target) GetOCSPBasicRes(self, bs); if ((n = OCSP_resp_find(bs, id, -1)) == -1) - return Qnil; + return Qnil; return ossl_ocspsres_new(OCSP_resp_get0(bs, n)); } @@ -1012,12 +1012,12 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); if (!NIL_P(flags)) - flg = NUM2INT(flags); + flg = NUM2INT(flags); md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); if (NIL_P(certs)) - flg |= OCSP_NOCERTS; + flg |= OCSP_NOCERTS; else - x509s = ossl_x509_ary2sk(certs); + x509s = ossl_x509_ary2sk(certs); ret = OCSP_basic_sign(bs, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); @@ -1051,7 +1051,7 @@ ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) result = OCSP_basic_verify(bs, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); if (result <= 0) - ossl_clear_error(); + ossl_clear_error(); return result > 0 ? Qtrue : Qfalse; } @@ -1072,11 +1072,11 @@ ossl_ocspbres_to_der(VALUE self) GetOCSPBasicRes(self, res); if ((len = i2d_OCSP_BASICRESP(res, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_BASICRESP(res, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -1110,7 +1110,7 @@ ossl_ocspsres_alloc(VALUE klass) obj = NewOCSPSingleRes(klass); if (!(sres = OCSP_SINGLERESP_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPSingleRes(obj, sres); return obj; @@ -1135,7 +1135,7 @@ ossl_ocspsres_initialize(VALUE self, VALUE arg) p = (unsigned char*)RSTRING_PTR(arg); res_new = d2i_OCSP_SINGLERESP(NULL, &p, RSTRING_LEN(arg)); if (!res_new) - ossl_raise(eOCSPError, "d2i_OCSP_SINGLERESP"); + ossl_raise(eOCSPError, "d2i_OCSP_SINGLERESP"); SetOCSPSingleRes(self, res_new); OCSP_SINGLERESP_free(res); @@ -1154,7 +1154,7 @@ ossl_ocspsres_initialize_copy(VALUE self, VALUE other) sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); if (!sres_new) - ossl_raise(eOCSPError, "ASN1_item_dup"); + ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPSingleRes(self, sres_new); OCSP_SINGLERESP_free(sres_old); @@ -1193,15 +1193,15 @@ ossl_ocspsres_check_validity(int argc, VALUE *argv, VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, &this_update, &next_update); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); ret = OCSP_check_validity(this_update, next_update, nsec, maxsec); if (ret) - return Qtrue; + return Qtrue; else { - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; } } @@ -1243,7 +1243,7 @@ ossl_ocspsres_get_cert_status(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, NULL, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); return INT2NUM(status); } @@ -1262,9 +1262,9 @@ ossl_ocspsres_get_this_update(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, &time, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -1283,9 +1283,9 @@ ossl_ocspsres_get_next_update(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, NULL, &time); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -1304,11 +1304,11 @@ ossl_ocspsres_get_revocation_time(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, &time, NULL, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) - ossl_raise(eOCSPError, "certificate is not revoked"); + ossl_raise(eOCSPError, "certificate is not revoked"); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -1326,9 +1326,9 @@ ossl_ocspsres_get_revocation_reason(VALUE self) GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, &reason, NULL, NULL, NULL); if (status < 0) - ossl_raise(eOCSPError, "OCSP_single_get0_status"); + ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) - ossl_raise(eOCSPError, "certificate is not revoked"); + ossl_raise(eOCSPError, "certificate is not revoked"); return INT2NUM(reason); } @@ -1350,8 +1350,8 @@ ossl_ocspsres_get_extensions(VALUE self) count = OCSP_SINGLERESP_get_ext_count(sres); ary = rb_ary_new2(count); for (i = 0; i < count; i++) { - ext = OCSP_SINGLERESP_get_ext(sres, i); - rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ + ext = OCSP_SINGLERESP_get_ext(sres, i); + rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ } return ary; @@ -1373,11 +1373,11 @@ ossl_ocspsres_to_der(VALUE self) GetOCSPSingleRes(self, sres); if ((len = i2d_OCSP_SINGLERESP(sres, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_SINGLERESP(sres, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; @@ -1395,7 +1395,7 @@ ossl_ocspcid_alloc(VALUE klass) obj = NewOCSPCertId(klass); if(!(id = OCSP_CERTID_new())) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); SetOCSPCertId(obj, id); return obj; @@ -1413,7 +1413,7 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other) cid_new = OCSP_CERTID_dup(cid); if (!cid_new) - ossl_raise(eOCSPError, "OCSP_CERTID_dup"); + ossl_raise(eOCSPError, "OCSP_CERTID_dup"); SetOCSPCertId(self, cid_new); OCSP_CERTID_free(cid_old); @@ -1443,28 +1443,28 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) GetOCSPCertId(self, id); if (rb_scan_args(argc, argv, "12", &subject, &issuer, &digest) == 1) { - VALUE arg; - const unsigned char *p; - - arg = ossl_to_der_if_possible(subject); - StringValue(arg); - p = (unsigned char *)RSTRING_PTR(arg); - newid = d2i_OCSP_CERTID(NULL, &p, RSTRING_LEN(arg)); - if (!newid) - ossl_raise(eOCSPError, "d2i_OCSP_CERTID"); + VALUE arg; + const unsigned char *p; + + arg = ossl_to_der_if_possible(subject); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + newid = d2i_OCSP_CERTID(NULL, &p, RSTRING_LEN(arg)); + if (!newid) + ossl_raise(eOCSPError, "d2i_OCSP_CERTID"); } else { - X509 *x509s, *x509i; - const EVP_MD *md; + X509 *x509s, *x509i; + const EVP_MD *md; VALUE md_holder; - x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ - x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ + x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ + x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder); - newid = OCSP_cert_to_id(md, x509s, x509i); - if (!newid) - ossl_raise(eOCSPError, "OCSP_cert_to_id"); + newid = OCSP_cert_to_id(md, x509s, x509i); + if (!newid) + ossl_raise(eOCSPError, "OCSP_cert_to_id"); } SetOCSPCertId(self, newid); @@ -1613,11 +1613,11 @@ ossl_ocspcid_to_der(VALUE self) GetOCSPCertId(self, id); if ((len = i2d_OCSP_CERTID(id, NULL)) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_CERTID(id, &p) <= 0) - ossl_raise(eOCSPError, NULL); + ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c index 8aff77a6dad934..32b82a881c361f 100644 --- a/ext/openssl/ossl_pkcs12.c +++ b/ext/openssl/ossl_pkcs12.c @@ -42,7 +42,7 @@ ossl_pkcs12_free(void *ptr) static const rb_data_type_t ossl_pkcs12_type = { "OpenSSL/PKCS12", { - 0, ossl_pkcs12_free, + 0, ossl_pkcs12_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -72,7 +72,7 @@ ossl_pkcs12_initialize_copy(VALUE self, VALUE other) p12_new = ASN1_dup((i2d_of_void *)i2d_PKCS12, (d2i_of_void *)d2i_PKCS12, (char *)p12); if (!p12_new) - ossl_raise(ePKCS12Error, "ASN1_dup"); + ossl_raise(ePKCS12Error, "ASN1_dup"); SetPKCS12(self, p12_new); PKCS12_free(p12_old); @@ -122,11 +122,11 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) /* TODO: make a VALUE to nid function */ if (!NIL_P(key_nid)) { if ((nkey = OBJ_txt2nid(StringValueCStr(key_nid))) == NID_undef) - ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, key_nid); + ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, key_nid); } if (!NIL_P(cert_nid)) { if ((ncert = OBJ_txt2nid(StringValueCStr(cert_nid))) == NID_undef) - ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, cert_nid); + ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, cert_nid); } if (!NIL_P(key_iter)) kiter = NUM2INT(key_iter); @@ -209,18 +209,18 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self) pkey = cert = ca = Qnil; if(!PKCS12_parse(pkcs, passphrase, &key, &x509, &x509s)) - ossl_raise(ePKCS12Error, "PKCS12_parse"); + ossl_raise(ePKCS12Error, "PKCS12_parse"); if (key) { - pkey = rb_protect(ossl_pkey_wrap_i, (VALUE)key, &st); - if (st) goto err; + pkey = rb_protect(ossl_pkey_wrap_i, (VALUE)key, &st); + if (st) goto err; } if (x509) { - cert = rb_protect(ossl_x509_new_i, (VALUE)x509, &st); - if (st) goto err; + cert = rb_protect(ossl_x509_new_i, (VALUE)x509, &st); + if (st) goto err; } if (x509s) { - ca = rb_protect(ossl_x509_sk2ary_i, (VALUE)x509s, &st); - if (st) goto err; + ca = rb_protect(ossl_x509_sk2ary_i, (VALUE)x509s, &st); + if (st) goto err; } err: @@ -244,11 +244,11 @@ ossl_pkcs12_to_der(VALUE self) GetPKCS12(self, p12); if((len = i2d_PKCS12(p12, NULL)) <= 0) - ossl_raise(ePKCS12Error, NULL); + ossl_raise(ePKCS12Error, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_PKCS12(p12, &p) <= 0) - ossl_raise(ePKCS12Error, NULL); + ossl_raise(ePKCS12Error, NULL); ossl_str_adjust(str, p); return str; diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 7bd6c18f92acbb..88f06c8831c188 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -28,14 +28,14 @@ TypedData_Wrap_Struct((klass), &ossl_pkcs7_signer_info_type, 0) #define SetPKCS7si(obj, p7si) do { \ if (!(p7si)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (p7si); \ } while (0) #define GetPKCS7si(obj, p7si) do { \ TypedData_Get_Struct((obj), PKCS7_SIGNER_INFO, &ossl_pkcs7_signer_info_type, (p7si)); \ if (!(p7si)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ } while (0) @@ -43,14 +43,14 @@ TypedData_Wrap_Struct((klass), &ossl_pkcs7_recip_info_type, 0) #define SetPKCS7ri(obj, p7ri) do { \ if (!(p7ri)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (p7ri); \ } while (0) #define GetPKCS7ri(obj, p7ri) do { \ TypedData_Get_Struct((obj), PKCS7_RECIP_INFO, &ossl_pkcs7_recip_info_type, (p7ri)); \ if (!(p7ri)) { \ - ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ } while (0) @@ -79,7 +79,7 @@ ossl_pkcs7_free(void *ptr) static const rb_data_type_t ossl_pkcs7_type = { "OpenSSL/PKCS7", { - 0, ossl_pkcs7_free, + 0, ossl_pkcs7_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -107,7 +107,7 @@ ossl_pkcs7_signer_info_free(void *ptr) static const rb_data_type_t ossl_pkcs7_signer_info_type = { "OpenSSL/PKCS7/SIGNER_INFO", { - 0, ossl_pkcs7_signer_info_free, + 0, ossl_pkcs7_signer_info_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -121,7 +121,7 @@ ossl_pkcs7_recip_info_free(void *ptr) static const rb_data_type_t ossl_pkcs7_recip_info_type = { "OpenSSL/PKCS7/RECIP_INFO", { - 0, ossl_pkcs7_recip_info_free, + 0, ossl_pkcs7_recip_info_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -238,7 +238,7 @@ ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass) if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7); GetPKCS7(pkcs7, p7); if(!NIL_P(data) && PKCS7_is_detached(p7)) - flg |= PKCS7_DETACHED; + flg |= PKCS7_DETACHED; in = NIL_P(data) ? NULL : ossl_obj2bio(&data); if(!(out = BIO_new(BIO_s_mem()))){ BIO_free(in); @@ -279,16 +279,16 @@ ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) in = ossl_obj2bio(&data); if(NIL_P(certs)) x509s = NULL; else{ - x509s = ossl_protect_x509_ary2sk(certs, &status); - if(status){ - BIO_free(in); - rb_jump_tag(status); - } + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + BIO_free(in); + rb_jump_tag(status); + } } if(!(pkcs7 = PKCS7_sign(x509, pkey, x509s, in, flg))){ - BIO_free(in); - sk_X509_pop_free(x509s, X509_free); - ossl_raise(ePKCS7Error, NULL); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7(ret, pkcs7); ossl_pkcs7_set_data(ret, data); @@ -333,13 +333,13 @@ ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass) in = ossl_obj2bio(&data); x509s = ossl_protect_x509_ary2sk(certs, &status); if(status){ - BIO_free(in); - rb_jump_tag(status); + BIO_free(in); + rb_jump_tag(status); } if (!(p7 = PKCS7_encrypt(x509s, in, ciph, flg))) { - BIO_free(in); - sk_X509_pop_free(x509s, X509_free); - ossl_raise(ePKCS7Error, NULL); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); } BIO_free(in); SetPKCS7(ret, p7); @@ -358,7 +358,7 @@ ossl_pkcs7_alloc(VALUE klass) obj = NewPKCS7(klass); if (!(pkcs7 = PKCS7_new())) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7(obj, pkcs7); @@ -380,7 +380,7 @@ ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) VALUE arg; if(rb_scan_args(argc, argv, "01", &arg) == 0) - return self; + return self; arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); p7 = d2i_PKCS7_bio(in, NULL); @@ -418,7 +418,7 @@ ossl_pkcs7_copy(VALUE self, VALUE other) pkcs7 = PKCS7_dup(b); if (!pkcs7) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } DATA_PTR(self) = pkcs7; PKCS7_free(a); @@ -450,13 +450,13 @@ ossl_pkcs7_sym2typeid(VALUE sym) RSTRING_GETMEM(sym, s, l); for(i = 0; ; i++){ - if(i == numberof(p7_type_tab)) - ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym); - if(strlen(p7_type_tab[i].name) != l) continue; - if(strcmp(p7_type_tab[i].name, s) == 0){ - ret = p7_type_tab[i].nid; - break; - } + if(i == numberof(p7_type_tab)) + ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym); + if(strlen(p7_type_tab[i].name) != l) continue; + if(strcmp(p7_type_tab[i].name, s) == 0){ + ret = p7_type_tab[i].nid; + break; + } } return ret; @@ -473,7 +473,7 @@ ossl_pkcs7_set_type(VALUE self, VALUE type) GetPKCS7(self, p7); if(!PKCS7_set_type(p7, ossl_pkcs7_sym2typeid(type))) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); return type; } @@ -489,15 +489,15 @@ ossl_pkcs7_get_type(VALUE self) GetPKCS7(self, p7); if(PKCS7_type_is_signed(p7)) - return ID2SYM(rb_intern("signed")); + return ID2SYM(rb_intern("signed")); if(PKCS7_type_is_encrypted(p7)) - return ID2SYM(rb_intern("encrypted")); + return ID2SYM(rb_intern("encrypted")); if(PKCS7_type_is_enveloped(p7)) - return ID2SYM(rb_intern("enveloped")); + return ID2SYM(rb_intern("enveloped")); if(PKCS7_type_is_signedAndEnveloped(p7)) - return ID2SYM(rb_intern("signedAndEnveloped")); + return ID2SYM(rb_intern("signedAndEnveloped")); if(PKCS7_type_is_data(p7)) - return ID2SYM(rb_intern("data")); + return ID2SYM(rb_intern("data")); return Qnil; } @@ -508,9 +508,9 @@ ossl_pkcs7_set_detached(VALUE self, VALUE flag) GetPKCS7(self, p7); if(flag != Qtrue && flag != Qfalse) - ossl_raise(ePKCS7Error, "must specify a boolean"); + ossl_raise(ePKCS7Error, "must specify a boolean"); if(!PKCS7_set_detached(p7, flag == Qtrue ? 1 : 0)) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); return flag; } @@ -584,8 +584,8 @@ ossl_pkcs7_get_signer(VALUE self) num = sk_PKCS7_SIGNER_INFO_num(sk); ary = rb_ary_new_capa(num); for (i=0; id.enveloped->recipientinfo; + sk = pkcs7->d.enveloped->recipientinfo; else if (PKCS7_type_is_signedAndEnveloped(pkcs7)) - sk = pkcs7->d.signed_and_enveloped->recipientinfo; + sk = pkcs7->d.signed_and_enveloped->recipientinfo; else sk = NULL; if (!sk) return rb_ary_new(); num = sk_PKCS7_RECIP_INFO_num(sk); @@ -646,7 +646,7 @@ ossl_pkcs7_add_certificate(VALUE self, VALUE cert) GetPKCS7(self, pkcs7); x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ if (!PKCS7_add_certificate(pkcs7, x509)){ - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } return self; @@ -662,13 +662,13 @@ pkcs7_get_certs(VALUE self) GetPKCS7(self, pkcs7); i = OBJ_obj2nid(pkcs7->type); switch(i){ - case NID_pkcs7_signed: + case NID_pkcs7_signed: certs = pkcs7->d.sign->cert; break; - case NID_pkcs7_signedAndEnveloped: + case NID_pkcs7_signedAndEnveloped: certs = pkcs7->d.signed_and_enveloped->cert; break; - default: + default: certs = NULL; } @@ -685,13 +685,13 @@ pkcs7_get_crls(VALUE self) GetPKCS7(self, pkcs7); i = OBJ_obj2nid(pkcs7->type); switch(i){ - case NID_pkcs7_signed: + case NID_pkcs7_signed: crls = pkcs7->d.sign->crl; break; - case NID_pkcs7_signedAndEnveloped: + case NID_pkcs7_signedAndEnveloped: crls = pkcs7->d.signed_and_enveloped->crl; break; - default: + default: crls = NULL; } @@ -738,7 +738,7 @@ ossl_pkcs7_add_crl(VALUE self, VALUE crl) GetPKCS7(self, pkcs7); /* NO DUP needed! */ x509crl = GetX509CRLPtr(crl); if (!PKCS7_add_crl(pkcs7, x509crl)) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } return self; @@ -794,16 +794,16 @@ ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self) in = NIL_P(indata) ? NULL : ossl_obj2bio(&indata); if(NIL_P(certs)) x509s = NULL; else{ - x509s = ossl_protect_x509_ary2sk(certs, &status); - if(status){ - BIO_free(in); - rb_jump_tag(status); - } + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + BIO_free(in); + rb_jump_tag(status); + } } if(!(out = BIO_new(BIO_s_mem()))){ - BIO_free(in); - sk_X509_pop_free(x509s, X509_free); - ossl_raise(ePKCS7Error, NULL); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); } ok = PKCS7_verify(p7, x509s, x509st, in, out, flg); BIO_free(in); @@ -837,10 +837,10 @@ ossl_pkcs7_decrypt(int argc, VALUE *argv, VALUE self) flg = NIL_P(flags) ? 0 : NUM2INT(flags); GetPKCS7(self, p7); if(!(out = BIO_new(BIO_s_mem()))) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); if(!PKCS7_decrypt(p7, key, x509, out, flg)){ - BIO_free(out); - ossl_raise(ePKCS7Error, NULL); + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); } str = ossl_membio2str(out); /* out will be free */ @@ -899,11 +899,11 @@ ossl_pkcs7_to_der(VALUE self) GetPKCS7(self, pkcs7); if((len = i2d_PKCS7(pkcs7, NULL)) <= 0) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_PKCS7(pkcs7, &p) <= 0) - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); ossl_str_adjust(str, p); return str; @@ -937,11 +937,11 @@ ossl_pkcs7_to_pem(VALUE self) GetPKCS7(self, pkcs7); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } if (!PEM_write_bio_PKCS7(out, pkcs7)) { - BIO_free(out); - ossl_raise(ePKCS7Error, NULL); + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); } str = ossl_membio2str(out); @@ -959,7 +959,7 @@ ossl_pkcs7si_alloc(VALUE klass) obj = NewPKCS7si(klass); if (!(p7si = PKCS7_SIGNER_INFO_new())) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7si(obj, p7si); @@ -1015,10 +1015,10 @@ ossl_pkcs7si_get_signed_time(VALUE self) GetPKCS7si(self, p7si); if (!(asn1obj = PKCS7_get_signed_attribute(p7si, NID_pkcs9_signingTime))) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } if (asn1obj->type == V_ASN1_UTCTIME) { - return asn1time_to_time(asn1obj->value.utctime); + return asn1time_to_time(asn1obj->value.utctime); } /* * OR @@ -1040,7 +1040,7 @@ ossl_pkcs7ri_alloc(VALUE klass) obj = NewPKCS7ri(klass); if (!(p7ri = PKCS7_RECIP_INFO_new())) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } SetPKCS7ri(obj, p7ri); @@ -1056,7 +1056,7 @@ ossl_pkcs7ri_initialize(VALUE self, VALUE cert) x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ GetPKCS7ri(self, p7ri); if (!PKCS7_RECIP_INFO_set(p7ri, x509)) { - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, NULL); } return self; diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 61ff472aadd1b5..d2fd5b29c32204 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -33,7 +33,7 @@ ossl_evp_pkey_free(void *ptr) const rb_data_type_t ossl_evp_pkey_type = { "OpenSSL/EVP_PKEY", { - 0, ossl_evp_pkey_free, + 0, ossl_evp_pkey_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -72,8 +72,8 @@ ossl_pkey_wrap(EVP_PKEY *pkey) obj = rb_protect(pkey_wrap0, (VALUE)pkey, &status); if (status) { - EVP_PKEY_free(pkey); - rb_jump_tag(status); + EVP_PKEY_free(pkey); + rb_jump_tag(status); } return obj; @@ -188,23 +188,23 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass) EVP_PKEY *pkey; if ((pkey = d2i_PrivateKey_bio(bio, NULL))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, ppass))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = d2i_PUBKEY_bio(bio, NULL))) - goto out; + goto out; OSSL_BIO_reset(bio); /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */ if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, ppass))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL))) - goto out; + goto out; OSSL_BIO_reset(bio); if ((pkey = PEM_read_bio_Parameters(bio, NULL))) - goto out; + goto out; out: return pkey; @@ -239,7 +239,7 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) pkey = ossl_pkey_read_generic(bio, ossl_pem_passwd_value(pass)); BIO_free(bio); if (!pkey) - ossl_raise(ePKeyError, "Could not parse PKey"); + ossl_raise(ePKeyError, "Could not parse PKey"); return ossl_pkey_wrap(pkey); } @@ -516,34 +516,34 @@ ossl_pkey_check_public_key(const EVP_PKEY *pkey) const BIGNUM *n, *e, *pubkey; if (EVP_PKEY_missing_parameters(pkey)) - ossl_raise(ePKeyError, "parameters missing"); + ossl_raise(ePKeyError, "parameters missing"); ptr = EVP_PKEY_get0(pkey); switch (EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: - RSA_get0_key(ptr, &n, &e, NULL); - if (n && e) - return; - break; + RSA_get0_key(ptr, &n, &e, NULL); + if (n && e) + return; + break; case EVP_PKEY_DSA: - DSA_get0_key(ptr, &pubkey, NULL); - if (pubkey) - return; - break; + DSA_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; case EVP_PKEY_DH: - DH_get0_key(ptr, &pubkey, NULL); - if (pubkey) - return; - break; + DH_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; #if !defined(OPENSSL_NO_EC) case EVP_PKEY_EC: - if (EC_KEY_get0_public_key(ptr)) - return; - break; + if (EC_KEY_get0_public_key(ptr)) + return; + break; #endif default: - /* unsupported type; assuming ok */ - return; + /* unsupported type; assuming ok */ + return; } ossl_raise(ePKeyError, "public key missing"); #endif @@ -610,7 +610,7 @@ static VALUE ossl_pkey_initialize(VALUE self) { if (rb_obj_is_instance_of(self, cPKey)) { - ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); + ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); } return self; } @@ -822,25 +822,25 @@ ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der) rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { enc = ossl_evp_cipher_fetch(cipher, &cipher_holder); - pass = ossl_pem_passwd_value(pass); + pass = ossl_pem_passwd_value(pass); } bio = BIO_new(BIO_s_mem()); if (!bio) - ossl_raise(ePKeyError, "BIO_new"); + ossl_raise(ePKeyError, "BIO_new"); if (to_der) { - if (!i2d_PrivateKey_bio(bio, pkey)) { - BIO_free(bio); - ossl_raise(ePKeyError, "i2d_PrivateKey_bio"); - } + if (!i2d_PrivateKey_bio(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PrivateKey_bio"); + } } else { - if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0, - ossl_pem_passwd_cb, - (void *)pass)) { - BIO_free(bio); - ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional"); - } + if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, + (void *)pass)) { + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional"); + } } return ossl_membio2str(bio); } @@ -856,30 +856,30 @@ do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der) GetPKey(self, pkey); rb_scan_args(argc, argv, "02", &cipher, &pass); if (argc > 0) { - /* - * TODO: EncryptedPrivateKeyInfo actually has more options. - * Should they be exposed? - */ + /* + * TODO: EncryptedPrivateKeyInfo actually has more options. + * Should they be exposed? + */ enc = ossl_evp_cipher_fetch(cipher, &cipher_holder); - pass = ossl_pem_passwd_value(pass); + pass = ossl_pem_passwd_value(pass); } bio = BIO_new(BIO_s_mem()); if (!bio) - ossl_raise(ePKeyError, "BIO_new"); + ossl_raise(ePKeyError, "BIO_new"); if (to_der) { - if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0, - ossl_pem_passwd_cb, (void *)pass)) { - BIO_free(bio); - ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio"); - } + if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, (void *)pass)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio"); + } } else { - if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0, - ossl_pem_passwd_cb, (void *)pass)) { - BIO_free(bio); - ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey"); - } + if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, (void *)pass)) { + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey"); + } } return ossl_membio2str(bio); } @@ -963,18 +963,18 @@ ossl_pkey_export_spki(VALUE self, int to_der) ossl_pkey_check_public_key(pkey); bio = BIO_new(BIO_s_mem()); if (!bio) - ossl_raise(ePKeyError, "BIO_new"); + ossl_raise(ePKeyError, "BIO_new"); if (to_der) { - if (!i2d_PUBKEY_bio(bio, pkey)) { - BIO_free(bio); - ossl_raise(ePKeyError, "i2d_PUBKEY_bio"); - } + if (!i2d_PUBKEY_bio(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PUBKEY_bio"); + } } else { - if (!PEM_write_bio_PUBKEY(bio, pkey)) { - BIO_free(bio); - ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY"); - } + if (!PEM_write_bio_PUBKEY(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY"); + } } return ossl_membio2str(bio); } diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 24823e0f3e721a..023361b90f2306 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -22,7 +22,7 @@ extern const rb_data_type_t ossl_evp_pkey_type; #define GetPKey(obj, pkey) do {\ TypedData_Get_Struct((obj), EVP_PKEY, &ossl_evp_pkey_type, (pkey)); \ if (!(pkey)) { \ - rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ + rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ } \ } while (0) @@ -45,7 +45,7 @@ VALUE ossl_pkey_export_spki(VALUE self, int to_der); * #to_der. */ VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, - int to_der); + int to_der); void Init_ossl_pkey(void); @@ -74,120 +74,120 @@ extern VALUE cEC; VALUE ossl_ec_new(EVP_PKEY *); void Init_ossl_ec(void); -#define OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, _name, _get) \ -/* \ - * call-seq: \ - * _keytype##.##_name -> aBN \ - */ \ -static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ -{ \ - const _type *obj; \ - const BIGNUM *bn; \ - \ - Get##_type(self, obj); \ - _get; \ - if (bn == NULL) \ - return Qnil; \ - return ossl_bn_new(bn); \ +#define OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, _name, _get) \ +/* \ + * call-seq: \ + * _keytype##.##_name -> aBN \ + */ \ +static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ +{ \ + const _type *obj; \ + const BIGNUM *bn; \ + \ + Get##_type(self, obj); \ + _get; \ + if (bn == NULL) \ + return Qnil; \ + return ossl_bn_new(bn); \ } -#define OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ - _type##_get0_##_group(obj, &bn, NULL, NULL)) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ - _type##_get0_##_group(obj, NULL, &bn, NULL)) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a3, \ - _type##_get0_##_group(obj, NULL, NULL, &bn)) +#define OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ + _type##_get0_##_group(obj, &bn, NULL, NULL)) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ + _type##_get0_##_group(obj, NULL, &bn, NULL)) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a3, \ + _type##_get0_##_group(obj, NULL, NULL, &bn)) -#define OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ - _type##_get0_##_group(obj, &bn, NULL)) \ - OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ - _type##_get0_##_group(obj, NULL, &bn)) +#define OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ + _type##_get0_##_group(obj, &bn, NULL)) \ + OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ + _type##_get0_##_group(obj, NULL, &bn)) #ifndef OSSL_HAVE_IMMUTABLE_PKEY -#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ -/* \ - * call-seq: \ - * _keytype##.set_##_group(a1, a2, a3) -> self \ - */ \ +#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ +/* \ + * call-seq: \ + * _keytype##.set_##_group(a1, a2, a3) -> self \ + */ \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \ -{ \ - _type *obj; \ - BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ - BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ - BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\ - \ - Get##_type(self, obj); \ - if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ - (orig_bn2 && !(bn2 = BN_dup(orig_bn2))) || \ - (orig_bn3 && !(bn3 = BN_dup(orig_bn3)))) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - BN_clear_free(bn3); \ - ossl_raise(ePKeyError, "BN_dup"); \ - } \ - \ - if (!_type##_set0_##_group(obj, bn1, bn2, bn3)) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - BN_clear_free(bn3); \ - ossl_raise(ePKeyError, #_type"_set0_"#_group); \ - } \ - return self; \ +{ \ + _type *obj; \ + BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ + BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ + BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\ + \ + Get##_type(self, obj); \ + if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ + (orig_bn2 && !(bn2 = BN_dup(orig_bn2))) || \ + (orig_bn3 && !(bn3 = BN_dup(orig_bn3)))) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + BN_clear_free(bn3); \ + ossl_raise(ePKeyError, "BN_dup"); \ + } \ + \ + if (!_type##_set0_##_group(obj, bn1, bn2, bn3)) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + BN_clear_free(bn3); \ + ossl_raise(ePKeyError, #_type"_set0_"#_group); \ + } \ + return self; \ } -#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ -/* \ - * call-seq: \ - * _keytype##.set_##_group(a1, a2) -> self \ - */ \ +#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ +/* \ + * call-seq: \ + * _keytype##.set_##_group(a1, a2) -> self \ + */ \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \ -{ \ - _type *obj; \ - BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ - BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ - \ - Get##_type(self, obj); \ - if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ - (orig_bn2 && !(bn2 = BN_dup(orig_bn2)))) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - ossl_raise(ePKeyError, "BN_dup"); \ - } \ - \ - if (!_type##_set0_##_group(obj, bn1, bn2)) { \ - BN_clear_free(bn1); \ - BN_clear_free(bn2); \ - ossl_raise(ePKeyError, #_type"_set0_"#_group); \ - } \ - return self; \ +{ \ + _type *obj; \ + BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ + BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ + \ + Get##_type(self, obj); \ + if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) || \ + (orig_bn2 && !(bn2 = BN_dup(orig_bn2)))) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + ossl_raise(ePKeyError, "BN_dup"); \ + } \ + \ + if (!_type##_set0_##_group(obj, bn1, bn2)) { \ + BN_clear_free(bn1); \ + BN_clear_free(bn2); \ + ossl_raise(ePKeyError, #_type"_set0_"#_group); \ + } \ + return self; \ } #else -#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ +#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \ -{ \ - rb_raise(ePKeyError, \ +{ \ + rb_raise(ePKeyError, \ #_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \ } -#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ +#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \ -{ \ - rb_raise(ePKeyError, \ +{ \ + rb_raise(ePKeyError, \ #_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \ } #endif -#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \ - OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ - OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) +#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \ + OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ + OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) -#define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2) \ - OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ - OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) +#define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2) \ + OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ + OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) -#define DEF_OSSL_PKEY_BN(class, keytype, name) \ - rb_define_method((class), #name, ossl_##keytype##_get_##name, 0) +#define DEF_OSSL_PKEY_BN(class, keytype, name) \ + rb_define_method((class), #name, ossl_##keytype##_get_##name, 0) #endif /* OSSL_PKEY_H */ diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 60cd20a857fc73..3f2975c5a3fdc4 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -14,7 +14,7 @@ #define GetPKeyDH(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) { /* PARANOIA? */ \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \ } \ } while (0) #define GetDH(obj, dh) do { \ @@ -151,19 +151,19 @@ ossl_dh_initialize_copy(VALUE self, VALUE other) dh = DHparams_dup(dh_other); if (!dh) - ossl_raise(ePKeyError, "DHparams_dup"); + ossl_raise(ePKeyError, "DHparams_dup"); DH_get0_key(dh_other, &pub, &priv); if (pub) { - BIGNUM *pub2 = BN_dup(pub); - BIGNUM *priv2 = BN_dup(priv); + BIGNUM *pub2 = BN_dup(pub); + BIGNUM *priv2 = BN_dup(priv); if (!pub2 || (priv && !priv2)) { - BN_clear_free(pub2); - BN_clear_free(priv2); - ossl_raise(ePKeyError, "BN_dup"); - } - DH_set0_key(dh, pub2, priv2); + BN_clear_free(pub2); + BN_clear_free(priv2); + ossl_raise(ePKeyError, "BN_dup"); + } + DH_set0_key(dh, pub2, priv2); } pkey = EVP_PKEY_new(); @@ -249,11 +249,11 @@ ossl_dh_export(VALUE self) GetDH(self, dh); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(ePKeyError, NULL); + ossl_raise(ePKeyError, NULL); } if (!PEM_write_bio_DHparams(out, dh)) { - BIO_free(out); - ossl_raise(ePKeyError, NULL); + BIO_free(out); + ossl_raise(ePKeyError, NULL); } str = ossl_membio2str(out); @@ -283,11 +283,11 @@ ossl_dh_to_der(VALUE self) GetDH(self, dh); if((len = i2d_DHparams(dh, NULL)) <= 0) - ossl_raise(ePKeyError, NULL); + ossl_raise(ePKeyError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_DHparams(dh, &p) < 0) - ossl_raise(ePKeyError, NULL); + ossl_raise(ePKeyError, NULL); ossl_str_adjust(str, p); return str; diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 8b141afab775b1..041646a0585d84 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -14,7 +14,7 @@ #define GetPKeyDSA(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { /* PARANOIA? */ \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ } \ } while (0) #define GetDSA(obj, dsa) do { \ @@ -163,7 +163,7 @@ ossl_dsa_initialize_copy(VALUE self, VALUE other) (d2i_of_void *)d2i_DSAPrivateKey, (char *)dsa); if (!dsa_new) - ossl_raise(ePKeyError, "ASN1_dup"); + ossl_raise(ePKeyError, "ASN1_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa_new) != 1) { diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index bf77e0fa0556d3..bb19533edf4744 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -15,7 +15,7 @@ static const rb_data_type_t ossl_ec_point_type; #define GetPKeyEC(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!"); \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!"); \ } \ } while (0) #define GetEC(obj, key) do { \ @@ -29,13 +29,13 @@ static const rb_data_type_t ossl_ec_point_type; #define GetECGroup(obj, group) do { \ TypedData_Get_Struct(obj, EC_GROUP, &ossl_ec_group_type, group); \ if ((group) == NULL) \ - ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ + ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ } while (0) #define GetECPoint(obj, point) do { \ TypedData_Get_Struct(obj, EC_POINT, &ossl_ec_point_type, point); \ if ((point) == NULL) \ - ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ + ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ } while (0) #define GetECPointGroup(obj, group) do { \ VALUE _group = rb_attr_get(obj, id_i_group); \ @@ -66,27 +66,27 @@ ec_key_new_from_group(VALUE arg) EC_KEY *ec; if (rb_obj_is_kind_of(arg, cEC_GROUP)) { - EC_GROUP *group; + EC_GROUP *group; - GetECGroup(arg, group); - if (!(ec = EC_KEY_new())) - ossl_raise(ePKeyError, NULL); + GetECGroup(arg, group); + if (!(ec = EC_KEY_new())) + ossl_raise(ePKeyError, NULL); - if (!EC_KEY_set_group(ec, group)) { - EC_KEY_free(ec); - ossl_raise(ePKeyError, NULL); - } + if (!EC_KEY_set_group(ec, group)) { + EC_KEY_free(ec); + ossl_raise(ePKeyError, NULL); + } } else { - int nid = OBJ_sn2nid(StringValueCStr(arg)); + int nid = OBJ_sn2nid(StringValueCStr(arg)); - if (nid == NID_undef) - ossl_raise(ePKeyError, "invalid curve name"); + if (nid == NID_undef) + ossl_raise(ePKeyError, "invalid curve name"); - if (!(ec = EC_KEY_new_by_curve_name(nid))) - ossl_raise(ePKeyError, NULL); + if (!(ec = EC_KEY_new_by_curve_name(nid))) + ossl_raise(ePKeyError, NULL); - EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); - EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); } return ec; @@ -118,7 +118,7 @@ ossl_ec_key_s_generate(VALUE klass, VALUE arg) RTYPEDDATA_DATA(obj) = pkey; if (!EC_KEY_generate_key(ec)) - ossl_raise(ePKeyError, "EC_KEY_generate_key"); + ossl_raise(ePKeyError, "EC_KEY_generate_key"); return obj; } @@ -208,7 +208,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other) ec_new = EC_KEY_dup(ec); if (!ec_new) - ossl_raise(ePKeyError, "EC_KEY_dup"); + ossl_raise(ePKeyError, "EC_KEY_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec_new) != 1) { @@ -237,7 +237,7 @@ ossl_ec_key_get_group(VALUE self) GetEC(self, ec); group = EC_KEY_get0_group(ec); if (!group) - return Qnil; + return Qnil; return ec_group_new(group); } @@ -305,13 +305,13 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key) bn = GetBNPtr(private_key); switch (EC_KEY_set_private_key(ec, bn)) { - case 1: + case 1: break; - case 0: + case 0: if (bn == NULL) break; - /* fallthrough */ - default: + /* fallthrough */ + default: ossl_raise(ePKeyError, "EC_KEY_set_private_key"); } @@ -356,13 +356,13 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) GetECPoint(public_key, point); switch (EC_KEY_set_public_key(ec, point)) { - case 1: + case 1: break; - case 0: + case 0: if (point == NULL) break; - /* fallthrough */ - default: + /* fallthrough */ + default: ossl_raise(ePKeyError, "EC_KEY_set_public_key"); } @@ -524,7 +524,7 @@ static VALUE ossl_ec_key_generate_key(VALUE self) GetEC(self, ec); if (EC_KEY_generate_key(ec) != 1) - ossl_raise(ePKeyError, "EC_KEY_generate_key"); + ossl_raise(ePKeyError, "EC_KEY_generate_key"); return self; #endif @@ -570,7 +570,7 @@ static VALUE ossl_ec_key_check_key(VALUE self) GetEC(self, ec); if (EC_KEY_check_key(ec) != 1) - ossl_raise(ePKeyError, "EC_KEY_check_key"); + ossl_raise(ePKeyError, "EC_KEY_check_key"); #endif return Qtrue; @@ -588,7 +588,7 @@ ossl_ec_group_free(void *ptr) static const rb_data_type_t ossl_ec_group_type = { "OpenSSL/ec_group", { - 0, ossl_ec_group_free, + 0, ossl_ec_group_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -608,7 +608,7 @@ ec_group_new(const EC_GROUP *group) obj = ossl_ec_group_alloc(cEC_GROUP); group_new = EC_GROUP_dup(group); if (!group_new) - ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); RTYPEDDATA_DATA(obj) = group_new; return obj; @@ -636,7 +636,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) ossl_raise(rb_eRuntimeError, "EC_GROUP is already initialized"); switch (rb_scan_args(argc, argv, "13", &arg1, &arg2, &arg3, &arg4)) { - case 1: + case 1: if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { const EC_GROUP *arg1_group; @@ -648,7 +648,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL); if (!group) { - OSSL_BIO_reset(in); + OSSL_BIO_reset(in); group = d2i_ECPKParameters_bio(in, NULL); } @@ -658,7 +658,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) const char *name = StringValueCStr(arg1); int nid = OBJ_sn2nid(name); - ossl_clear_error(); /* ignore errors in d2i_ECPKParameters_bio() */ + ossl_clear_error(); /* ignore errors in d2i_ECPKParameters_bio() */ if (nid == NID_undef) ossl_raise(eEC_GROUP, "unknown curve name (%"PRIsVALUE")", arg1); #if !defined(OPENSSL_IS_AWSLC) @@ -675,7 +675,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) } break; - case 4: + case 4: if (SYMBOL_P(arg1)) { EC_GROUP *(*new_curve)(const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; const BIGNUM *p = GetBNPtr(arg2); @@ -697,11 +697,11 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) if ((group = new_curve(p, a, b, ossl_bn_ctx)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_new_by_GF*"); } else { - ossl_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); + ossl_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); } break; - default: + default: ossl_raise(rb_eArgError, "wrong number of arguments"); } @@ -719,12 +719,12 @@ ossl_ec_group_initialize_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group_new); if (group_new) - ossl_raise(eEC_GROUP, "EC::Group already initialized"); + ossl_raise(eEC_GROUP, "EC::Group already initialized"); GetECGroup(other, group); group_new = EC_GROUP_dup(group); if (!group_new) - ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); RTYPEDDATA_DATA(self) = group_new; return self; @@ -746,9 +746,9 @@ static VALUE ossl_ec_group_eql(VALUE a, VALUE b) GetECGroup(b, group2); switch (EC_GROUP_cmp(group1, group2, ossl_bn_ctx)) { - case 0: return Qtrue; - case 1: return Qfalse; - default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp"); + case 0: return Qtrue; + case 1: return Qfalse; + default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp"); } } @@ -768,7 +768,7 @@ static VALUE ossl_ec_group_get_generator(VALUE self) GetECGroup(self, group); generator = EC_GROUP_get0_generator(group); if (!generator) - return Qnil; + return Qnil; return ec_point_new(generator, group); } @@ -981,11 +981,11 @@ static point_conversion_form_t parse_point_conversion_form_symbol(VALUE sym) { if (sym == sym_uncompressed) - return POINT_CONVERSION_UNCOMPRESSED; + return POINT_CONVERSION_UNCOMPRESSED; if (sym == sym_compressed) - return POINT_CONVERSION_COMPRESSED; + return POINT_CONVERSION_COMPRESSED; if (sym == sym_hybrid) - return POINT_CONVERSION_HYBRID; + return POINT_CONVERSION_HYBRID; ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE " (expected :compressed, :uncompressed, or :hybrid)", sym); } @@ -1092,15 +1092,15 @@ static VALUE ossl_ec_group_to_string(VALUE self, int format) ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); switch(format) { - case EXPORT_PEM: + case EXPORT_PEM: i = PEM_write_bio_ECPKParameters(out, group); - break; - case EXPORT_DER: + break; + case EXPORT_DER: i = i2d_ECPKParameters_bio(out, group); - break; - default: + break; + default: BIO_free(out); - ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); + ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); } if (i != 1) { @@ -1149,11 +1149,11 @@ static VALUE ossl_ec_group_to_text(VALUE self) GetECGroup(self, group); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); + ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); } if (!ECPKParameters_print(out, group, 0)) { - BIO_free(out); - ossl_raise(eEC_GROUP, NULL); + BIO_free(out); + ossl_raise(eEC_GROUP, NULL); } str = ossl_membio2str(out); @@ -1173,7 +1173,7 @@ ossl_ec_point_free(void *ptr) static const rb_data_type_t ossl_ec_point_type = { "OpenSSL/EC_POINT", { - 0, ossl_ec_point_free, + 0, ossl_ec_point_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -1193,7 +1193,7 @@ ec_point_new(const EC_POINT *point, const EC_GROUP *group) obj = ossl_ec_point_alloc(cEC_POINT); point_new = EC_POINT_dup(point, group); if (!point_new) - ossl_raise(eEC_POINT, "EC_POINT_dup"); + ossl_raise(eEC_POINT, "EC_POINT_dup"); RTYPEDDATA_DATA(obj) = point_new; rb_ivar_set(obj, id_i_group, ec_group_new(group)); @@ -1221,39 +1221,39 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point); if (point) - rb_raise(eEC_POINT, "EC_POINT already initialized"); + rb_raise(eEC_POINT, "EC_POINT already initialized"); rb_scan_args(argc, argv, "11", &group_v, &arg2); if (rb_obj_is_kind_of(group_v, cEC_POINT)) { - if (argc != 1) - rb_raise(rb_eArgError, "invalid second argument"); - return ossl_ec_point_initialize_copy(self, group_v); + if (argc != 1) + rb_raise(rb_eArgError, "invalid second argument"); + return ossl_ec_point_initialize_copy(self, group_v); } GetECGroup(group_v, group); if (argc == 1) { - point = EC_POINT_new(group); - if (!point) - ossl_raise(eEC_POINT, "EC_POINT_new"); + point = EC_POINT_new(group); + if (!point) + ossl_raise(eEC_POINT, "EC_POINT_new"); } else { - if (rb_obj_is_kind_of(arg2, cBN)) { - point = EC_POINT_bn2point(group, GetBNPtr(arg2), NULL, ossl_bn_ctx); - if (!point) - ossl_raise(eEC_POINT, "EC_POINT_bn2point"); - } - else { - StringValue(arg2); - point = EC_POINT_new(group); - if (!point) - ossl_raise(eEC_POINT, "EC_POINT_new"); - if (!EC_POINT_oct2point(group, point, - (unsigned char *)RSTRING_PTR(arg2), - RSTRING_LEN(arg2), ossl_bn_ctx)) { - EC_POINT_free(point); - ossl_raise(eEC_POINT, "EC_POINT_oct2point"); - } - } + if (rb_obj_is_kind_of(arg2, cBN)) { + point = EC_POINT_bn2point(group, GetBNPtr(arg2), NULL, ossl_bn_ctx); + if (!point) + ossl_raise(eEC_POINT, "EC_POINT_bn2point"); + } + else { + StringValue(arg2); + point = EC_POINT_new(group); + if (!point) + ossl_raise(eEC_POINT, "EC_POINT_new"); + if (!EC_POINT_oct2point(group, point, + (unsigned char *)RSTRING_PTR(arg2), + RSTRING_LEN(arg2), ossl_bn_ctx)) { + EC_POINT_free(point); + ossl_raise(eEC_POINT, "EC_POINT_oct2point"); + } + } } RTYPEDDATA_DATA(self) = point; @@ -1272,7 +1272,7 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point_new); if (point_new) - ossl_raise(eEC_POINT, "EC::Point already initialized"); + ossl_raise(eEC_POINT, "EC::Point already initialized"); GetECPoint(other, point); group_v = rb_obj_dup(rb_attr_get(other, id_i_group)); @@ -1280,7 +1280,7 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other) point_new = EC_POINT_dup(point, group); if (!point_new) - ossl_raise(eEC_POINT, "EC_POINT_dup"); + ossl_raise(eEC_POINT, "EC_POINT_dup"); RTYPEDDATA_DATA(self) = point_new; rb_ivar_set(self, id_i_group, group_v); @@ -1307,9 +1307,9 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b) GetECGroup(group_v1, group); switch (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx)) { - case 0: return Qtrue; - case 1: return Qfalse; - default: ossl_raise(eEC_POINT, "EC_POINT_cmp"); + case 0: return Qtrue; + case 1: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_cmp"); } UNREACHABLE; @@ -1328,9 +1328,9 @@ static VALUE ossl_ec_point_is_at_infinity(VALUE self) GetECPointGroup(self, group); switch (EC_POINT_is_at_infinity(group, point)) { - case 1: return Qtrue; - case 0: return Qfalse; - default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity"); + case 1: return Qtrue; + case 0: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity"); } UNREACHABLE; @@ -1349,9 +1349,9 @@ static VALUE ossl_ec_point_is_on_curve(VALUE self) GetECPointGroup(self, group); switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) { - case 1: return Qtrue; - case 0: return Qfalse; - default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve"); + case 1: return Qtrue; + case 0: return Qfalse; + default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve"); } UNREACHABLE; @@ -1443,12 +1443,12 @@ ossl_ec_point_to_octet_string(VALUE self, VALUE conversion_form) len = EC_POINT_point2oct(group, point, form, NULL, 0, ossl_bn_ctx); if (!len) - ossl_raise(eEC_POINT, "EC_POINT_point2oct"); + ossl_raise(eEC_POINT, "EC_POINT_point2oct"); str = rb_str_new(NULL, (long)len); if (!EC_POINT_point2oct(group, point, form, - (unsigned char *)RSTRING_PTR(str), len, - ossl_bn_ctx)) - ossl_raise(eEC_POINT, "EC_POINT_point2oct"); + (unsigned char *)RSTRING_PTR(str), len, + ossl_bn_ctx)) + ossl_raise(eEC_POINT, "EC_POINT_point2oct"); return str; } diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 9cc0bfa37933f9..039b2c6a34668b 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -14,7 +14,7 @@ #define GetPKeyRSA(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { /* PARANOIA? */ \ - ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \ } \ } while (0) #define GetRSA(obj, rsa) do { \ @@ -95,7 +95,7 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) rb_raise(rb_eArgError, "OpenSSL::PKey::RSA.new cannot be called " \ "without arguments; pkeys are immutable with OpenSSL 3.0"); #else - rsa = RSA_new(); + rsa = RSA_new(); if (!rsa) ossl_raise(ePKeyError, "RSA_new"); goto legacy; @@ -159,7 +159,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other) (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa); if (!rsa_new) - ossl_raise(ePKeyError, "ASN1_dup"); + ossl_raise(ePKeyError, "ASN1_dup"); pkey = EVP_PKEY_new(); if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa_new) != 1) { @@ -358,17 +358,17 @@ ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) int salt_len; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt_length"); - kwargs_ids[1] = rb_intern_const("mgf1_hash"); + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); } rb_scan_args(argc, argv, "2:", &digest, &data, &options); rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); if (kwargs[0] == ID2SYM(rb_intern("max"))) - salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ + salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ else if (kwargs[0] == ID2SYM(rb_intern("digest"))) - salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ else - salt_len = NUM2INT(kwargs[0]); + salt_len = NUM2INT(kwargs[0]); mgf1md = ossl_evp_md_fetch(kwargs[1], &mgf1md_holder); pkey = GetPrivPKeyPtr(self); @@ -379,25 +379,25 @@ ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) md_ctx = EVP_MD_CTX_new(); if (!md_ctx) - goto err; + goto err; if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) - goto err; + goto err; if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) - goto err; + goto err; if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1) - goto err; + goto err; rb_str_set_len(signature, (long)buf_len); @@ -444,17 +444,17 @@ ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) int result, salt_len; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("salt_length"); - kwargs_ids[1] = rb_intern_const("mgf1_hash"); + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); } rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options); rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); if (kwargs[0] == ID2SYM(rb_intern("auto"))) - salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ + salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ else if (kwargs[0] == ID2SYM(rb_intern("digest"))) - salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ else - salt_len = NUM2INT(kwargs[0]); + salt_len = NUM2INT(kwargs[0]); mgf1md = ossl_evp_md_fetch(kwargs[1], &mgf1md_holder); GetPKey(self, pkey); @@ -464,34 +464,34 @@ ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) md_ctx = EVP_MD_CTX_new(); if (!md_ctx) - goto err; + goto err; if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) - goto err; + goto err; if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) - goto err; + goto err; if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) - goto err; + goto err; result = EVP_DigestVerifyFinal(md_ctx, - (unsigned char *)RSTRING_PTR(signature), - RSTRING_LEN(signature)); + (unsigned char *)RSTRING_PTR(signature), + RSTRING_LEN(signature)); EVP_MD_CTX_free(md_ctx); switch (result) { case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; case 1: - return Qtrue; + return Qtrue; default: ossl_raise(ePKeyError, "EVP_DigestVerifyFinal"); } diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index 97d01903e276ae..753f8b25f77fa5 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -68,7 +68,7 @@ static VALUE ossl_rand_load_file(VALUE self, VALUE filename) { if(!RAND_load_file(StringValueCStr(filename), -1)) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } @@ -85,14 +85,14 @@ static VALUE ossl_rand_write_file(VALUE self, VALUE filename) { if (RAND_write_file(StringValueCStr(filename)) == -1) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } /* * call-seq: - * random_bytes(length) -> string + * random_bytes(length) -> string * * Generates a String with _length_ number of cryptographically strong * pseudo-random bytes. @@ -112,9 +112,9 @@ ossl_rand_bytes(VALUE self, VALUE len) str = rb_str_new(0, n); ret = RAND_bytes((unsigned char *)RSTRING_PTR(str), n); if (ret == 0) { - ossl_raise(eRandomError, "RAND_bytes"); + ossl_raise(eRandomError, "RAND_bytes"); } else if (ret == -1) { - ossl_raise(eRandomError, "RAND_bytes is not supported"); + ossl_raise(eRandomError, "RAND_bytes is not supported"); } return str; @@ -131,7 +131,7 @@ static VALUE ossl_rand_egd(VALUE self, VALUE filename) { if (RAND_egd(StringValueCStr(filename)) == -1) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } @@ -151,7 +151,7 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) int n = NUM2INT(len); if (RAND_egd_bytes(StringValueCStr(filename), n) == -1) { - ossl_raise(eRandomError, NULL); + ossl_raise(eRandomError, NULL); } return Qtrue; } diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 454f3de4a4b9ed..630d46e43f256e 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -25,7 +25,7 @@ #endif #define GetSSLCTX(obj, ctx) do { \ - TypedData_Get_Struct((obj), SSL_CTX, &ossl_sslctx_type, (ctx)); \ + TypedData_Get_Struct((obj), SSL_CTX, &ossl_sslctx_type, (ctx)); \ } while (0) VALUE mSSL; @@ -40,13 +40,13 @@ static ID id_call, ID_callback_state, id_npn_protocols_encoded, id_each; static VALUE sym_exception, sym_wait_readable, sym_wait_writable; static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, - id_i_verify_depth, id_i_verify_callback, id_i_client_ca, - id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert, - id_i_client_cert_cb, id_i_timeout, - id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, - id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, - id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, - id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback; + id_i_verify_depth, id_i_verify_callback, id_i_client_ca, + id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert, + id_i_client_cert_cb, id_i_timeout, + id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, + id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, + id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, + id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback; static ID id_i_io, id_i_context, id_i_hostname; static int ossl_ssl_ex_ptr_idx; @@ -78,9 +78,9 @@ ossl_sslctx_s_alloc(VALUE klass) { SSL_CTX *ctx; long mode = 0 | - SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | - SSL_MODE_RELEASE_BUFFERS; + SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS; VALUE obj; obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0); @@ -104,7 +104,7 @@ ossl_call_client_cert_cb(VALUE obj) ctx_obj = rb_attr_get(obj, id_i_context); cb = rb_attr_get(ctx_obj, id_i_client_cert_cb); if (NIL_P(cb)) - return Qnil; + return Qnil; ary = rb_funcallv(cb, id_call, 1, &obj); Check_Type(ary, T_ARRAY); @@ -122,7 +122,7 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); ret = rb_protect(ossl_call_client_cert_cb, obj, NULL); if (NIL_P(ret)) - return 0; + return 0; *x509 = DupX509CertPtr(RARRAY_AREF(ret, 0)); *pkey = DupPKeyPtr(RARRAY_AREF(ret, 1)); @@ -167,8 +167,8 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) struct tmp_dh_callback_args args = {rb_ssl, is_export, keylength}; VALUE ret = rb_protect(ossl_call_tmp_dh_callback, (VALUE)&args, &state); if (state) { - rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); - return NULL; + rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); + return NULL; } return (DH *)ret; } @@ -186,13 +186,13 @@ call_verify_certificate_identity(VALUE ctx_v) hostname = rb_attr_get(ssl_obj, id_i_hostname); if (!RTEST(hostname)) { - rb_warning("verify_hostname requires hostname to be set"); - return Qtrue; + rb_warning("verify_hostname requires hostname to be set"); + return Qtrue; } cert_obj = ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx)); return rb_funcall(mSSL, rb_intern("verify_certificate_identity"), 2, - cert_obj, hostname); + cert_obj, hostname); } static int @@ -209,12 +209,12 @@ ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) verify_hostname = rb_attr_get(sslctx_obj, id_i_verify_hostname); if (preverify_ok && RTEST(verify_hostname) && !SSL_is_server(ssl) && - !X509_STORE_CTX_get_error_depth(ctx)) { - ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status); - if (status) { - rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); - return 0; - } + !X509_STORE_CTX_get_error_depth(ctx)) { + ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status); + if (status) { + rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); + return 0; + } if (ret != Qtrue) { preverify_ok = 0; X509_STORE_CTX_set_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH); @@ -385,7 +385,7 @@ ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) * when SSL_CTX_free() is called. */ if (rb_during_gc()) - return; + return; OSSL_Debug("SSL SESSION remove callback entered"); @@ -448,8 +448,8 @@ ossl_call_servername_cb(VALUE arg) ossl_raise(eSSLError, "SSL_set_SSL_CTX"); rb_ivar_set(ssl_obj, id_i_context, ret_obj); } else if (!NIL_P(ret_obj)) { - ossl_raise(rb_eArgError, "servername_cb must return an " - "OpenSSL::SSL::SSLContext object or nil"); + ossl_raise(rb_eArgError, "servername_cb must return an " + "OpenSSL::SSL::SSLContext object or nil"); } return Qnil; @@ -489,7 +489,7 @@ ssl_npn_encode_protocol_i(RB_BLOCK_CALL_FUNC_ARGLIST(cur, encoded)) int len = RSTRING_LENINT(cur); char len_byte; if (len < 1 || len > 255) - ossl_raise(eSSLError, "Advertised protocol must have length 1..255"); + ossl_raise(eSSLError, "Advertised protocol must have length 1..255"); /* Encode the length byte */ len_byte = len; rb_str_buf_cat(encoded, &len_byte, 1); @@ -523,16 +523,16 @@ npn_select_cb_common_i(VALUE tmp) /* assume OpenSSL verifies this format */ /* The format is len_1|proto_1|...|len_n|proto_n */ while (in < in_end) { - l = *in++; - rb_ary_push(protocols, rb_str_new((const char *)in, l)); - in += l; + l = *in++; + rb_ary_push(protocols, rb_str_new((const char *)in, l)); + in += l; } selected = rb_funcallv(args->cb, id_call, 1, &protocols); StringValue(selected); len = RSTRING_LEN(selected); if (len < 1 || len >= 256) { - ossl_raise(eSSLError, "Selected protocol name must have length 1..255"); + ossl_raise(eSSLError, "Selected protocol name must have length 1..255"); } return selected; @@ -540,8 +540,8 @@ npn_select_cb_common_i(VALUE tmp) static int ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen) + unsigned char *outlen, const unsigned char *in, + unsigned int inlen) { VALUE selected; int status; @@ -553,10 +553,10 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, selected = rb_protect(npn_select_cb_common_i, (VALUE)&args, &status); if (status) { - VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); + VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); - return SSL_TLSEXT_ERR_ALERT_FATAL; + rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); + return SSL_TLSEXT_ERR_ALERT_FATAL; } *out = (unsigned char *)RSTRING_PTR(selected); @@ -568,7 +568,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, #ifdef OSSL_USE_NEXTPROTONEG static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, - void *arg) + void *arg) { VALUE protocols = rb_attr_get((VALUE)arg, id_npn_protocols_encoded); @@ -580,7 +580,7 @@ ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, static int ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) + const unsigned char *in, unsigned int inlen, void *arg) { VALUE sslctx_obj, cb; @@ -588,13 +588,13 @@ ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, cb = rb_attr_get(sslctx_obj, id_i_npn_select_cb); return ssl_npn_select_cb_common(ssl, cb, (const unsigned char **)out, - outlen, in, inlen); + outlen, in, inlen); } #endif static int ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) + const unsigned char *in, unsigned int inlen, void *arg) { VALUE sslctx_obj, cb; @@ -611,7 +611,7 @@ ssl_info_cb(const SSL *ssl, int where, int val) int is_server = SSL_is_server((SSL *)ssl); if (is_server && where & SSL_CB_HANDSHAKE_START) { - ssl_renegotiation_cb(ssl); + ssl_renegotiation_cb(ssl); } } @@ -657,9 +657,9 @@ ossl_sslctx_set_options(VALUE self, VALUE options) SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx)); if (NIL_P(options)) { - SSL_CTX_set_options(ctx, SSL_OP_ALL); + SSL_CTX_set_options(ctx, SSL_OP_ALL); } else { - SSL_CTX_set_options(ctx, NUM2ULONG(options)); + SSL_CTX_set_options(ctx, NUM2ULONG(options)); } return self; @@ -701,14 +701,14 @@ ossl_sslctx_setup(VALUE self) val = rb_attr_get(self, id_i_cert_store); if (!NIL_P(val)) { - X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */ - SSL_CTX_set_cert_store(ctx, store); - X509_STORE_up_ref(store); + X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */ + SSL_CTX_set_cert_store(ctx, store); + X509_STORE_up_ref(store); } val = rb_attr_get(self, id_i_extra_chain_cert); if(!NIL_P(val)){ - rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self); + rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self); } /* private key may be bundled in certificate file. */ @@ -732,22 +732,22 @@ ossl_sslctx_setup(VALUE self) val = rb_attr_get(self, id_i_client_ca); if(!NIL_P(val)){ - if (RB_TYPE_P(val, T_ARRAY)) { - for(i = 0; i < RARRAY_LEN(val); i++){ - client_ca = GetX509CertPtr(RARRAY_AREF(val, i)); - if (!SSL_CTX_add_client_CA(ctx, client_ca)){ - /* Copies X509_NAME => FREE it. */ - ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); - } - } + if (RB_TYPE_P(val, T_ARRAY)) { + for(i = 0; i < RARRAY_LEN(val); i++){ + client_ca = GetX509CertPtr(RARRAY_AREF(val, i)); + if (!SSL_CTX_add_client_CA(ctx, client_ca)){ + /* Copies X509_NAME => FREE it. */ + ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); + } + } } - else{ - client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ + else{ + client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ if (!SSL_CTX_add_client_CA(ctx, client_ca)){ - /* Copies X509_NAME => FREE it. */ - ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); + /* Copies X509_NAME => FREE it. */ + ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } - } + } } val = rb_attr_get(self, id_i_ca_file); @@ -770,7 +770,7 @@ ossl_sslctx_setup(VALUE self) verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val); SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback); if (RTEST(rb_attr_get(self, id_i_client_cert_cb))) - SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb); + SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb); val = rb_attr_get(self, id_i_timeout); if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val)); @@ -781,60 +781,60 @@ ossl_sslctx_setup(VALUE self) #ifdef OSSL_USE_NEXTPROTONEG val = rb_attr_get(self, id_i_npn_protocols); if (!NIL_P(val)) { - VALUE encoded = ssl_encode_npn_protocols(val); - rb_ivar_set(self, id_npn_protocols_encoded, encoded); - SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); - OSSL_Debug("SSL NPN advertise callback added"); + VALUE encoded = ssl_encode_npn_protocols(val); + rb_ivar_set(self, id_npn_protocols_encoded, encoded); + SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self); + OSSL_Debug("SSL NPN advertise callback added"); } if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) { - SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); - OSSL_Debug("SSL NPN select callback added"); + SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); + OSSL_Debug("SSL NPN select callback added"); } #endif val = rb_attr_get(self, id_i_alpn_protocols); if (!NIL_P(val)) { - VALUE rprotos = ssl_encode_npn_protocols(val); + VALUE rprotos = ssl_encode_npn_protocols(val); - /* returns 0 on success */ - if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), - RSTRING_LENINT(rprotos))) - ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos"); - OSSL_Debug("SSL ALPN values added"); + /* returns 0 on success */ + if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), + RSTRING_LENINT(rprotos))) + ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos"); + OSSL_Debug("SSL ALPN values added"); } if (RTEST(rb_attr_get(self, id_i_alpn_select_cb))) { - SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); - OSSL_Debug("SSL ALPN select callback added"); + SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); + OSSL_Debug("SSL ALPN select callback added"); } rb_obj_freeze(self); val = rb_attr_get(self, id_i_session_id_context); if (!NIL_P(val)){ - StringValue(val); - if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val), - RSTRING_LENINT(val))){ - ossl_raise(eSSLError, "SSL_CTX_set_session_id_context"); - } + StringValue(val); + if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val), + RSTRING_LENINT(val))){ + ossl_raise(eSSLError, "SSL_CTX_set_session_id_context"); + } } if (RTEST(rb_attr_get(self, id_i_session_get_cb))) { - SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb); - OSSL_Debug("SSL SESSION get callback added"); + SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb); + OSSL_Debug("SSL SESSION get callback added"); } if (RTEST(rb_attr_get(self, id_i_session_new_cb))) { - SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb); - OSSL_Debug("SSL SESSION new callback added"); + SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb); + OSSL_Debug("SSL SESSION new callback added"); } if (RTEST(rb_attr_get(self, id_i_session_remove_cb))) { - SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb); - OSSL_Debug("SSL SESSION remove callback added"); + SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb); + OSSL_Debug("SSL SESSION remove callback added"); } val = rb_attr_get(self, id_i_servername_cb); if (!NIL_P(val)) { SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); - OSSL_Debug("SSL TLSEXT servername callback added"); + OSSL_Debug("SSL TLSEXT servername callback added"); } #if !OSSL_IS_LIBRESSL @@ -857,28 +857,28 @@ parse_proto_version(VALUE str) { int i; static const struct { - const char *name; - int version; + const char *name; + int version; } map[] = { - { "SSL2", SSL2_VERSION }, - { "SSL3", SSL3_VERSION }, - { "TLS1", TLS1_VERSION }, - { "TLS1_1", TLS1_1_VERSION }, - { "TLS1_2", TLS1_2_VERSION }, - { "TLS1_3", TLS1_3_VERSION }, + { "SSL2", SSL2_VERSION }, + { "SSL3", SSL3_VERSION }, + { "TLS1", TLS1_VERSION }, + { "TLS1_1", TLS1_1_VERSION }, + { "TLS1_2", TLS1_2_VERSION }, + { "TLS1_3", TLS1_3_VERSION }, }; if (NIL_P(str)) - return 0; + return 0; if (RB_INTEGER_TYPE_P(str)) - return NUM2INT(str); + return NUM2INT(str); if (SYMBOL_P(str)) - str = rb_sym2str(str); + str = rb_sym2str(str); StringValue(str); for (i = 0; i < numberof(map); i++) - if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str))) - return map[i].version; + if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str))) + return map[i].version; rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str); } @@ -1344,20 +1344,20 @@ ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self) pub_pkey = X509_get_pubkey(x509); EVP_PKEY_free(pub_pkey); if (!pub_pkey) - rb_raise(rb_eArgError, "certificate does not contain public key"); + rb_raise(rb_eArgError, "certificate does not contain public key"); if (EVP_PKEY_eq(pub_pkey, pkey) != 1) - rb_raise(rb_eArgError, "public key mismatch"); + rb_raise(rb_eArgError, "public key mismatch"); if (argc >= 3) - extra_chain = ossl_x509_ary2sk(extra_chain_ary); + extra_chain = ossl_x509_ary2sk(extra_chain_ary); if (!SSL_CTX_use_certificate(ctx, x509)) { - sk_X509_pop_free(extra_chain, X509_free); - ossl_raise(eSSLError, "SSL_CTX_use_certificate"); + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_use_certificate"); } if (!SSL_CTX_use_PrivateKey(ctx, pkey)) { - sk_X509_pop_free(extra_chain, X509_free); - ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); } if (extra_chain && !SSL_CTX_set0_chain(ctx, extra_chain)) { sk_X509_pop_free(extra_chain, X509_free); @@ -1637,23 +1637,23 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) TypedData_Get_Struct(self, SSL, &ossl_ssl_type, ssl); if (ssl) - ossl_raise(eSSLError, "SSL already initialized"); + ossl_raise(eSSLError, "SSL already initialized"); if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1) - v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); + v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); GetSSLCTX(v_ctx, ctx); rb_ivar_set(self, id_i_context, v_ctx); ossl_sslctx_setup(v_ctx); if (rb_respond_to(io, rb_intern("nonblock="))) - rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); + rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); Check_Type(io, T_FILE); rb_ivar_set(self, id_i_io, io); ssl = SSL_new(ctx); if (!ssl) - ossl_raise(eSSLError, NULL); + ossl_raise(eSSLError, NULL); RTYPEDDATA_DATA(self) = ssl; SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self); @@ -1684,7 +1684,7 @@ ossl_ssl_setup(VALUE self) GetSSL(self, ssl); if (ssl_started(ssl)) - return Qtrue; + return Qtrue; io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); @@ -1706,14 +1706,14 @@ static void write_would_block(int nonblock) { if (nonblock) - ossl_raise(eSSLErrorWaitWritable, "write would block"); + ossl_raise(eSSLErrorWaitWritable, "write would block"); } static void read_would_block(int nonblock) { if (nonblock) - ossl_raise(eSSLErrorWaitReadable, "read would block"); + ossl_raise(eSSLErrorWaitReadable, "read would block"); } static int @@ -1721,7 +1721,7 @@ no_exception_p(VALUE opts) { if (RB_TYPE_P(opts, T_HASH) && rb_hash_lookup2(opts, sym_exception, Qundef) == Qfalse) - return 1; + return 1; return 0; } @@ -1945,9 +1945,9 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) VALUE opts = Qnil; if (nonblock) { - rb_scan_args(argc, argv, "11:", &len, &str, &opts); + rb_scan_args(argc, argv, "11:", &len, &str, &opts); } else { - rb_scan_args(argc, argv, "11", &len, &str); + rb_scan_args(argc, argv, "11", &len, &str); } GetSSL(self, ssl); if (!ssl_started(ssl)) @@ -1955,13 +1955,13 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) ilen = NUM2INT(len); if (NIL_P(str)) - str = rb_str_new(0, ilen); + str = rb_str_new(0, ilen); else { - StringValue(str); - if (RSTRING_LEN(str) >= ilen) - rb_str_modify(str); - else - rb_str_modify_expand(str, ilen - RSTRING_LEN(str)); + StringValue(str); + if (RSTRING_LEN(str) >= ilen) + rb_str_modify(str); + else + rb_str_modify_expand(str, ilen - RSTRING_LEN(str)); } if (ilen == 0) { @@ -2198,12 +2198,12 @@ ossl_ssl_stop(VALUE self) GetSSL(self, ssl); if (!ssl_started(ssl)) - return Qnil; + return Qnil; ret = SSL_shutdown(ssl); if (ret == 1) /* Have already received close_notify */ - return Qnil; + return Qnil; if (ret == 0) /* Sent close_notify, but we don't wait for reply */ - return Qnil; + return Qnil; /* * XXX: Something happened. Possibly it failed because the underlying socket @@ -2289,20 +2289,20 @@ ossl_ssl_get_peer_cert_chain(VALUE self) num = sk_X509_num(chain); ary = rb_ary_new2(num); for (i = 0; i < num; i++){ - cert = sk_X509_value(chain, i); - rb_ary_push(ary, ossl_x509_new(cert)); + cert = sk_X509_value(chain, i); + rb_ary_push(ary, ossl_x509_new(cert)); } return ary; } /* -* call-seq: -* ssl.ssl_version => String -* -* Returns a String representing the SSL/TLS version that was negotiated -* for the connection, for example "TLSv1.2". -*/ + * call-seq: + * ssl.ssl_version => String + * + * Returns a String representing the SSL/TLS version that was negotiated + * for the connection, for example "TLSv1.2". + */ static VALUE ossl_ssl_get_version(VALUE self) { @@ -2423,10 +2423,10 @@ ossl_ssl_set_hostname(VALUE self, VALUE arg) GetSSL(self, ssl); if (!NIL_P(arg)) - hostname = StringValueCStr(arg); + hostname = StringValueCStr(arg); if (!SSL_set_tlsext_host_name(ssl, hostname)) - ossl_raise(eSSLError, NULL); + ossl_raise(eSSLError, NULL); /* for SSLSocket#hostname */ rb_ivar_set(self, id_i_hostname, arg); @@ -2547,9 +2547,9 @@ ossl_ssl_npn_protocol(VALUE self) SSL_get0_next_proto_negotiated(ssl, &out, &outlen); if (!outlen) - return Qnil; + return Qnil; else - return rb_str_new((const char *) out, outlen); + return rb_str_new((const char *) out, outlen); } # endif @@ -2571,9 +2571,9 @@ ossl_ssl_alpn_protocol(VALUE self) SSL_get0_alpn_selected(ssl, &out, &outlen); if (!outlen) - return Qnil; + return Qnil; else - return rb_str_new((const char *) out, outlen); + return rb_str_new((const char *) out, outlen); } /* @@ -2606,15 +2606,15 @@ ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self) str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (!NIL_P(context)) { - use_ctx = 1; - StringValue(context); - ctx = (unsigned char *)RSTRING_PTR(context); - ctx_len = RSTRING_LEN(context); + use_ctx = 1; + StringValue(context); + ctx = (unsigned char *)RSTRING_PTR(context); + ctx_len = RSTRING_LEN(context); } ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label), - RSTRING_LENINT(label), ctx, ctx_len, use_ctx); + RSTRING_LENINT(label), ctx, ctx_len, use_ctx); if (ret == 0 || ret == -1) { - ossl_raise(eSSLError, "SSL_export_keying_material"); + ossl_raise(eSSLError, "SSL_export_keying_material"); } return str; } @@ -2633,7 +2633,7 @@ ossl_ssl_tmp_key(VALUE self) GetSSL(self, ssl); if (!SSL_get_server_tmp_key(ssl, &key)) - return Qnil; + return Qnil; return ossl_pkey_wrap(key); } @@ -2714,10 +2714,10 @@ Init_ossl_ssl(void) ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_ptr_idx", 0, 0, 0); if (ossl_ssl_ex_ptr_idx < 0) - ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); + ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); ossl_sslctx_ex_ptr_idx = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_ptr_idx", 0, 0, 0); if (ossl_sslctx_ex_ptr_idx < 0) - ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); + ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); /* Document-module: OpenSSL::SSL * diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h index a92985c601ebf3..a87e62d450d50d 100644 --- a/ext/openssl/ossl_ssl.h +++ b/ext/openssl/ossl_ssl.h @@ -11,17 +11,17 @@ #define _OSSL_SSL_H_ #define GetSSL(obj, ssl) do { \ - TypedData_Get_Struct((obj), SSL, &ossl_ssl_type, (ssl)); \ - if (!(ssl)) { \ - ossl_raise(rb_eRuntimeError, "SSL is not initialized"); \ - } \ + TypedData_Get_Struct((obj), SSL, &ossl_ssl_type, (ssl)); \ + if (!(ssl)) { \ + ossl_raise(rb_eRuntimeError, "SSL is not initialized"); \ + } \ } while (0) #define GetSSLSession(obj, sess) do { \ - TypedData_Get_Struct((obj), SSL_SESSION, &ossl_ssl_session_type, (sess)); \ - if (!(sess)) { \ - ossl_raise(rb_eRuntimeError, "SSL Session wasn't initialized."); \ - } \ + TypedData_Get_Struct((obj), SSL_SESSION, &ossl_ssl_session_type, (sess)); \ + if (!(sess)) { \ + ossl_raise(rb_eRuntimeError, "SSL Session wasn't initialized."); \ + } \ } while (0) extern const rb_data_type_t ossl_ssl_type; diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index 010adf20ac9618..8a2fbf4100ca8b 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -17,14 +17,14 @@ ossl_ssl_session_free(void *ptr) const rb_data_type_t ossl_ssl_session_type = { "OpenSSL/SSL/Session", { - 0, ossl_ssl_session_free, + 0, ossl_ssl_session_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; static VALUE ossl_ssl_session_alloc(VALUE klass) { - return TypedData_Wrap_Struct(klass, &ossl_ssl_session_type, NULL); + return TypedData_Wrap_Struct(klass, &ossl_ssl_session_type, NULL); } /* @@ -80,9 +80,9 @@ ossl_ssl_session_initialize_copy(VALUE self, VALUE other) GetSSLSession(other, sess_other); sess_new = ASN1_dup((i2d_of_void *)i2d_SSL_SESSION, (d2i_of_void *)d2i_SSL_SESSION, - (char *)sess_other); + (char *)sess_other); if (!sess_new) - ossl_raise(eSSLSession, "ASN1_dup"); + ossl_raise(eSSLSession, "ASN1_dup"); RTYPEDDATA_DATA(self) = sess_new; SSL_SESSION_free(sess); @@ -99,9 +99,9 @@ ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) const unsigned char *b_sid = SSL_SESSION_get_id(b, &b_len); if (SSL_SESSION_get_protocol_version(a) != SSL_SESSION_get_protocol_version(b)) - return 1; + return 1; if (a_len != b_len) - return 1; + return 1; return CRYPTO_memcmp(a_sid, b_sid, a_len); } @@ -114,15 +114,15 @@ ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) */ static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2) { - SSL_SESSION *ctx1, *ctx2; + SSL_SESSION *ctx1, *ctx2; - GetSSLSession(val1, ctx1); - GetSSLSession(val2, ctx2); + GetSSLSession(val1, ctx1); + GetSSLSession(val2, ctx2); - switch (ossl_SSL_SESSION_cmp(ctx1, ctx2)) { - case 0: return Qtrue; - default: return Qfalse; - } + switch (ossl_SSL_SESSION_cmp(ctx1, ctx2)) { + case 0: return Qtrue; + default: return Qfalse; + } } /* @@ -140,7 +140,7 @@ ossl_ssl_session_get_time(VALUE self) GetSSLSession(self, ctx); t = SSL_SESSION_get_time(ctx); if (t == 0) - return Qnil; + return Qnil; return rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(t)); } @@ -175,16 +175,16 @@ ossl_ssl_session_get_timeout(VALUE self) */ static VALUE ossl_ssl_session_set_time(VALUE self, VALUE time_v) { - SSL_SESSION *ctx; - long t; - - GetSSLSession(self, ctx); - if (rb_obj_is_instance_of(time_v, rb_cTime)) { - time_v = rb_funcall(time_v, rb_intern("to_i"), 0); - } - t = NUM2LONG(time_v); - SSL_SESSION_set_time(ctx, t); - return ossl_ssl_session_get_time(self); + SSL_SESSION *ctx; + long t; + + GetSSLSession(self, ctx); + if (rb_obj_is_instance_of(time_v, rb_cTime)) { + time_v = rb_funcall(time_v, rb_intern("to_i"), 0); + } + t = NUM2LONG(time_v); + SSL_SESSION_set_time(ctx, t); + return ossl_ssl_session_get_time(self); } /* @@ -195,13 +195,13 @@ static VALUE ossl_ssl_session_set_time(VALUE self, VALUE time_v) */ static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) { - SSL_SESSION *ctx; - long t; + SSL_SESSION *ctx; + long t; - GetSSLSession(self, ctx); - t = NUM2LONG(time_v); - SSL_SESSION_set_timeout(ctx, t); - return ossl_ssl_session_get_timeout(self); + GetSSLSession(self, ctx); + t = NUM2LONG(time_v); + SSL_SESSION_set_timeout(ctx, t); + return ossl_ssl_session_get_timeout(self); } /* @@ -209,18 +209,18 @@ static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) * session.id -> String * * Returns the Session ID. -*/ + */ static VALUE ossl_ssl_session_get_id(VALUE self) { - SSL_SESSION *ctx; - const unsigned char *p = NULL; - unsigned int i = 0; + SSL_SESSION *ctx; + const unsigned char *p = NULL; + unsigned int i = 0; - GetSSLSession(self, ctx); + GetSSLSession(self, ctx); - p = SSL_SESSION_get_id(ctx, &i); + p = SSL_SESSION_get_id(ctx, &i); - return rb_str_new((const char *) p, i); + return rb_str_new((const char *) p, i); } /* @@ -231,22 +231,22 @@ static VALUE ossl_ssl_session_get_id(VALUE self) */ static VALUE ossl_ssl_session_to_der(VALUE self) { - SSL_SESSION *ctx; - unsigned char *p; - int len; - VALUE str; - - GetSSLSession(self, ctx); - len = i2d_SSL_SESSION(ctx, NULL); - if (len <= 0) { - ossl_raise(eSSLSession, "i2d_SSL_SESSION"); - } - - str = rb_str_new(0, len); - p = (unsigned char *)RSTRING_PTR(str); - i2d_SSL_SESSION(ctx, &p); - ossl_str_adjust(str, p); - return str; + SSL_SESSION *ctx; + unsigned char *p; + int len; + VALUE str; + + GetSSLSession(self, ctx); + len = i2d_SSL_SESSION(ctx, NULL); + if (len <= 0) { + ossl_raise(eSSLSession, "i2d_SSL_SESSION"); + } + + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + i2d_SSL_SESSION(ctx, &p); + ossl_str_adjust(str, p); + return str; } /* @@ -257,22 +257,22 @@ static VALUE ossl_ssl_session_to_der(VALUE self) */ static VALUE ossl_ssl_session_to_pem(VALUE self) { - SSL_SESSION *ctx; - BIO *out; + SSL_SESSION *ctx; + BIO *out; - GetSSLSession(self, ctx); + GetSSLSession(self, ctx); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eSSLSession, "BIO_s_mem()"); - } + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eSSLSession, "BIO_s_mem()"); + } - if (!PEM_write_bio_SSL_SESSION(out, ctx)) { - BIO_free(out); - ossl_raise(eSSLSession, "SSL_SESSION_print()"); - } + if (!PEM_write_bio_SSL_SESSION(out, ctx)) { + BIO_free(out); + ossl_raise(eSSLSession, "SSL_SESSION_print()"); + } - return ossl_membio2str(out); + return ossl_membio2str(out); } @@ -284,21 +284,21 @@ static VALUE ossl_ssl_session_to_pem(VALUE self) */ static VALUE ossl_ssl_session_to_text(VALUE self) { - SSL_SESSION *ctx; - BIO *out; + SSL_SESSION *ctx; + BIO *out; - GetSSLSession(self, ctx); + GetSSLSession(self, ctx); - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eSSLSession, "BIO_s_mem()"); - } + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eSSLSession, "BIO_s_mem()"); + } - if (!SSL_SESSION_print(out, ctx)) { - BIO_free(out); - ossl_raise(eSSLSession, "SSL_SESSION_print()"); - } + if (!SSL_SESSION_print(out, ctx)) { + BIO_free(out); + ossl_raise(eSSLSession, "SSL_SESSION_print()"); + } - return ossl_membio2str(out); + return ossl_membio2str(out); } #endif /* !defined(OPENSSL_NO_SOCK) */ @@ -306,22 +306,22 @@ static VALUE ossl_ssl_session_to_text(VALUE self) void Init_ossl_ssl_session(void) { #ifndef OPENSSL_NO_SOCK - cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); - eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); - - rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); - rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); - rb_define_method(cSSLSession, "initialize_copy", ossl_ssl_session_initialize_copy, 1); - - rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); - - rb_define_method(cSSLSession, "time", ossl_ssl_session_get_time, 0); - rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); - rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); - rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); - rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); - rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); - rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); - rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); + cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); + eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); + + rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); + rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); + rb_define_method(cSSLSession, "initialize_copy", ossl_ssl_session_initialize_copy, 1); + + rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); + + rb_define_method(cSSLSession, "time", ossl_ssl_session_get_time, 0); + rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); + rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); + rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); + rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); + rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); + rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); + rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); #endif /* !defined(OPENSSL_NO_SOCK) */ } diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index e071ee84811c75..615eedc016a0f4 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -103,7 +103,7 @@ static const rb_data_type_t ossl_ts_resp_type = { static void ossl_ts_token_info_free(void *ptr) { - TS_TST_INFO_free(ptr); + TS_TST_INFO_free(ptr); } static const rb_data_type_t ossl_ts_token_info_type = { @@ -1226,7 +1226,7 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) if (rb_obj_is_kind_of(additional_certs, rb_cArray)) { inter_certs = ossl_protect_x509_ary2sk(additional_certs, &status); if (status) - goto end; + goto end; /* this dups the sk_X509 and ups each cert's ref count */ TS_RESP_CTX_set_certs(ctx, inter_certs); @@ -1281,7 +1281,7 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request) SetTSResponse(tsresp, response); ret = tsresp; -end: + end: ASN1_INTEGER_free(asn1_serial); ASN1_OBJECT_free(def_policy_id_obj); TS_RESP_CTX_free(ctx); diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c index 04adfab960799a..e341ca1fbb4c1c 100644 --- a/ext/openssl/ossl_x509.c +++ b/ext/openssl/ossl_x509.c @@ -13,7 +13,7 @@ VALUE mX509; #define DefX509Const(x) rb_define_const(mX509, #x, INT2NUM(X509_##x)) #define DefX509Default(x,i) \ - rb_define_const(mX509, "DEFAULT_" #x, rb_str_new2(X509_get_default_##i())) + rb_define_const(mX509, "DEFAULT_" #x, rb_str_new2(X509_get_default_##i())) ASN1_TIME * ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index 998f87b0e8649b..4769e56e1e8501 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509attr_type, 0) #define SetX509Attr(obj, attr) do { \ if (!(attr)) { \ - ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (attr); \ } while (0) #define GetX509Attr(obj, attr) do { \ TypedData_Get_Struct((obj), X509_ATTRIBUTE, &ossl_x509attr_type, (attr)); \ if (!(attr)) { \ - ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ } while (0) @@ -39,7 +39,7 @@ ossl_x509attr_free(void *ptr) static const rb_data_type_t ossl_x509attr_type = { "OpenSSL/X509/ATTRIBUTE", { - 0, ossl_x509attr_free, + 0, ossl_x509attr_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -83,7 +83,7 @@ ossl_x509attr_alloc(VALUE klass) obj = NewX509Attr(klass); if (!(attr = X509_ATTRIBUTE_new())) - ossl_raise(eX509AttrError, NULL); + ossl_raise(eX509AttrError, NULL); SetX509Attr(obj, attr); return obj; @@ -102,15 +102,15 @@ ossl_x509attr_initialize(int argc, VALUE *argv, VALUE self) GetX509Attr(self, attr); if(rb_scan_args(argc, argv, "11", &oid, &value) == 1){ - oid = ossl_to_der_if_possible(oid); - StringValue(oid); - p = (unsigned char *)RSTRING_PTR(oid); - x = d2i_X509_ATTRIBUTE(&attr, &p, RSTRING_LEN(oid)); - DATA_PTR(self) = attr; - if(!x){ - ossl_raise(eX509AttrError, NULL); - } - return self; + oid = ossl_to_der_if_possible(oid); + StringValue(oid); + p = (unsigned char *)RSTRING_PTR(oid); + x = d2i_X509_ATTRIBUTE(&attr, &p, RSTRING_LEN(oid)); + DATA_PTR(self) = attr; + if(!x){ + ossl_raise(eX509AttrError, NULL); + } + return self; } rb_funcall(self, rb_intern("oid="), 1, oid); rb_funcall(self, rb_intern("value="), 1, value); @@ -130,7 +130,7 @@ ossl_x509attr_initialize_copy(VALUE self, VALUE other) attr_new = X509_ATTRIBUTE_dup(attr_other); if (!attr_new) - ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); SetX509Attr(self, attr_new); X509_ATTRIBUTE_free(attr); @@ -154,8 +154,8 @@ ossl_x509attr_set_oid(VALUE self, VALUE oid) obj = OBJ_txt2obj(s, 0); if(!obj) ossl_raise(eX509AttrError, NULL); if (!X509_ATTRIBUTE_set1_object(attr, obj)) { - ASN1_OBJECT_free(obj); - ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_object"); + ASN1_OBJECT_free(obj); + ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_object"); } ASN1_OBJECT_free(obj); @@ -236,21 +236,21 @@ ossl_x509attr_get_value(VALUE self) GetX509Attr(self, attr); /* there is no X509_ATTRIBUTE_get0_set() :( */ if (!(sk = sk_ASN1_TYPE_new_null())) - ossl_raise(eX509AttrError, "sk_new"); + ossl_raise(eX509AttrError, "sk_new"); count = X509_ATTRIBUTE_count(attr); for (i = 0; i < count; i++) - sk_ASN1_TYPE_push(sk, X509_ATTRIBUTE_get0_type(attr, i)); + sk_ASN1_TYPE_push(sk, X509_ATTRIBUTE_get0_type(attr, i)); if ((len = i2d_ASN1_SET_ANY(sk, NULL)) <= 0) { - sk_ASN1_TYPE_free(sk); - ossl_raise(eX509AttrError, NULL); + sk_ASN1_TYPE_free(sk); + ossl_raise(eX509AttrError, NULL); } str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_ASN1_SET_ANY(sk, &p) <= 0) { - sk_ASN1_TYPE_free(sk); - ossl_raise(eX509AttrError, NULL); + sk_ASN1_TYPE_free(sk); + ossl_raise(eX509AttrError, NULL); } ossl_str_adjust(str, p); sk_ASN1_TYPE_free(sk); @@ -272,11 +272,11 @@ ossl_x509attr_to_der(VALUE self) GetX509Attr(self, attr); if((len = i2d_X509_ATTRIBUTE(attr, NULL)) <= 0) - ossl_raise(eX509AttrError, NULL); + ossl_raise(eX509AttrError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_ATTRIBUTE(attr, &p) <= 0) - ossl_raise(eX509AttrError, NULL); + ossl_raise(eX509AttrError, NULL); ossl_str_adjust(str, p); return str; diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index dca28395935a12..b1e82a2790adf6 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509_type, 0) #define SetX509(obj, x509) do { \ if (!(x509)) { \ - ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (x509); \ } while (0) #define GetX509(obj, x509) do { \ TypedData_Get_Struct((obj), X509, &ossl_x509_type, (x509)); \ if (!(x509)) { \ - ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ } while (0) @@ -39,7 +39,7 @@ ossl_x509_free(void *ptr) static const rb_data_type_t ossl_x509_type = { "OpenSSL/X509", { - 0, ossl_x509_free, + 0, ossl_x509_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -115,8 +115,8 @@ ossl_x509_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (rb_scan_args(argc, argv, "01", &arg) == 0) { - /* create just empty X509Cert */ - return self; + /* create just empty X509Cert */ + return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); @@ -170,11 +170,11 @@ ossl_x509_to_der(VALUE self) GetX509(self, x509); if ((len = i2d_X509(x509, NULL)) <= 0) - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509(x509, &p) <= 0) - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); ossl_str_adjust(str, p); return str; @@ -196,8 +196,8 @@ ossl_x509_to_pem(VALUE self) if (!out) ossl_raise(eX509CertError, NULL); if (!PEM_write_bio_X509(out, x509)) { - BIO_free(out); - ossl_raise(eX509CertError, NULL); + BIO_free(out); + ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); @@ -221,8 +221,8 @@ ossl_x509_to_text(VALUE self) if (!out) ossl_raise(eX509CertError, NULL); if (!X509_print(out, x509)) { - BIO_free(out); - ossl_raise(eX509CertError, NULL); + BIO_free(out); + ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); @@ -242,7 +242,7 @@ ossl_x509_to_req(VALUE self) GetX509(self, x509); if (!(req = X509_to_X509_REQ(x509, NULL, EVP_md5()))) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } obj = ossl_x509req_new(req); X509_REQ_free(req); @@ -276,11 +276,11 @@ ossl_x509_set_version(VALUE self, VALUE version) long ver; if ((ver = NUM2LONG(version)) < 0) { - ossl_raise(eX509CertError, "version must be >= 0!"); + ossl_raise(eX509CertError, "version must be >= 0!"); } GetX509(self, x509); if (!X509_set_version(x509, ver)) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return version; @@ -349,7 +349,7 @@ ossl_x509_get_subject(VALUE self) GetX509(self, x509); if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return ossl_x509name_new(name); @@ -366,7 +366,7 @@ ossl_x509_set_subject(VALUE self, VALUE subject) GetX509(self, x509); if (!X509_set_subject_name(x509, GetX509NamePtr(subject))) { /* DUPs name */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return subject; @@ -384,7 +384,7 @@ ossl_x509_get_issuer(VALUE self) GetX509(self, x509); if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return ossl_x509name_new(name); @@ -401,7 +401,7 @@ ossl_x509_set_issuer(VALUE self, VALUE issuer) GetX509(self, x509); if (!X509_set_issuer_name(x509, GetX509NamePtr(issuer))) { /* DUPs name */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return issuer; @@ -419,7 +419,7 @@ ossl_x509_get_not_before(VALUE self) GetX509(self, x509); if (!(asn1time = X509_get0_notBefore(x509))) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return asn1time_to_time(asn1time); @@ -438,8 +438,8 @@ ossl_x509_set_not_before(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_set1_notBefore(x509, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CertError, "X509_set_notBefore"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CertError, "X509_set_notBefore"); } ASN1_TIME_free(asn1time); @@ -458,7 +458,7 @@ ossl_x509_get_not_after(VALUE self) GetX509(self, x509); if (!(asn1time = X509_get0_notAfter(x509))) { - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return asn1time_to_time(asn1time); @@ -477,8 +477,8 @@ ossl_x509_set_not_after(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_set1_notAfter(x509, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CertError, "X509_set_notAfter"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CertError, "X509_set_notAfter"); } ASN1_TIME_free(asn1time); @@ -497,7 +497,7 @@ ossl_x509_get_public_key(VALUE self) GetX509(self, x509); if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference */ - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } return ossl_pkey_wrap(pkey); @@ -517,7 +517,7 @@ ossl_x509_set_public_key(VALUE self, VALUE key) pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!X509_set_pubkey(x509, pkey)) - ossl_raise(eX509CertError, "X509_set_pubkey"); + ossl_raise(eX509CertError, "X509_set_pubkey"); return key; } @@ -561,12 +561,12 @@ ossl_x509_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (X509_verify(x509, pkey)) { case 1: - return Qtrue; + return Qtrue; case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; default: - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, NULL); } } @@ -587,8 +587,8 @@ ossl_x509_check_private_key(VALUE self, VALUE key) pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ GetX509(self, x509); if (!X509_check_private_key(x509, pkey)) { - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; } return Qtrue; @@ -610,8 +610,8 @@ ossl_x509_get_extensions(VALUE self) count = X509_get_ext_count(x509); ary = rb_ary_new_capa(count); for (i=0; i 0; i--) X509_EXTENSION_free(X509_delete_ext(x509, 0)); for (i=0; i", - rb_obj_class(self), - ossl_x509_get_subject(self), - ossl_x509_get_issuer(self), - ossl_x509_get_serial(self), - ossl_x509_get_not_before(self), - ossl_x509_get_not_after(self)); + "issuer=%+"PRIsVALUE", serial=%+"PRIsVALUE", " + "not_before=%+"PRIsVALUE", not_after=%+"PRIsVALUE">", + rb_obj_class(self), + ossl_x509_get_subject(self), + ossl_x509_get_issuer(self), + ossl_x509_get_serial(self), + ossl_x509_get_not_before(self), + ossl_x509_get_not_after(self)); } /* @@ -693,7 +693,7 @@ ossl_x509_eq(VALUE self, VALUE other) GetX509(self, a); if (!rb_obj_is_kind_of(other, cX509Cert)) - return Qfalse; + return Qfalse; GetX509(other, b); return !X509_cmp(a, b) ? Qtrue : Qfalse; @@ -788,7 +788,7 @@ load_chained_certificates_PEM(BIO *in) { certificates = load_chained_certificates_append(Qnil, certificate); while ((certificate = PEM_read_bio_X509(in, NULL, NULL, NULL))) { - load_chained_certificates_append(certificates, certificate); + load_chained_certificates_append(certificates, certificate); } /* We tried to read one more certificate but could not read start line: */ diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index 19f08053e15c82..a221429c347d5e 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509crl_type, 0) #define SetX509CRL(obj, crl) do { \ if (!(crl)) { \ - ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (crl); \ } while (0) #define GetX509CRL(obj, crl) do { \ TypedData_Get_Struct((obj), X509_CRL, &ossl_x509crl_type, (crl)); \ if (!(crl)) { \ - ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ } while (0) @@ -39,7 +39,7 @@ ossl_x509crl_free(void *ptr) static const rb_data_type_t ossl_x509crl_type = { "OpenSSL/X509/CRL", { - 0, ossl_x509crl_free, + 0, ossl_x509crl_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -83,7 +83,7 @@ ossl_x509crl_alloc(VALUE klass) obj = NewX509CRL(klass); if (!(crl = X509_CRL_new())) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } SetX509CRL(obj, crl); @@ -99,7 +99,7 @@ ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (rb_scan_args(argc, argv, "01", &arg) == 0) { - return self; + return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); @@ -129,7 +129,7 @@ ossl_x509crl_copy(VALUE self, VALUE other) GetX509CRL(self, a); GetX509CRL(other, b); if (!(crl = X509_CRL_dup(b))) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } X509_CRL_free(a); DATA_PTR(self) = crl; @@ -156,11 +156,11 @@ ossl_x509crl_set_version(VALUE self, VALUE version) long ver; if ((ver = NUM2LONG(version)) < 0) { - ossl_raise(eX509CRLError, "version must be >= 0!"); + ossl_raise(eX509CRLError, "version must be >= 0!"); } GetX509CRL(self, crl); if (!X509_CRL_set_version(crl, ver)) { - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } return version; @@ -206,7 +206,7 @@ ossl_x509crl_set_issuer(VALUE self, VALUE issuer) GetX509CRL(self, crl); if (!X509_CRL_set_issuer_name(crl, GetX509NamePtr(issuer))) { /* DUPs name */ - ossl_raise(eX509CRLError, NULL); + ossl_raise(eX509CRLError, NULL); } return issuer; } @@ -220,7 +220,7 @@ ossl_x509crl_get_last_update(VALUE self) GetX509CRL(self, crl); time = X509_CRL_get0_lastUpdate(crl); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -234,8 +234,8 @@ ossl_x509crl_set_last_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_CRL_set1_lastUpdate(crl, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); } ASN1_TIME_free(asn1time); @@ -251,7 +251,7 @@ ossl_x509crl_get_next_update(VALUE self) GetX509CRL(self, crl); time = X509_CRL_get0_nextUpdate(crl); if (!time) - return Qnil; + return Qnil; return asn1time_to_time(time); } @@ -265,8 +265,8 @@ ossl_x509crl_set_next_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_CRL_set1_nextUpdate(crl, asn1time)) { - ASN1_TIME_free(asn1time); - ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); + ASN1_TIME_free(asn1time); + ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); } ASN1_TIME_free(asn1time); @@ -289,8 +289,8 @@ ossl_x509crl_get_revoked(VALUE self) num = sk_X509_REVOKED_num(sk); ary = rb_ary_new_capa(num); for(i=0; i 0; i--) X509_EXTENSION_free(X509_CRL_delete_ext(crl, 0)); for (i=0; idata, value->length); } @@ -424,11 +424,11 @@ ossl_x509ext_to_der(VALUE obj) GetX509Ext(obj, ext); if((len = i2d_X509_EXTENSION(ext, NULL)) <= 0) - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_EXTENSION(ext, &p) < 0) - ossl_raise(eX509ExtError, NULL); + ossl_raise(eX509ExtError, NULL); ossl_str_adjust(str, p); return str; diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c index 3c0c6aca2905be..b91c92c1ff9670 100644 --- a/ext/openssl/ossl_x509name.c +++ b/ext/openssl/ossl_x509name.c @@ -13,21 +13,21 @@ TypedData_Wrap_Struct((klass), &ossl_x509name_type, 0) #define SetX509Name(obj, name) do { \ if (!(name)) { \ - ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (name); \ } while (0) #define GetX509Name(obj, name) do { \ TypedData_Get_Struct((obj), X509_NAME, &ossl_x509name_type, (name)); \ if (!(name)) { \ - ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ + ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ } while (0) #define OBJECT_TYPE_TEMPLATE \ - rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE")) + rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE")) #define DEFAULT_OBJECT_TYPE \ - rb_const_get(cX509Name, rb_intern("DEFAULT_OBJECT_TYPE")) + rb_const_get(cX509Name, rb_intern("DEFAULT_OBJECT_TYPE")) /* * Classes @@ -44,7 +44,7 @@ ossl_x509name_free(void *ptr) static const rb_data_type_t ossl_x509name_type = { "OpenSSL/X509/NAME", { - 0, ossl_x509name_free, + 0, ossl_x509name_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -88,7 +88,7 @@ ossl_x509name_alloc(VALUE klass) obj = NewX509Name(klass); if (!(name = X509_NAME_new())) { - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); } SetX509Name(obj, name); @@ -145,28 +145,28 @@ ossl_x509name_initialize(int argc, VALUE *argv, VALUE self) GetX509Name(self, name); if (rb_scan_args(argc, argv, "02", &arg, &template) == 0) { - return self; + return self; } else { - VALUE tmp = rb_check_array_type(arg); - if (!NIL_P(tmp)) { - VALUE args; - if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE; - args = rb_ary_new3(2, self, template); - rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args); - } - else{ - const unsigned char *p; - VALUE str = ossl_to_der_if_possible(arg); - X509_NAME *x; - StringValue(str); - p = (unsigned char *)RSTRING_PTR(str); - x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str)); - DATA_PTR(self) = name; - if(!x){ - ossl_raise(eX509NameError, NULL); - } - } + VALUE tmp = rb_check_array_type(arg); + if (!NIL_P(tmp)) { + VALUE args; + if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE; + args = rb_ary_new3(2, self, template); + rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args); + } + else{ + const unsigned char *p; + VALUE str = ossl_to_der_if_possible(arg); + X509_NAME *x; + StringValue(str); + p = (unsigned char *)RSTRING_PTR(str); + x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str)); + DATA_PTR(self) = name; + if(!x){ + ossl_raise(eX509NameError, NULL); + } + } } return self; @@ -184,7 +184,7 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other) name_new = X509_NAME_dup(name_other); if (!name_new) - ossl_raise(eX509NameError, "X509_NAME_dup"); + ossl_raise(eX509NameError, "X509_NAME_dup"); SetX509Name(self, name_new); X509_NAME_free(name); @@ -221,8 +221,8 @@ VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self) int loc = -1, set = 0; if (!kwargs_ids[0]) { - kwargs_ids[0] = rb_intern_const("loc"); - kwargs_ids[1] = rb_intern_const("set"); + kwargs_ids[0] = rb_intern_const("loc"); + kwargs_ids[1] = rb_intern_const("set"); } rb_scan_args(argc, argv, "21:", &oid, &value, &type, &opts); rb_get_kwargs(opts, kwargs_ids, 0, 2, kwargs); @@ -230,14 +230,14 @@ VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self) StringValue(value); if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid); if (kwargs[0] != Qundef) - loc = NUM2INT(kwargs[0]); + loc = NUM2INT(kwargs[0]); if (kwargs[1] != Qundef) - set = NUM2INT(kwargs[1]); + set = NUM2INT(kwargs[1]); GetX509Name(self, name); if (!X509_NAME_add_entry_by_txt(name, oid_name, NUM2INT(type), - (unsigned char *)RSTRING_PTR(value), - RSTRING_LENINT(value), loc, set)) - ossl_raise(eX509NameError, "X509_NAME_add_entry_by_txt"); + (unsigned char *)RSTRING_PTR(value), + RSTRING_LENINT(value), loc, set)) + ossl_raise(eX509NameError, "X509_NAME_add_entry_by_txt"); return self; } @@ -250,7 +250,7 @@ ossl_x509name_to_s_old(VALUE self) GetX509Name(self, name); buf = X509_NAME_oneline(name, NULL, 0); if (!buf) - ossl_raise(eX509NameError, "X509_NAME_oneline"); + ossl_raise(eX509NameError, "X509_NAME_oneline"); return ossl_buf2str(buf, rb_long2int(strlen(buf))); } @@ -264,11 +264,11 @@ x509name_print(VALUE self, unsigned long iflag) GetX509Name(self, name); out = BIO_new(BIO_s_mem()); if (!out) - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); ret = X509_NAME_print_ex(out, name, 0, iflag); if (ret < 0 || (iflag == XN_FLAG_COMPAT && ret == 0)) { - BIO_free(out); - ossl_raise(eX509NameError, "X509_NAME_print_ex"); + BIO_free(out); + ossl_raise(eX509NameError, "X509_NAME_print_ex"); } return ossl_membio2str(out); } @@ -302,9 +302,9 @@ ossl_x509name_to_s(int argc, VALUE *argv, VALUE self) rb_check_arity(argc, 0, 1); /* name.to_s(nil) was allowed */ if (!argc || NIL_P(argv[0])) - return ossl_x509name_to_s_old(self); + return ossl_x509name_to_s_old(self); else - return x509name_print(self, NUM2ULONG(argv[0])); + return x509name_print(self, NUM2ULONG(argv[0])); } /* @@ -327,7 +327,7 @@ static VALUE ossl_x509name_inspect(VALUE self) { return rb_enc_sprintf(rb_utf8_encoding(), "#<%"PRIsVALUE" %"PRIsVALUE">", - rb_obj_class(self), ossl_x509name_to_utf8(self)); + rb_obj_class(self), ossl_x509name_to_utf8(self)); } /* @@ -387,7 +387,7 @@ ossl_x509name_cmp(VALUE self, VALUE other) int result; if (!rb_obj_is_kind_of(other, cX509Name)) - return Qnil; + return Qnil; result = ossl_x509name_cmp0(self, other); if (result < 0) return INT2FIX(-1); @@ -406,7 +406,7 @@ static VALUE ossl_x509name_eql(VALUE self, VALUE other) { if (!rb_obj_is_kind_of(other, cX509Name)) - return Qfalse; + return Qfalse; return ossl_x509name_cmp0(self, other) == 0 ? Qtrue : Qfalse; } @@ -466,11 +466,11 @@ ossl_x509name_to_der(VALUE self) GetX509Name(self, name); if((len = i2d_X509_NAME(name, NULL)) <= 0) - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_NAME(name, &p) <= 0) - ossl_raise(eX509NameError, NULL); + ossl_raise(eX509NameError, NULL); ossl_str_adjust(str, p); return str; diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index d7252379cdda4d..433cc461a9933b 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -13,14 +13,14 @@ TypedData_Wrap_Struct((klass), &ossl_x509req_type, 0) #define SetX509Req(obj, req) do { \ if (!(req)) { \ - ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (req); \ } while (0) #define GetX509Req(obj, req) do { \ TypedData_Get_Struct((obj), X509_REQ, &ossl_x509req_type, (req)); \ if (!(req)) { \ - ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ + ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ } while (0) @@ -39,7 +39,7 @@ ossl_x509req_free(void *ptr) static const rb_data_type_t ossl_x509req_type = { "OpenSSL/X509/REQ", { - 0, ossl_x509req_free, + 0, ossl_x509req_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; @@ -68,7 +68,7 @@ ossl_x509req_alloc(VALUE klass) obj = NewX509Req(klass); if (!(req = X509_REQ_new())) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } SetX509Req(obj, req); @@ -84,7 +84,7 @@ ossl_x509req_initialize(int argc, VALUE *argv, VALUE self) rb_check_frozen(self); if (rb_scan_args(argc, argv, "01", &arg) == 0) { - return self; + return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); @@ -114,7 +114,7 @@ ossl_x509req_copy(VALUE self, VALUE other) GetX509Req(self, a); GetX509Req(other, b); if (!(req = X509_REQ_dup(b))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } X509_REQ_free(a); DATA_PTR(self) = req; @@ -130,11 +130,11 @@ ossl_x509req_to_pem(VALUE self) GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } if (!PEM_write_bio_X509_REQ(out, req)) { - BIO_free(out); - ossl_raise(eX509ReqError, NULL); + BIO_free(out); + ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); @@ -150,11 +150,11 @@ ossl_x509req_to_der(VALUE self) GetX509Req(self, req); if ((len = i2d_X509_REQ(req, NULL)) <= 0) - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509_REQ(req, &p) <= 0) - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); ossl_str_adjust(str, p); return str; @@ -168,11 +168,11 @@ ossl_x509req_to_text(VALUE self) GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } if (!X509_REQ_print(out, req)) { - BIO_free(out); - ossl_raise(eX509ReqError, NULL); + BIO_free(out); + ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); @@ -191,7 +191,7 @@ ossl_x509req_to_x509(VALUE self, VALUE days, VALUE key) GetX509Req(self, req); ... if (!(x509 = X509_REQ_to_X509(req, d, pkey))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return ossl_x509_new(x509); @@ -217,11 +217,11 @@ ossl_x509req_set_version(VALUE self, VALUE version) long ver; if ((ver = NUM2LONG(version)) < 0) { - ossl_raise(eX509ReqError, "version must be >= 0!"); + ossl_raise(eX509ReqError, "version must be >= 0!"); } GetX509Req(self, req); if (!X509_REQ_set_version(req, ver)) { - ossl_raise(eX509ReqError, "X509_REQ_set_version"); + ossl_raise(eX509ReqError, "X509_REQ_set_version"); } return version; @@ -235,7 +235,7 @@ ossl_x509req_get_subject(VALUE self) GetX509Req(self, req); if (!(name = X509_REQ_get_subject_name(req))) { /* NO DUP - don't free */ - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return ossl_x509name_new(name); @@ -249,7 +249,7 @@ ossl_x509req_set_subject(VALUE self, VALUE subject) GetX509Req(self, req); /* DUPs name */ if (!X509_REQ_set_subject_name(req, GetX509NamePtr(subject))) { - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return subject; @@ -285,7 +285,7 @@ ossl_x509req_get_public_key(VALUE self) GetX509Req(self, req); if (!(pkey = X509_REQ_get_pubkey(req))) { /* adds reference */ - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } return ossl_pkey_wrap(pkey); @@ -301,7 +301,7 @@ ossl_x509req_set_public_key(VALUE self, VALUE key) pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!X509_REQ_set_pubkey(req, pkey)) - ossl_raise(eX509ReqError, "X509_REQ_set_pubkey"); + ossl_raise(eX509ReqError, "X509_REQ_set_pubkey"); return key; } @@ -337,12 +337,12 @@ ossl_x509req_verify(VALUE self, VALUE key) ossl_pkey_check_public_key(pkey); switch (X509_REQ_verify(req, pkey)) { case 1: - return Qtrue; + return Qtrue; case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; default: - ossl_raise(eX509ReqError, NULL); + ossl_raise(eX509ReqError, NULL); } } @@ -358,13 +358,13 @@ ossl_x509req_get_attributes(VALUE self) count = X509_REQ_get_attr_count(req); if (count < 0) { - OSSL_Debug("count < 0???"); - return rb_ary_new(); + OSSL_Debug("count < 0???"); + return rb_ary_new(); } ary = rb_ary_new2(count); for (i=0; i 0; i--) X509_ATTRIBUTE_free(X509_REQ_delete_attr(req, 0)); for (i=0;i 0; i--) X509_EXTENSION_free(X509_REVOKED_delete_ext(rev, 0)); for (i=0; iproc, rb_intern("call"), 2, - args->preverify_ok, args->store_ctx); + args->preverify_ok, args->store_ctx); } int @@ -73,33 +73,33 @@ ossl_verify_cb_call(VALUE proc, int ok, X509_STORE_CTX *ctx) int state; if (NIL_P(proc)) - return ok; + return ok; ret = Qfalse; rctx = rb_protect(ossl_x509stctx_new_i, (VALUE)ctx, &state); if (state) { - rb_set_errinfo(Qnil); - rb_warn("StoreContext initialization failure"); + rb_set_errinfo(Qnil); + rb_warn("StoreContext initialization failure"); } else { - args.proc = proc; - args.preverify_ok = ok ? Qtrue : Qfalse; - args.store_ctx = rctx; - ret = rb_protect(call_verify_cb_proc, (VALUE)&args, &state); - if (state) { - rb_set_errinfo(Qnil); - rb_warn("exception in verify_callback is ignored"); - } - RTYPEDDATA_DATA(rctx) = NULL; + args.proc = proc; + args.preverify_ok = ok ? Qtrue : Qfalse; + args.store_ctx = rctx; + ret = rb_protect(call_verify_cb_proc, (VALUE)&args, &state); + if (state) { + rb_set_errinfo(Qnil); + rb_warn("exception in verify_callback is ignored"); + } + RTYPEDDATA_DATA(rctx) = NULL; } if (ret == Qtrue) { - X509_STORE_CTX_set_error(ctx, X509_V_OK); - ok = 1; + X509_STORE_CTX_set_error(ctx, X509_V_OK); + ok = 1; } else { - if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) - X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); - ok = 0; + if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); + ok = 0; } return ok; @@ -159,10 +159,10 @@ x509store_verify_cb(int ok, X509_STORE_CTX *ctx) proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx); if (!proc) - proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), - store_ex_verify_cb_idx); + proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), + store_ex_verify_cb_idx); if (!proc) - return ok; + return ok; return ossl_verify_cb_call(proc, ok, ctx); } @@ -484,7 +484,7 @@ ossl_x509store_verify(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "11", &cert, &chain); ctx = rb_funcall(cX509StoreContext, rb_intern("new"), 3, self, cert, chain); proc = rb_block_given_p() ? rb_block_proc() : - rb_iv_get(self, "@verify_callback"); + rb_iv_get(self, "@verify_callback"); rb_iv_set(ctx, "@verify_callback", proc); result = rb_funcall(ctx, rb_intern("verify"), 0); @@ -513,9 +513,9 @@ ossl_x509stctx_free(void *ptr) { X509_STORE_CTX *ctx = ptr; if (X509_STORE_CTX_get0_untrusted(ctx)) - sk_X509_pop_free(X509_STORE_CTX_get0_untrusted(ctx), X509_free); + sk_X509_pop_free(X509_STORE_CTX_get0_untrusted(ctx), X509_free); if (X509_STORE_CTX_get0_cert(ctx)) - X509_free(X509_STORE_CTX_get0_cert(ctx)); + X509_free(X509_STORE_CTX_get0_cert(ctx)); X509_STORE_CTX_free(ctx); } @@ -763,7 +763,7 @@ ossl_x509stctx_get_curr_crl(VALUE self) GetX509StCtx(self, ctx); crl = X509_STORE_CTX_get0_current_crl(ctx); if (!crl) - return Qnil; + return Qnil; return ossl_x509crl_new(crl); } @@ -862,10 +862,10 @@ Init_ossl_x509store(void) /* Register ext_data slot for verify callback Proc */ stctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"stctx_ex_verify_cb_idx", 0, 0, 0); if (stctx_ex_verify_cb_idx < 0) - ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); + ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); store_ex_verify_cb_idx = X509_STORE_get_ex_new_index(0, (void *)"store_ex_verify_cb_idx", 0, 0, 0); if (store_ex_verify_cb_idx < 0) - ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index"); + ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index"); eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError); From 2aaea665bb19be8ed64605ec9aa5c990fddbd2ce Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 5 Dec 2025 02:40:59 +0900 Subject: [PATCH 163/367] fix typo s/sharable/shareable/ --- bootstraptest/test_ractor.rb | 6 +++--- doc/syntax/comments.rdoc | 2 +- include/ruby/internal/core/rtypeddata.h | 2 +- include/ruby/ractor.h | 2 +- ractor.c | 2 +- ractor.rb | 8 ++++---- test/ruby/test_parse.rb | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index cc00fa3586af93..271abb3c84006a 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1203,9 +1203,9 @@ def /(other) end } -# Ractor.make_sharable(Method/UnboundMethod) +# Ractor.make_shareable(Method/UnboundMethod) assert_equal 'true', %q{ - # raise because receiver is unsharable + # raise because receiver is unshareable begin _m0 = Ractor.make_shareable(self.method(:__id__)) rescue => e @@ -1214,7 +1214,7 @@ def /(other) raise "no error" end - # Method with sharable receiver + # Method with shareable receiver M1 = Ractor.make_shareable(Object.method(:__id__)) # UnboundMethod diff --git a/doc/syntax/comments.rdoc b/doc/syntax/comments.rdoc index 00d19d588af508..cb6829a984a32e 100644 --- a/doc/syntax/comments.rdoc +++ b/doc/syntax/comments.rdoc @@ -170,7 +170,7 @@ In this mode, all values assigned to constants are made shareable. # shareable_constant_value: experimental_everything FOO = Set[1, 2, {foo: []}] - # same as FOO = Ractor.make_sharable(...) + # same as FOO = Ractor.make_shareable(...) # OR same as `FOO = Set[1, 2, {foo: [].freeze}.freeze].freeze` var = [{foo: []}] diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index ee79c2e2a90a18..ebcac2c8469db6 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -160,7 +160,7 @@ rbimpl_typeddata_flags { // experimental flag // Similar to RUBY_TYPED_FROZEN_SHAREABLE, but doesn't make shareable // reachable objects from this T_DATA object on the Ractor.make_shareable. - // If it refers to unsharable objects, simply raise an error. + // If it refers to unshareable objects, simply raise an error. // RUBY_TYPED_FROZEN_SHAREABLE_NO_REC = RUBY_FL_FINALIZE, /** diff --git a/include/ruby/ractor.h b/include/ruby/ractor.h index 85222bbe115860..8cfca2162107c8 100644 --- a/include/ruby/ractor.h +++ b/include/ruby/ractor.h @@ -217,7 +217,7 @@ VALUE rb_ractor_make_shareable(VALUE obj); * * @param[in] obj Arbitrary ruby object to duplicate. * @exception rb_eRactorError Ractors cannot share `obj` by nature. - * @return A deep copy of `obj` which is sharable among Ractors. + * @return A deep copy of `obj` which is shareable among Ractors. */ VALUE rb_ractor_make_shareable_copy(VALUE obj); diff --git a/ractor.c b/ractor.c index 6bd38b6ab875f0..b7c7d9467924b6 100644 --- a/ractor.c +++ b/ractor.c @@ -2366,7 +2366,7 @@ ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data); } -// sharable_proc +// shareable_proc static VALUE ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_lambda) diff --git a/ractor.rb b/ractor.rb index 3c0c6744e6148e..d992f0a04702c9 100644 --- a/ractor.rb +++ b/ractor.rb @@ -607,7 +607,7 @@ def unmonitor port # # call-seq: - # Ractor.sharable_proc(self: nil){} -> sharable proc + # Ractor.shareable_proc(self: nil){} -> shareable proc # # It returns shareable Proc object. The Proc object is # shareable and the self in a block will be replaced with @@ -619,7 +619,7 @@ def unmonitor port # Ractor.shareable_proc{ p a } # #=> can not isolate a Proc because it accesses outer variables (a). (ArgumentError) # - # The `self` should be a sharable object + # The `self` should be a shareable object # # Ractor.shareable_proc(self: self){} # #=> self should be shareable: main (Ractor::IsolationError) @@ -634,9 +634,9 @@ def self.shareable_proc self: nil # # call-seq: - # Ractor.sharable_proc{} -> sharable proc + # Ractor.shareable_proc{} -> shareable proc # - # Same as Ractor.sharable_proc, but returns lambda proc. + # Same as Ractor.shareable_proc, but returns lambda proc. # def self.shareable_lambda self: nil Primitive.attr! :use_block diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 98e95b98afaf77..78a5638647af67 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1544,7 +1544,7 @@ def test_shareable_constant_value_ignored end def test_shareable_constant_value_simple - obj = [['unsharable_value']] + obj = [['unshareable_value']] a, b, c = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}") begin; # shareable_constant_value: experimental_everything From d9aced864bed7183059785c3aaf37ae835434f4a Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 5 Dec 2025 03:15:50 +0900 Subject: [PATCH 164/367] Add openssl reformatting to .git-blame-ignore-revs [ci skip] --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 3ca28ad72c8917..d98646febf69c1 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -10,6 +10,7 @@ e63a2115f64433b21cb5dd67c5bf8b30f87ef293 712ac99e4d0384a941c80a9f48f62943ba7d97c0 d1474affa8e105bece209cc9d594bb0a989859e1 2da92388b948821269b18d6b178a680f17e41750 +5062c0c621d887367af8a054e5e5d83d7ec57dd3 # Enable Style/StringLiterals cop for RubyGems/Bundler d7ffd3fea402239b16833cc434404a7af82d44f3 From de2c2bd60fdce52cc7ba38a25f3e9436442af604 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 4 Dec 2025 16:10:21 -0500 Subject: [PATCH 165/367] Take VM lock in `class_switch_superclass` (#15356) Safe multi-ractor subclass list mutation We need to lock around mutation and accesses of a class's subclasses list. Unfortunately we also need to do this when creating singleton classes, as the singleton class does need to go into `super`'s subclasses list for CC invalidation purposes. --- class.c | 6 ++++-- test/ruby/test_class.rb | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/class.c b/class.c index 0cec526b74fe98..9716ba07dae2e2 100644 --- a/class.c +++ b/class.c @@ -734,8 +734,10 @@ class_detach_subclasses(VALUE klass, VALUE arg) static void class_switch_superclass(VALUE super, VALUE klass) { - class_detach_subclasses(klass, Qnil); - rb_class_subclass_add(super, klass); + RB_VM_LOCKING() { + class_detach_subclasses(klass, Qnil); + rb_class_subclass_add(super, klass); + } } /** diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 10b7655e9a5562..1faecd0cb25e13 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -917,4 +917,17 @@ class T end end; end + + def test_safe_multi_ractor_subclasses_list_mutation + assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}" + begin; + 4.times.map do + Ractor.new do + 20_000.times do + Object.new.singleton_class + end + end + end.each(&:join) + end; + end end From 1d3fe2c382fd543bf12b2e12c2633162ea4b3a0a Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 30 Oct 2025 12:05:25 -0700 Subject: [PATCH 166/367] Change bmethod defined_ractor to use id instead When defining a bmethod, we recorded the current Ractor's object in the method. However that was never marked and so could be GC'd and reused by a future Ractor. Instead we can use the Ractor's id, which we expect to be unique forever. Co-authored-by: Luke Gruber --- bootstraptest/test_ractor.rb | 25 +++++++++++++++++++++++++ method.h | 2 +- vm_insnhelper.c | 4 ++-- vm_method.c | 3 +-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 271abb3c84006a..b19349391cce81 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -2413,3 +2413,28 @@ def call_test(obj) :ok end RUBY + +# When creating bmethods in Ractors, they should only be usable from their +# defining ractor, even if it is GC'd +assert_equal 'ok', <<~'RUBY' +CLASSES = 1000.times.map do + Ractor.new do + Class.new do + define_method(:foo) {} + end + end +end.map(&:value).freeze + +any = 1000.times.map do + Ractor.new do + CLASSES.any? do |klass| + begin + klass.new.foo + true + rescue RuntimeError + false + end + end + end +end.map(&:value).none? && :ok +RUBY diff --git a/method.h b/method.h index 87fb2498a054d7..b174c6fccb4d94 100644 --- a/method.h +++ b/method.h @@ -167,7 +167,7 @@ typedef struct rb_method_refined_struct { typedef struct rb_method_bmethod_struct { VALUE proc; /* should be marked */ struct rb_hook_list_struct *hooks; - VALUE defined_ractor; + rb_serial_t defined_ractor_id; } rb_method_bmethod_t; enum method_optimized_type { diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 60e1b647ae5d8d..8c1992de43d495 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4121,7 +4121,7 @@ vm_call_bmethod_body(rb_execution_context_t *ec, struct rb_calling_info *calling VALUE procv = cme->def->body.bmethod.proc; if (!RB_OBJ_SHAREABLE_P(procv) && - cme->def->body.bmethod.defined_ractor != rb_ractor_self(rb_ec_ractor_ptr(ec))) { + cme->def->body.bmethod.defined_ractor_id != rb_ractor_id(rb_ec_ractor_ptr(ec))) { rb_raise(rb_eRuntimeError, "defined with an un-shareable Proc in a different Ractor"); } @@ -4144,7 +4144,7 @@ vm_call_iseq_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct VALUE procv = cme->def->body.bmethod.proc; if (!RB_OBJ_SHAREABLE_P(procv) && - cme->def->body.bmethod.defined_ractor != rb_ractor_self(rb_ec_ractor_ptr(ec))) { + cme->def->body.bmethod.defined_ractor_id != rb_ractor_id(rb_ec_ractor_ptr(ec))) { rb_raise(rb_eRuntimeError, "defined with an un-shareable Proc in a different Ractor"); } diff --git a/vm_method.c b/vm_method.c index 179deb749daeec..c4f391b5afe38a 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1020,7 +1020,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de } case VM_METHOD_TYPE_BMETHOD: RB_OBJ_WRITE(me, &def->body.bmethod.proc, (VALUE)opts); - RB_OBJ_WRITE(me, &def->body.bmethod.defined_ractor, rb_ractor_self(GET_RACTOR())); + def->body.bmethod.defined_ractor_id = rb_ractor_id(rb_ec_ractor_ptr(GET_EC())); return; case VM_METHOD_TYPE_NOTIMPLEMENTED: setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), (VALUE(*)(ANYARGS))rb_f_notimplement_internal, -1); @@ -1060,7 +1060,6 @@ method_definition_reset(const rb_method_entry_t *me) break; case VM_METHOD_TYPE_BMETHOD: RB_OBJ_WRITTEN(me, Qundef, def->body.bmethod.proc); - RB_OBJ_WRITTEN(me, Qundef, def->body.bmethod.defined_ractor); /* give up to check all in a list */ if (def->body.bmethod.hooks) rb_gc_writebarrier_remember((VALUE)me); break; From 7d9558f99b5bb8279c138860670b56735a28a3e4 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 1 Dec 2025 19:31:38 -0800 Subject: [PATCH 167/367] Adjust test to avoid bug --- bootstraptest/test_ractor.rb | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index b19349391cce81..7d09ac52838f66 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -2417,13 +2417,24 @@ def call_test(obj) # When creating bmethods in Ractors, they should only be usable from their # defining ractor, even if it is GC'd assert_equal 'ok', <<~'RUBY' -CLASSES = 1000.times.map do - Ractor.new do - Class.new do - define_method(:foo) {} - end +CLASSES = 1000.times.map { Class.new }.freeze + +# This would be better to run in parallel, but there's a bug with lambda +# creation and YJIT causing crashes in dev mode +ractors = CLASSES.map do |klass| + Ractor.new(klass) do |klass| + Ractor.receive + klass.define_method(:foo) {} end -end.map(&:value).freeze +end + +ractors.each do |ractor| + ractor << nil + ractor.join +end + +ractors.clear +GC.start any = 1000.times.map do Ractor.new do From 8d8159e7d87e4fd1594ce2fad3d2653e47fb1026 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 4 Dec 2025 16:51:11 -0500 Subject: [PATCH 168/367] Fix thread scheduler issue with thread_sched_wait_events (#15392) Fix race between timer thread dequeuing waiting thread and thread skipping sleeping due to being dequeued. We now use `th->event_serial` which is protected by `thread_sched_lock`. When a thread is put on timer thread's waiting list, the event serial is saved on the item. The timer thread checks that the saved serial is the same as current thread's serial before calling `thread_sched_to_ready`. The following script (taken from a test in `test_thread.rb` used to crash on scheduler debug assertions. It would likely crash in non-debug mode as well. ```ruby def assert_nil(val) if val != nil raise "Expected #{val} to be nil" end end def assert_equal(expected, actual) if expected != actual raise "Expected #{expected} to be #{actual}" end end def test_join2 ok = false t1 = Thread.new { ok = true; sleep } Thread.pass until ok Thread.pass until t1.stop? t2 = Thread.new do Thread.pass while ok t1.join(0.01) end t3 = Thread.new do ok = false t1.join end assert_nil(t2.value) t1.wakeup assert_equal(t1, t3.value) ensure t1&.kill&.join t2&.kill&.join t3&.kill&.join end rs = 30.times.map do Ractor.new do test_join2 end end rs.each(&:join) ``` --- thread_pthread.c | 31 ++++++++++++++++++------------- thread_pthread.h | 1 + thread_pthread_mn.c | 34 ++++++++++++++++++++++------------ vm_core.h | 1 + 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/thread_pthread.c b/thread_pthread.c index 2eaa407f10ca94..93b32e55c0f72b 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -1122,8 +1122,10 @@ thread_sched_to_waiting_until_wakeup(struct rb_thread_sched *sched, rb_thread_t { if (!RUBY_VM_INTERRUPTED(th->ec)) { bool can_direct_transfer = !th_has_dedicated_nt(th); + th->status = THREAD_STOPPED_FOREVER; thread_sched_wakeup_next_thread(sched, th, can_direct_transfer); thread_sched_wait_running_turn(sched, th, can_direct_transfer); + th->status = THREAD_RUNNABLE; } else { RUBY_DEBUG_LOG("th:%u interrupted", rb_th_serial(th)); @@ -1149,6 +1151,7 @@ thread_sched_yield(struct rb_thread_sched *sched, rb_thread_t *th) bool can_direct_transfer = !th_has_dedicated_nt(th); thread_sched_to_ready_common(sched, th, false, can_direct_transfer); thread_sched_wait_running_turn(sched, th, can_direct_transfer); + th->status = THREAD_RUNNABLE; } else { VM_ASSERT(sched->readyq_cnt == 0); @@ -1338,7 +1341,7 @@ void rb_ractor_lock_self(rb_ractor_t *r); void rb_ractor_unlock_self(rb_ractor_t *r); // The current thread for a ractor is put to "sleep" (descheduled in the STOPPED_FOREVER state) waiting for -// a ractor action to wake it up. See docs for `ractor_sched_sleep_with_cleanup` for more info. +// a ractor action to wake it up. void rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf, void *ubf_arg) { @@ -2868,7 +2871,7 @@ static struct { static void timer_thread_check_timeslice(rb_vm_t *vm); static int timer_thread_set_timeout(rb_vm_t *vm); -static void timer_thread_wakeup_thread(rb_thread_t *th); +static void timer_thread_wakeup_thread(rb_thread_t *th, uint32_t event_serial); #include "thread_pthread_mn.c" @@ -2970,7 +2973,7 @@ timer_thread_check_exceed(rb_hrtime_t abs, rb_hrtime_t now) } static rb_thread_t * -timer_thread_deq_wakeup(rb_vm_t *vm, rb_hrtime_t now) +timer_thread_deq_wakeup(rb_vm_t *vm, rb_hrtime_t now, uint32_t *event_serial) { struct rb_thread_sched_waiting *w = ccan_list_top(&timer_th.waiting, struct rb_thread_sched_waiting, node); @@ -2987,32 +2990,31 @@ timer_thread_deq_wakeup(rb_vm_t *vm, rb_hrtime_t now) w->flags = thread_sched_waiting_none; w->data.result = 0; - return thread_sched_waiting_thread(w); + rb_thread_t *th = thread_sched_waiting_thread(w); + *event_serial = w->data.event_serial; + return th; } return NULL; } static void -timer_thread_wakeup_thread_locked(struct rb_thread_sched *sched, rb_thread_t *th) +timer_thread_wakeup_thread_locked(struct rb_thread_sched *sched, rb_thread_t *th, uint32_t event_serial) { - if (sched->running != th) { + if (sched->running != th && th->event_serial == event_serial) { thread_sched_to_ready_common(sched, th, true, false); } - else { - // will be release the execution right - } } static void -timer_thread_wakeup_thread(rb_thread_t *th) +timer_thread_wakeup_thread(rb_thread_t *th, uint32_t event_serial) { RUBY_DEBUG_LOG("th:%u", rb_th_serial(th)); struct rb_thread_sched *sched = TH_SCHED(th); thread_sched_lock(sched, th); { - timer_thread_wakeup_thread_locked(sched, th); + timer_thread_wakeup_thread_locked(sched, th, event_serial); } thread_sched_unlock(sched, th); } @@ -3022,11 +3024,14 @@ timer_thread_check_timeout(rb_vm_t *vm) { rb_hrtime_t now = rb_hrtime_now(); rb_thread_t *th; + uint32_t event_serial; rb_native_mutex_lock(&timer_th.waiting_lock); { - while ((th = timer_thread_deq_wakeup(vm, now)) != NULL) { - timer_thread_wakeup_thread(th); + while ((th = timer_thread_deq_wakeup(vm, now, &event_serial)) != NULL) { + rb_native_mutex_unlock(&timer_th.waiting_lock); + timer_thread_wakeup_thread(th, event_serial); + rb_native_mutex_lock(&timer_th.waiting_lock); } } rb_native_mutex_unlock(&timer_th.waiting_lock); diff --git a/thread_pthread.h b/thread_pthread.h index d635948c4bbb7c..f579e53781a74c 100644 --- a/thread_pthread.h +++ b/thread_pthread.h @@ -39,6 +39,7 @@ struct rb_thread_sched_waiting { #else uint64_t timeout; #endif + uint32_t event_serial; int fd; // -1 for timeout only int result; } data; diff --git a/thread_pthread_mn.c b/thread_pthread_mn.c index 8100fd534e461e..2211d4d1067c39 100644 --- a/thread_pthread_mn.c +++ b/thread_pthread_mn.c @@ -3,7 +3,7 @@ #if USE_MN_THREADS static void timer_thread_unregister_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags); -static void timer_thread_wakeup_thread_locked(struct rb_thread_sched *sched, rb_thread_t *th); +static void timer_thread_wakeup_thread_locked(struct rb_thread_sched *sched, rb_thread_t *th, uint32_t event_serial); static bool timer_thread_cancel_waiting(rb_thread_t *th) @@ -15,9 +15,7 @@ timer_thread_cancel_waiting(rb_thread_t *th) if (th->sched.waiting_reason.flags) { canceled = true; ccan_list_del_init(&th->sched.waiting_reason.node); - if (th->sched.waiting_reason.flags & (thread_sched_waiting_io_read | thread_sched_waiting_io_write)) { - timer_thread_unregister_waiting(th, th->sched.waiting_reason.data.fd, th->sched.waiting_reason.flags); - } + timer_thread_unregister_waiting(th, th->sched.waiting_reason.data.fd, th->sched.waiting_reason.flags); th->sched.waiting_reason.flags = thread_sched_waiting_none; } } @@ -57,7 +55,7 @@ ubf_event_waiting(void *ptr) thread_sched_unlock(sched, th); } -static bool timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags, rb_hrtime_t *rel); +static bool timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags, rb_hrtime_t *rel, uint32_t event_serial); // return true if timed out static bool @@ -67,13 +65,15 @@ thread_sched_wait_events(struct rb_thread_sched *sched, rb_thread_t *th, int fd, volatile bool timedout = false, need_cancel = false; + uint32_t event_serial = ++th->event_serial; // overflow is okay + if (ubf_set(th, ubf_event_waiting, (void *)th)) { return false; } thread_sched_lock(sched, th); { - if (timer_thread_register_waiting(th, fd, events, rel)) { + if (timer_thread_register_waiting(th, fd, events, rel, event_serial)) { RUBY_DEBUG_LOG("wait fd:%d", fd); RB_VM_SAVE_MACHINE_CONTEXT(th); @@ -81,9 +81,11 @@ thread_sched_wait_events(struct rb_thread_sched *sched, rb_thread_t *th, int fd, RB_INTERNAL_THREAD_HOOK(RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, th); if (th->sched.waiting_reason.flags == thread_sched_waiting_none) { - // already awaken + th->event_serial++; + // timer thread has dequeued us already, but it won't try to wake us because we bumped our serial } else if (RUBY_VM_INTERRUPTED(th->ec)) { + th->event_serial++; // make sure timer thread doesn't try to wake us need_cancel = true; } else { @@ -111,7 +113,8 @@ thread_sched_wait_events(struct rb_thread_sched *sched, rb_thread_t *th, int fd, } thread_sched_unlock(sched, th); - ubf_clear(th); // TODO: maybe it is already NULL? + // if ubf triggered between sched unlock and ubf clear, sched->running == th here + ubf_clear(th); VM_ASSERT(sched->running == th); @@ -680,7 +683,7 @@ kqueue_already_registered(int fd) // return false if the fd is not waitable or not need to wait. static bool -timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags, rb_hrtime_t *rel) +timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags, rb_hrtime_t *rel, uint32_t event_serial) { RUBY_DEBUG_LOG("th:%u fd:%d flag:%d rel:%lu", rb_th_serial(th), fd, flags, rel ? (unsigned long)*rel : 0); @@ -807,6 +810,7 @@ timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting th->sched.waiting_reason.data.timeout = abs; th->sched.waiting_reason.data.fd = fd; th->sched.waiting_reason.data.result = 0; + th->sched.waiting_reason.data.event_serial = event_serial; } if (abs == 0) { // no timeout @@ -855,6 +859,10 @@ timer_thread_register_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting static void timer_thread_unregister_waiting(rb_thread_t *th, int fd, enum thread_sched_waiting_flag flags) { + if (!(th->sched.waiting_reason.flags & (thread_sched_waiting_io_read | thread_sched_waiting_io_write))) { + return; + } + RUBY_DEBUG_LOG("th:%u fd:%d", rb_th_serial(th), fd); #if HAVE_SYS_EVENT_H kqueue_unregister_waiting(fd, flags); @@ -885,7 +893,7 @@ timer_thread_setup_mn(void) #endif RUBY_DEBUG_LOG("comm_fds:%d/%d", timer_th.comm_fds[0], timer_th.comm_fds[1]); - timer_thread_register_waiting(NULL, timer_th.comm_fds[0], thread_sched_waiting_io_read | thread_sched_waiting_io_force, NULL); + timer_thread_register_waiting(NULL, timer_th.comm_fds[0], thread_sched_waiting_io_read | thread_sched_waiting_io_force, NULL, 0); } static int @@ -986,8 +994,9 @@ timer_thread_polling(rb_vm_t *vm) th->sched.waiting_reason.flags = thread_sched_waiting_none; th->sched.waiting_reason.data.fd = -1; th->sched.waiting_reason.data.result = filter; + uint32_t event_serial = th->sched.waiting_reason.data.event_serial; - timer_thread_wakeup_thread_locked(sched, th); + timer_thread_wakeup_thread_locked(sched, th, event_serial); } else { // already released @@ -1031,8 +1040,9 @@ timer_thread_polling(rb_vm_t *vm) th->sched.waiting_reason.flags = thread_sched_waiting_none; th->sched.waiting_reason.data.fd = -1; th->sched.waiting_reason.data.result = (int)events; + uint32_t event_serial = th->sched.waiting_reason.data.event_serial; - timer_thread_wakeup_thread_locked(sched, th); + timer_thread_wakeup_thread_locked(sched, th, event_serial); } else { // already released diff --git a/vm_core.h b/vm_core.h index 9d11457966ed71..09f27f6e859fd1 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1127,6 +1127,7 @@ typedef struct rb_thread_struct { struct rb_thread_sched_item sched; bool mn_schedulable; rb_atomic_t serial; // only for RUBY_DEBUG_LOG() + uint32_t event_serial; VALUE last_status; /* $? */ From 6d0c9598f5c62901e0bd9c39e34f0aa2fc7846ed Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Tue, 2 Dec 2025 19:59:15 +0000 Subject: [PATCH 169/367] [DOC] Cross-link between README pages --- README.ja.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.ja.md b/README.ja.md index 278285dc83db3c..9bbc3a83a5fdee 100644 --- a/README.ja.md +++ b/README.ja.md @@ -4,6 +4,8 @@ [![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master) [![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby) +[English](rdoc-ref:README.md) + # Rubyとは Rubyはシンプルかつ強力なオブジェクト指向スクリプト言語です. Rubyは純粋なオブジェクト指向言語として設計されているので, diff --git a/README.md b/README.md index 5ed312cca872a0..02435b419e026d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows") [![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby) +[日本語](rdoc-ref:README.ja.md) + # What is Ruby? Ruby is an interpreted object-oriented programming language often From 412895ae84c204466f085d1aae6ca90db88e255a Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 4 Dec 2025 14:19:00 +0900 Subject: [PATCH 170/367] [ruby/rubygems] Try to split and run three runners for Windows I organized all examples the followings: ``` Total test time: 2468.41 seconds Total files: 168 Group A: 42 files, 617.08 seconds Group B: 42 files, 617.05 seconds Group C: 42 files, 617.14 seconds Group D: 42 files, 617.14 seconds ``` https://github.com/ruby/rubygems/commit/94d41e6c7c --- spec/bundler/spec_helper.rb | 10 ++ spec/bundler/support/windows_tag_group.rb | 185 ++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 spec/bundler/support/windows_tag_group.rb diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb index 96cd7be4720a66..f0d6bce006ef20 100644 --- a/spec/bundler/spec_helper.rb +++ b/spec/bundler/spec_helper.rb @@ -38,6 +38,7 @@ require_relative "support/matchers" require_relative "support/permissions" require_relative "support/platforms" +require_relative "support/windows_tag_group" $debug = false @@ -56,6 +57,7 @@ def self.ruby=(ruby) config.include Spec::Path config.include Spec::Platforms config.include Spec::Permissions + config.include Spec::WindowsTagGroup # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = ".rspec_status" @@ -129,4 +131,12 @@ def self.ruby=(ruby) ensure reset! end + + Spec::WindowsTagGroup::EXAMPLE_MAPPINGS.each do |tag, file_paths| + file_pattern = Regexp.union(file_paths.map {|path| Regexp.new(Regexp.escape(path) + "$") }) + + config.define_derived_metadata(file_path: file_pattern) do |metadata| + metadata[tag] = true + end + end end diff --git a/spec/bundler/support/windows_tag_group.rb b/spec/bundler/support/windows_tag_group.rb new file mode 100644 index 00000000000000..77112a2c6cd1ce --- /dev/null +++ b/spec/bundler/support/windows_tag_group.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +module Spec + module WindowsTagGroup + EXAMPLE_MAPPINGS = { + windows_a: [ + "spec/runtime/setup_spec.rb", + "spec/commands/install_spec.rb", + "spec/commands/add_spec.rb", + "spec/install/gems/compact_index_spec.rb", + "spec/commands/config_spec.rb", + "spec/commands/pristine_spec.rb", + "spec/install/gemfile/path_spec.rb", + "spec/update/git_spec.rb", + "spec/commands/open_spec.rb", + "spec/commands/remove_spec.rb", + "spec/commands/show_spec.rb", + "spec/plugins/source/example_spec.rb", + "spec/commands/console_spec.rb", + "spec/runtime/require_spec.rb", + "spec/runtime/env_helpers_spec.rb", + "spec/runtime/gem_tasks_spec.rb", + "spec/install/gemfile_spec.rb", + "spec/commands/fund_spec.rb", + "spec/commands/init_spec.rb", + "spec/bundler/ruby_dsl_spec.rb", + "spec/bundler/mirror_spec.rb", + "spec/bundler/source/git/git_proxy_spec.rb", + "spec/bundler/source_list_spec.rb", + "spec/bundler/plugin/installer_spec.rb", + "spec/bundler/friendly_errors_spec.rb", + "spec/resolver/platform_spec.rb", + "spec/bundler/fetcher/downloader_spec.rb", + "spec/update/force_spec.rb", + "spec/bundler/env_spec.rb", + "spec/install/gems/mirror_spec.rb", + "spec/install/failure_spec.rb", + "spec/bundler/yaml_serializer_spec.rb", + "spec/bundler/environment_preserver_spec.rb", + "spec/install/gemfile/install_if_spec.rb", + "spec/install/gems/gemfile_source_header_spec.rb", + "spec/bundler/fetcher/base_spec.rb", + "spec/bundler/rubygems_integration_spec.rb", + "spec/bundler/worker_spec.rb", + "spec/bundler/dependency_spec.rb", + "spec/bundler/ui_spec.rb", + "spec/bundler/plugin/source_list_spec.rb", + "spec/bundler/source/path_spec.rb", + ], + windows_b: [ + "spec/install/gemfile/git_spec.rb", + "spec/install/gems/standalone_spec.rb", + "spec/commands/lock_spec.rb", + "spec/cache/gems_spec.rb", + "spec/other/major_deprecation_spec.rb", + "spec/install/gems/dependency_api_spec.rb", + "spec/install/gemfile/gemspec_spec.rb", + "spec/plugins/install_spec.rb", + "spec/commands/binstubs_spec.rb", + "spec/install/gems/flex_spec.rb", + "spec/runtime/inline_spec.rb", + "spec/commands/post_bundle_message_spec.rb", + "spec/runtime/executable_spec.rb", + "spec/lock/git_spec.rb", + "spec/plugins/hook_spec.rb", + "spec/install/allow_offline_install_spec.rb", + "spec/install/gems/post_install_spec.rb", + "spec/install/gemfile/ruby_spec.rb", + "spec/install/security_policy_spec.rb", + "spec/install/yanked_spec.rb", + "spec/update/gemfile_spec.rb", + "spec/runtime/load_spec.rb", + "spec/plugins/command_spec.rb", + "spec/commands/version_spec.rb", + "spec/install/prereleases_spec.rb", + "spec/bundler/uri_credentials_filter_spec.rb", + "spec/bundler/plugin_spec.rb", + "spec/install/gems/mirror_probe_spec.rb", + "spec/plugins/list_spec.rb", + "spec/bundler/compact_index_client/parser_spec.rb", + "spec/bundler/gem_version_promoter_spec.rb", + "spec/other/cli_dispatch_spec.rb", + "spec/bundler/source/rubygems_spec.rb", + "spec/cache/platform_spec.rb", + "spec/update/gems/fund_spec.rb", + "spec/bundler/stub_specification_spec.rb", + "spec/bundler/retry_spec.rb", + "spec/bundler/installer/spec_installation_spec.rb", + "spec/bundler/spec_set_spec.rb", + "spec/quality_es_spec.rb", + "spec/bundler/index_spec.rb", + "spec/other/cli_man_pages_spec.rb", + ], + windows_c: [ + "spec/commands/newgem_spec.rb", + "spec/commands/exec_spec.rb", + "spec/commands/clean_spec.rb", + "spec/commands/platform_spec.rb", + "spec/cache/git_spec.rb", + "spec/install/gemfile/groups_spec.rb", + "spec/commands/cache_spec.rb", + "spec/commands/check_spec.rb", + "spec/commands/list_spec.rb", + "spec/install/path_spec.rb", + "spec/bundler/cli_spec.rb", + "spec/install/bundler_spec.rb", + "spec/install/git_spec.rb", + "spec/commands/doctor_spec.rb", + "spec/bundler/dsl_spec.rb", + "spec/install/gems/fund_spec.rb", + "spec/install/gems/env_spec.rb", + "spec/bundler/ruby_version_spec.rb", + "spec/bundler/definition_spec.rb", + "spec/install/gemfile/eval_gemfile_spec.rb", + "spec/plugins/source_spec.rb", + "spec/install/gems/dependency_api_fallback_spec.rb", + "spec/plugins/uninstall_spec.rb", + "spec/bundler/plugin/index_spec.rb", + "spec/bundler/bundler_spec.rb", + "spec/bundler/fetcher_spec.rb", + "spec/bundler/source/rubygems/remote_spec.rb", + "spec/bundler/lockfile_parser_spec.rb", + "spec/cache/cache_path_spec.rb", + "spec/bundler/source/git_spec.rb", + "spec/bundler/source_spec.rb", + "spec/commands/ssl_spec.rb", + "spec/bundler/fetcher/compact_index_spec.rb", + "spec/bundler/plugin/api_spec.rb", + "spec/bundler/endpoint_specification_spec.rb", + "spec/bundler/fetcher/index_spec.rb", + "spec/bundler/settings/validator_spec.rb", + "spec/bundler/build_metadata_spec.rb", + "spec/bundler/current_ruby_spec.rb", + "spec/bundler/installer/gem_installer_spec.rb", + "spec/bundler/cli_common_spec.rb", + "spec/bundler/ci_detector_spec.rb", + ], + windows_d: [ + "spec/commands/outdated_spec.rb", + "spec/commands/update_spec.rb", + "spec/lock/lockfile_spec.rb", + "spec/install/deploy_spec.rb", + "spec/install/gemfile/sources_spec.rb", + "spec/runtime/self_management_spec.rb", + "spec/install/gemfile/specific_platform_spec.rb", + "spec/commands/info_spec.rb", + "spec/install/gems/resolving_spec.rb", + "spec/install/gemfile/platform_spec.rb", + "spec/bundler/gem_helper_spec.rb", + "spec/install/global_cache_spec.rb", + "spec/runtime/platform_spec.rb", + "spec/update/gems/post_install_spec.rb", + "spec/install/gems/native_extensions_spec.rb", + "spec/install/force_spec.rb", + "spec/cache/path_spec.rb", + "spec/install/gemspecs_spec.rb", + "spec/commands/help_spec.rb", + "spec/bundler/shared_helpers_spec.rb", + "spec/bundler/settings_spec.rb", + "spec/resolver/basic_spec.rb", + "spec/install/gemfile/force_ruby_platform_spec.rb", + "spec/commands/licenses_spec.rb", + "spec/install/gemfile/lockfile_spec.rb", + "spec/bundler/fetcher/dependency_spec.rb", + "spec/quality_spec.rb", + "spec/bundler/remote_specification_spec.rb", + "spec/install/process_lock_spec.rb", + "spec/install/binstubs_spec.rb", + "spec/bundler/compact_index_client/updater_spec.rb", + "spec/bundler/ui/shell_spec.rb", + "spec/other/ext_spec.rb", + "spec/commands/issue_spec.rb", + "spec/update/path_spec.rb", + "spec/bundler/plugin/api/source_spec.rb", + "spec/install/gems/win32_spec.rb", + "spec/bundler/plugin/dsl_spec.rb", + "spec/runtime/requiring_spec.rb", + "spec/bundler/plugin/events_spec.rb", + "spec/bundler/resolver/candidate_spec.rb", + "spec/bundler/digest_spec.rb", + "spec/bundler/fetcher/gem_remote_fetcher_spec.rb", + ], + }.freeze + end +end From d105709f2b989c03261371306b08f8fc5ccaa680 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 5 Dec 2025 06:33:34 +0900 Subject: [PATCH 171/367] [ruby/rubygems] Add before(:context) hook to warn when spec files are not assigned to any Windows runner group https://github.com/ruby/rubygems/commit/3ddb740969 --- spec/bundler/spec_helper.rb | 7 +++++++ spec/bundler/support/windows_tag_group.rb | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb index f0d6bce006ef20..fad1d4ce3266d4 100644 --- a/spec/bundler/spec_helper.rb +++ b/spec/bundler/spec_helper.rb @@ -139,4 +139,11 @@ def self.ruby=(ruby) metadata[tag] = true end end + + config.before(:context) do |example| + metadata = example.class.metadata + if metadata[:type] != :aruba && metadata.keys.none? {|k| Spec::WindowsTagGroup::EXAMPLE_MAPPINGS.keys.include?(k) } + warn "#{metadata[:file_path]} is not assigned to any Windows runner group. see spec/support/windows_tag_group.rb for details." + end + end end diff --git a/spec/bundler/support/windows_tag_group.rb b/spec/bundler/support/windows_tag_group.rb index 77112a2c6cd1ce..8eb0a749dafbaf 100644 --- a/spec/bundler/support/windows_tag_group.rb +++ b/spec/bundler/support/windows_tag_group.rb @@ -1,5 +1,10 @@ # frozen_string_literal: true +# This group classifies test files into 4 groups by running `bin/rspec --profile 10000` +# to ensure balanced execution times. When adding new test files, it is recommended to +# re-aggregate and adjust the groups to keep them balanced. +# For now, please add new files to group 'windows_d'. + module Spec module WindowsTagGroup EXAMPLE_MAPPINGS = { From 02cddcc2be3150fad1da37ebdeb6e2d9ec29306c Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 5 Dec 2025 02:48:05 +0900 Subject: [PATCH 172/367] Ractor.shareable_proc(&pr) should copy pr `pr` should not change on this method. --- bootstraptest/test_ractor.rb | 10 ++++++++++ ractor.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 7d09ac52838f66..bec742da4be0d5 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -157,6 +157,16 @@ Ractor.shareable_proc(&closure).call } +# Ractor.make_shareable makes a copy of given Proc +assert_equal '[true, true]', %q{ + pr1 = Proc.new do + self + end + pr2 = Ractor.shareable_proc(&pr1) + + [pr1.call == self, pr2.call == nil] +} + # Ractor::IsolationError cases assert_equal '3', %q{ ok = 0 diff --git a/ractor.c b/ractor.c index b7c7d9467924b6..9fec70a1dfab71 100644 --- a/ractor.c +++ b/ractor.c @@ -2376,7 +2376,7 @@ ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_la } else { VALUE proc = is_lambda ? rb_block_lambda() : rb_block_proc(); - return rb_proc_ractor_make_shareable(proc, replace_self); + return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self); } } From d3a9a17b6f908a64d8cf7b35c5e7337be3fc7cc5 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 5 Dec 2025 11:00:17 +0900 Subject: [PATCH 173/367] Skip Windows runner group warning under ruby/ruby repo --- spec/bundler/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb index fad1d4ce3266d4..a79e33fbb01684 100644 --- a/spec/bundler/spec_helper.rb +++ b/spec/bundler/spec_helper.rb @@ -145,5 +145,5 @@ def self.ruby=(ruby) if metadata[:type] != :aruba && metadata.keys.none? {|k| Spec::WindowsTagGroup::EXAMPLE_MAPPINGS.keys.include?(k) } warn "#{metadata[:file_path]} is not assigned to any Windows runner group. see spec/support/windows_tag_group.rb for details." end - end + end unless Spec::Path.ruby_core? end From e822209d3e3dfde39a7d6818951869606ae8cbf0 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 5 Dec 2025 14:16:25 +0900 Subject: [PATCH 174/367] Update NEWS.md for Ruby 4.0.0 (#15369) I extracted the relevant descriptions from the draft NEWS.md that hsbt-san had Gemini generate by analyzing `git log`. Co-authored-by: Hiroshi SHIBATA Co-authored-by: Alan Wu Co-authored-by: Jeremy Evans Co-Authored-By: Earlopain <14981592+Earlopain@users.noreply.github.com> --- NEWS.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 89f45deb99cf7b..84f8eaaaac2116 100644 --- a/NEWS.md +++ b/NEWS.md @@ -64,6 +64,12 @@ Note: We're only listing outstanding class updates. Also, `Binding#local_variable_get` and `Binding#local_variable_set` reject to handle numbered parameters. [[Bug #21049]] +* File + + * `File::Stat#birthtime` is now available on Linux via the statx + system call when supported by the kernel and filesystem. + [[Feature #21205]] + * IO * `IO.select` accepts `Float::INFINITY` as a timeout argument. @@ -76,10 +82,20 @@ Note: We're only listing outstanding class updates. * `Math.log1p` and `Math.expm1` are added. [[Feature #21527]] -* Socket +* Method - * `Socket.tcp` & `TCPSocket.new` accepts `open_timeout` as a keyword argument to specify - the timeout for the initial connection. [[Feature #21347]] + * `Method#source_location`, `Proc#source_location`, and + `UnboundMethod#source_location` now return extended location + information with 5 elements: `[path, start_line, start_column, + end_line, end_column]`. The previous 2-element format `[path, + line]` can still be obtained by calling `.take(2)` on the result. + [[Feature #6012]] + +* Proc + + * `Proc#parameters` now shows anonymous optional parameters as `[:opt]` + instead of `[:opt, nil]`, making the output consistent with when the + anonymous parameter is required. [[Bug #20974]] * Ractor @@ -127,11 +143,41 @@ Note: We're only listing outstanding class updates. to make shareable Proc or lambda. [[Feature #21550]], [[Feature #21557]] +* Range + + * `Range#to_set` and `Enumerator#to_set` now perform size checks to prevent + issues with endless ranges. [[Bug #21654]] + + * `Range#overlap?` now correctly handles infinite (unbounded) ranges. + [[Bug #21185]] + + * `Range#max` behavior on beginless integer ranges has been fixed. + [[Bug #21174]] [[Bug #21175]] + +* Ruby + + * A new toplevel module `Ruby` has been defined, which contains + Ruby-related constants. This module was reserved in Ruby 3.4 + and is now officially defined. [[Feature #20884]] + * `Set` * `Set` is now a core class, instead of an autoloaded stdlib class. [[Feature #21216]] + * `Set#inspect` now returns a string suitable for `eval`, using the + `Set[]` syntax (e.g., `Set[1, 2, 3]` instead of + `#`). This makes it consistent with other core + collection classes like Array and Hash. [[Feature #21389]] + + * Passing arguments to `Set#to_set` and `Enumerable#to_set` is now deprecated. + [[Feature #21390]] + +* Socket + + * `Socket.tcp` & `TCPSocket.new` accepts an `open_timeout` keyword argument to specify + the timeout for the initial connection. [[Feature #21347]] + * String * Update Unicode to Version 17.0.0 and Emoji Version 17.0. @@ -240,6 +286,11 @@ The following bundled gems are updated. ## Supported platforms +* Windows + + * Dropped support for MSVC versions older than 14.0 (_MSC_VER 1900). + This means Visual Studio 2015 or later is now required. + ## Compatibility issues * The following methods were removed from Ractor due to the addition of `Ractor::Port`: @@ -253,6 +304,14 @@ The following bundled gems are updated. * `ObjectSpace._id2ref` is deprecated. [[Feature #15408]] +* `Process::Status#&` and `Process::Status#>>` have been removed. + They were deprecated in Ruby 3.3. [[Bug #19868]] + +* `rb_path_check` has been removed. This function was used for + `$SAFE` path checking which was removed in Ruby 2.7, + and was already deprecated,. + [[Feature #20971]] + ## Stdlib compatibility issues * CGI library is removed from the default gems. Now we only provide `cgi/escape` for @@ -320,17 +379,26 @@ A lot of work has gone into making Ractors more stable, performant, and usable. * `--rjit` is removed. We will move the implementation of the third-party JIT API to the [ruby/rjit](https://github.com/ruby/rjit) repository. +[Feature #6012]: https://bugs.ruby-lang.org/issues/6012 [Feature #15408]: https://bugs.ruby-lang.org/issues/15408 [Feature #17473]: https://bugs.ruby-lang.org/issues/17473 [Feature #18455]: https://bugs.ruby-lang.org/issues/18455 [Feature #19630]: https://bugs.ruby-lang.org/issues/19630 +[Bug #19868]: https://bugs.ruby-lang.org/issues/19868 [Feature #19908]: https://bugs.ruby-lang.org/issues/19908 [Feature #20610]: https://bugs.ruby-lang.org/issues/20610 [Feature #20724]: https://bugs.ruby-lang.org/issues/20724 +[Feature #20884]: https://bugs.ruby-lang.org/issues/20884 [Feature #20925]: https://bugs.ruby-lang.org/issues/20925 +[Feature #20971]: https://bugs.ruby-lang.org/issues/20971 +[Bug #20974]: https://bugs.ruby-lang.org/issues/20974 [Feature #21047]: https://bugs.ruby-lang.org/issues/21047 [Bug #21049]: https://bugs.ruby-lang.org/issues/21049 [Feature #21166]: https://bugs.ruby-lang.org/issues/21166 +[Bug #21174]: https://bugs.ruby-lang.org/issues/21174 +[Bug #21175]: https://bugs.ruby-lang.org/issues/21175 +[Bug #21185]: https://bugs.ruby-lang.org/issues/21185 +[Feature #21205]: https://bugs.ruby-lang.org/issues/21205 [Feature #21216]: https://bugs.ruby-lang.org/issues/21216 [Feature #21219]: https://bugs.ruby-lang.org/issues/21219 [Feature #21258]: https://bugs.ruby-lang.org/issues/21258 @@ -339,6 +407,9 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #21287]: https://bugs.ruby-lang.org/issues/21287 [Feature #21347]: https://bugs.ruby-lang.org/issues/21347 [Feature #21360]: https://bugs.ruby-lang.org/issues/21360 +[Feature #21389]: https://bugs.ruby-lang.org/issues/21389 +[Feature #21390]: https://bugs.ruby-lang.org/issues/21390 [Feature #21527]: https://bugs.ruby-lang.org/issues/21527 [Feature #21550]: https://bugs.ruby-lang.org/issues/21550 [Feature #21557]: https://bugs.ruby-lang.org/issues/21557 +[Bug #21654]: https://bugs.ruby-lang.org/issues/21654 From 5f6b31c23ff1eefdf3cd5807ce5f80abf24eb1b2 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 5 Dec 2025 06:20:39 +0100 Subject: [PATCH 175/367] Correctly handle `Process.fork` with an active `Fiber.scheduler`. (#15385) In the child process, nullify the current fiber scheduler and set the current fiber to blocking. --- cont.c | 2 ++ test/fiber/test_scheduler.rb | 57 ++++++++++++++++++++++++++++++++++++ thread.c | 1 + 3 files changed, 60 insertions(+) diff --git a/cont.c b/cont.c index 50e8ffb3498ec4..acfcefc81e89f2 100644 --- a/cont.c +++ b/cont.c @@ -3371,6 +3371,8 @@ rb_fiber_atfork(rb_thread_t *th) th->root_fiber = th->ec->fiber_ptr; } th->root_fiber->prev = 0; + th->root_fiber->blocking = 1; + th->blocking = 1; } } #endif diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 7c77bd8cf0dba4..97ce94bd270b69 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -226,4 +226,61 @@ def test_condition_variable thread.join assert_kind_of RuntimeError, error end + + def test_post_fork_scheduler_reset + omit 'fork not supported' unless Process.respond_to?(:fork) + + forked_scheduler_state = nil + thread = Thread.new do + r, w = IO.pipe + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + forked_pid = fork do + r.close + w << (Fiber.scheduler ? 'set' : 'reset') + w.close + end + w.close + forked_scheduler_state = r.read + Process.wait(forked_pid) + ensure + r.close rescue nil + w.close rescue nil + end + thread.join + assert_equal 'reset', forked_scheduler_state + ensure + thread.kill rescue nil + end + + def test_post_fork_fiber_blocking + omit 'fork not supported' unless Process.respond_to?(:fork) + + fiber_blocking_state = nil + thread = Thread.new do + r, w = IO.pipe + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + forked_pid = nil + Fiber.schedule do + forked_pid = fork do + r.close + w << (Fiber.current.blocking? ? 'blocking' : 'nonblocking') + w.close + end + end + w.close + fiber_blocking_state = r.read + Process.wait(forked_pid) + ensure + r.close rescue nil + w.close rescue nil + end + thread.join + assert_equal 'blocking', fiber_blocking_state + ensure + thread.kill rescue nil + end end diff --git a/thread.c b/thread.c index 6e5c8e89ff075a..5f592a42562adf 100644 --- a/thread.c +++ b/thread.c @@ -4997,6 +4997,7 @@ rb_thread_atfork(void) rb_threadptr_pending_interrupt_clear(th); rb_thread_atfork_internal(th, terminate_atfork_i); th->join_list = NULL; + th->scheduler = Qnil; rb_fiber_atfork(th); /* We don't want reproduce CVE-2003-0900. */ From 479185daa140f9d5894e9c254f2f881d36df1ddc Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Thu, 4 Dec 2025 21:52:55 -0800 Subject: [PATCH 176/367] Add Set C API to news Also, don't use backticks around Set in the top level of the Core classes updates section, as other classes/modules do not use that format. --- NEWS.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 84f8eaaaac2116..53ed47cb8c3265 100644 --- a/NEWS.md +++ b/NEWS.md @@ -160,7 +160,7 @@ Note: We're only listing outstanding class updates. Ruby-related constants. This module was reserved in Ruby 3.4 and is now officially defined. [[Feature #20884]] -* `Set` +* Set * `Set` is now a core class, instead of an autoloaded stdlib class. [[Feature #21216]] @@ -342,6 +342,20 @@ The following bundled gems are updated. `IO` objects share the same file descriptor, closing one does not affect the other. [[Feature #18455]] +* Set + + * A C API for `Set` has been added. The following methods are supported: + [[Feature #21459]] + + * `rb_set_foreach` + * `rb_set_new` + * `rb_set_new_capa` + * `rb_set_lookup` + * `rb_set_add` + * `rb_set_clear` + * `rb_set_delete` + * `rb_set_size` + ## Implementation improvements ### Ractor @@ -409,6 +423,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #21360]: https://bugs.ruby-lang.org/issues/21360 [Feature #21389]: https://bugs.ruby-lang.org/issues/21389 [Feature #21390]: https://bugs.ruby-lang.org/issues/21390 +[Feature #21459]: https://bugs.ruby-lang.org/issues/21459 [Feature #21527]: https://bugs.ruby-lang.org/issues/21527 [Feature #21550]: https://bugs.ruby-lang.org/issues/21550 [Feature #21557]: https://bugs.ruby-lang.org/issues/21557 From b35aff5813193a1f676bb1ff7b390797a892ad4e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 12:33:03 +0900 Subject: [PATCH 177/367] [DOC] Centerize Variable, English, and Constant columns --- doc/language/globals.md | 106 ++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/doc/language/globals.md b/doc/language/globals.md index b9315f5ff975e4..221ad17e44e0c3 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -14,70 +14,70 @@ require 'English' ### Exceptions -| Variable | English | Contains | -|-------------|-------------------|----------------------------------------------------| -| `$!` | `$ERROR_INFO` | Exception object; set by Kernel#raise. | -| `$@` | `$ERROR_POSITION` | Array of backtrace positions; set by Kernel#raise. | +| Variable | English | Contains | +|:--------:|:-----------------:|----------------------------------------------------| +| `$!` | `$ERROR_INFO` | Exception object; set by Kernel#raise. | +| `$@` | `$ERROR_POSITION` | Array of backtrace positions; set by Kernel#raise. | ### Pattern Matching -| Variable | English | Contains | -|---------------|---------------------|--------------------------------------------------| -| `$~` | `$LAST_MATCH_INFO` | MatchData object; set by matcher method. | -| `$&` | `$MATCH` | Matched substring; set by matcher method. | -| `` $` `` | `$PRE_MATCH` | Substring left of match; set by matcher method. | -| `$'` | `$POST_MATCH` | Substring right of match; set by matcher method. | -| `$+` | `$LAST_PAREN_MATCH` | Last group matched; set by matcher method. | -| `$1` | | First group matched; set by matcher method. | -| `$2` | | Second group matched; set by matcher method. | +| Variable | English | Contains | +|:-------------:|:-------------------:|--------------------------------------------------| +| `$~` | `$LAST_MATCH_INFO` | MatchData object; set by matcher method. | +| `$&` | `$MATCH` | Matched substring; set by matcher method. | +| `` $` `` | `$PRE_MATCH` | Substring left of match; set by matcher method. | +| `$'` | `$POST_MATCH` | Substring right of match; set by matcher method. | +| `$+` | `$LAST_PAREN_MATCH` | Last group matched; set by matcher method. | +| `$1` | | First group matched; set by matcher method. | +| `$2` | | Second group matched; set by matcher method. | | $_n_ | | nth group matched; set by matcher method. | ### Separators -| Variable | English | Contains | -|----------|----------------------------|--------------------------------------------| -| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | -| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | +| Variable | English | Contains | +|:--------:|:--------------------------:|--------------------------------------------| +| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | +| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | ### Streams -| Variable | English | Contains | -|-----------|-----------------------------|-----------------------------------------------| +| Variable | English | Contains | +|:---------:|:---------------------------:|-----------------------------------------------| | `$stdin` | | Standard input stream; initially `STDIN`. | | `$stdout` | | Standard input stream; initially `STDIOUT`. | | `$stderr` | | Standard input stream; initially `STDERR`. | -| `$<` | `$DEFAULT_INPUT` | Default standard input; `ARGF` or `$stdin`. | -| `$>` | `$DEFAULT_OUTPUT` | Default standard output; initially `$stdout`. | -| `$.` | `$INPUT_LINE_NUMBER`, `$NR` | Input position of most recently read stream. | -| `$_` | `$LAST_READ_LINE` | String from most recently read stream. | +| `$<` | `$DEFAULT_INPUT` | Default standard input; `ARGF` or `$stdin`. | +| `$>` | `$DEFAULT_OUTPUT` | Default standard output; initially `$stdout`. | +| `$.` | `$INPUT_LINE_NUMBER`, `$NR` | Input position of most recently read stream. | +| `$_` | `$LAST_READ_LINE` | String from most recently read stream. | ### Processes -| Variable | English | Contains | -|---------------------------|-----------------------|--------------------------------------------------------| -| `$0` | | Initially, the name of the executing program. | -| `$*` | `$ARGV` | Points to the `ARGV` array. | -| `$$` | `$PROCESS_ID`, `$PID` | Process ID of the current process. | -| `$?` | `$CHILD_STATUS` | Process::Status of most recently exited child process. | +| Variable | English | Contains | +|:-------------------------:|:---------------------:|--------------------------------------------------------| +| `$0` | | Initially, the name of the executing program. | +| `$*` | `$ARGV` | Points to the `ARGV` array. | +| `$$` | `$PROCESS_ID`, `$PID` | Process ID of the current process. | +| `$?` | `$CHILD_STATUS` | Process::Status of most recently exited child process. | | `$LOAD_PATH`, `$:`, `$-I` | | Array of paths to be searched. | | `$LOADED_FEATURES`, `$"` | | Array of paths to loaded files. | ### Debugging -| Variable | English | Contains | -|-------------|---------|--------------------------------------------------------| +| Variable | English | Contains | +|:-----------:|:-------:|--------------------------------------------------------| | `$FILENAME` | | The value returned by method ARGF.filename. | -| `$DEBUG` | | Initially, whether option `-d` or `--debug` was given. | +| `$DEBUG` | | Initially, whether option `-d` or `--debug` was given. | | `$VERBOSE` | | Initially, whether option `-V` or `-W` was given. | ### Other Variables | Variable | English | Contains | -|----------|---------|------------------------------------------------| -| `$-a` | | Whether option `-a` was given. | -| `$-i` | | Extension given with command-line option `-i`. | -| `$-l` | | Whether option `-l` was given. | -| `$-p` | | Whether option `-p` was given. | +|:--------:|:-------:|------------------------------------------------| +| `$-a` | | Whether option `-a` was given. | +| `$-i` | | Extension given with command-line option `-i`. | +| `$-l` | | Whether option `-l` was given. | +| `$-p` | | Whether option `-p` was given. | ## Exceptions @@ -374,33 +374,33 @@ Whether command-line option `-p` was given; read-only. ### Streams | Constant | Contains | -|----------|-------------------------| +|:--------:|-------------------------| | `STDIN` | Standard input stream. | | `STDOUT` | Standard output stream. | | `STDERR` | Standard error stream. | ### Environment -| Constant | Contains | -|-----------------------|-------------------------------------------------------------------------------| -| `ENV` | Hash of current environment variable names and values. | -| `ARGF` | String concatenation of files given on the command line, or `$stdin` if none. | -| `ARGV` | Array of the given command-line arguments. | -| `TOPLEVEL_BINDING` | Binding of the top level scope. | -| `RUBY_VERSION` | String Ruby version. | -| `RUBY_RELEASE_DATE` | String Ruby release date. | -| `RUBY_PLATFORM` | String Ruby platform. | -| `RUBY_PATCH_LEVEL` | String Ruby patch level. | -| `RUBY_REVISION` | String Ruby revision. | -| `RUBY_COPYRIGHT` | String Ruby copyright. | -| `RUBY_ENGINE` | String Ruby engine. | +| Constant | Contains | +|:---------------------:|-------------------------------------------------------------------------------| +| `ENV` | Hash of current environment variable names and values. | +| `ARGF` | String concatenation of files given on the command line, or `$stdin` if none. | +| `ARGV` | Array of the given command-line arguments. | +| `TOPLEVEL_BINDING` | Binding of the top level scope. | +| `RUBY_VERSION` | String Ruby version. | +| `RUBY_RELEASE_DATE` | String Ruby release date. | +| `RUBY_PLATFORM` | String Ruby platform. | +| `RUBY_PATCH_LEVEL` | String Ruby patch level. | +| `RUBY_REVISION` | String Ruby revision. | +| `RUBY_COPYRIGHT` | String Ruby copyright. | +| `RUBY_ENGINE` | String Ruby engine. | | `RUBY_ENGINE_VERSION` | String Ruby engine version. | -| `RUBY_DESCRIPTION` | String Ruby description. | +| `RUBY_DESCRIPTION` | String Ruby description. | ### Embedded Data | Constant | Contains | -|----------|--------------------------------------------------------------------| +|:--------:|--------------------------------------------------------------------| | `DATA` | File containing embedded data (lines following `__END__`, if any). | ## Streams From 1e7373ef3061a3e0f3010ee580e5d4f80baf2e1a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 13:02:22 +0900 Subject: [PATCH 178/367] [DOC] Describe the global variables set by command line options These variables are set by command line options, but it is deprecated to assign them any value other than nil in ruby code. --- doc/language/globals.md | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/doc/language/globals.md b/doc/language/globals.md index 221ad17e44e0c3..7030cc1bfd7b0c 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -34,10 +34,10 @@ require 'English' ### Separators -| Variable | English | Contains | -|:--------:|:--------------------------:|--------------------------------------------| -| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | -| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | +| Variable | English | Contains | +|:-----------:|:--------------------------:|--------------------------------------------| +| `$/`, `$-0` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | +| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | ### Streams @@ -72,12 +72,13 @@ require 'English' ### Other Variables -| Variable | English | Contains | -|:--------:|:-------:|------------------------------------------------| -| `$-a` | | Whether option `-a` was given. | -| `$-i` | | Extension given with command-line option `-i`. | -| `$-l` | | Whether option `-l` was given. | -| `$-p` | | Whether option `-p` was given. | +| Variable | English | Contains | +|:-----------:|:-------:|------------------------------------------------| +| `$-F`, `$;` | | Separator given with command-line option `-F`. | +| `$-a` | | Whether option `-a` was given. | +| `$-i` | | Extension given with command-line option `-i`. | +| `$-l` | | Whether option `-l` was given. | +| `$-p` | | Whether option `-p` was given. | ## Exceptions @@ -174,6 +175,10 @@ No \English. ### `$/` (Input Record Separator) An input record separator, initially newline. +Set by the command-line option `-0`. + +Setting to non-nil value by other than the command-line option is +deprecated. English - `$INPUT_RECORD_SEPARATOR`, `$RS`. @@ -182,6 +187,10 @@ Aliased as `$-0`. ### `$\` (Output Record Separator) An output record separator, initially `nil`. +Copied from `$/` when the command-line option `-l` is given. + +Setting to non-nil value by other than the command-line option is +deprecated. English - `$OUTPUT_RECORD_SEPARATOR`, `$ORS`. @@ -340,6 +349,16 @@ Aliased as `$-v` and `$-w`. ## Other Variables +### `$-F` + +The default field separator in String#split; must be a String or a +Regexp, and can be set with command-line option `-F`. + +Setting to non-nil value by other than the command-line option is +deprecated. + +Aliased as `$;`. + ### `$-a` Whether command-line option `-a` was given; read-only. @@ -365,8 +384,6 @@ Whether command-line option `-p` was given; read-only. ### `$,` -### `$;` - # Pre-Defined Global Constants ## Summary @@ -401,7 +418,7 @@ Whether command-line option `-p` was given; read-only. | Constant | Contains | |:--------:|--------------------------------------------------------------------| -| `DATA` | File containing embedded data (lines following `__END__`, if any). | +| `DATA` | File containing embedded data (lines following `__END__`, if any). | ## Streams From 1cad20e2d5179b283cbb1aabe2496449f8edcbcb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 16:12:49 +0900 Subject: [PATCH 179/367] [DOC] Describe `$F` This variation is used when `-a` option is given. --- doc/language/globals.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/language/globals.md b/doc/language/globals.md index 7030cc1bfd7b0c..b22363f1da1b71 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -79,6 +79,7 @@ require 'English' | `$-i` | | Extension given with command-line option `-i`. | | `$-l` | | Whether option `-l` was given. | | `$-p` | | Whether option `-p` was given. | +| `$F` | | Array of `$_` split by `$-F`. | ## Exceptions @@ -378,6 +379,12 @@ Whether command-line option `-l` was set; read-only. Whether command-line option `-p` was given; read-only. +### `$F` + +If the command line option `-a` is given, the array obtained by +splitting `$_` by `$-F` is assigned at the start of each `-l`/`-p` +loop. + ## Deprecated ### `$=` From 95ea3bd5ee9abc9e32c68e6a4fe2d795d853d907 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 12:32:35 +0100 Subject: [PATCH 180/367] [ruby/timeout] Only the timeout method should be public on the Timeout module https://github.com/ruby/timeout/commit/cd51eac3ca --- lib/timeout.rb | 10 +++++++--- test/test_timeout.rb | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index e1f0a4a78c6f59..f173d028a39d0a 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -133,6 +133,7 @@ def self.ensure_timeout_thread_created end end end + private_class_method :ensure_timeout_thread_created # We keep a private reference so that time mocking libraries won't break # Timeout. @@ -167,7 +168,7 @@ def self.ensure_timeout_thread_created # Note that this is both a method of module Timeout, so you can include # Timeout into your classes so they have a #timeout method, as well as # a module method, so you can call it directly as Timeout.timeout(). - def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ + def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return yield(sec) if sec == nil or sec.zero? raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec @@ -177,7 +178,7 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return scheduler.timeout_after(sec, klass || Error, message, &block) end - Timeout.ensure_timeout_thread_created + ensure_timeout_thread_created perform = Proc.new do |exc| request = Request.new(Thread.current, sec, exc, message) QUEUE_MUTEX.synchronize do @@ -197,5 +198,8 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ Error.handle_timeout(message, &perform) end end - module_function :timeout + + private def timeout(*args, &block) + Timeout.timeout(*args, &block) + end end diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 01156867b05609..e367df757cc9b2 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -4,6 +4,12 @@ class TestTimeout < Test::Unit::TestCase + def test_public_methods + assert_equal [:timeout], Timeout.private_instance_methods(false) + assert_equal [], Timeout.public_instance_methods(false) + assert_equal [:timeout], Timeout.singleton_class.public_instance_methods(false) + end + def test_work_is_done_in_same_thread_as_caller assert_equal Thread.current, Timeout.timeout(10){ Thread.current } end From bf2b9c09473ee1a49766c6d7c6e1d683236a13ee Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 5 Dec 2025 02:52:38 +0900 Subject: [PATCH 181/367] [ruby/openssl] asn1: reorder declarations Move variable declarations for OpenSSL::ASN1 classes to the top of the file. asn1time_to_time() will need eASN1Error in the next patch. https://github.com/ruby/openssl/commit/6c0ef87897 --- ext/openssl/ossl_asn1.c | 92 ++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index cb13ac6ecf876d..91acf5c6522740 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -9,9 +9,48 @@ */ #include "ossl.h" -static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, - int depth, int yield, long *num_read); -static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); +/********/ +/* + * ASN1 module + */ +#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) +#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) +#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) +#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) +#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) + +#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) +#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) +#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) +#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) +#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) + +VALUE mASN1; +static VALUE eASN1Error; + +VALUE cASN1Data; +static VALUE cASN1Primitive; +static VALUE cASN1Constructive; + +static VALUE cASN1EndOfContent; +static VALUE cASN1Boolean; /* BOOLEAN */ +static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ +static VALUE cASN1BitString; /* BIT STRING */ +static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ +static VALUE cASN1NumericString, cASN1PrintableString; +static VALUE cASN1T61String, cASN1VideotexString; +static VALUE cASN1IA5String, cASN1GraphicString; +static VALUE cASN1ISO64String, cASN1GeneralString; +static VALUE cASN1UniversalString, cASN1BMPString; +static VALUE cASN1Null; /* NULL */ +static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ +static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ +static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ + +static VALUE sym_IMPLICIT, sym_EXPLICIT; +static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; +static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; +static ID id_each; /* * DATE conversion @@ -190,49 +229,6 @@ ossl_asn1obj_to_string_long_name(const ASN1_OBJECT *obj) return ossl_asn1obj_to_string_oid(obj); } -/********/ -/* - * ASN1 module - */ -#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) -#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) -#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) -#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) -#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) - -#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) -#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) -#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) -#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) -#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) - -VALUE mASN1; -static VALUE eASN1Error; - -VALUE cASN1Data; -static VALUE cASN1Primitive; -static VALUE cASN1Constructive; - -static VALUE cASN1EndOfContent; -static VALUE cASN1Boolean; /* BOOLEAN */ -static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ -static VALUE cASN1BitString; /* BIT STRING */ -static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ -static VALUE cASN1NumericString, cASN1PrintableString; -static VALUE cASN1T61String, cASN1VideotexString; -static VALUE cASN1IA5String, cASN1GraphicString; -static VALUE cASN1ISO64String, cASN1GeneralString; -static VALUE cASN1UniversalString, cASN1BMPString; -static VALUE cASN1Null; /* NULL */ -static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ -static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ -static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ - -static VALUE sym_IMPLICIT, sym_EXPLICIT; -static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; -static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; -static ID id_each; - /* * Ruby to ASN1 converters */ @@ -777,6 +773,10 @@ ossl_asn1data_to_der(VALUE self) } } +static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); +static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, + int depth, int yield, long *num_read); + static VALUE int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, VALUE tc, long *num_read) From f179885d3c454c6a98c23b2a977480657bb0f676 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 28 Feb 2025 03:10:35 +0900 Subject: [PATCH 182/367] [ruby/openssl] asn1: use ASN1_TIME_to_tm() to decode UTCTime and GeneralizedTime The current logic relies on sscanf() and error checks are almost entirely missing. It also assumes that ASN1_STRING contents are NUL terminated, which is undocumented and not guaranteed for all valid ASN1_TIME objects. Switch to using ASN1_TIME_to_tm() added in OpenSSL 1.1.1. It is also supported by LibreSSL and AWS-LC. In the long term, we may want to replace ASN1_TIME_to_tm() with a hand-rolled decoder, since the function is intended for a specific use-case. It is too permissive for strict DER, yet still does not support all valid DER inputs and silently drops information such as fractional seconds. However, it handles everything that the current sscanf() code could handle. https://github.com/ruby/openssl/commit/73484f6794 --- ext/openssl/ossl_asn1.c | 72 ++++++++++++++------------------------- test/openssl/test_asn1.rb | 59 +++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 91acf5c6522740..b21a79484ccc4a 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -55,57 +55,35 @@ static ID id_each; /* * DATE conversion */ +static VALUE +time_utc_new(VALUE args) +{ + return rb_funcallv(rb_cTime, rb_intern("utc"), 6, (VALUE *)args); +} + +static VALUE +time_utc_new_rescue(VALUE args, VALUE exc) +{ + rb_raise(eASN1Error, "invalid time"); +} + VALUE asn1time_to_time(const ASN1_TIME *time) { struct tm tm; - VALUE argv[6]; - int count; - - memset(&tm, 0, sizeof(struct tm)); - - switch (time->type) { - case V_ASN1_UTCTIME: - count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - - if (count == 5) { - tm.tm_sec = 0; - } else if (count != 6) { - ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"", - time->data); - } - if (tm.tm_year < 50) { - tm.tm_year += 2000; - } else { - tm.tm_year += 1900; - } - break; - case V_ASN1_GENERALIZEDTIME: - count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - if (count == 5) { - tm.tm_sec = 0; - } - else if (count != 6) { - ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"", - time->data); - } - break; - default: - rb_warning("unknown time format"); - return Qnil; - } - argv[0] = INT2NUM(tm.tm_year); - argv[1] = INT2NUM(tm.tm_mon); - argv[2] = INT2NUM(tm.tm_mday); - argv[3] = INT2NUM(tm.tm_hour); - argv[4] = INT2NUM(tm.tm_min); - argv[5] = INT2NUM(tm.tm_sec); - - return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); + if (!ASN1_TIME_to_tm(time, &tm)) + ossl_raise(eASN1Error, "ASN1_TIME_to_tm"); + + VALUE args[] = { + INT2NUM(tm.tm_year + 1900), + INT2NUM(tm.tm_mon + 1), + INT2NUM(tm.tm_mday), + INT2NUM(tm.tm_hour), + INT2NUM(tm.tm_min), + INT2NUM(tm.tm_sec), + }; + return rb_rescue2(time_utc_new, (VALUE)args, time_utc_new_rescue, Qnil, + rb_eArgError, 0); } static VALUE diff --git a/test/openssl/test_asn1.rb b/test/openssl/test_asn1.rb index df8b0accb36c84..5978ecf6736e10 100644 --- a/test/openssl/test_asn1.rb +++ b/test/openssl/test_asn1.rb @@ -426,17 +426,28 @@ def test_utctime OpenSSL::ASN1::UTCTime.new(Time.new(2049, 12, 31, 23, 0, 0, "-04:00")).to_der } - # not implemented + # UTC offset (BER): ASN1_TIME_to_tm() may or may not support it # decode_test B(%w{ 17 11 }) + "500908234339+0930".b, # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30")) # decode_test B(%w{ 17 0F }) + "5009082343-0930".b, # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30")) - # assert_raise(OpenSSL::ASN1::ASN1Error) { - # OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b) - # } - # assert_raise(OpenSSL::ASN1::ASN1Error) { - # OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b) - # } + + # Seconds is omitted (BER) + # decode_test B(%w{ 18 0D }) + "201612081934Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0)) + + # Fractional seconds is not allowed in UTCTime + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 17 0F }) + "160908234339.5Z".b) + } + + # Missing "Z" + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b) + } + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b) + } end def test_generalizedtime @@ -444,24 +455,46 @@ def test_generalizedtime OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29)) encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b, OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39)) - # not implemented + + # Fractional seconds (DER). Not supported by ASN1_TIME_to_tm() + # because struct tm cannot store it. + # encode_decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5)) + + # UTC offset (BER): ASN1_TIME_to_tm() may or may not support it # decode_test B(%w{ 18 13 }) + "20161208193439+0930".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30")) # decode_test B(%w{ 18 11 }) + "201612081934-0930".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30")) # decode_test B(%w{ 18 11 }) + "201612081934-09".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00")) + + # Minutes and seconds are omitted (BER) + # decode_test B(%w{ 18 0B }) + "2016120819Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 0, 0)) + # Fractional hours (BER) # decode_test B(%w{ 18 0D }) + "2016120819.5Z".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0)) + # Fractional hours with "," as the decimal separator (BER) # decode_test B(%w{ 18 0D }) + "2016120819,5Z".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0)) + + # Seconds is omitted (BER) + # decode_test B(%w{ 18 0D }) + "201612081934Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0)) + # Fractional minutes (BER) # decode_test B(%w{ 18 0F }) + "201612081934.5Z".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30)) - # decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b, - # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5)) - # assert_raise(OpenSSL::ASN1::ASN1Error) { - # OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b) - # } + + # Missing "Z" + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 18 0F }) + "20161208193429Y".b) + } + + # Encoding year out of range + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1::GeneralizedTime.new(Time.utc(10000, 9, 8, 23, 43, 39)).to_der + } end def test_basic_asn1data From ea415e9636d3be8890d5dc97cf0b67fc95403a46 Mon Sep 17 00:00:00 2001 From: Daisuke Aritomo Date: Thu, 4 Dec 2025 21:52:53 +0900 Subject: [PATCH 183/367] [ruby/net-http] open: Never call Timeout.timeout in rescue clause The try-open_timeout-then-fallback-to-timeout introduced in https://github.com/ruby/net-http/commit/1903cedd8cd0 works well, but when it errors due to any reason in Rubies which do not support `open_timeout`, it spits the rescued ArgumentError that is unrelated to user code and not actionable. Net::HTTP.start('foo.bar', 80) /.../net-http-0.8.0/lib/net/http.rb:1691:in 'TCPSocket#initialize': Failed to open TCP connection to foo.bar:80 (getaddrinfo(3): nodename nor servname provided, or not known) (Socket::ResolutionError) from /.../net-http-0.8.0/lib/net/http.rb:1691:in 'IO.open' from /.../net-http-0.8.0/lib/net/http.rb:1691:in 'block in Net::HTTP#connect' from /.../timeout-0.4.4/lib/timeout.rb:188:in 'block in Timeout.timeout' from /.../timeout-0.4.4/lib/timeout.rb:195:in 'Timeout.timeout' from /.../net-http-0.8.0/lib/net/http.rb:1690:in 'Net::HTTP#connect' from /.../net-http-0.8.0/lib/net/http.rb:1655:in 'Net::HTTP#do_start' from /.../net-http-0.8.0/lib/net/http.rb:1635:in 'Net::HTTP#start' from /.../net-http-0.8.0/lib/net/http.rb:1064:in 'Net::HTTP.start' (snip) /.../net-http-0.8.0/lib/net/http.rb:1682:in 'TCPSocket#initialize': unknown keyword: :open_timeout (ArgumentError) sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ from /.../net-http-0.8.0/lib/net/http.rb:1682:in 'IO.open' from /.../net-http-0.8.0/lib/net/http.rb:1682:in 'Net::HTTP#connect' from /.../net-http-0.8.0/lib/net/http.rb:1655:in 'Net::HTTP#do_start' from /.../net-http-0.8.0/lib/net/http.rb:1635:in 'Net::HTTP#start' from /.../net-http-0.8.0/lib/net/http.rb:1064:in 'Net::HTTP.start' (snip) ... 8 levels... This patch suppresses the ArgumentError by moving the retry out of the rescue clause. https://github.com/ruby/net-http/commit/86232d62f5 --- lib/net/http.rb | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 4205d414609bf0..8a4ff481872abc 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1674,30 +1674,7 @@ def connect debug "opening connection to #{conn_addr}:#{conn_port}..." begin - s = - case @tcpsocket_supports_open_timeout - when nil, true - begin - # Use built-in timeout in TCPSocket.open if available - sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) - @tcpsocket_supports_open_timeout = true - sock - rescue ArgumentError => e - raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)')) - @tcpsocket_supports_open_timeout = false - - # Fallback to Timeout.timeout if TCPSocket.open does not support open_timeout - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } - end - when false - # The current Ruby is known to not support TCPSocket(open_timeout:). - # Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue. - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } - end + s = timeouted_connect(conn_addr, conn_port) rescue => e e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions raise e, "Failed to open TCP connection to " + @@ -1795,6 +1772,29 @@ def connect end private :connect + def timeouted_connect(conn_addr, conn_port) + if @tcpsocket_supports_open_timeout == nil || @tcpsocket_supports_open_timeout == true + # Try to use built-in open_timeout in TCPSocket.open if: + # - The current Ruby runtime is known to support it, or + # - It is unknown whether the current Ruby runtime supports it (so we'll try). + begin + sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + @tcpsocket_supports_open_timeout = true + return sock + rescue ArgumentError => e + raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)')) + @tcpsocket_supports_open_timeout = false + end + end + + # This Ruby runtime is known not to support `TCPSocket(open_timeout:)`. + # Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue. + Timeout.timeout(@open_timeout, Net::OpenTimeout) { + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + } + end + private :timeouted_connect + def on_connect end private :on_connect From b0c9286d98929db56514d8009040fe206b3d310f Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Sun, 2 Nov 2025 21:13:37 -0800 Subject: [PATCH 184/367] Use VWA for bignum Previously we only allocated bignums from the 40 byte sizepool, and embedded bignum used a fixed size. --- bignum.c | 62 ++++++++++++++++++++++++++++++++-------- ext/-test-/bignum/depend | 12 ++++++++ internal/bignum.h | 25 +++++++++------- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/bignum.c b/bignum.c index 054b6c1cc9afe3..9e8d6a91f1004d 100644 --- a/bignum.c +++ b/bignum.c @@ -79,7 +79,6 @@ STATIC_ASSERT(sizeof_bdigit_and_dbl, SIZEOF_BDIGIT*2 <= SIZEOF_BDIGIT_DBL); STATIC_ASSERT(bdigit_signedness, 0 < (BDIGIT)-1); STATIC_ASSERT(bdigit_dbl_signedness, 0 < (BDIGIT_DBL)-1); STATIC_ASSERT(bdigit_dbl_signed_signedness, 0 > (BDIGIT_DBL_SIGNED)-1); -STATIC_ASSERT(rbignum_embed_len_max, BIGNUM_EMBED_LEN_MAX <= (BIGNUM_EMBED_LEN_MASK >> BIGNUM_EMBED_LEN_SHIFT)); #if SIZEOF_BDIGIT < SIZEOF_LONG STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_LONG % SIZEOF_BDIGIT == 0); @@ -2990,25 +2989,56 @@ rb_cmpint(VALUE val, VALUE a, VALUE b) ((l) << BIGNUM_EMBED_LEN_SHIFT)) : \ (void)(RBIGNUM(b)->as.heap.len = (l))) +static size_t +big_embed_capa(VALUE big) +{ + size_t size = rb_gc_obj_slot_size(big) - offsetof(struct RBignum, as.ary); + RUBY_ASSERT(size % sizeof(BDIGIT) == 0); + size_t capa = size / sizeof(BDIGIT); + RUBY_ASSERT(capa <= BIGNUM_EMBED_LEN_MAX); + return capa; +} + +static size_t +big_embed_size(size_t capa) +{ + size_t size = offsetof(struct RBignum, as.ary) + (sizeof(BDIGIT) * capa); + if (size < sizeof(struct RBignum)) { + size = sizeof(struct RBignum); + } + return size; +} + +static bool +big_embeddable_p(size_t capa) +{ + if (capa > BIGNUM_EMBED_LEN_MAX) { + return false; + } + return rb_gc_size_allocatable_p(big_embed_size(capa)); +} + static void rb_big_realloc(VALUE big, size_t len) { BDIGIT *ds; + size_t embed_capa = big_embed_capa(big); + if (BIGNUM_EMBED_P(big)) { - if (BIGNUM_EMBED_LEN_MAX < len) { + if (embed_capa < len) { ds = ALLOC_N(BDIGIT, len); - MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, BIGNUM_EMBED_LEN_MAX); + MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, embed_capa); RBIGNUM(big)->as.heap.len = BIGNUM_LEN(big); RBIGNUM(big)->as.heap.digits = ds; FL_UNSET_RAW(big, BIGNUM_EMBED_FLAG); } } else { - if (len <= BIGNUM_EMBED_LEN_MAX) { + if (len <= embed_capa) { ds = RBIGNUM(big)->as.heap.digits; FL_SET_RAW(big, BIGNUM_EMBED_FLAG); BIGNUM_SET_LEN(big, len); - (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)RBIGNUM(big)->as.ary, sizeof(RBIGNUM(big)->as.ary)); + (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)RBIGNUM(big)->as.ary, embed_capa * sizeof(BDIGIT)); if (ds) { MEMCPY(RBIGNUM(big)->as.ary, ds, BDIGIT, len); xfree(ds); @@ -3035,16 +3065,24 @@ rb_big_resize(VALUE big, size_t len) static VALUE bignew_1(VALUE klass, size_t len, int sign) { - NEWOBJ_OF(big, struct RBignum, klass, - T_BIGNUM | (RGENGC_WB_PROTECTED_BIGNUM ? FL_WB_PROTECTED : 0), sizeof(struct RBignum), 0); - VALUE bigv = (VALUE)big; - BIGNUM_SET_SIGN(bigv, sign); - if (len <= BIGNUM_EMBED_LEN_MAX) { - FL_SET_RAW(bigv, BIGNUM_EMBED_FLAG); + VALUE bigv; + + if (big_embeddable_p(len)) { + size_t size = big_embed_size(len); + RUBY_ASSERT(rb_gc_size_allocatable_p(size)); + NEWOBJ_OF(big, struct RBignum, klass, + T_BIGNUM | BIGNUM_EMBED_FLAG | (RGENGC_WB_PROTECTED_BIGNUM ? FL_WB_PROTECTED : 0), + size, 0); + bigv = (VALUE)big; + BIGNUM_SET_SIGN(bigv, sign); BIGNUM_SET_LEN(bigv, len); - (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)big->as.ary, sizeof(big->as.ary)); + (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)big->as.ary, len * sizeof(BDIGIT)); } else { + NEWOBJ_OF(big, struct RBignum, klass, + T_BIGNUM | (RGENGC_WB_PROTECTED_BIGNUM ? FL_WB_PROTECTED : 0), sizeof(struct RBignum), 0); + bigv = (VALUE)big; + BIGNUM_SET_SIGN(bigv, sign); big->as.heap.digits = ALLOC_N(BDIGIT, len); big->as.heap.len = len; } diff --git a/ext/-test-/bignum/depend b/ext/-test-/bignum/depend index 049f0c7b520970..82972f10327311 100644 --- a/ext/-test-/bignum/depend +++ b/ext/-test-/bignum/depend @@ -6,6 +6,7 @@ big2str.o: $(hdrdir)/ruby/backward.h big2str.o: $(hdrdir)/ruby/backward/2/assume.h big2str.o: $(hdrdir)/ruby/backward/2/attributes.h big2str.o: $(hdrdir)/ruby/backward/2/bool.h +big2str.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h big2str.o: $(hdrdir)/ruby/backward/2/inttypes.h big2str.o: $(hdrdir)/ruby/backward/2/limits.h big2str.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -159,6 +160,7 @@ big2str.o: $(hdrdir)/ruby/ruby.h big2str.o: $(hdrdir)/ruby/st.h big2str.o: $(hdrdir)/ruby/subst.h big2str.o: $(top_srcdir)/internal/bignum.h +big2str.o: $(top_srcdir)/internal/compilers.h big2str.o: big2str.c bigzero.o: $(RUBY_EXTCONF_H) bigzero.o: $(arch_hdrdir)/ruby/config.h @@ -167,6 +169,7 @@ bigzero.o: $(hdrdir)/ruby/backward.h bigzero.o: $(hdrdir)/ruby/backward/2/assume.h bigzero.o: $(hdrdir)/ruby/backward/2/attributes.h bigzero.o: $(hdrdir)/ruby/backward/2/bool.h +bigzero.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h bigzero.o: $(hdrdir)/ruby/backward/2/inttypes.h bigzero.o: $(hdrdir)/ruby/backward/2/limits.h bigzero.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -320,6 +323,7 @@ bigzero.o: $(hdrdir)/ruby/ruby.h bigzero.o: $(hdrdir)/ruby/st.h bigzero.o: $(hdrdir)/ruby/subst.h bigzero.o: $(top_srcdir)/internal/bignum.h +bigzero.o: $(top_srcdir)/internal/compilers.h bigzero.o: bigzero.c div.o: $(RUBY_EXTCONF_H) div.o: $(arch_hdrdir)/ruby/config.h @@ -328,6 +332,7 @@ div.o: $(hdrdir)/ruby/backward.h div.o: $(hdrdir)/ruby/backward/2/assume.h div.o: $(hdrdir)/ruby/backward/2/attributes.h div.o: $(hdrdir)/ruby/backward/2/bool.h +div.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h div.o: $(hdrdir)/ruby/backward/2/inttypes.h div.o: $(hdrdir)/ruby/backward/2/limits.h div.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -481,6 +486,7 @@ div.o: $(hdrdir)/ruby/ruby.h div.o: $(hdrdir)/ruby/st.h div.o: $(hdrdir)/ruby/subst.h div.o: $(top_srcdir)/internal/bignum.h +div.o: $(top_srcdir)/internal/compilers.h div.o: div.c init.o: $(RUBY_EXTCONF_H) init.o: $(arch_hdrdir)/ruby/config.h @@ -650,6 +656,7 @@ intpack.o: $(hdrdir)/ruby/backward.h intpack.o: $(hdrdir)/ruby/backward/2/assume.h intpack.o: $(hdrdir)/ruby/backward/2/attributes.h intpack.o: $(hdrdir)/ruby/backward/2/bool.h +intpack.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h intpack.o: $(hdrdir)/ruby/backward/2/inttypes.h intpack.o: $(hdrdir)/ruby/backward/2/limits.h intpack.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -803,6 +810,7 @@ intpack.o: $(hdrdir)/ruby/ruby.h intpack.o: $(hdrdir)/ruby/st.h intpack.o: $(hdrdir)/ruby/subst.h intpack.o: $(top_srcdir)/internal/bignum.h +intpack.o: $(top_srcdir)/internal/compilers.h intpack.o: intpack.c mul.o: $(RUBY_EXTCONF_H) mul.o: $(arch_hdrdir)/ruby/config.h @@ -811,6 +819,7 @@ mul.o: $(hdrdir)/ruby/backward.h mul.o: $(hdrdir)/ruby/backward/2/assume.h mul.o: $(hdrdir)/ruby/backward/2/attributes.h mul.o: $(hdrdir)/ruby/backward/2/bool.h +mul.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h mul.o: $(hdrdir)/ruby/backward/2/inttypes.h mul.o: $(hdrdir)/ruby/backward/2/limits.h mul.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -964,6 +973,7 @@ mul.o: $(hdrdir)/ruby/ruby.h mul.o: $(hdrdir)/ruby/st.h mul.o: $(hdrdir)/ruby/subst.h mul.o: $(top_srcdir)/internal/bignum.h +mul.o: $(top_srcdir)/internal/compilers.h mul.o: mul.c str2big.o: $(RUBY_EXTCONF_H) str2big.o: $(arch_hdrdir)/ruby/config.h @@ -972,6 +982,7 @@ str2big.o: $(hdrdir)/ruby/backward.h str2big.o: $(hdrdir)/ruby/backward/2/assume.h str2big.o: $(hdrdir)/ruby/backward/2/attributes.h str2big.o: $(hdrdir)/ruby/backward/2/bool.h +str2big.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h str2big.o: $(hdrdir)/ruby/backward/2/inttypes.h str2big.o: $(hdrdir)/ruby/backward/2/limits.h str2big.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -1125,5 +1136,6 @@ str2big.o: $(hdrdir)/ruby/ruby.h str2big.o: $(hdrdir)/ruby/st.h str2big.o: $(hdrdir)/ruby/subst.h str2big.o: $(top_srcdir)/internal/bignum.h +str2big.o: $(top_srcdir)/internal/compilers.h str2big.o: str2big.c # AUTOGENERATED DEPENDENCIES END diff --git a/internal/bignum.h b/internal/bignum.h index 0ba21a492334f1..e5b6b425631f80 100644 --- a/internal/bignum.h +++ b/internal/bignum.h @@ -9,6 +9,7 @@ * @brief Internal header for Bignums. */ #include "ruby/internal/config.h" /* for HAVE_LIBGMP */ +#include "internal/compilers.h" /* for FLEX_ARY_LEN */ #include /* for size_t */ #ifdef HAVE_SYS_TYPES_H @@ -76,18 +77,17 @@ #define RBIGNUM(obj) ((struct RBignum *)(obj)) #define BIGNUM_SIGN_BIT FL_USER1 #define BIGNUM_EMBED_FLAG ((VALUE)FL_USER2) -#define BIGNUM_EMBED_LEN_NUMBITS 3 + +/* This is likely more bits than we need today and will also need adjustment if + * we change GC slot sizes. + */ +#define BIGNUM_EMBED_LEN_NUMBITS 9 #define BIGNUM_EMBED_LEN_MASK \ - (~(~(VALUE)0U << BIGNUM_EMBED_LEN_NUMBITS) << BIGNUM_EMBED_LEN_SHIFT) + (RUBY_FL_USER11 | RUBY_FL_USER10 | RUBY_FL_USER9 | RUBY_FL_USER8 | RUBY_FL_USER7 | \ + RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3) #define BIGNUM_EMBED_LEN_SHIFT \ (FL_USHIFT+3) /* bit offset of BIGNUM_EMBED_LEN_MASK */ -#ifndef BIGNUM_EMBED_LEN_MAX -# if (SIZEOF_VALUE*RBIMPL_RVALUE_EMBED_LEN_MAX/SIZEOF_ACTUAL_BDIGIT) < (1 << BIGNUM_EMBED_LEN_NUMBITS)-1 -# define BIGNUM_EMBED_LEN_MAX (SIZEOF_VALUE*RBIMPL_RVALUE_EMBED_LEN_MAX/SIZEOF_ACTUAL_BDIGIT) -# else -# define BIGNUM_EMBED_LEN_MAX ((1 << BIGNUM_EMBED_LEN_NUMBITS)-1) -# endif -#endif +#define BIGNUM_EMBED_LEN_MAX (BIGNUM_EMBED_LEN_MASK >> BIGNUM_EMBED_LEN_SHIFT) enum rb_int_parse_flags { RB_INT_PARSE_SIGN = 0x01, @@ -104,7 +104,12 @@ struct RBignum { size_t len; BDIGIT *digits; } heap; - BDIGIT ary[BIGNUM_EMBED_LEN_MAX]; + /* This is a length 1 array because: + * 1. GCC has a bug that does not optimize C flexible array members + * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452) + * 2. Zero length arrays are not supported by all compilers + */ + BDIGIT ary[1]; } as; }; From 930afa1c7ba747658cc4253c2cb6a367d7a6a36b Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 3 Nov 2025 15:48:28 -0800 Subject: [PATCH 185/367] Never shrink bignum on realloc As far as I can tell, this only ever shrinks by one, and it's really not worth the expensive realloc for that. --- bignum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bignum.c b/bignum.c index 9e8d6a91f1004d..8d3eac8e0a9173 100644 --- a/bignum.c +++ b/bignum.c @@ -3048,7 +3048,7 @@ rb_big_realloc(VALUE big, size_t len) if (BIGNUM_LEN(big) == 0) { RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len); } - else { + else if (BIGNUM_LEN(big) < len) { REALLOC_N(RBIGNUM(big)->as.heap.digits, BDIGIT, len); } } From 65dbd571c1e0aae3b0919ae6e64704726f18b265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Fri, 5 Dec 2025 15:33:44 +0100 Subject: [PATCH 186/367] [ruby/psych] Use Node#to_ruby parse_symbols option https://github.com/ruby/psych/commit/907fd4fa97 --- ext/psych/lib/psych/nodes/node.rb | 2 +- test/psych/visitors/test_to_ruby.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/psych/lib/psych/nodes/node.rb b/ext/psych/lib/psych/nodes/node.rb index f89c82b7f76df8..fc27448f2e5ec8 100644 --- a/ext/psych/lib/psych/nodes/node.rb +++ b/ext/psych/lib/psych/nodes/node.rb @@ -46,7 +46,7 @@ def each &block # # See also Psych::Visitors::ToRuby def to_ruby(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true) - Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: true).accept(self) + Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols).accept(self) end alias :transform :to_ruby diff --git a/test/psych/visitors/test_to_ruby.rb b/test/psych/visitors/test_to_ruby.rb index 89c367665191f9..c9b501dfa274db 100644 --- a/test/psych/visitors/test_to_ruby.rb +++ b/test/psych/visitors/test_to_ruby.rb @@ -328,6 +328,12 @@ def test_mapping_with_str_tag mapping.children << Nodes::Scalar.new('bar') assert_equal({'foo' => 'bar'}, mapping.to_ruby) end + + def test_parse_symbols + node = Nodes::Scalar.new(':foo') + assert_equal :foo, node.to_ruby + assert_equal ':foo', node.to_ruby(parse_symbols: false) + end end end end From ec28bd75a869c2e525388582370239a9ff8c19e7 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 5 Dec 2025 01:49:31 +0900 Subject: [PATCH 187/367] [ruby/timeout] support Ractor 1. Introduce State to store all status. 2. Store State instance to the Ractor local storage if possible 3. Make `GET_TIME` (Method object) shareable if possible 3 is supporeted Ruby 4.0 and later, so the Rator support is works only on Ruby 4.0 and later. https://github.com/ruby/timeout/commit/54ff671c6c --- lib/timeout.rb | 166 ++++++++++++++++++++++++++++--------------- test/test_timeout.rb | 20 ++++++ 2 files changed, 127 insertions(+), 59 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index f173d028a39d0a..2bf3e7514653f6 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -44,12 +44,107 @@ def self.handle_timeout(message) # :nodoc: end # :stopdoc: - CONDVAR = ConditionVariable.new - QUEUE = Queue.new - QUEUE_MUTEX = Mutex.new - TIMEOUT_THREAD_MUTEX = Mutex.new - @timeout_thread = nil - private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX + + # We keep a private reference so that time mocking libraries won't break + # Timeout. + GET_TIME = + if defined?(Ractor.make_shareable) + begin + Ractor.make_shareable(Process.method(:clock_gettime)) + rescue # failed on Ruby 3.4 + Process.method(:clock_gettime) + end + else + Process.method(:clock_gettime) + end + + private_constant :GET_TIME + + class State + attr_reader :condvar, :queue, :queue_mutex # shared with Timeout.timeout() + + def initialize + @condvar = ConditionVariable.new + @queue = Queue.new + @queue_mutex = Mutex.new + + @timeout_thread = nil + @timeout_thread_mutex = Mutex.new + end + + if defined?(Ractor.store_if_absent) && + defined?(Ractor.shareble?) && Ractor.shareable?(GET_TIME) + + # Ractor support if + # 1. Ractor.store_if_absent is available + # 2. Method object can be shareable (4.0~) + + Ractor.store_if_absent :timeout_gem_state do + State.new + end + + def self.instance + Ractor[:timeout_gem_state] + end + + ::Timeout::RACTOR_SUPPORT = true # for test + else + @GLOBAL_STATE = State.new + + def self.instance + @GLOBAL_STATE + end + end + + def create_timeout_thread + watcher = Thread.new do + requests = [] + while true + until @queue.empty? and !requests.empty? # wait to have at least one request + req = @queue.pop + requests << req unless req.done? + end + closest_deadline = requests.min_by(&:deadline).deadline + + now = 0.0 + @queue_mutex.synchronize do + while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and @queue.empty? + @condvar.wait(@queue_mutex, closest_deadline - now) + end + end + + requests.each do |req| + req.interrupt if req.expired?(now) + end + requests.reject!(&:done?) + end + end + + if !watcher.group.enclosed? && (!defined?(Ractor.main?) || Ractor.main?) + ThreadGroup::Default.add(watcher) + end + + watcher.name = "Timeout stdlib thread" + watcher.thread_variable_set(:"\0__detached_thread__", true) + watcher + end + + def ensure_timeout_thread_created + unless @timeout_thread&.alive? + # If the Mutex is already owned we are in a signal handler. + # In that case, just return and let the main thread create the Timeout thread. + return if @timeout_thread_mutex.owned? + + @timeout_thread_mutex.synchronize do + unless @timeout_thread&.alive? + @timeout_thread = create_timeout_thread + end + end + end + end + end + + private_constant :State class Request attr_reader :deadline @@ -91,55 +186,6 @@ def finished end private_constant :Request - def self.create_timeout_thread - watcher = Thread.new do - requests = [] - while true - until QUEUE.empty? and !requests.empty? # wait to have at least one request - req = QUEUE.pop - requests << req unless req.done? - end - closest_deadline = requests.min_by(&:deadline).deadline - - now = 0.0 - QUEUE_MUTEX.synchronize do - while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? - CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now) - end - end - - requests.each do |req| - req.interrupt if req.expired?(now) - end - requests.reject!(&:done?) - end - end - ThreadGroup::Default.add(watcher) unless watcher.group.enclosed? - watcher.name = "Timeout stdlib thread" - watcher.thread_variable_set(:"\0__detached_thread__", true) - watcher - end - private_class_method :create_timeout_thread - - def self.ensure_timeout_thread_created - unless @timeout_thread and @timeout_thread.alive? - # If the Mutex is already owned we are in a signal handler. - # In that case, just return and let the main thread create the @timeout_thread. - return if TIMEOUT_THREAD_MUTEX.owned? - TIMEOUT_THREAD_MUTEX.synchronize do - unless @timeout_thread and @timeout_thread.alive? - @timeout_thread = create_timeout_thread - end - end - end - end - private_class_method :ensure_timeout_thread_created - - # We keep a private reference so that time mocking libraries won't break - # Timeout. - GET_TIME = Process.method(:clock_gettime) - private_constant :GET_TIME - # :startdoc: # Perform an operation in a block, raising an error if it takes longer than @@ -178,12 +224,14 @@ def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return scheduler.timeout_after(sec, klass || Error, message, &block) end - ensure_timeout_thread_created + state = State.instance + state.ensure_timeout_thread_created + perform = Proc.new do |exc| request = Request.new(Thread.current, sec, exc, message) - QUEUE_MUTEX.synchronize do - QUEUE << request - CONDVAR.signal + state.queue_mutex.synchronize do + state.queue << request + state.condvar.signal end begin return yield(sec) diff --git a/test/test_timeout.rb b/test/test_timeout.rb index e367df757cc9b2..233f54eb82bf2d 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -280,4 +280,24 @@ def test_handling_enclosed_threadgroup }.join end; end + + def test_ractor + assert_separately(%w[-rtimeout -W0], <<-'end;') + r = Ractor.new do + Timeout.timeout(1) { 42 } + end.value + + assert_equal 42, r + + r = Ractor.new do + begin + Timeout.timeout(0.1) { sleep } + rescue Timeout::Error + :ok + end + end.value + + assert_equal :ok, r + end; + end if Timeout.const_defined?(:RACTOR_SUPPORT) end From a523e9d872d51e64eecbf3feeaa8d4d8769f72bd Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 19:14:28 +0100 Subject: [PATCH 188/367] [ruby/timeout] Minor tweaks https://github.com/ruby/timeout/commit/daab9a2193 --- lib/timeout.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 2bf3e7514653f6..3ad0193ac5f785 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -57,7 +57,6 @@ def self.handle_timeout(message) # :nodoc: else Process.method(:clock_gettime) end - private_constant :GET_TIME class State @@ -89,10 +88,10 @@ def self.instance ::Timeout::RACTOR_SUPPORT = true # for test else - @GLOBAL_STATE = State.new + GLOBAL_STATE = State.new def self.instance - @GLOBAL_STATE + GLOBAL_STATE end end @@ -143,7 +142,6 @@ def ensure_timeout_thread_created end end end - private_constant :State class Request From dc406af9cb9a269ae550483fa1278eedf297fb92 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 19:19:13 +0100 Subject: [PATCH 189/367] [ruby/timeout] Fix condition and fix test to catch that broken condition https://github.com/ruby/timeout/commit/82fb6f6925 --- lib/timeout.rb | 4 +--- test/test_timeout.rb | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 3ad0193ac5f785..47410af386295c 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -72,7 +72,7 @@ def initialize end if defined?(Ractor.store_if_absent) && - defined?(Ractor.shareble?) && Ractor.shareable?(GET_TIME) + defined?(Ractor.shareable?) && Ractor.shareable?(GET_TIME) # Ractor support if # 1. Ractor.store_if_absent is available @@ -85,8 +85,6 @@ def initialize def self.instance Ractor[:timeout_gem_state] end - - ::Timeout::RACTOR_SUPPORT = true # for test else GLOBAL_STATE = State.new diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 233f54eb82bf2d..3f94134fb04d9c 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -299,5 +299,5 @@ def test_ractor assert_equal :ok, r end; - end if Timeout.const_defined?(:RACTOR_SUPPORT) + end if defined?(::Ractor) && RUBY_VERSION >= '4.0' end From 3e189ddb9db486d1bc7d5c15f395017b4fb0c136 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 19:21:37 +0100 Subject: [PATCH 190/367] [ruby/timeout] Fix logic for Ractor support * Fix indentation to stay a multiple of 2 spaces. https://github.com/ruby/timeout/commit/a1d784cb66 --- lib/timeout.rb | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 47410af386295c..eab1a1be9e19a4 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -45,8 +45,7 @@ def self.handle_timeout(message) # :nodoc: # :stopdoc: - # We keep a private reference so that time mocking libraries won't break - # Timeout. + # We keep a private reference so that time mocking libraries won't break Timeout. GET_TIME = if defined?(Ractor.make_shareable) begin @@ -71,20 +70,15 @@ def initialize @timeout_thread_mutex = Mutex.new end - if defined?(Ractor.store_if_absent) && - defined?(Ractor.shareable?) && Ractor.shareable?(GET_TIME) - - # Ractor support if - # 1. Ractor.store_if_absent is available - # 2. Method object can be shareable (4.0~) - - Ractor.store_if_absent :timeout_gem_state do - State.new - end - - def self.instance - Ractor[:timeout_gem_state] - end + if defined?(Ractor.store_if_absent) && defined?(Ractor.shareable?) && Ractor.shareable?(GET_TIME) + # Ractor support if + # 1. Ractor.store_if_absent is available + # 2. Method object can be shareable (4.0~) + def self.instance + Ractor.store_if_absent :timeout_gem_state do + State.new + end + end else GLOBAL_STATE = State.new From 00b91c727fdd0dd3bcd970dd4bc6c2b598cf4e1b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 19:25:22 +0100 Subject: [PATCH 191/367] [ruby/timeout] Simplify logic to make GET_TIME shareable https://github.com/ruby/timeout/commit/281b2507e7 --- lib/timeout.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index eab1a1be9e19a4..36cd0f915bc7ff 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -46,16 +46,11 @@ def self.handle_timeout(message) # :nodoc: # :stopdoc: # We keep a private reference so that time mocking libraries won't break Timeout. - GET_TIME = - if defined?(Ractor.make_shareable) - begin - Ractor.make_shareable(Process.method(:clock_gettime)) - rescue # failed on Ruby 3.4 - Process.method(:clock_gettime) - end - else - Process.method(:clock_gettime) - end + GET_TIME = Process.method(:clock_gettime) + if defined?(Ractor.make_shareable) + # Ractor.make_shareable(Method) only works on Ruby 4+ + Ractor.make_shareable(GET_TIME) rescue nil + end private_constant :GET_TIME class State From 8c4f79d5f30fb2fe647c4f3fd262a5fdeacaeca2 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 6 Dec 2025 03:33:12 +0900 Subject: [PATCH 192/367] [ruby/openssl] x509cert: handle invalid validity periods in Certificate#inspect In a newly allocated OpenSSL X509 object, the notBefore and notAfter fields contain an ASN1_STRING object with type V_ASN1_UNDEF rather than an ASN1_TIME. Commit https://github.com/ruby/openssl/commit/73484f67949a made asn1time_to_time() stricter and it now raises an exception if the argument is not an ASN1_TIME. Previously, it would print a verbose-mode warning and return nil. OpenSSL::X509::Certificate#inspect should work even when the certificate is invalid. Let's handle this. https://github.com/ruby/openssl/commit/18c283f2b6 --- ext/openssl/lib/openssl/x509.rb | 9 +++++++++ ext/openssl/ossl_x509cert.c | 15 --------------- test/openssl/test_x509cert.rb | 8 ++++++++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb index 6459d37b12766e..66765ffeabf0dc 100644 --- a/ext/openssl/lib/openssl/x509.rb +++ b/ext/openssl/lib/openssl/x509.rb @@ -346,6 +346,15 @@ class Certificate include Extension::CRLDistributionPoints include Extension::AuthorityInfoAccess + def inspect + "#<#{self.class}: " \ + "subject=#{subject.inspect}, " \ + "issuer=#{issuer.inspect}, " \ + "serial=#{serial.inspect}, " \ + "not_before=#{not_before.inspect rescue "(error)"}, " \ + "not_after=#{not_after.inspect rescue "(error)"}>" + end + def pretty_print(q) q.object_group(self) { q.breakable diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index b1e82a2790adf6..4d69008fdd9a81 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -665,20 +665,6 @@ ossl_x509_add_extension(VALUE self, VALUE extension) return extension; } -static VALUE -ossl_x509_inspect(VALUE self) -{ - return rb_sprintf("#<%"PRIsVALUE": subject=%+"PRIsVALUE", " - "issuer=%+"PRIsVALUE", serial=%+"PRIsVALUE", " - "not_before=%+"PRIsVALUE", not_after=%+"PRIsVALUE">", - rb_obj_class(self), - ossl_x509_get_subject(self), - ossl_x509_get_issuer(self), - ossl_x509_get_serial(self), - ossl_x509_get_not_before(self), - ossl_x509_get_not_after(self)); -} - /* * call-seq: * cert1 == cert2 -> true | false @@ -1013,7 +999,6 @@ Init_ossl_x509cert(void) rb_define_method(cX509Cert, "extensions", ossl_x509_get_extensions, 0); rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1); rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1); - rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0); rb_define_method(cX509Cert, "==", ossl_x509_eq, 1); rb_define_method(cX509Cert, "tbs_bytes", ossl_x509_tbs_bytes, 0); } diff --git a/test/openssl/test_x509cert.rb b/test/openssl/test_x509cert.rb index 877eac69ce5e34..9e0aa4edf6b372 100644 --- a/test/openssl/test_x509cert.rb +++ b/test/openssl/test_x509cert.rb @@ -298,6 +298,14 @@ def test_eq assert_equal false, cert3 == cert4 end + def test_inspect + cacert = issue_cert(@ca, @rsa1, 1, [], nil, nil) + assert_include(cacert.inspect, "subject=#{@ca.inspect}") + + # Do not raise an exception for an invalid certificate + assert_instance_of(String, OpenSSL::X509::Certificate.new.inspect) + end + def test_marshal now = Time.now cacert = issue_cert(@ca, @rsa1, 1, [], nil, nil, From 2b057859414d1ccfa4d88a898d27defc8c74dc1c Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Thu, 26 Sep 2024 16:34:39 -0400 Subject: [PATCH 193/367] Allow rb_thread_call_with_gvl() to work when thread already has GVL [Feature #20750] Co-authored-by: Benoit Daloze --- NEWS.md | 7 +++++++ gc.c | 6 +----- thread.c | 6 +++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 53ed47cb8c3265..b44df19ca2b9b2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -342,6 +342,12 @@ The following bundled gems are updated. `IO` objects share the same file descriptor, closing one does not affect the other. [[Feature #18455]] +* GVL + + * `rb_thread_call_with_gvl` now works with or without the GVL. + This allows gems to avoid checking `ruby_thread_has_gvl_p`. + Please still be diligent about the GVL. [[Feature #20750]] + * Set * A C API for `Set` has been added. The following methods are supported: @@ -402,6 +408,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #19908]: https://bugs.ruby-lang.org/issues/19908 [Feature #20610]: https://bugs.ruby-lang.org/issues/20610 [Feature #20724]: https://bugs.ruby-lang.org/issues/20724 +[Feature #20750]: https://bugs.ruby-lang.org/issues/20750 [Feature #20884]: https://bugs.ruby-lang.org/issues/20884 [Feature #20925]: https://bugs.ruby-lang.org/issues/20925 [Feature #20971]: https://bugs.ruby-lang.org/issues/20971 diff --git a/gc.c b/gc.c index 7b4547e2ea0bca..18badba00581d8 100644 --- a/gc.c +++ b/gc.c @@ -5063,11 +5063,7 @@ gc_raise(VALUE exc, const char *fmt, ...) exc, fmt, &ap, }; - if (ruby_thread_has_gvl_p()) { - gc_vraise(&argv); - UNREACHABLE; - } - else if (ruby_native_thread_p()) { + if (ruby_native_thread_p()) { rb_thread_call_with_gvl(gc_vraise, &argv); UNREACHABLE; } diff --git a/thread.c b/thread.c index 5f592a42562adf..7c88c979029a10 100644 --- a/thread.c +++ b/thread.c @@ -2042,6 +2042,9 @@ rb_thread_io_blocking_region(struct rb_io *io, rb_blocking_function_t *func, voi * created as Ruby thread (created by Thread.new or so). In other * words, this function *DOES NOT* associate or convert a NON-Ruby * thread to a Ruby thread. + * + * NOTE: If this thread has already acquired the GVL, then the method call + * is performed without acquiring or releasing the GVL (from Ruby 4.0). */ void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1) @@ -2065,7 +2068,8 @@ rb_thread_call_with_gvl(void *(*func)(void *), void *data1) prev_unblock = th->unblock; if (brb == 0) { - rb_bug("rb_thread_call_with_gvl: called by a thread which has GVL."); + /* the GVL is already acquired, call method directly */ + return (*func)(data1); } blocking_region_end(th, brb); From 834adc358ae967dfa52890a9abba551a070ab75e Mon Sep 17 00:00:00 2001 From: Steven Johnstone Date: Fri, 5 Dec 2025 16:07:33 +0000 Subject: [PATCH 194/367] [ruby/prism] Avoid out-of-bounds reads Fixes https://github.com/ruby/prism/pull/3784. https://github.com/ruby/prism/commit/3fe862534b --- prism/encoding.c | 84 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/prism/encoding.c b/prism/encoding.c index 424f149b8f833f..d7e5616840483f 100644 --- a/prism/encoding.c +++ b/prism/encoding.c @@ -2377,6 +2377,10 @@ pm_encoding_utf_8_char_width(const uint8_t *b, ptrdiff_t n) { */ size_t pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0; } @@ -2397,6 +2401,10 @@ pm_encoding_utf_8_alpha_char(const uint8_t *b, ptrdiff_t n) { */ size_t pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; } @@ -2417,6 +2425,10 @@ pm_encoding_utf_8_alnum_char(const uint8_t *b, ptrdiff_t n) { */ bool pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false; } @@ -2435,7 +2447,8 @@ pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n) { static pm_unicode_codepoint_t pm_cesu_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) { - if (b[0] < 0x80) { + + if ((n > 0) && (b[0] < 0x80)) { *width = 1; return (pm_unicode_codepoint_t) b[0]; } @@ -2474,6 +2487,10 @@ pm_cesu_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) { static size_t pm_encoding_cesu_8_char_width(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + size_t width; pm_cesu_8_codepoint(b, n, &width); return width; @@ -2481,6 +2498,10 @@ pm_encoding_cesu_8_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_cesu_8_alpha_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) ? 1 : 0; } @@ -2497,6 +2518,10 @@ pm_encoding_cesu_8_alpha_char(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_cesu_8_alnum_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & (PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; } @@ -2513,6 +2538,10 @@ pm_encoding_cesu_8_alnum_char(const uint8_t *b, ptrdiff_t n) { static bool pm_encoding_cesu_8_isupper_char(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } + if (*b < 0x80) { return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_UPPERCASE_BIT) ? true : false; } @@ -3928,14 +3957,14 @@ static const uint8_t pm_encoding_windows_874_table[256] = { }; #define PRISM_ENCODING_TABLE(name) \ - static size_t pm_encoding_ ##name ## _alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \ - return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHABETIC_BIT); \ + static size_t pm_encoding_ ##name ## _alpha_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHABETIC_BIT)); \ } \ - static size_t pm_encoding_ ##name ## _alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \ - return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; \ + static size_t pm_encoding_ ##name ## _alnum_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; \ } \ - static bool pm_encoding_ ##name ## _isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { \ - return (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_UPPERCASE_BIT); \ + static bool pm_encoding_ ##name ## _isupper_char(const uint8_t *b, ptrdiff_t n) { \ + return ((n > 0) && (pm_encoding_ ##name ## _table[*b] & PRISM_ENCODING_UPPERCASE_BIT)); \ } PRISM_ENCODING_TABLE(cp850) @@ -4004,8 +4033,8 @@ PRISM_ENCODING_TABLE(windows_874) * means that if the top bit is not set, the character is 1 byte long. */ static size_t -pm_encoding_ascii_char_width(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { - return *b < 0x80 ? 1 : 0; +pm_encoding_ascii_char_width(const uint8_t *b, ptrdiff_t n) { + return ((n > 0) && (*b < 0x80)) ? 1 : 0; } /** @@ -4013,8 +4042,8 @@ pm_encoding_ascii_char_width(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t * alphabetical character. */ static size_t -pm_encoding_ascii_alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { - return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT); +pm_encoding_ascii_alpha_char(const uint8_t *b, ptrdiff_t n) { + return (n > 0) ? (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT) : 0; } /** @@ -4024,7 +4053,7 @@ pm_encoding_ascii_alpha_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t */ static size_t pm_encoding_ascii_alpha_char_7bit(const uint8_t *b, ptrdiff_t n) { - return (*b < 0x80) ? pm_encoding_ascii_alpha_char(b, n) : 0; + return ((n > 0) && (*b < 0x80)) ? pm_encoding_ascii_alpha_char(b, n) : 0; } /** @@ -4032,8 +4061,8 @@ pm_encoding_ascii_alpha_char_7bit(const uint8_t *b, ptrdiff_t n) { * alphanumeric character. */ static size_t -pm_encoding_ascii_alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { - return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +pm_encoding_ascii_alnum_char(const uint8_t *b, ptrdiff_t n) { + return ((n > 0) && (pm_encoding_ascii_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; } /** @@ -4043,7 +4072,7 @@ pm_encoding_ascii_alnum_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t */ static size_t pm_encoding_ascii_alnum_char_7bit(const uint8_t *b, ptrdiff_t n) { - return (*b < 0x80) ? pm_encoding_ascii_alnum_char(b, n) : 0; + return ((n > 0) && (*b < 0x80)) ? pm_encoding_ascii_alnum_char(b, n) : 0; } /** @@ -4051,8 +4080,8 @@ pm_encoding_ascii_alnum_char_7bit(const uint8_t *b, ptrdiff_t n) { * character. */ static bool -pm_encoding_ascii_isupper_char(const uint8_t *b, PRISM_ATTRIBUTE_UNUSED ptrdiff_t n) { - return (pm_encoding_ascii_table[*b] & PRISM_ENCODING_UPPERCASE_BIT); +pm_encoding_ascii_isupper_char(const uint8_t *b, ptrdiff_t n) { + return (n > 0) && (pm_encoding_ascii_table[*b] & PRISM_ENCODING_UPPERCASE_BIT); } /** @@ -4071,7 +4100,7 @@ pm_encoding_single_char_width(PRISM_ATTRIBUTE_UNUSED const uint8_t *b, PRISM_ATT static size_t pm_encoding_euc_jp_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4115,6 +4144,9 @@ pm_encoding_euc_jp_isupper_char(const uint8_t *b, ptrdiff_t n) { */ static size_t pm_encoding_shift_jis_char_width(const uint8_t *b, ptrdiff_t n) { + if (n == 0) { + return 0; + } // These are the single byte characters. if (b[0] < 0x80 || (b[0] >= 0xA1 && b[0] <= 0xDF)) { return 1; @@ -4178,7 +4210,7 @@ pm_encoding_shift_jis_isupper_char(const uint8_t *b, ptrdiff_t n) { */ static bool pm_encoding_ascii_isupper_char_7bit(const uint8_t *b, ptrdiff_t n) { - return (*b < 0x80) && pm_encoding_ascii_isupper_char(b, n); + return (n > 0) && (*b < 0x80) && pm_encoding_ascii_isupper_char(b, n); } /** @@ -4188,7 +4220,7 @@ pm_encoding_ascii_isupper_char_7bit(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_big5_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4207,7 +4239,7 @@ pm_encoding_big5_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_cp949_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters - if (*b <= 0x80) { + if ((n > 0) && (*b <= 0x80)) { return 1; } @@ -4226,7 +4258,7 @@ pm_encoding_cp949_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_emacs_mule_char_width(const uint8_t *b, ptrdiff_t n) { // These are the 1 byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4269,7 +4301,7 @@ pm_encoding_emacs_mule_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_euc_kr_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4288,7 +4320,7 @@ pm_encoding_euc_kr_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_euc_tw_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4312,7 +4344,7 @@ pm_encoding_euc_tw_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_gb18030_char_width(const uint8_t *b, ptrdiff_t n) { // These are the 1 byte characters. - if (*b < 0x80) { + if ((n > 0) && (*b < 0x80)) { return 1; } @@ -4336,7 +4368,7 @@ pm_encoding_gb18030_char_width(const uint8_t *b, ptrdiff_t n) { static size_t pm_encoding_gbk_char_width(const uint8_t *b, ptrdiff_t n) { // These are the single byte characters. - if (*b <= 0x80) { + if ((n > 0) && (*b <= 0x80)) { return 1; } From be12e19856c95ac722efa944af74122a9b2a6bef Mon Sep 17 00:00:00 2001 From: Steven Johnstone Date: Fri, 5 Dec 2025 17:44:00 +0000 Subject: [PATCH 195/367] [ruby/prism] Avoid undefined int overflow behaviour Fixes https://github.com/ruby/prism/pull/3786. https://github.com/ruby/prism/commit/b72b664675 --- prism/templates/src/serialize.c.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/templates/src/serialize.c.erb b/prism/templates/src/serialize.c.erb index 3e15a11039ef94..0f0aace445a680 100644 --- a/prism/templates/src/serialize.c.erb +++ b/prism/templates/src/serialize.c.erb @@ -315,7 +315,7 @@ pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) // buffer offset. We will add a leading 1 to indicate that this // is a buffer offset. uint32_t content_offset = pm_sizet_to_u32(buffer->length); - uint32_t owned_mask = (uint32_t) (1 << 31); + uint32_t owned_mask = 1U << 31; assert(content_offset < owned_mask); content_offset |= owned_mask; From 786f67393875b0a5089da73504ac57179e8ef829 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 5 Dec 2025 15:17:09 -0500 Subject: [PATCH 196/367] [ruby/prism] Correct constant pool bucket type logic When replacing an owned constant by a different type (constant or shared) replace with the correct type instead of defaulting to shared. https://github.com/ruby/prism/commit/fbe9b131a1 --- prism/util/pm_constant_pool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prism/util/pm_constant_pool.c b/prism/util/pm_constant_pool.c index 38ea01a2289d65..922ce6a18cfaff 100644 --- a/prism/util/pm_constant_pool.c +++ b/prism/util/pm_constant_pool.c @@ -264,7 +264,7 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l // constant and replace it with the shared constant. xfree((void *) constant->start); constant->start = start; - bucket->type = (unsigned int) (PM_CONSTANT_POOL_BUCKET_DEFAULT & 0x3); + bucket->type = (unsigned int) (type & 0x3); } return bucket->id; From ee7923288fd4915199954f615980e516750cb8bb Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 2 Dec 2025 21:50:06 -0500 Subject: [PATCH 197/367] ZJIT: Skip GC.auto_compact test when unsupported --- test/ruby/test_zjit.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 00550bbe2058e2..45ee42e6a29162 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -2421,6 +2421,7 @@ def test_require_rubygems end def test_require_rubygems_with_auto_compact + omit("GC.auto_compact= support is required for this test") unless GC.respond_to?(:auto_compact=) assert_runs 'true', %q{ GC.auto_compact = true require 'rubygems' From 3269ae1b0d1aaa78d12b1527a2f6b095e148c5d3 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 2 Dec 2025 22:11:20 -0500 Subject: [PATCH 198/367] ZJIT: Fix -Wpedantic warning in C99 mode when built with YJIT > insns.def:857:5: error: assigning to 'rb_zjit_func_t' (aka 'unsigned > long (*)(struct rb_execution_context_struct *, struct > rb_control_frame_struct *, unsigned long (*)(struct > rb_execution_context_struct *, struct rb_control_frame_struct *))') from > 'void *' converts between void pointer and function pointer > [-Werror,-Wpedantic] --- vm_exec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm_exec.h b/vm_exec.h index 033a48f1e7683c..641ace4eaf29b9 100644 --- a/vm_exec.h +++ b/vm_exec.h @@ -185,7 +185,7 @@ default: \ if (ec->tag->state) THROW_EXCEPTION(val); \ } \ } \ - else if ((zjit_entry = rb_zjit_entry)) { \ + else if ((zjit_entry = (rb_zjit_func_t)rb_zjit_entry)) { \ rb_jit_func_t func = zjit_compile(ec); \ if (func) { \ val = zjit_entry(ec, ec->cfp, func); \ From 7ecd369a87d65879f98d95d726772238c1dabc16 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 14:31:48 -0500 Subject: [PATCH 199/367] ZJIT: Account for when YJIT is on by default in test_zjit_enable --- test/ruby/test_zjit.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 45ee42e6a29162..03d2461fa0a0af 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -60,7 +60,9 @@ def test_enable_through_env end def test_zjit_enable - assert_separately([], <<~'RUBY') + # --disable-all is important in case the build/environment has YJIT enabled by + # default through e.g. -DYJIT_FORCE_ENABLE. Can't enable ZJIT when YJIT is on. + assert_separately(["--disable-all"], <<~'RUBY') refute_predicate RubyVM::ZJIT, :enabled? refute_predicate RubyVM::ZJIT, :stats_enabled? refute_includes RUBY_DESCRIPTION, "+ZJIT" From 02ca507aa3fe45640ace494e5440d9e8bfd5a517 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 15:05:01 -0500 Subject: [PATCH 200/367] JITs: rb_iseq_opcode_at_pc(): Accommodate switch-case interpreter --- jit.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jit.c b/jit.c index a886b66278c2aa..ae592b2205c0be 100644 --- a/jit.c +++ b/jit.c @@ -52,8 +52,11 @@ rb_iseq_pc_at_idx(const rb_iseq_t *iseq, uint32_t insn_idx) int rb_iseq_opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc) { - // YJIT should only use iseqs after AST to bytecode compilation - RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)); + // YJIT should only use iseqs after AST to bytecode compilation. + // (Certain non-default interpreter configurations never set ISEQ_TRANSLATED) + if (OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE) { + RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)); + } const VALUE at_pc = *pc; return rb_vm_insn_addr2opcode((const void *)at_pc); From f01fd2bde2c79dd4f3e23154c953baca623b6460 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 15:41:21 -0500 Subject: [PATCH 201/367] JITs: Update bindings to include interpreter zjit_ opcodes Mostly YJIT. ZJIT already has the right bindings and this just tweaks the CI configuration. --- .github/workflows/yjit-ubuntu.yml | 3 ++- .github/workflows/zjit-ubuntu.yml | 3 ++- yjit/src/cruby_bindings.inc.rs | 32 ++++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 00214709b93e65..7dce69cda4ea78 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -81,7 +81,8 @@ jobs: include: - test_task: 'yjit-bindgen' hint: 'To fix: use patch in logs' - configure: '--with-gcc=clang-14 --enable-yjit=dev' + # Build with YJIT+ZJIT for output that works in the most number of configurations + configure: '--with-gcc=clang-14 --enable-yjit=dev --enable-zjit' libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1' - test_task: 'check' diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 5d281eab0b9947..37a9000d704c5a 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -73,7 +73,8 @@ jobs: - test_task: 'zjit-bindgen' hint: 'To fix: use patch in logs' - configure: '--enable-zjit=dev --with-gcc=clang-16' + # Build with YJIT+ZJIT for output that works in the most number of configurations + configure: '--enable-zjit=dev --enable-yjit --with-gcc=clang-16' clang_path: '/usr/bin/clang-16' runs-on: 'ubuntu-24.04' # for clang-16 diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index b9a8197184a21a..0bd2cefc08acf2 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -919,7 +919,37 @@ pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 214; pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 215; pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 216; pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 217; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 218; +pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 218; +pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 219; +pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 220; +pub const YARVINSN_zjit_send: ruby_vminsn_type = 221; +pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 222; +pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 223; +pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 224; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 247; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 248; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), From 8132b3d1d8abbe38810a1e268c59b6d49e46d9a1 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 21:15:04 -0500 Subject: [PATCH 202/367] YJIT: Fix including stats for ZJIT instructions when ZJIT not in build --- yjit.c | 9 +++++++++ yjit/bindgen/src/main.rs | 1 + yjit/src/cruby_bindings.inc.rs | 1 + yjit/src/stats.rs | 7 +++++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/yjit.c b/yjit.c index 4b78cfbae25a25..16debf6eca5555 100644 --- a/yjit.c +++ b/yjit.c @@ -521,6 +521,15 @@ rb_yjit_set_exception_return(rb_control_frame_t *cfp, void *leave_exit, void *le } } +// VM_INSTRUCTION_SIZE changes depending on if ZJIT is in the build. Since +// bindgen can only grab one version of the constant and copy that to rust, +// we make that the upper bound and this the accurate value. +uint32_t +rb_vm_instruction_size(void) +{ + return VM_INSTRUCTION_SIZE; +} + // Primitives used by yjit.rb VALUE rb_yjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self); VALUE rb_yjit_print_stats_p(rb_execution_context_t *ec, VALUE self); diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 9c28177a6054b2..8d11b4216ef0b0 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -304,6 +304,7 @@ fn main() { .allowlist_function("rb_mod_name") .allowlist_function("rb_const_get") .allowlist_var("rb_vm_insn_count") + .allowlist_function("rb_vm_instruction_size") .allowlist_function("rb_get_alloc_func") .allowlist_function("rb_class_allocate_instance") .allowlist_function("rb_obj_equal") diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 0bd2cefc08acf2..b9e5fb3ab12843 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1204,6 +1204,7 @@ extern "C" { leave_exit: *mut ::std::os::raw::c_void, leave_exception: *mut ::std::os::raw::c_void, ); + pub fn rb_vm_instruction_size() -> u32; pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_iseq_pc_at_idx(iseq: *const rb_iseq_t, insn_idx: u32) -> *mut VALUE; pub fn rb_iseq_opcode_at_pc(iseq: *const rb_iseq_t, pc: *const VALUE) -> ::std::os::raw::c_int; diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index e8ce7b8b353d63..4f23d97bce7360 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -90,7 +90,9 @@ pub extern "C" fn incr_iseq_counter(idx: usize) { iseq_call_count[idx] += 1; } -// YJIT exit counts for each instruction type +/// YJIT exit counts for each instruction type. +/// Note that `VM_INSTRUCTION_SIZE` is an upper bound and the actual number +/// of VM opcodes may be different in the build. See [`rb_vm_instruction_size()`] const VM_INSTRUCTION_SIZE_USIZE: usize = VM_INSTRUCTION_SIZE as usize; static mut EXIT_OP_COUNT: [u64; VM_INSTRUCTION_SIZE_USIZE] = [0; VM_INSTRUCTION_SIZE_USIZE]; @@ -804,7 +806,8 @@ fn rb_yjit_gen_stats_dict(key: VALUE) -> VALUE { // For each entry in exit_op_count, add a stats entry with key "exit_INSTRUCTION_NAME" // and the value is the count of side exits for that instruction. - for op_idx in 0..VM_INSTRUCTION_SIZE_USIZE { + use crate::utils::IntoUsize; + for op_idx in 0..rb_vm_instruction_size().as_usize() { let op_name = insn_name(op_idx); let key_string = "exit_".to_owned() + &op_name; let count = EXIT_OP_COUNT[op_idx]; From 109ddd291ebc4f07f786e7b4ed277b20e3faaa8a Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 16:03:23 -0500 Subject: [PATCH 203/367] ZJIT: Avoid binding to `rb_iseq_constant_body` Its definition changes depending on e.g. whether there is YJIT in the build. --- zjit.c | 4 ++++ zjit/bindgen/src/main.rs | 2 ++ zjit/src/cruby.rs | 13 +++++++++++- zjit/src/cruby_bindings.inc.rs | 36 ++-------------------------------- 4 files changed, 20 insertions(+), 35 deletions(-) diff --git a/zjit.c b/zjit.c index b8a89aad1a8498..d62c5c58c6334c 100644 --- a/zjit.c +++ b/zjit.c @@ -31,6 +31,10 @@ #include +enum zjit_struct_offsets { + ISEQ_BODY_OFFSET_PARAM = offsetof(struct rb_iseq_constant_body, param) +}; + #define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) // For a given raw_sample (frame), set the hash with the caller's diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index e07953ffd5df08..30e85149744e22 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -301,6 +301,7 @@ fn main() { .allowlist_function("rb_zjit_defined_ivar") .allowlist_function("rb_zjit_insn_leaf") .allowlist_type("jit_bindgen_constants") + .allowlist_type("zjit_struct_offsets") .allowlist_function("rb_assert_holding_vm_lock") .allowlist_function("rb_jit_shape_too_complex_p") .allowlist_function("rb_jit_multi_ractor_p") @@ -428,6 +429,7 @@ fn main() { // We define these manually, don't import them .blocklist_type("VALUE") .blocklist_type("ID") + .blocklist_type("rb_iseq_constant_body") // Avoid binding to stuff we don't use .blocklist_item("rb_thread_struct.*") diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index b255c28e52cd17..e653602874df77 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -231,6 +231,16 @@ pub fn insn_len(opcode: usize) -> u32 { } } +/// We avoid using bindgen for `rb_iseq_constant_body` since its definition changes depending +/// on build configuration while we need one bindgen file that works for all configurations. +/// Use an opaque type for it instead. +/// See: +#[repr(C)] +pub struct rb_iseq_constant_body { + _data: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + /// An object handle similar to VALUE in the C code. Our methods assume /// that this is a handle. Sometimes the C code briefly uses VALUE as /// an unsigned integer type and don't necessarily store valid handles but @@ -683,7 +693,8 @@ pub trait IseqAccess { impl IseqAccess for IseqPtr { /// Get a description of the ISEQ's signature. Analogous to `ISEQ_BODY(iseq)->param` in C. unsafe fn params<'a>(self) -> &'a IseqParameters { - unsafe { &(*(*self).body).param } + use crate::cast::IntoUsize; + unsafe { &*((*self).body.byte_add(ISEQ_BODY_OFFSET_PARAM.to_usize()) as *const IseqParameters) } } } diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index a80f3d83c5f414..aed35c3c636846 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -595,40 +595,6 @@ pub type rb_jit_func_t = ::std::option::Option< ) -> VALUE, >; #[repr(C)] -pub struct rb_iseq_constant_body { - pub type_: rb_iseq_type, - pub iseq_size: ::std::os::raw::c_uint, - pub iseq_encoded: *mut VALUE, - pub param: rb_iseq_constant_body_rb_iseq_parameters, - pub location: rb_iseq_location_t, - pub insns_info: rb_iseq_constant_body_iseq_insn_info, - pub local_table: *const ID, - pub lvar_states: *mut rb_iseq_constant_body_lvar_state, - pub catch_table: *mut iseq_catch_table, - pub parent_iseq: *const rb_iseq_struct, - pub local_iseq: *mut rb_iseq_struct, - pub is_entries: *mut iseq_inline_storage_entry, - pub call_data: *mut rb_call_data, - pub variable: rb_iseq_constant_body__bindgen_ty_1, - pub local_table_size: ::std::os::raw::c_uint, - pub ic_size: ::std::os::raw::c_uint, - pub ise_size: ::std::os::raw::c_uint, - pub ivc_size: ::std::os::raw::c_uint, - pub icvarc_size: ::std::os::raw::c_uint, - pub ci_size: ::std::os::raw::c_uint, - pub stack_max: ::std::os::raw::c_uint, - pub builtin_attrs: ::std::os::raw::c_uint, - pub prism: bool, - pub mark_bits: rb_iseq_constant_body__bindgen_ty_2, - pub outer_variables: *mut rb_id_table, - pub mandatory_only_iseq: *const rb_iseq_t, - pub jit_entry: rb_jit_func_t, - pub jit_entry_calls: ::std::os::raw::c_ulong, - pub jit_exception: rb_jit_func_t, - pub jit_exception_calls: ::std::os::raw::c_ulong, - pub zjit_payload: *mut ::std::os::raw::c_void, -} -#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct rb_iseq_constant_body_rb_iseq_parameters { pub flags: rb_iseq_constant_body_rb_iseq_parameters__bindgen_ty_1, @@ -1866,6 +1832,8 @@ pub const DEFINED_REF: defined_type = 15; pub const DEFINED_FUNC: defined_type = 16; pub const DEFINED_CONST_FROM: defined_type = 17; pub type defined_type = u32; +pub const ISEQ_BODY_OFFSET_PARAM: zjit_struct_offsets = 16; +pub type zjit_struct_offsets = u32; pub const ROBJECT_OFFSET_AS_HEAP_FIELDS: jit_bindgen_constants = 16; pub const ROBJECT_OFFSET_AS_ARY: jit_bindgen_constants = 16; pub const RUBY_OFFSET_RSTRING_LEN: jit_bindgen_constants = 16; From fb72ff7be0b927cdad518da4eca041c191a91404 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 16:14:40 -0500 Subject: [PATCH 204/367] CI: Avoid building ZJIT when LLVM is too old --- .github/workflows/compilers.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 97a698613a2f10..5e54d39e9fa6b9 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -132,11 +132,11 @@ jobs: - { uses: './.github/actions/compilers', name: 'clang 12', with: { tag: 'clang-12' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 11', with: { tag: 'clang-11' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'clang 10', with: { tag: 'clang-10' }, timeout-minutes: 5 } - # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o. - - { uses: './.github/actions/compilers', name: 'clang 9', with: { tag: 'clang-9', append_configure: '--disable-yjit' }, timeout-minutes: 5 } - - { uses: './.github/actions/compilers', name: 'clang 8', with: { tag: 'clang-8', append_configure: '--disable-yjit' }, timeout-minutes: 5 } - - { uses: './.github/actions/compilers', name: 'clang 7', with: { tag: 'clang-7', append_configure: '--disable-yjit' }, timeout-minutes: 5 } - - { uses: './.github/actions/compilers', name: 'clang 6', with: { tag: 'clang-6.0', append_configure: '--disable-yjit' }, timeout-minutes: 5 } + # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o and fail `make test-leaked-globals`. + - { uses: './.github/actions/compilers', name: 'clang 9', with: { tag: 'clang-9', append_configure: '--disable-yjit --disable-zjit' }, timeout-minutes: 5 } + - { uses: './.github/actions/compilers', name: 'clang 8', with: { tag: 'clang-8', append_configure: '--disable-yjit --disable-zjit' }, timeout-minutes: 5 } + - { uses: './.github/actions/compilers', name: 'clang 7', with: { tag: 'clang-7', append_configure: '--disable-yjit --disable-zjit' }, timeout-minutes: 5 } + - { uses: './.github/actions/compilers', name: 'clang 6', with: { tag: 'clang-6.0', append_configure: '--disable-yjit --disable-zjit' }, timeout-minutes: 5 } compile5: name: 'omnibus compilations, #5' From 2bc9b5a85454d2536d18be32e0302842bde18a3f Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 16:55:26 -0500 Subject: [PATCH 205/367] tool/update-deps: Skip ZJIT and YJIT+ZJIT build objects --- tool/update-deps | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tool/update-deps b/tool/update-deps index c927d2483e9a7a..2d4a5674be72a7 100755 --- a/tool/update-deps +++ b/tool/update-deps @@ -326,6 +326,8 @@ def read_make_deps(cwd) deps.delete_if {|dep| /\.time\z/ =~ dep} # skip timestamp next if /\.o\z/ !~ target.to_s next if /libyjit.o\z/ =~ target.to_s # skip YJIT Rust object (no corresponding C source) + next if /libzjit.o\z/ =~ target.to_s # skip ZJIT Rust object (no corresponding C source) + next if /target\/release\/libruby.o\z/ =~ target.to_s # skip YJIT+ZJIT Rust object (no corresponding C source) next if /\.bundle\// =~ target.to_s next if /\A\./ =~ target.to_s # skip rules such as ".c.o" #p [curdir, target, deps] From 8296524fd6cf2fb9ae1eca4be722fe9ee2c26f37 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 17:16:27 -0500 Subject: [PATCH 206/367] ZJIT: Fix duplicate make rule warning in combo build ~/zjit/zjit.mk:30: warning: overriding commands for target `~/build-default/' ~/yjit/yjit.mk:26: warning: ignoring old commands for target `~/build-default/' ~/zjit/zjit.mk:30: warning: overriding commands for target `~/build-default/' ~/yjit/yjit.mk:26: warning: ignoring old commands for target `~/build-default/' --- zjit/zjit.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zjit/zjit.mk b/zjit/zjit.mk index f31b948845678a..01274dc3073642 100644 --- a/zjit/zjit.mk +++ b/zjit/zjit.mk @@ -24,8 +24,8 @@ ZJIT_LIB_TOUCH = touch $@ # the "target" dir in the source directory through VPATH. BUILD_ZJIT_LIBS = $(TOP_BUILD_DIR)/$(ZJIT_LIBS) -# ZJIT_SUPPORT=yes when `configure` gets `--enable-zjit` -ifeq ($(ZJIT_SUPPORT),yes) +# In a ZJIT-only build (no YJIT) +ifneq ($(strip $(ZJIT_LIBS)),) $(BUILD_ZJIT_LIBS): $(ZJIT_SRC_FILES) $(ECHO) 'building Rust ZJIT (release mode)' +$(Q) $(RUSTC) $(ZJIT_RUSTC_ARGS) From addeafdd5b9e2404eef555f523bc92a6c373d294 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 17:20:44 -0500 Subject: [PATCH 207/367] YJIT: Fix duplicate make rule warning in combo build --- yjit/yjit.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yjit/yjit.mk b/yjit/yjit.mk index cf68edb29770b8..e019e4a08cf7b8 100644 --- a/yjit/yjit.mk +++ b/yjit/yjit.mk @@ -19,8 +19,8 @@ YJIT_LIB_TOUCH = touch $@ # the "target" dir in the source directory through VPATH. BUILD_YJIT_LIBS = $(TOP_BUILD_DIR)/$(YJIT_LIBS) -# YJIT_SUPPORT=yes when `configure` gets `--enable-yjit` -ifeq ($(YJIT_SUPPORT),yes) +# In a YJIT-only build (no ZJIT) +ifneq ($(strip $(YJIT_LIBS)),) yjit-libs: $(BUILD_YJIT_LIBS) $(BUILD_YJIT_LIBS): $(YJIT_SRC_FILES) $(ECHO) 'building Rust YJIT (release mode)' From dce716e25b5101cc57ab0699db9265d961d59d82 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 20:26:51 -0500 Subject: [PATCH 208/367] ZJIT: Update `depend` for zjit.o --- depend | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/depend b/depend index 569f14a04da5bf..81ab8274710947 100644 --- a/depend +++ b/depend @@ -20241,6 +20241,7 @@ zjit.$(OBJEXT): $(top_srcdir)/internal/array.h zjit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h zjit.$(OBJEXT): $(top_srcdir)/internal/bignum.h zjit.$(OBJEXT): $(top_srcdir)/internal/bits.h +zjit.$(OBJEXT): $(top_srcdir)/internal/box.h zjit.$(OBJEXT): $(top_srcdir)/internal/class.h zjit.$(OBJEXT): $(top_srcdir)/internal/compile.h zjit.$(OBJEXT): $(top_srcdir)/internal/compilers.h @@ -20252,6 +20253,7 @@ zjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h zjit.$(OBJEXT): $(top_srcdir)/internal/numeric.h zjit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h zjit.$(OBJEXT): $(top_srcdir)/internal/serial.h +zjit.$(OBJEXT): $(top_srcdir)/internal/set_table.h zjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h zjit.$(OBJEXT): $(top_srcdir)/internal/string.h zjit.$(OBJEXT): $(top_srcdir)/internal/variable.h @@ -20428,6 +20430,7 @@ zjit.$(OBJEXT): {$(VPATH)}internal/intern/re.h zjit.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h zjit.$(OBJEXT): {$(VPATH)}internal/intern/select.h zjit.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h +zjit.$(OBJEXT): {$(VPATH)}internal/intern/set.h zjit.$(OBJEXT): {$(VPATH)}internal/intern/signal.h zjit.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h zjit.$(OBJEXT): {$(VPATH)}internal/intern/string.h @@ -20479,6 +20482,7 @@ zjit.$(OBJEXT): {$(VPATH)}vm_debug.h zjit.$(OBJEXT): {$(VPATH)}vm_insnhelper.h zjit.$(OBJEXT): {$(VPATH)}vm_opts.h zjit.$(OBJEXT): {$(VPATH)}vm_sync.h +zjit.$(OBJEXT): {$(VPATH)}yjit.h zjit.$(OBJEXT): {$(VPATH)}zjit.c zjit.$(OBJEXT): {$(VPATH)}zjit.h zjit.$(OBJEXT): {$(VPATH)}zjit.rbinc From 9a2710013c52d81174fa289b9002df1be379d39d Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 21:16:23 -0500 Subject: [PATCH 209/367] YJIT: Fix unused_unsafe warning in `StatsAlloc` --- jit/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/jit/src/lib.rs b/jit/src/lib.rs index 6079d00f2fd886..c0f043131e0d74 100644 --- a/jit/src/lib.rs +++ b/jit/src/lib.rs @@ -1,4 +1,5 @@ //! Shared code between YJIT and ZJIT. +#![warn(unsafe_op_in_unsafe_fn)] // Adopt 2024 edition default when targeting 2021 editions use std::sync::atomic::{AtomicUsize, Ordering}; use std::alloc::{GlobalAlloc, Layout, System}; From ffe99a56de887bbc24e6ff63cd93f6a88fdd1cf4 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Mon, 1 Dec 2025 22:57:04 -0500 Subject: [PATCH 210/367] ZJIT: configure.ac logic to detect suitable build environment This runs the detection, but does nothing with the result. * Fixed version requirement in messages -- ZJIT requires >= 1.85 unlike YJIT. * New: Detect when rust 1.85 is available, and neither --enable-yjit nor --enable-zjit is passed to ./configure, include both YJIT and ZJIT in the build --- configure.ac | 55 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/configure.ac b/configure.ac index 21f2e5a466e382..ffdf86e1da21ef 100644 --- a/configure.ac +++ b/configure.ac @@ -3875,12 +3875,13 @@ AC_SUBST(INSTALL_STATIC_LIBRARY) [begin]_group "JIT section" && { AC_CHECK_PROG(RUSTC, [rustc], [rustc], [no]) dnl no ac_tool_prefix +AC_CHECK_TOOL(CARGO, [cargo], [no]) -dnl check if rustc is recent enough to build YJIT/ZJIT (rustc >= 1.58.0) +dnl check if rustc is recent enough to build YJIT (rustc >= 1.58.0) JIT_RUSTC_OK=no +JIT_TARGET_ARCH= AS_IF([test "$RUSTC" != "no"], - AC_MSG_CHECKING([whether ${RUSTC} works for YJIT/ZJIT]) - JIT_TARGET_ARCH= + AC_MSG_CHECKING([whether ${RUSTC} works for YJIT]) AS_CASE(["$target_cpu"], [arm64|aarch64], [JIT_TARGET_ARCH=aarch64], [x86_64], [JIT_TARGET_ARCH=x86_64], @@ -3914,33 +3915,46 @@ AS_IF([test "$cross_compiling" = no], ) ) -dnl build ZJIT in release mode if rustc >= 1.58.0 is present and we are on a supported platform -AC_ARG_ENABLE(zjit, - AS_HELP_STRING([--enable-zjit], - [enable in-process JIT compiler that requires Rust build tools. enabled by default on supported platforms if rustc 1.58.0+ is available]), - [ZJIT_SUPPORT=$enableval], - [AS_CASE(["$JIT_TARGET_OK:$JIT_RUSTC_OK:$YJIT_SUPPORT"], - [yes:yes:no], [ - ZJIT_SUPPORT=yes - ], - [ZJIT_SUPPORT=no] - )] -) - dnl build YJIT in release mode if rustc >= 1.58.0 is present and we are on a supported platform AC_ARG_ENABLE(yjit, AS_HELP_STRING([--enable-yjit], [enable in-process JIT compiler that requires Rust build tools. enabled by default on supported platforms if rustc 1.58.0+ is available]), [YJIT_SUPPORT=$enableval], - [AS_CASE(["$JIT_TARGET_OK:$JIT_RUSTC_OK:$ZJIT_SUPPORT"], - [yes:yes:no], [ + [AS_CASE(["$JIT_TARGET_OK:$JIT_RUSTC_OK"], + [yes:yes], [ YJIT_SUPPORT=yes ], [YJIT_SUPPORT=no] )] ) -CARGO= +dnl build ZJIT in release mode if rustc >= 1.85.0 is present and we are on a supported platform +ZJIT_SUPPORT=no +AC_ARG_ENABLE(zjit, + AS_HELP_STRING([--enable-zjit], + [enable experimental JIT compiler that requires Rust build tools. enabled by default on supported platforms if rustc 1.85.0+ is available]), + [ZJIT_SUPPORT=$enableval], + [AS_CASE(["$JIT_TARGET_OK"], + [yes], [ + rb_zjit_build_possible=no + AC_MSG_CHECKING([possible to build ZJIT])dnl only checked when --enable-zjit is not specified + # Fails in case rustc target doesn't match ruby target. Can happen on Rosetta, for example. + # 1.85.0 is the first stable version that supports the 2024 edition. + AS_IF([test "$RUSTC" != "no" && echo "#[cfg(target_arch = \"$JIT_TARGET_ARCH\")] fn main() {}" | + $RUSTC - --edition=2024 --emit asm=/dev/null 2>/dev/null], + AS_IF([test "$YJIT_SUPPORT" = "no" -o "$CARGO" != "no"], [ + # When only building ZJIT, we don't need cargo; it's required for YJIT+ZJIT build. + # Assume that if rustc is new enough, then cargo is also. + # TODO(alan): Get rid of dependency on cargo in YJIT+ZJIT build. Cargo's offline mode + # still too unreliable: https://github.com/rust-lang/cargo/issues/10352 + rb_zjit_build_possible=yes + ]) + ) + AC_MSG_RESULT($rb_zjit_build_possible) + ] + )] +) + CARGO_BUILD_ARGS= YJIT_LIBS= JIT_CARGO_SUPPORT=no @@ -4019,9 +4033,8 @@ AS_CASE(["${ZJIT_SUPPORT}"], # if YJIT+ZJIT release build, or any build that requires Cargo AS_IF([test x"$JIT_CARGO_SUPPORT" != "xno" -o \( x"$YJIT_SUPPORT" != "xno" -a x"$ZJIT_SUPPORT" != "xno" \)], [ - AC_CHECK_TOOL(CARGO, [cargo], [no]) AS_IF([test x"$CARGO" = "xno"], - AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install])) + AC_MSG_ERROR([this build configuration requires cargo. Installation instructions available at https://www.rust-lang.org/tools/install])) YJIT_LIBS= ZJIT_LIBS= From f559a9106c703c3ecadefa2f2eb26290b9ffa7fe Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 3 Dec 2025 16:55:55 -0500 Subject: [PATCH 211/367] ZJIT: configure.ac: Look for GNU make when detecting build environment Building ZJIT requires GNU make at the moment. To get access to `$gnumake`, lift the `make` flavour detection up to the environment section, before the JIT section runs. --- configure.ac | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index ffdf86e1da21ef..9a35917cac88d3 100644 --- a/configure.ac +++ b/configure.ac @@ -296,6 +296,8 @@ AC_CHECK_TOOLS([OBJCOPY], [gobjcopy objcopy], [:]) AC_CHECK_TOOLS([OBJDUMP], [gobjdump objdump]) AC_CHECK_TOOLS([STRIP], [gstrip strip], [:]) +FIRSTMAKEFILE="" + # nm errors with Rust's LLVM bitcode when Rust uses a newer LLVM version than nm. # In case we're working with llvm-nm, tell it to not worry about the bitcode. AS_IF([${NM} --help 2>&1 | grep -q 'llvm-bc'], [NM="$NM --no-llvm-bc"]) @@ -470,6 +472,8 @@ AS_CASE(["$target_os"], # so wrap clang to insert our fake wasm-opt, which does nothing, in PATH. CC_WRAPPER=`cd -P "${tooldir}" && pwd`/wasm-clangw CC="$CC_WRAPPER $CC" + + FIRSTMAKEFILE=GNUmakefile:wasm/GNUmakefile.in ]) cc_version= @@ -511,6 +515,8 @@ AS_CASE(["$target_os"], target_cpu=`echo $target_cpu | sed s/i.86/i386/` AS_CASE(["$target"], [-*], [ target="$target_cpu${target}"]) AS_CASE(["$target_alias"], [-*], [ target_alias="$target_cpu${target_alias}"]) + # cygwin/GNUmakefile.in is not exclusively for cygwin. + FIRSTMAKEFILE=GNUmakefile:cygwin/GNUmakefile.in AS_CASE(["$target_os"], [mingw*], [ test "$rb_cv_msvcrt" = "" && unset rb_cv_msvcrt @@ -613,6 +619,22 @@ AS_IF([test -f conf$$.dir/src/cdcmd], [ rm -fr conf$$.dir AC_MSG_RESULT([$CHDIR]) AC_SUBST(CHDIR) + +AS_CASE(["$FIRSTMAKEFILE"], [*GNUmakefile:*], [gnumake=yes], [ + AC_MSG_CHECKING([if ${MAKE-make} is GNU make]) + mkdir conftest.dir + echo "all:; @echo yes" > conftest.dir/GNUmakefile + echo "all:; @echo no" > conftest.dir/Makefile + gnumake=`(cd conftest.dir; ${MAKE-make})` + rm -fr conftest.dir + AS_CASE(["$gnumake"], + [*yes*], [ + FIRSTMAKEFILE=GNUmakefile:template/GNUmakefile.in + gnumake=yes], + [ + gnumake=no]) + AC_MSG_RESULT($gnumake) +]) } [begin]_group "compiler section" && { @@ -3512,7 +3534,6 @@ AC_SUBST(RUNRUBY) AC_SUBST(XRUBY) AC_SUBST(EXTOUT, [${EXTOUT=.ext}]) -FIRSTMAKEFILE="" LIBRUBY_A='lib$(RUBY_SO_NAME)-static.a' LIBRUBY='$(LIBRUBY_A)' LIBRUBYARG_STATIC='-l$(RUBY_SO_NAME)-static' @@ -3942,7 +3963,7 @@ AC_ARG_ENABLE(zjit, # 1.85.0 is the first stable version that supports the 2024 edition. AS_IF([test "$RUSTC" != "no" && echo "#[cfg(target_arch = \"$JIT_TARGET_ARCH\")] fn main() {}" | $RUSTC - --edition=2024 --emit asm=/dev/null 2>/dev/null], - AS_IF([test "$YJIT_SUPPORT" = "no" -o "$CARGO" != "no"], [ + AS_IF([test "$gnumake" = "yes" -a \( "$YJIT_SUPPORT" = "no" -o "$CARGO" != "no" \)], [ # When only building ZJIT, we don't need cargo; it's required for YJIT+ZJIT build. # Assume that if rustc is new enough, then cargo is also. # TODO(alan): Get rid of dependency on cargo in YJIT+ZJIT build. Cargo's offline mode @@ -4200,7 +4221,6 @@ enum { PLATFORM_DIR=win32 ]) LIBRUBY_ALIASES='' - FIRSTMAKEFILE=GNUmakefile:cygwin/GNUmakefile.in AS_IF([test x"$enable_shared" = xyes], [ LIBRUBY='lib$(RUBY_SO_NAME).dll.a' ], [ @@ -4210,7 +4230,6 @@ enum { ]) ], [wasi*], [ - FIRSTMAKEFILE=GNUmakefile:wasm/GNUmakefile.in AC_LIBOBJ([wasm/missing]) AC_LIBOBJ([wasm/runtime]) AC_LIBOBJ([wasm/fiber]) @@ -4227,21 +4246,6 @@ AC_ARG_ENABLE(debug-env, AS_HELP_STRING([--enable-debug-env], [enable RUBY_DEBUG environment variable]), [AC_SUBST(ENABLE_DEBUG_ENV, yes)]) -AS_CASE(["$FIRSTMAKEFILE"], [*GNUmakefile:*], [gnumake=yes], [ - AC_MSG_CHECKING([if ${MAKE-make} is GNU make]) - mkdir conftest.dir - echo "all:; @echo yes" > conftest.dir/GNUmakefile - echo "all:; @echo no" > conftest.dir/Makefile - gnumake=`(cd conftest.dir; ${MAKE-make})` - rm -fr conftest.dir - AS_CASE(["$gnumake"], - [*yes*], [ - FIRSTMAKEFILE=GNUmakefile:template/GNUmakefile.in - gnumake=yes], - [ - gnumake=no]) - AC_MSG_RESULT($gnumake) -]) AS_IF([test "$gnumake" = yes], [ NULLCMD=: ], [ AC_MSG_CHECKING([for safe null command for ${MAKE-make}]) mkdir conftest.dir From d396a66a82992d4ff3f4d52a6e7c493b560ae9b2 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 4 Dec 2025 22:43:28 -0500 Subject: [PATCH 212/367] ZJIT: Build by default when build environment allows "Default" means when `--enable-zjit` is absent from `./configure` arguments. --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 9a35917cac88d3..14b0234ef0f9aa 100644 --- a/configure.ac +++ b/configure.ac @@ -3972,6 +3972,7 @@ AC_ARG_ENABLE(zjit, ]) ) AC_MSG_RESULT($rb_zjit_build_possible) + ZJIT_SUPPORT=$rb_zjit_build_possible ] )] ) From c4c909b538a2114491be0785ec4aa3d26d2916c4 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 5 Dec 2025 22:00:24 +0000 Subject: [PATCH 213/367] ZJIT: Include local variable names in `Get|SetLocal` insn's print value (#15423) ZJIT: Print local variable names GetLocal and SetLocal instructions --- zjit/src/hir.rs | 57 ++++- zjit/src/hir/opt_tests.rs | 452 +++++++++++++++++++------------------- zjit/src/hir/tests.rs | 324 +++++++++++++-------------- 3 files changed, 433 insertions(+), 400 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 32d6f63b9ed74c..4323b145feb1b6 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -956,8 +956,8 @@ impl Insn { } } - pub fn print<'a>(&self, ptr_map: &'a PtrPrintMap) -> InsnPrinter<'a> { - InsnPrinter { inner: self.clone(), ptr_map } + pub fn print<'a>(&self, ptr_map: &'a PtrPrintMap, iseq: Option) -> InsnPrinter<'a> { + InsnPrinter { inner: self.clone(), ptr_map, iseq } } /// Return true if the instruction needs to be kept around. For example, if the instruction @@ -1020,6 +1020,30 @@ impl Insn { pub struct InsnPrinter<'a> { inner: Insn, ptr_map: &'a PtrPrintMap, + iseq: Option, +} + +/// Get the name of a local variable given iseq, level, and ep_offset. +/// Returns +/// - `":name"` if iseq is available and name is a real identifier, +/// - `""` for anonymous locals. +/// - `None` if iseq is not available. +/// (When `Insn` is printed in a panic/debug message the `Display::fmt` method is called, which can't access an iseq.) +/// +/// This mimics local_var_name() from iseq.c. +fn get_local_var_name_for_printer(iseq: Option, level: u32, ep_offset: u32) -> Option { + let mut current_iseq = iseq?; + for _ in 0..level { + current_iseq = unsafe { rb_get_iseq_body_parent_iseq(current_iseq) }; + } + let local_idx = ep_offset_to_local_idx(current_iseq, ep_offset); + let id: ID = unsafe { rb_zjit_local_id(current_iseq, local_idx.try_into().unwrap()) }; + + if id.0 == 0 || unsafe { rb_id2str(id) } == Qfalse { + return Some(String::from("")); + } + + Some(format!(":{}", id.contents_lossy())) } static REGEXP_FLAGS: &[(u32, &str)] = &[ @@ -1302,9 +1326,18 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy()), Insn::GetGlobal { id, .. } => write!(f, "GetGlobal :{}", id.contents_lossy()), Insn::SetGlobal { id, val, .. } => write!(f, "SetGlobal :{}, {val}", id.contents_lossy()), - &Insn::GetLocal { level, ep_offset, use_sp: true, rest_param } => write!(f, "GetLocal l{level}, SP@{}{}", ep_offset + 1, if rest_param { ", *" } else { "" }), - &Insn::GetLocal { level, ep_offset, use_sp: false, rest_param } => write!(f, "GetLocal l{level}, EP@{ep_offset}{}", if rest_param { ", *" } else { "" }), - Insn::SetLocal { val, level, ep_offset } => write!(f, "SetLocal l{level}, EP@{ep_offset}, {val}"), + &Insn::GetLocal { level, ep_offset, use_sp: true, rest_param } => { + let name = get_local_var_name_for_printer(self.iseq, level, ep_offset).map_or(String::new(), |x| format!("{x}, ")); + write!(f, "GetLocal {name}l{level}, SP@{}{}", ep_offset + 1, if rest_param { ", *" } else { "" }) + }, + &Insn::GetLocal { level, ep_offset, use_sp: false, rest_param } => { + let name = get_local_var_name_for_printer(self.iseq, level, ep_offset).map_or(String::new(), |x| format!("{x}, ")); + write!(f, "GetLocal {name}l{level}, EP@{ep_offset}{}", if rest_param { ", *" } else { "" }) + }, + &Insn::SetLocal { val, level, ep_offset } => { + let name = get_local_var_name_for_printer(self.iseq, level, ep_offset).map_or(String::new(), |x| format!("{x}, ")); + write!(f, "SetLocal {name}l{level}, EP@{ep_offset}, {val}") + }, Insn::GetSpecialSymbol { symbol_type, .. } => write!(f, "GetSpecialSymbol {symbol_type:?}"), Insn::GetSpecialNumber { nth, .. } => write!(f, "GetSpecialNumber {nth}"), Insn::GetClassVar { id, .. } => write!(f, "GetClassVar :{}", id.contents_lossy()), @@ -1346,7 +1379,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { impl std::fmt::Display for Insn { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.print(&PtrPrintMap::identity()).fmt(f) + self.print(&PtrPrintMap::identity(), None).fmt(f) } } @@ -4052,7 +4085,7 @@ impl Function { }; - let opcode = insn.print(&ptr_map).to_string(); + let opcode = insn.print(&ptr_map, Some(self.iseq)).to_string(); // Traverse the worklist to get inputs for a given instruction. let mut inputs = VecDeque::new(); @@ -4606,7 +4639,7 @@ impl<'a> std::fmt::Display for FunctionPrinter<'a> { write!(f, "{insn_id}:{} = ", insn_type.print(&self.ptr_map))?; } } - writeln!(f, "{}", insn.print(&self.ptr_map))?; + writeln!(f, "{}", insn.print(&self.ptr_map, Some(fun.iseq)))?; } } Ok(()) @@ -4682,7 +4715,7 @@ impl<'a> std::fmt::Display for FunctionGraphvizPrinter<'a> { if let Insn::Jump(ref target) | Insn::IfTrue { ref target, .. } | Insn::IfFalse { ref target, .. } = insn { edges.push((insn_id, target.target)); } - write_encoded!(f, "{}", insn.print(&self.ptr_map))?; + write_encoded!(f, "{}", insn.print(&self.ptr_map, Some(fun.iseq)))?; writeln!(f, " ")?; } writeln!(f, ">];")?; @@ -6723,8 +6756,8 @@ mod graphviz_tests { bb0()  EntryPoint interpreter  v1:BasicObject = LoadSelf  - v2:BasicObject = GetLocal l0, SP@5  - v3:BasicObject = GetLocal l0, SP@4  + v2:BasicObject = GetLocal :x, l0, SP@5  + v3:BasicObject = GetLocal :y, l0, SP@4  Jump bb2(v1, v2, v3)  >]; bb0:v4 -> bb2:params:n; @@ -6774,7 +6807,7 @@ mod graphviz_tests { bb0()  EntryPoint interpreter  v1:BasicObject = LoadSelf  - v2:BasicObject = GetLocal l0, SP@4  + v2:BasicObject = GetLocal :c, l0, SP@4  Jump bb2(v1, v2)  >]; bb0:v3 -> bb2:params:n; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index a174ac1b69280e..5646af804a2d0f 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -219,7 +219,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :n, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -551,7 +551,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :object, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -580,7 +580,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -610,7 +610,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:ArrayExact = GetLocal l0, SP@4, * + v2:ArrayExact = GetLocal :array, l0, SP@4, * Jump bb2(v1, v2) bb1(v5:BasicObject, v6:ArrayExact): EntryPoint JIT(0) @@ -623,8 +623,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :k, l0, SP@5 + v3:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -637,7 +637,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :k, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -650,7 +650,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -664,8 +664,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:ArrayExact = GetLocal l0, SP@5, * - v3:BasicObject = GetLocal l0, SP@4 + v2:ArrayExact = GetLocal :rest, l0, SP@5, * + v3:BasicObject = GetLocal :post, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:ArrayExact, v8:BasicObject): EntryPoint JIT(0) @@ -774,7 +774,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -784,7 +784,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1000) v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] v24:BasicObject = CCallWithFrame v23, :C#fun_new_map@0x1038, block=0x1040 - v15:BasicObject = GetLocal l0, EP@3 + v15:BasicObject = GetLocal :o, l0, EP@3 CheckInterrupts Return v24 "); @@ -1064,8 +1064,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1091,8 +1091,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1119,7 +1119,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1146,7 +1146,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1173,8 +1173,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1201,7 +1201,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1228,7 +1228,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1316,7 +1316,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1343,7 +1343,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1370,7 +1370,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1397,7 +1397,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1451,7 +1451,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1481,7 +1481,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1574,7 +1574,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :a, l0, SP@5 v3:NilClass = Const Value(nil) Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject): @@ -1630,8 +1630,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :aval, l0, SP@6 + v3:BasicObject = GetLocal :bval, l0, SP@5 v4:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4) bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject): @@ -1776,8 +1776,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1807,8 +1807,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1838,8 +1838,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1869,8 +1869,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1901,8 +1901,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1933,8 +1933,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1964,8 +1964,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1995,8 +1995,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2026,8 +2026,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2057,8 +2057,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2088,8 +2088,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2143,7 +2143,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2540,7 +2540,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2568,7 +2568,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :x, l0, SP@5 v3:NilClass = Const Value(nil) Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject): @@ -2636,7 +2636,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :c, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2700,10 +2700,10 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:Fixnum[1] = Const Value(1) - SetLocal l0, EP@3, v13 + SetLocal :a, l0, EP@3, v13 v19:BasicObject = Send v8, 0x1000, :foo - v20:BasicObject = GetLocal l0, EP@3 - v24:BasicObject = GetLocal l0, EP@3 + v20:BasicObject = GetLocal :a, l0, EP@3 + v24:BasicObject = GetLocal :a, l0, EP@3 CheckInterrupts Return v24 "); @@ -3260,8 +3260,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -3287,8 +3287,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -3314,7 +3314,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :block, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4150,7 +4150,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4182,7 +4182,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4211,7 +4211,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4762,7 +4762,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4789,7 +4789,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4816,7 +4816,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4843,7 +4843,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4870,7 +4870,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4897,7 +4897,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4924,7 +4924,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4952,7 +4952,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -4980,7 +4980,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5007,7 +5007,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5037,7 +5037,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5074,7 +5074,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5102,7 +5102,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5131,8 +5131,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -5161,8 +5161,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -5190,8 +5190,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -5256,7 +5256,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5295,7 +5295,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5485,7 +5485,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5518,7 +5518,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5587,7 +5587,7 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:ArrayExact = NewArray - SetLocal l0, EP@3, v13 + SetLocal :result, l0, EP@3, v13 PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, A) v36:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -5597,8 +5597,8 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) PatchPoint NoSingletonClass(Array@0x1020) v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39 - v25:BasicObject = GetLocal l0, EP@3 - v29:BasicObject = GetLocal l0, EP@3 + v25:BasicObject = GetLocal :result, l0, EP@3 + v29:BasicObject = GetLocal :result, l0, EP@3 CheckInterrupts Return v29 "); @@ -5615,7 +5615,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :block, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5741,7 +5741,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5773,7 +5773,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5805,7 +5805,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5840,7 +5840,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5872,7 +5872,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5900,7 +5900,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5932,7 +5932,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -5961,8 +5961,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@5 + v3:BasicObject = GetLocal :v, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -5993,8 +5993,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@5 + v3:BasicObject = GetLocal :v, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6150,7 +6150,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6176,7 +6176,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6201,7 +6201,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6226,7 +6226,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6287,8 +6287,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@5 + v3:BasicObject = GetLocal :idx, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6319,8 +6319,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@5 + v3:BasicObject = GetLocal :idx, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6382,8 +6382,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :hash, l0, SP@5 + v3:BasicObject = GetLocal :key, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6413,8 +6413,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :hash, l0, SP@5 + v3:BasicObject = GetLocal :key, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6503,7 +6503,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6533,7 +6533,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6563,7 +6563,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6593,7 +6593,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6623,7 +6623,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6651,7 +6651,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arr, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6679,7 +6679,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6706,8 +6706,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@5 + v3:BasicObject = GetLocal :i, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6743,8 +6743,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@5 + v3:BasicObject = GetLocal :i, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -6779,9 +6779,9 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 - v4:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@6 + v3:BasicObject = GetLocal :idx, l0, SP@5 + v4:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2, v3, v4) bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject): EntryPoint JIT(0) @@ -6820,9 +6820,9 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 - v4:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@6 + v3:BasicObject = GetLocal :idx, l0, SP@5 + v4:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2, v3, v4) bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject): EntryPoint JIT(0) @@ -6859,9 +6859,9 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 - v4:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@6 + v3:BasicObject = GetLocal :idx, l0, SP@5 + v4:BasicObject = GetLocal :val, l0, SP@4 Jump bb2(v1, v2, v3, v4) bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject): EntryPoint JIT(0) @@ -6889,7 +6889,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6922,7 +6922,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6950,7 +6950,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -6978,7 +6978,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7004,7 +7004,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7032,7 +7032,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7059,7 +7059,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7086,8 +7086,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7112,7 +7112,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7139,7 +7139,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7165,7 +7165,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7191,8 +7191,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7217,8 +7217,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7246,8 +7246,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7277,8 +7277,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7308,8 +7308,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7387,7 +7387,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7460,8 +7460,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7491,8 +7491,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7519,8 +7519,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7542,8 +7542,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7565,8 +7565,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7590,8 +7590,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -7614,7 +7614,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :hash, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7644,7 +7644,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :hash, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7674,7 +7674,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7706,7 +7706,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7741,7 +7741,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7775,7 +7775,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7810,7 +7810,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7845,7 +7845,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7879,7 +7879,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7913,7 +7913,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7946,7 +7946,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -7982,7 +7982,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8299,7 +8299,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8324,7 +8324,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8349,8 +8349,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :l, l0, SP@5 + v3:BasicObject = GetLocal :r, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -8380,8 +8380,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :l, l0, SP@5 + v3:BasicObject = GetLocal :r, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -8411,8 +8411,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :l, l0, SP@5 + v3:BasicObject = GetLocal :r, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -8440,8 +8440,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :l, l0, SP@5 + v3:BasicObject = GetLocal :r, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -8471,8 +8471,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :l, l0, SP@5 + v3:BasicObject = GetLocal :r, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -8502,8 +8502,8 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :l, l0, SP@5 + v3:BasicObject = GetLocal :r, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -8533,7 +8533,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8563,7 +8563,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8592,7 +8592,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8623,7 +8623,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8652,7 +8652,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8679,7 +8679,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8709,7 +8709,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8739,7 +8739,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8769,7 +8769,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8801,7 +8801,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8834,7 +8834,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8864,7 +8864,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8894,7 +8894,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -8926,7 +8926,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -9011,7 +9011,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :s, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -9090,7 +9090,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -9123,7 +9123,7 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :o, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -9217,9 +9217,9 @@ mod hir_opt_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :_b, l0, SP@6 + v4:BasicObject = GetLocal :_c, l0, SP@5 v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject): @@ -9228,9 +9228,9 @@ mod hir_opt_tests { Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:NilClass): CheckInterrupts - v27:BasicObject = GetLocal l0, EP@6 - SetLocal l0, EP@3, v27 - v39:BasicObject = GetLocal l0, EP@3 + v27:BasicObject = GetLocal :a, l0, EP@6 + SetLocal :formatted, l0, EP@3, v27 + v39:BasicObject = GetLocal :formatted, l0, EP@3 PatchPoint SingleRactorMode v56:HeapBasicObject = GuardType v14, HeapBasicObject v57:HeapBasicObject = GuardShape v56, 0x1000 @@ -9242,10 +9242,10 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) v65:BasicObject = CCallWithFrame v45, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 - v48:BasicObject = GetLocal l0, EP@6 - v49:BasicObject = GetLocal l0, EP@5 - v50:BasicObject = GetLocal l0, EP@4 - v51:BasicObject = GetLocal l0, EP@3 + v48:BasicObject = GetLocal :a, l0, EP@6 + v49:BasicObject = GetLocal :_b, l0, EP@5 + v50:BasicObject = GetLocal :_c, l0, EP@4 + v51:BasicObject = GetLocal :formatted, l0, EP@3 CheckInterrupts Return v65 "); diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 79ba1d30c53443..76be941fe529f9 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -22,8 +22,8 @@ mod snapshot_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -127,7 +127,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 v3:CPtr = LoadPC v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) v5:CBool = IsBitEqual v3, v4 @@ -199,7 +199,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -220,8 +220,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -242,7 +242,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -264,8 +264,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -286,7 +286,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -308,8 +308,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -392,8 +392,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :aval, l0, SP@5 + v3:BasicObject = GetLocal :bval, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -773,16 +773,16 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v10:BasicObject = GetLocal l2, EP@4 - SetLocal l1, EP@3, v10 - v15:BasicObject = GetLocal l1, EP@3 - v17:BasicObject = GetLocal l2, EP@4 + v10:BasicObject = GetLocal :l2, l2, EP@4 + SetLocal :l1, l1, EP@3, v10 + v15:BasicObject = GetLocal :l1, l1, EP@3 + v17:BasicObject = GetLocal :l2, l2, EP@4 v20:BasicObject = SendWithoutBlock v15, :+, v17 - SetLocal l2, EP@4, v20 - v25:BasicObject = GetLocal l2, EP@4 - v27:BasicObject = GetLocal l3, EP@5 + SetLocal :l2, l2, EP@4, v20 + v25:BasicObject = GetLocal :l2, l2, EP@4 + v27:BasicObject = GetLocal :l3, l3, EP@5 v30:BasicObject = SendWithoutBlock v25, :+, v27 - SetLocal l3, EP@5, v30 + SetLocal :l3, l3, EP@5, v30 CheckInterrupts Return v30 " @@ -800,7 +800,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :a, l0, SP@5 v3:NilClass = Const Value(nil) v4:CPtr = LoadPC v5:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) @@ -838,7 +838,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :a, l0, SP@5 v3:NilClass = Const Value(nil) v4:CPtr = LoadPC v5:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) @@ -873,7 +873,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 v3:CPtr = LoadPC v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) v5:CBool = IsBitEqual v3, v4 @@ -904,7 +904,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject): EntryPoint JIT(0) @@ -1021,7 +1021,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :cond, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1057,7 +1057,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :cond, l0, SP@5 v3:NilClass = Const Value(nil) Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject): @@ -1095,8 +1095,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1120,8 +1120,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1145,8 +1145,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1170,8 +1170,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1195,8 +1195,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1220,8 +1220,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1245,8 +1245,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1270,8 +1270,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1295,8 +1295,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1320,8 +1320,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1398,8 +1398,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1494,14 +1494,14 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:BasicObject = Send v9, 0x1000, :each - v15:BasicObject = GetLocal l0, EP@3 + v15:BasicObject = GetLocal :a, l0, EP@3 CheckInterrupts Return v14 "); @@ -1575,7 +1575,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1598,7 +1598,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1620,7 +1620,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1643,7 +1643,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1731,7 +1731,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :..., l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1749,7 +1749,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :..., l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1773,7 +1773,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1803,7 +1803,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:ArrayExact = GetLocal l0, SP@4, * + v2:ArrayExact = GetLocal :*, l0, SP@4, * Jump bb2(v1, v2) bb1(v5:BasicObject, v6:ArrayExact): EntryPoint JIT(0) @@ -1828,7 +1828,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :..., l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -1850,10 +1850,10 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@8 - v3:ArrayExact = GetLocal l0, SP@7, * - v4:BasicObject = GetLocal l0, SP@6 - v5:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :a, l0, SP@8 + v3:ArrayExact = GetLocal :*, l0, SP@7, * + v4:BasicObject = GetLocal :**, l0, SP@6 + v5:BasicObject = GetLocal :&, l0, SP@5 v6:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5, v6) bb1(v9:BasicObject, v10:BasicObject, v11:ArrayExact, v12:BasicObject, v13:BasicObject): @@ -1938,8 +1938,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1970,8 +1970,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -1997,8 +1997,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2029,8 +2029,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2071,8 +2071,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2103,8 +2103,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2137,8 +2137,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2153,7 +2153,7 @@ pub mod hir_build_tests { v30:StringExact = StringCopy v29 v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v37:StringExact = StringCopy v36 - v39:BasicObject = GetLocal l0, EP@3 + v39:BasicObject = GetLocal :buf, l0, EP@3 SideExit UnhandledNewarraySend(PACK_BUFFER) "); } @@ -2174,8 +2174,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2221,8 +2221,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5) @@ -2250,7 +2250,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2282,7 +2282,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2303,8 +2303,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2328,8 +2328,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2529,7 +2529,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2552,7 +2552,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2578,7 +2578,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2603,7 +2603,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2632,8 +2632,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2658,8 +2658,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :a, l0, SP@5 + v3:BasicObject = GetLocal :b, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2682,7 +2682,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2705,7 +2705,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2728,8 +2728,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2752,8 +2752,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2776,7 +2776,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2799,8 +2799,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :regexp, l0, SP@5 + v3:BasicObject = GetLocal :matchee, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -2927,7 +2927,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -2952,9 +2952,9 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 - v3:BasicObject = GetLocal l0, SP@5 - v4:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :arg, l0, SP@6 + v3:BasicObject = GetLocal :exception, l0, SP@5 + v4:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3, v4) bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject): EntryPoint JIT(0) @@ -3000,10 +3000,10 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@8 - v3:BasicObject = GetLocal l0, SP@7 - v4:BasicObject = GetLocal l0, SP@6 - v5:BasicObject = GetLocal l0, SP@5 + v2:BasicObject = GetLocal :name, l0, SP@8 + v3:BasicObject = GetLocal :encoding, l0, SP@7 + v4:BasicObject = GetLocal , l0, SP@6 + v5:BasicObject = GetLocal :block, l0, SP@5 v6:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5, v6) bb1(v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject, v13:BasicObject): @@ -3063,10 +3063,10 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 - v3:BasicObject = GetLocal l0, SP@6 - v4:BasicObject = GetLocal l0, SP@5 - v5:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :full_mark, l0, SP@7 + v3:BasicObject = GetLocal :immediate_mark, l0, SP@6 + v4:BasicObject = GetLocal :immediate_sweep, l0, SP@5 + v5:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3, v4, v5) bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject): EntryPoint JIT(0) @@ -3134,7 +3134,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@4 Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): EntryPoint JIT(0) @@ -3369,8 +3369,8 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :x, l0, SP@5 + v3:BasicObject = GetLocal :y, l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) @@ -3395,7 +3395,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :o, l0, SP@6 v3:NilClass = Const Value(nil) v4:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4) @@ -3431,7 +3431,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@6 + v2:BasicObject = GetLocal :o, l0, SP@6 v3:NilClass = Const Value(nil) v4:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4) @@ -3458,7 +3458,7 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@7 + v2:BasicObject = GetLocal :o, l0, SP@7 v3:NilClass = Const Value(nil) v4:NilClass = Const Value(nil) v5:NilClass = Const Value(nil) @@ -3485,14 +3485,14 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@5 - v3:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :kw, l0, SP@5 + v3:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v15:BasicObject = GetLocal l0, EP@3 + v15:BasicObject = GetLocal , l0, EP@3 v16:BoolExact = FixnumBitCheck v15, 0 CheckInterrupts v19:CBool = Test v16 @@ -3526,40 +3526,40 @@ pub mod hir_build_tests { bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf - v2:BasicObject = GetLocal l0, SP@37 - v3:BasicObject = GetLocal l0, SP@36 - v4:BasicObject = GetLocal l0, SP@35 - v5:BasicObject = GetLocal l0, SP@34 - v6:BasicObject = GetLocal l0, SP@33 - v7:BasicObject = GetLocal l0, SP@32 - v8:BasicObject = GetLocal l0, SP@31 - v9:BasicObject = GetLocal l0, SP@30 - v10:BasicObject = GetLocal l0, SP@29 - v11:BasicObject = GetLocal l0, SP@28 - v12:BasicObject = GetLocal l0, SP@27 - v13:BasicObject = GetLocal l0, SP@26 - v14:BasicObject = GetLocal l0, SP@25 - v15:BasicObject = GetLocal l0, SP@24 - v16:BasicObject = GetLocal l0, SP@23 - v17:BasicObject = GetLocal l0, SP@22 - v18:BasicObject = GetLocal l0, SP@21 - v19:BasicObject = GetLocal l0, SP@20 - v20:BasicObject = GetLocal l0, SP@19 - v21:BasicObject = GetLocal l0, SP@18 - v22:BasicObject = GetLocal l0, SP@17 - v23:BasicObject = GetLocal l0, SP@16 - v24:BasicObject = GetLocal l0, SP@15 - v25:BasicObject = GetLocal l0, SP@14 - v26:BasicObject = GetLocal l0, SP@13 - v27:BasicObject = GetLocal l0, SP@12 - v28:BasicObject = GetLocal l0, SP@11 - v29:BasicObject = GetLocal l0, SP@10 - v30:BasicObject = GetLocal l0, SP@9 - v31:BasicObject = GetLocal l0, SP@8 - v32:BasicObject = GetLocal l0, SP@7 - v33:BasicObject = GetLocal l0, SP@6 - v34:BasicObject = GetLocal l0, SP@5 - v35:BasicObject = GetLocal l0, SP@4 + v2:BasicObject = GetLocal :k1, l0, SP@37 + v3:BasicObject = GetLocal :k2, l0, SP@36 + v4:BasicObject = GetLocal :k3, l0, SP@35 + v5:BasicObject = GetLocal :k4, l0, SP@34 + v6:BasicObject = GetLocal :k5, l0, SP@33 + v7:BasicObject = GetLocal :k6, l0, SP@32 + v8:BasicObject = GetLocal :k7, l0, SP@31 + v9:BasicObject = GetLocal :k8, l0, SP@30 + v10:BasicObject = GetLocal :k9, l0, SP@29 + v11:BasicObject = GetLocal :k10, l0, SP@28 + v12:BasicObject = GetLocal :k11, l0, SP@27 + v13:BasicObject = GetLocal :k12, l0, SP@26 + v14:BasicObject = GetLocal :k13, l0, SP@25 + v15:BasicObject = GetLocal :k14, l0, SP@24 + v16:BasicObject = GetLocal :k15, l0, SP@23 + v17:BasicObject = GetLocal :k16, l0, SP@22 + v18:BasicObject = GetLocal :k17, l0, SP@21 + v19:BasicObject = GetLocal :k18, l0, SP@20 + v20:BasicObject = GetLocal :k19, l0, SP@19 + v21:BasicObject = GetLocal :k20, l0, SP@18 + v22:BasicObject = GetLocal :k21, l0, SP@17 + v23:BasicObject = GetLocal :k22, l0, SP@16 + v24:BasicObject = GetLocal :k23, l0, SP@15 + v25:BasicObject = GetLocal :k24, l0, SP@14 + v26:BasicObject = GetLocal :k25, l0, SP@13 + v27:BasicObject = GetLocal :k26, l0, SP@12 + v28:BasicObject = GetLocal :k27, l0, SP@11 + v29:BasicObject = GetLocal :k28, l0, SP@10 + v30:BasicObject = GetLocal :k29, l0, SP@9 + v31:BasicObject = GetLocal :k30, l0, SP@8 + v32:BasicObject = GetLocal :k31, l0, SP@7 + v33:BasicObject = GetLocal :k32, l0, SP@6 + v34:BasicObject = GetLocal :k33, l0, SP@5 + v35:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) bb1(v38:BasicObject, v39:BasicObject, v40:BasicObject, v41:BasicObject, v42:BasicObject, v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:BasicObject, v51:BasicObject, v52:BasicObject, v53:BasicObject, v54:BasicObject, v55:BasicObject, v56:BasicObject, v57:BasicObject, v58:BasicObject, v59:BasicObject, v60:BasicObject, v61:BasicObject, v62:BasicObject, v63:BasicObject, v64:BasicObject, v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject, v69:BasicObject, v70:BasicObject, v71:BasicObject, v72:BasicObject): EntryPoint JIT(0) From 65995c22f892e103fdf2601fdccd0202483a4fca Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 23:08:48 +0100 Subject: [PATCH 214/367] [ruby/timeout] Exclude constantly-failing test on x86_64-darwin * https://github.com/ruby/ruby-dev-builder/actions/runs/19973218359/job/57293388626 https://github.com/ruby/timeout/commit/45816b1b26 --- test/test_timeout.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 3f94134fb04d9c..d6ae0c9b50b0ec 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -299,5 +299,6 @@ def test_ractor assert_equal :ok, r end; - end if defined?(::Ractor) && RUBY_VERSION >= '4.0' + end if defined?(::Ractor) && RUBY_VERSION >= '4.0' && !RUBY_PLATFORM.include?('x86_64-darwin') + # Exclude on x86_64-darwin as it failed 4 times out of 4 tries in the CI of ruby/ruby-dev-builder end From 791acc5697afc8f256e652169f7c85a3d90b3f06 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 5 Dec 2025 17:08:20 -0500 Subject: [PATCH 215/367] Revert "gc.c: Pass shape_id to `newobj_init`" This reverts commit 228d13f6ed914d1e7f6bd2416e3f5be8283be865. This commit makes default.c and mmtk.c depend on shape.h, which prevents them from building independently. --- gc.c | 5 +++-- gc/default/default.c | 33 +++++++++++++++++---------------- gc/gc_impl.h | 2 +- gc/mmtk/mmtk.c | 5 ++--- shape.h | 9 ++------- 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/gc.c b/gc.c index 18badba00581d8..4f5f041fb348fb 100644 --- a/gc.c +++ b/gc.c @@ -622,7 +622,7 @@ typedef struct gc_function_map { void (*stress_set)(void *objspace_ptr, VALUE flag); VALUE (*stress_get)(void *objspace_ptr); // Object allocation - VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t alloc_size); + VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size); size_t (*obj_slot_size)(VALUE obj); size_t (*heap_id_for_size)(void *objspace_ptr, size_t size); bool (*size_allocatable_p)(size_t size); @@ -993,7 +993,8 @@ gc_validate_pc(VALUE obj) static inline VALUE newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t size) { - VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, shape_id, wb_protected, size); + VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size); + RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); gc_validate_pc(obj); diff --git a/gc/default/default.c b/gc/default/default.c index f8fd40b44fd8b4..a03fda859cf4ca 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -32,7 +32,6 @@ #include "darray.h" #include "gc/gc.h" #include "gc/gc_impl.h" -#include "shape.h" #ifndef BUILDING_MODULAR_GC # include "probes.h" @@ -2148,13 +2147,15 @@ rb_gc_impl_source_location_cstr(int *ptr) #endif static inline VALUE -newobj_init(VALUE klass, VALUE flags, shape_id_t shape_id, int wb_protected, rb_objspace_t *objspace, VALUE obj) +newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, VALUE obj) { GC_ASSERT(BUILTIN_TYPE(obj) == T_NONE); GC_ASSERT((flags & FL_WB_PROTECTED) == 0); RBASIC(obj)->flags = flags; *((VALUE *)&RBASIC(obj)->klass) = klass; - RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id); +#if RBASIC_SHAPE_ID_FIELD + RBASIC(obj)->shape_id = 0; +#endif int t = flags & RUBY_T_MASK; if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) { @@ -2438,10 +2439,10 @@ newobj_alloc(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t he return obj; } -ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx)); +ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx)); static inline VALUE -newobj_slowpath(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx) +newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, int wb_protected, size_t heap_idx) { VALUE obj; unsigned int lev; @@ -2466,32 +2467,32 @@ newobj_slowpath(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *ob } obj = newobj_alloc(objspace, cache, heap_idx, true); - newobj_init(klass, flags, shape_id, wb_protected, objspace, obj); + newobj_init(klass, flags, wb_protected, objspace, obj); } RB_GC_CR_UNLOCK(lev); return obj; } -NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, shape_id_t shape_id, +NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx)); -NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, shape_id_t shape_id, +NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx)); static VALUE -newobj_slowpath_wb_protected(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) +newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) { - return newobj_slowpath(klass, flags, shape_id, objspace, cache, TRUE, heap_idx); + return newobj_slowpath(klass, flags, objspace, cache, TRUE, heap_idx); } static VALUE -newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, shape_id_t shape_id, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) +newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size_t heap_idx) { - return newobj_slowpath(klass, flags, shape_id, objspace, cache, FALSE, heap_idx); + return newobj_slowpath(klass, flags, objspace, cache, FALSE, heap_idx); } VALUE -rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t alloc_size) +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size) { VALUE obj; rb_objspace_t *objspace = objspace_ptr; @@ -2512,14 +2513,14 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags if (!RB_UNLIKELY(during_gc || ruby_gc_stressful) && wb_protected) { obj = newobj_alloc(objspace, cache, heap_idx, false); - newobj_init(klass, flags, shape_id, wb_protected, objspace, obj); + newobj_init(klass, flags, wb_protected, objspace, obj); } else { RB_DEBUG_COUNTER_INC(obj_newobj_slowpath); obj = wb_protected ? - newobj_slowpath_wb_protected(klass, flags, shape_id, objspace, cache, heap_idx) : - newobj_slowpath_wb_unprotected(klass, flags, shape_id, objspace, cache, heap_idx); + newobj_slowpath_wb_protected(klass, flags, objspace, cache, heap_idx) : + newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, heap_idx); } return obj; diff --git a/gc/gc_impl.h b/gc/gc_impl.h index 65c0586d46bfad..3250483639e775 100644 --- a/gc/gc_impl.h +++ b/gc/gc_impl.h @@ -55,7 +55,7 @@ GC_IMPL_FN VALUE rb_gc_impl_stress_get(void *objspace_ptr); GC_IMPL_FN VALUE rb_gc_impl_config_get(void *objspace_ptr); GC_IMPL_FN void rb_gc_impl_config_set(void *objspace_ptr, VALUE hash); // Object allocation -GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, uint32_t /* shape_id_t */ shape_id, bool wb_protected, size_t alloc_size); +GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size); GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj); GC_IMPL_FN size_t rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size); GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size); diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index a3bdf1cd4a9a42..e1678dcf6ab0b4 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -7,7 +7,6 @@ #include "gc/gc.h" #include "gc/gc_impl.h" -#include "shape.h" #include "gc/mmtk/mmtk.h" #include "ccan/list/list.h" @@ -604,7 +603,7 @@ rb_gc_impl_config_set(void *objspace_ptr, VALUE hash) // Object allocation VALUE -rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, shape_id_t shape_id, bool wb_protected, size_t alloc_size) +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size) { #define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0 struct objspace *objspace = objspace_ptr; @@ -626,7 +625,7 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags VALUE *alloc_obj = mmtk_alloc(ractor_cache->mutator, alloc_size + 8, MMTk_MIN_OBJ_ALIGN, 0, MMTK_ALLOCATION_SEMANTICS_DEFAULT); alloc_obj++; alloc_obj[-1] = alloc_size; - alloc_obj[0] = RSHAPE_COMBINE_IN_FLAGS(flags, shape_id);; + alloc_obj[0] = flags; alloc_obj[1] = klass; mmtk_post_alloc(ractor_cache->mutator, (void*)alloc_obj, alloc_size + 8, MMTK_ALLOCATION_SEMANTICS_DEFAULT); diff --git a/shape.h b/shape.h index bebfaba608c7fa..af6add9e08158a 100644 --- a/shape.h +++ b/shape.h @@ -162,12 +162,6 @@ RBASIC_SHAPE_ID_FOR_READ(VALUE obj) bool rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id); #endif -static inline VALUE -RSHAPE_COMBINE_IN_FLAGS(VALUE flags, shape_id_t shape_id) -{ - return (flags &SHAPE_FLAG_MASK) | (((VALUE)shape_id) << SHAPE_FLAG_SHIFT); -} - static inline void RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) { @@ -175,7 +169,8 @@ RBASIC_SET_SHAPE_ID_NO_CHECKS(VALUE obj, shape_id_t shape_id) RBASIC(obj)->shape_id = (VALUE)shape_id; #else // Object shapes are occupying top bits - RBASIC(obj)->flags = RSHAPE_COMBINE_IN_FLAGS(RBASIC(obj)->flags, shape_id); + RBASIC(obj)->flags &= SHAPE_FLAG_MASK; + RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT); #endif } From 8f9838476dc8cc857859a0a93da285d792be7d3b Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 5 Dec 2025 17:58:11 -0500 Subject: [PATCH 216/367] Fix fields object in embedded struct We don't set RSTRUCT_GEN_FIELDS when RCLASS_MAX_IV_COUNT(klass) != 0, so we need to set RSTRUCT_SET_FIELDS_OBJ to 0 otherwise it may have an invalid value and crash. --- struct.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/struct.c b/struct.c index a6155d4684249f..667d35424fb8d1 100644 --- a/struct.c +++ b/struct.c @@ -826,12 +826,17 @@ struct_alloc(VALUE klass) } NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0); - if (RCLASS_MAX_IV_COUNT(klass) == 0 - && !rb_shape_obj_has_fields((VALUE)st) - && embedded_size < rb_gc_obj_slot_size((VALUE)st)) { - FL_UNSET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS); + if (RCLASS_MAX_IV_COUNT(klass) == 0) { + if (!rb_shape_obj_has_fields((VALUE)st) + && embedded_size < rb_gc_obj_slot_size((VALUE)st)) { + FL_UNSET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS); + RSTRUCT_SET_FIELDS_OBJ((VALUE)st, 0); + } + } + else { RSTRUCT_SET_FIELDS_OBJ((VALUE)st, 0); } + rb_mem_clear((VALUE *)st->as.ary, n); return (VALUE)st; From a7dc53b91c8475323b34d5a332fdb25d190e277d Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 6 Dec 2025 15:55:32 +1300 Subject: [PATCH 217/367] Add support for `u128`, `U128`, `s128` and `S128` integers to `IO::Buffer`. (#15399) --- bignum.c | 6 +- internal/bignum.h | 6 + internal/numeric.h | 43 +++++++ io_buffer.c | 70 +++++++++++ numeric.c | 223 ++++++++++++++++++++++++++++++++++++ test/ruby/test_io_buffer.rb | 147 ++++++++++++++++++++++++ 6 files changed, 492 insertions(+), 3 deletions(-) diff --git a/bignum.c b/bignum.c index 8d3eac8e0a9173..ed53d75149085d 100644 --- a/bignum.c +++ b/bignum.c @@ -4515,7 +4515,7 @@ rb_str2big_gmp(VALUE arg, int base, int badcheck) #if HAVE_LONG_LONG -static VALUE +VALUE rb_ull2big(unsigned LONG_LONG n) { long i; @@ -4537,7 +4537,7 @@ rb_ull2big(unsigned LONG_LONG n) return big; } -static VALUE +VALUE rb_ll2big(LONG_LONG n) { long neg = 0; @@ -4575,7 +4575,7 @@ rb_ll2inum(LONG_LONG n) #endif /* HAVE_LONG_LONG */ #ifdef HAVE_INT128_T -static VALUE +VALUE rb_uint128t2big(uint128_t n) { long i; diff --git a/internal/bignum.h b/internal/bignum.h index e5b6b425631f80..0692bafed30d73 100644 --- a/internal/bignum.h +++ b/internal/bignum.h @@ -169,7 +169,13 @@ VALUE rb_str2big_gmp(VALUE arg, int base, int badcheck); VALUE rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits, int base, int flags); RUBY_SYMBOL_EXPORT_END +#if HAVE_LONG_LONG +VALUE rb_ull2big(unsigned LONG_LONG n); +VALUE rb_ll2big(LONG_LONG n); +#endif + #if defined(HAVE_INT128_T) +VALUE rb_uint128t2big(uint128_t n); VALUE rb_int128t2big(int128_t n); #endif diff --git a/internal/numeric.h b/internal/numeric.h index 58f42f41ac4bea..75181a7f168620 100644 --- a/internal/numeric.h +++ b/internal/numeric.h @@ -127,6 +127,49 @@ VALUE rb_int_bit_length(VALUE num); VALUE rb_int_uminus(VALUE num); VALUE rb_int_comp(VALUE num); +// Unified 128-bit integer structures that work with or without native support: +union rb_uint128 { +#ifdef WORDS_BIGENDIAN + struct { + uint64_t high; + uint64_t low; + } parts; +#else + struct { + uint64_t low; + uint64_t high; + } parts; +#endif +#ifdef HAVE_UINT128_T + uint128_t value; +#endif +}; +typedef union rb_uint128 rb_uint128_t; + +union rb_int128 { +#ifdef WORDS_BIGENDIAN + struct { + uint64_t high; + uint64_t low; + } parts; +#else + struct { + uint64_t low; + uint64_t high; + } parts; +#endif +#ifdef HAVE_UINT128_T + int128_t value; +#endif +}; +typedef union rb_int128 rb_int128_t; + +// Conversion functions for 128-bit integers: +rb_uint128_t rb_numeric_to_uint128(VALUE x); +rb_int128_t rb_numeric_to_int128(VALUE x); +VALUE rb_uint128_to_numeric(rb_uint128_t n); +VALUE rb_int128_to_numeric(rb_int128_t n); + static inline bool INT_POSITIVE_P(VALUE num) { diff --git a/io_buffer.c b/io_buffer.c index 89f169176f48b5..0d2cbdb4b7e704 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -1864,6 +1864,9 @@ io_buffer_validate_type(size_t size, size_t offset) // :u64, :U64 | unsigned 64-bit integer. // :s64, :S64 | signed 64-bit integer. // +// :u128, :U128 | unsigned 128-bit integer. +// :s128, :S128 | signed 128-bit integer. +// // :f32, :F32 | 32-bit floating point number. // :f64, :F64 | 64-bit floating point number. @@ -1895,6 +1898,45 @@ ruby_swapf64(double value) return swap.value; } +// Structures and conversion functions are now in numeric.h/numeric.c +// Unified swap function for 128-bit integers (works with both signed and unsigned) +// Since both rb_uint128_t and rb_int128_t have the same memory layout, +// we can use a union to make the swap function work with both types +static inline rb_uint128_t +ruby_swap128_uint(rb_uint128_t x) +{ + rb_uint128_t result; +#ifdef HAVE_UINT128_T +#if __has_builtin(__builtin_bswap128) + result.value = __builtin_bswap128(x.value); +#else + // Manual byte swap for 128-bit integers + uint64_t low = (uint64_t)x.value; + uint64_t high = (uint64_t)(x.value >> 64); + low = ruby_swap64(low); + high = ruby_swap64(high); + result.value = ((uint128_t)low << 64) | high; +#endif +#else + // Fallback swap function using two 64-bit integers + // For big-endian data on little-endian host (or vice versa): + // 1. Swap bytes within each 64-bit part + // 2. Swap the order of the parts (since big-endian stores high first, little-endian stores low first) + result.parts.low = ruby_swap64(x.parts.high); + result.parts.high = ruby_swap64(x.parts.low); +#endif + return result; +} + +static inline rb_int128_t +ruby_swap128_int(rb_int128_t x) +{ + // Cast to unsigned, swap, then cast back + rb_uint128_t u = *(rb_uint128_t*)&x; + rb_uint128_t swapped = ruby_swap128_uint(u); + return *(rb_int128_t*)&swapped; +} + #define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ static ID RB_IO_BUFFER_DATA_TYPE_##name; \ \ @@ -1941,6 +1983,11 @@ IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NU IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(u128, rb_uint128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint) +IO_BUFFER_DECLARE_TYPE(U128, rb_uint128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint) +IO_BUFFER_DECLARE_TYPE(s128, rb_int128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int) +IO_BUFFER_DECLARE_TYPE(S128, rb_int128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int) + IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) @@ -1965,6 +2012,10 @@ io_buffer_buffer_type_size(ID buffer_type) IO_BUFFER_DATA_TYPE_SIZE(U64) IO_BUFFER_DATA_TYPE_SIZE(s64) IO_BUFFER_DATA_TYPE_SIZE(S64) + IO_BUFFER_DATA_TYPE_SIZE(u128) + IO_BUFFER_DATA_TYPE_SIZE(U128) + IO_BUFFER_DATA_TYPE_SIZE(s128) + IO_BUFFER_DATA_TYPE_SIZE(S128) IO_BUFFER_DATA_TYPE_SIZE(f32) IO_BUFFER_DATA_TYPE_SIZE(F32) IO_BUFFER_DATA_TYPE_SIZE(f64) @@ -2021,6 +2072,11 @@ rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *of IO_BUFFER_GET_VALUE(s64) IO_BUFFER_GET_VALUE(S64) + IO_BUFFER_GET_VALUE(u128) + IO_BUFFER_GET_VALUE(U128) + IO_BUFFER_GET_VALUE(s128) + IO_BUFFER_GET_VALUE(S128) + IO_BUFFER_GET_VALUE(f32) IO_BUFFER_GET_VALUE(F32) IO_BUFFER_GET_VALUE(f64) @@ -2050,6 +2106,10 @@ rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *of * * +:U64+: unsigned integer, 8 bytes, big-endian * * +:s64+: signed integer, 8 bytes, little-endian * * +:S64+: signed integer, 8 bytes, big-endian + * * +:u128+: unsigned integer, 16 bytes, little-endian + * * +:U128+: unsigned integer, 16 bytes, big-endian + * * +:s128+: signed integer, 16 bytes, little-endian + * * +:S128+: signed integer, 16 bytes, big-endian * * +:f32+: float, 4 bytes, little-endian * * +:F32+: float, 4 bytes, big-endian * * +:f64+: double, 8 bytes, little-endian @@ -2287,6 +2347,11 @@ rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *of IO_BUFFER_SET_VALUE(s64); IO_BUFFER_SET_VALUE(S64); + IO_BUFFER_SET_VALUE(u128); + IO_BUFFER_SET_VALUE(U128); + IO_BUFFER_SET_VALUE(s128); + IO_BUFFER_SET_VALUE(S128); + IO_BUFFER_SET_VALUE(f32); IO_BUFFER_SET_VALUE(F32); IO_BUFFER_SET_VALUE(f64); @@ -3859,6 +3924,11 @@ Init_IO_Buffer(void) IO_BUFFER_DEFINE_DATA_TYPE(s64); IO_BUFFER_DEFINE_DATA_TYPE(S64); + IO_BUFFER_DEFINE_DATA_TYPE(u128); + IO_BUFFER_DEFINE_DATA_TYPE(U128); + IO_BUFFER_DEFINE_DATA_TYPE(s128); + IO_BUFFER_DEFINE_DATA_TYPE(S128); + IO_BUFFER_DEFINE_DATA_TYPE(f32); IO_BUFFER_DEFINE_DATA_TYPE(F32); IO_BUFFER_DEFINE_DATA_TYPE(f64); diff --git a/numeric.c b/numeric.c index bc0edd6abe91f9..d9e837644ffa05 100644 --- a/numeric.c +++ b/numeric.c @@ -3420,6 +3420,229 @@ rb_num2ull(VALUE val) #endif /* HAVE_LONG_LONG */ +// Conversion functions for unified 128-bit integer structures, +// These work with or without native 128-bit integer support. + +#ifndef HAVE_UINT128_T +// Helper function to build 128-bit value from bignum digits (fallback path). +static inline void +rb_uint128_from_bignum_digits_fallback(rb_uint128_t *result, BDIGIT *digits, size_t length) +{ + // Build the 128-bit value from bignum digits: + for (long i = length - 1; i >= 0; i--) { + // Shift both low and high parts: + uint64_t carry = result->parts.low >> (64 - (SIZEOF_BDIGIT * CHAR_BIT)); + result->parts.low = (result->parts.low << (SIZEOF_BDIGIT * CHAR_BIT)) | digits[i]; + result->parts.high = (result->parts.high << (SIZEOF_BDIGIT * CHAR_BIT)) | carry; + } +} + +// Helper function to convert absolute value of negative bignum to two's complement. +// Ruby stores negative bignums as absolute values, so we need to convert to two's complement. +static inline void +rb_uint128_twos_complement_negate(rb_uint128_t *value) +{ + if (value->parts.low == 0) { + value->parts.high = ~value->parts.high + 1; + } + else { + value->parts.low = ~value->parts.low + 1; + value->parts.high = ~value->parts.high + (value->parts.low == 0 ? 1 : 0); + } +} +#endif + +rb_uint128_t +rb_numeric_to_uint128(VALUE x) +{ + rb_uint128_t result = {0}; + if (RB_FIXNUM_P(x)) { + long value = RB_FIX2LONG(x); + if (value < 0) { + rb_raise(rb_eRangeError, "negative integer cannot be converted to unsigned 128-bit integer"); + } +#ifdef HAVE_UINT128_T + result.value = (uint128_t)value; +#else + result.parts.low = (uint64_t)value; + result.parts.high = 0; +#endif + return result; + } + else if (RB_BIGNUM_TYPE_P(x)) { + if (BIGNUM_NEGATIVE_P(x)) { + rb_raise(rb_eRangeError, "negative integer cannot be converted to unsigned 128-bit integer"); + } + size_t length = BIGNUM_LEN(x); +#ifdef HAVE_UINT128_T + if (length > roomof(SIZEOF_INT128_T, SIZEOF_BDIGIT)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'unsigned 128-bit integer'"); + } + BDIGIT *digits = BIGNUM_DIGITS(x); + result.value = 0; + for (long i = length - 1; i >= 0; i--) { + result.value = (result.value << (SIZEOF_BDIGIT * CHAR_BIT)) | digits[i]; + } +#else + // Check if bignum fits in 128 bits (16 bytes) + if (length > roomof(16, SIZEOF_BDIGIT)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'unsigned 128-bit integer'"); + } + BDIGIT *digits = BIGNUM_DIGITS(x); + rb_uint128_from_bignum_digits_fallback(&result, digits, length); +#endif + return result; + } + else { + rb_raise(rb_eTypeError, "not an integer"); + } +} + +rb_int128_t +rb_numeric_to_int128(VALUE x) +{ + rb_int128_t result = {0}; + if (RB_FIXNUM_P(x)) { + long value = RB_FIX2LONG(x); +#ifdef HAVE_UINT128_T + result.value = (int128_t)value; +#else + if (value < 0) { + // Two's complement representation: for negative values, sign extend + // Convert to unsigned: for -1, we want all bits set + result.parts.low = (uint64_t)value; // This will be the two's complement representation + result.parts.high = UINT64_MAX; // Sign extend: all bits set for negative + } + else { + result.parts.low = (uint64_t)value; + result.parts.high = 0; + } +#endif + return result; + } + else if (RB_BIGNUM_TYPE_P(x)) { + size_t length = BIGNUM_LEN(x); +#ifdef HAVE_UINT128_T + if (length > roomof(SIZEOF_INT128_T, SIZEOF_BDIGIT)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'"); + } + BDIGIT *digits = BIGNUM_DIGITS(x); + uint128_t unsigned_result = 0; + for (long i = length - 1; i >= 0; i--) { + unsigned_result = (unsigned_result << (SIZEOF_BDIGIT * CHAR_BIT)) | digits[i]; + } + if (BIGNUM_NEGATIVE_P(x)) { + // Convert from two's complement + // Maximum negative value is 2^127 + if (unsigned_result > ((uint128_t)1 << 127)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'"); + } + result.value = -(int128_t)(unsigned_result - 1) - 1; + } + else { + // Maximum positive value is 2^127 - 1 + if (unsigned_result > (((uint128_t)1 << 127) - 1)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'"); + } + result.value = (int128_t)unsigned_result; + } +#else + if (length > roomof(16, SIZEOF_BDIGIT)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'"); + } + BDIGIT *digits = BIGNUM_DIGITS(x); + rb_uint128_t unsigned_result = {0}; + rb_uint128_from_bignum_digits_fallback(&unsigned_result, digits, length); + if (BIGNUM_NEGATIVE_P(x)) { + // Check if value fits in signed 128-bit (max negative is 2^127) + uint64_t max_neg_high = (uint64_t)1 << 63; + if (unsigned_result.parts.high > max_neg_high || (unsigned_result.parts.high == max_neg_high && unsigned_result.parts.low > 0)) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'"); + } + // Convert from absolute value to two's complement (Ruby stores negative as absolute value) + rb_uint128_twos_complement_negate(&unsigned_result); + result.parts.low = unsigned_result.parts.low; + result.parts.high = (int64_t)unsigned_result.parts.high; // Sign extend + } + else { + // Check if value fits in signed 128-bit (max positive is 2^127 - 1) + // Max positive: high = 0x7FFFFFFFFFFFFFFF, low = 0xFFFFFFFFFFFFFFFF + uint64_t max_pos_high = ((uint64_t)1 << 63) - 1; + if (unsigned_result.parts.high > max_pos_high) { + rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'"); + } + result.parts.low = unsigned_result.parts.low; + result.parts.high = unsigned_result.parts.high; + } +#endif + return result; + } + else { + rb_raise(rb_eTypeError, "not an integer"); + } +} + +VALUE +rb_uint128_to_numeric(rb_uint128_t n) +{ +#ifdef HAVE_UINT128_T + if (n.value <= (uint128_t)RUBY_FIXNUM_MAX) { + return LONG2FIX((long)n.value); + } + return rb_uint128t2big(n.value); +#else + // If high part is zero and low part fits in fixnum + if (n.parts.high == 0 && n.parts.low <= (uint64_t)RUBY_FIXNUM_MAX) { + return LONG2FIX((long)n.parts.low); + } + // Convert to bignum by building it from the two 64-bit parts + VALUE bignum = rb_ull2big(n.parts.low); + if (n.parts.high > 0) { + VALUE high_bignum = rb_ull2big(n.parts.high); + // Multiply high part by 2^64 and add to low part + VALUE shifted_value = rb_int_lshift(high_bignum, INT2FIX(64)); + bignum = rb_int_plus(bignum, shifted_value); + } + return bignum; +#endif +} + +VALUE +rb_int128_to_numeric(rb_int128_t n) +{ +#ifdef HAVE_UINT128_T + if (FIXABLE(n.value)) { + return LONG2FIX((long)n.value); + } + return rb_int128t2big(n.value); +#else + int64_t high = (int64_t)n.parts.high; + // If it's a small positive value that fits in fixnum + if (high == 0 && n.parts.low <= (uint64_t)RUBY_FIXNUM_MAX) { + return LONG2FIX((long)n.parts.low); + } + // Check if it's negative (high bit of high part is set) + if (high < 0) { + // Negative value - convert from two's complement to absolute value + rb_uint128_t unsigned_value = {0}; + if (n.parts.low == 0) { + unsigned_value.parts.low = 0; + unsigned_value.parts.high = ~n.parts.high + 1; + } + else { + unsigned_value.parts.low = ~n.parts.low + 1; + unsigned_value.parts.high = ~n.parts.high + (unsigned_value.parts.low == 0 ? 1 : 0); + } + VALUE bignum = rb_uint128_to_numeric(unsigned_value); + return rb_int_uminus(bignum); + } + else { + // Positive value + return rb_uint128_to_numeric(*(rb_uint128_t*)&n); + } +#endif +} + /******************************************************************** * * Document-class: Integer diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 1e4a6e2fd86c40..9ff22b4bb356eb 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -405,6 +405,11 @@ def test_zero_length_get_string :u64 => [0, 2**64-1], :s64 => [-2**63, 0, 2**63-1], + :U128 => [0, 2**64, 2**127-1, 2**128-1], + :S128 => [-2**127, -2**63-1, -1, 0, 2**63, 2**127-1], + :u128 => [0, 2**64, 2**127-1, 2**128-1], + :s128 => [-2**127, -2**63-1, -1, 0, 2**63, 2**127-1], + :F32 => [-1.0, 0.0, 0.5, 1.0, 128.0], :F64 => [-1.0, 0.0, 0.5, 1.0, 128.0], } @@ -759,4 +764,146 @@ def test_bug_21210 assert_predicate buf, :valid? end + + def test_128_bit_integers + buffer = IO::Buffer.new(32) + + # Test unsigned 128-bit integers + test_values_u128 = [ + 0, + 1, + 2**64 - 1, + 2**64, + 2**127 - 1, + 2**128 - 1, + ] + + test_values_u128.each do |value| + buffer.set_value(:u128, 0, value) + assert_equal value, buffer.get_value(:u128, 0), "u128: #{value}" + + buffer.set_value(:U128, 0, value) + assert_equal value, buffer.get_value(:U128, 0), "U128: #{value}" + end + + # Test signed 128-bit integers + test_values_s128 = [ + -2**127, + -2**63 - 1, + -1, + 0, + 1, + 2**63, + 2**127 - 1, + ] + + test_values_s128.each do |value| + buffer.set_value(:s128, 0, value) + assert_equal value, buffer.get_value(:s128, 0), "s128: #{value}" + + buffer.set_value(:S128, 0, value) + assert_equal value, buffer.get_value(:S128, 0), "S128: #{value}" + end + + # Test size_of + assert_equal 16, IO::Buffer.size_of(:u128) + assert_equal 16, IO::Buffer.size_of(:U128) + assert_equal 16, IO::Buffer.size_of(:s128) + assert_equal 16, IO::Buffer.size_of(:S128) + assert_equal 32, IO::Buffer.size_of([:u128, :u128]) + end + + def test_integer_endianness_swapping + # Test that byte order is swapped correctly for all signed and unsigned integers > 1 byte + host_is_le = IO::Buffer::HOST_ENDIAN == IO::Buffer::LITTLE_ENDIAN + host_is_be = IO::Buffer::HOST_ENDIAN == IO::Buffer::BIG_ENDIAN + + # Test values that will produce different byte patterns when swapped + # Format: [little_endian_type, big_endian_type, test_value, expected_swapped_value] + # expected_swapped_value is the result when writing as le_type and reading as be_type + # (or vice versa) on a little-endian host + test_cases = [ + [:u16, :U16, 0x1234, 0x3412], + [:s16, :S16, 0x1234, 0x3412], + [:u32, :U32, 0x12345678, 0x78563412], + [:s32, :S32, 0x12345678, 0x78563412], + [:u64, :U64, 0x0123456789ABCDEF, 0xEFCDAB8967452301], + [:s64, :S64, 0x0123456789ABCDEF, -1167088121787636991], + [:u128, :U128, 0x0123456789ABCDEF0123456789ABCDEF, 0xEFCDAB8967452301EFCDAB8967452301], + [:u128, :U128, 0x0123456789ABCDEFFEDCBA9876543210, 0x1032547698BADCFEEFCDAB8967452301], + [:u128, :U128, 0xFEDCBA98765432100123456789ABCDEF, 0xEFCDAB89674523011032547698BADCFE], + [:u128, :U128, 0x123456789ABCDEF0FEDCBA9876543210, 0x1032547698BADCFEF0DEBC9A78563412], + [:s128, :S128, 0x0123456789ABCDEF0123456789ABCDEF, -21528975894082904073953971026863512831], + [:s128, :S128, 0x0123456789ABCDEFFEDCBA9876543210, 0x1032547698BADCFEEFCDAB8967452301], + ] + + test_cases.each do |le_type, be_type, value, expected_swapped| + buffer_size = IO::Buffer.size_of(le_type) + buffer = IO::Buffer.new(buffer_size * 2) + + # Test little-endian round-trip + buffer.set_value(le_type, 0, value) + result_le = buffer.get_value(le_type, 0) + assert_equal value, result_le, "#{le_type}: round-trip failed" + + # Test big-endian round-trip + buffer.set_value(be_type, buffer_size, value) + result_be = buffer.get_value(be_type, buffer_size) + assert_equal value, result_be, "#{be_type}: round-trip failed" + + # Verify byte patterns are different when endianness differs from host + le_bytes = buffer.get_string(0, buffer_size) + be_bytes = buffer.get_string(buffer_size, buffer_size) + + if host_is_le + # On little-endian host: le_type should match host, be_type should be swapped + # So the byte patterns should be different (unless value is symmetric) + # Read back with opposite endianness to verify swapping + result_le_read_as_be = buffer.get_value(be_type, 0) + result_be_read_as_le = buffer.get_value(le_type, buffer_size) + + # The swapped reads should NOT equal the original value (unless it's symmetric) + # For most values, this will be different + if value != 0 && value != -1 && value.abs != 1 + refute_equal value, result_le_read_as_be, "#{le_type} written, read as #{be_type} should be swapped on LE host" + refute_equal value, result_be_read_as_le, "#{be_type} written, read as #{le_type} should be swapped on LE host" + end + + # Verify that reading back with correct endianness works + assert_equal value, buffer.get_value(le_type, 0), "#{le_type} should read correctly on LE host" + assert_equal value, buffer.get_value(be_type, buffer_size), "#{be_type} should read correctly on LE host (with swapping)" + elsif host_is_be + # On big-endian host: be_type should match host, le_type should be swapped + result_le_read_as_be = buffer.get_value(be_type, 0) + result_be_read_as_le = buffer.get_value(le_type, buffer_size) + + # The swapped reads should NOT equal the original value (unless it's symmetric) + if value != 0 && value != -1 && value.abs != 1 + refute_equal value, result_le_read_as_be, "#{le_type} written, read as #{be_type} should be swapped on BE host" + refute_equal value, result_be_read_as_le, "#{be_type} written, read as #{le_type} should be swapped on BE host" + end + + # Verify that reading back with correct endianness works + assert_equal value, buffer.get_value(be_type, buffer_size), "#{be_type} should read correctly on BE host" + assert_equal value, buffer.get_value(le_type, 0), "#{le_type} should read correctly on BE host (with swapping)" + end + + # Verify that when we write with one endianness and read with the opposite, + # we get the expected swapped value + buffer.set_value(le_type, 0, value) + swapped_value_le_to_be = buffer.get_value(be_type, 0) + assert_equal expected_swapped, swapped_value_le_to_be, "#{le_type} written, read as #{be_type} should produce expected swapped value" + + # Also verify the reverse direction + buffer.set_value(be_type, buffer_size, value) + swapped_value_be_to_le = buffer.get_value(le_type, buffer_size) + assert_equal expected_swapped, swapped_value_be_to_le, "#{be_type} written, read as #{le_type} should produce expected swapped value" + + # Verify that writing the swapped value back and reading with original endianness + # gives us the original value (double-swap should restore original) + buffer.set_value(be_type, 0, swapped_value_le_to_be) + round_trip_value = buffer.get_value(le_type, 0) + assert_equal value, round_trip_value, "#{le_type}/#{be_type}: double-swap should restore original value" + end + end end From 734dab5ec885107b9d1da81d5d071927e59fddee Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 5 Dec 2025 16:21:55 +0000 Subject: [PATCH 218/367] [ruby/stringio] [DOC] Link to on-page section, not class File doc https://github.com/ruby/stringio/commit/dc93aa51d2 --- ext/stringio/stringio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 603d9c5e22611e..5556426fdc387a 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -774,7 +774,7 @@ strio_set_lineno(VALUE self, VALUE lineno) * binmode -> self * * Sets the data mode in +self+ to binary mode; - * see {Data Mode}[rdoc-ref:File@Data+Mode]. + * see {Data Mode}[rdoc-ref:StringIO@Data+Mode]. * */ static VALUE From bbef73b2ff1fba2d72d16f5ee582063e6b6e8a1f Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 22 Nov 2025 21:23:25 +0000 Subject: [PATCH 219/367] [DOC] Better multibyte-character data --- doc/string/aref.rdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/string/aref.rdoc b/doc/string/aref.rdoc index bd33c4c0504523..ee4c3d33d4e655 100644 --- a/doc/string/aref.rdoc +++ b/doc/string/aref.rdoc @@ -8,7 +8,7 @@ returns the 1-character substring found in self at character offset index: 'hello'[0] # => "h" 'hello'[4] # => "o" 'hello'[5] # => nil - 'тест'[2] # => "с" + 'Привет'[2] # => "и" 'こんにちは'[4] # => "は" With negative integer argument +index+ given, @@ -92,7 +92,7 @@ returns the matching substring of +self+, if found: 'hello'['ell'] # => "ell" 'hello'[''] # => "" 'hello'['nosuch'] # => nil - 'тест'['ес'] # => "ес" + 'Привет'['ив'] # => "ив" 'こんにちは'['んにち'] # => "んにち" Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. From da2c67388ab7db8102bd8fba6f8ed43df713ad66 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 21 Nov 2025 18:22:34 +0000 Subject: [PATCH 220/367] [DOC] Tweaks for String#swapcase --- doc/string/swapcase.rdoc | 27 ++++++++++++++++++++------- string.c | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/doc/string/swapcase.rdoc b/doc/string/swapcase.rdoc index 93859ec8237fcc..916e711b7ec7b2 100644 --- a/doc/string/swapcase.rdoc +++ b/doc/string/swapcase.rdoc @@ -5,15 +5,28 @@ Returns a string containing the characters in +self+, with cases reversed: Examples: - 'Hello World!'.swapcase # => "hELLO wORLD!" - 'тест'.swapcase # => "ТЕСТ" + 'Hello'.swapcase # => "hELLO" + 'Straße'.swapcase # => "sTRASSE" + 'Привет'.swapcase # => "пРИВЕТ" + 'RubyGems.org'.swapcase # => "rUBYgEMS.ORG" -Some characters (and even character sets) do not have casing: +The sizes of +self+ and the upcased result may differ: - '12345'.swapcase # => "12345" - 'こんにちは'.swapcase # => "こんにちは" + s = 'Straße' + s.size # => 6 + s.swapcase # => "sTRASSE" + s.swapcase.size # => 7 -The casing may be affected by the given +mapping+; -see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. +Some characters (and some character sets) do not have upcase and downcase versions; +see {Case Mapping}[rdoc-ref:case_mapping.rdoc]: + + s = '1, 2, 3, ...' + s.swapcase == s # => true + s = 'こんにちは' + s.swapcase == s # => true + +The casing is affected by the given +mapping+, +which may be +:ascii+, +:fold+, or +:turkic+; +see {Case Mappings}[rdoc-ref:case_mapping.rdoc@Case+Mappings]. Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. diff --git a/string.c b/string.c index 34c79eca8e5cff..6f5040d8f86ace 100644 --- a/string.c +++ b/string.c @@ -8197,7 +8197,7 @@ rb_str_swapcase_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * swapcase(mapping) -> new_string + * swapcase(mapping = :ascii) -> new_string * * :include: doc/string/swapcase.rdoc * From e5e4175dbddb83eb5a53ec0d0cf3a5d0026c7bb9 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 21 Nov 2025 18:01:35 +0000 Subject: [PATCH 221/367] [DOC] Tweaks for String#upcase --- doc/string/upcase.rdoc | 17 ++++++++++++----- string.c | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/string/upcase.rdoc b/doc/string/upcase.rdoc index 5a9cce217f7972..ad859e89736687 100644 --- a/doc/string/upcase.rdoc +++ b/doc/string/upcase.rdoc @@ -1,6 +1,9 @@ Returns a new string containing the upcased characters in +self+: - 'Hello, World!'.upcase # => "HELLO, WORLD!" + 'hello'.upcase # => "HELLO" + 'straße'.upcase # => "STRASSE" + 'привет'.upcase # => "ПРИВЕТ" + 'RubyGems.org'.upcase # => "RUBYGEMS.ORG" The sizes of +self+ and the upcased result may differ: @@ -9,12 +12,16 @@ The sizes of +self+ and the upcased result may differ: s.upcase # => "STRASSE" s.upcase.size # => 7 -Some characters (and some character sets) do not have upcased and downcased versions: +Some characters (and some character sets) do not have upcase and downcase versions; +see {Case Mapping}[rdoc-ref:case_mapping.rdoc]: - s = 'よろしくお願いします' + s = '1, 2, 3, ...' + s.upcase == s # => true + s = 'こんにちは' s.upcase == s # => true -The casing may be affected by the given +mapping+; -see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. +The casing is affected by the given +mapping+, +which may be +:ascii+, +:fold+, or +:turkic+; +see {Case Mappings}[rdoc-ref:case_mapping.rdoc@Case+Mappings]. Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. diff --git a/string.c b/string.c index 6f5040d8f86ace..de8e37f5d3d1ce 100644 --- a/string.c +++ b/string.c @@ -7966,7 +7966,7 @@ rb_str_upcase_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * upcase(mapping) -> string + * upcase(mapping = :ascii) -> new_string * * :include: doc/string/upcase.rdoc */ From 2491a504ffd73e8fabad49fce020fcda484f0be2 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 21 Nov 2025 17:16:37 +0000 Subject: [PATCH 222/367] [DOC] Tweaks for String#downcase --- doc/string/downcase.rdoc | 20 ++++++++++++++------ string.c | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/string/downcase.rdoc b/doc/string/downcase.rdoc index 0fb67daaebe634..d5fffa037b89d0 100644 --- a/doc/string/downcase.rdoc +++ b/doc/string/downcase.rdoc @@ -1,12 +1,20 @@ Returns a new string containing the downcased characters in +self+: - 'Hello, World!'.downcase # => "hello, world!" - 'ТЕСТ'.downcase # => "тест" - 'よろしくお願いします'.downcase # => "よろしくお願いします" + 'HELLO'.downcase # => "hello" + 'STRAẞE'.downcase # => "straße" + 'ПРИВЕТ'.downcase # => "привет" + 'RubyGems.org'.downcase # => "rubygems.org" -Some characters do not have upcased and downcased versions. +Some characters (and some character sets) do not have upcase and downcase versions; +see {Case Mapping}[rdoc-ref:case_mapping.rdoc]: -The casing may be affected by the given +mapping+; -see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. + s = '1, 2, 3, ...' + s.downcase == s # => true + s = 'こんにちは' + s.downcase == s # => true + +The casing is affected by the given +mapping+, +which may be +:ascii+, +:fold+, or +:turkic+; +see {Case Mappings}[rdoc-ref:case_mapping.rdoc@Case+Mappings]. Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. diff --git a/string.c b/string.c index de8e37f5d3d1ce..ce90c07d158d80 100644 --- a/string.c +++ b/string.c @@ -8052,7 +8052,7 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * downcase(mapping) -> string + * downcase(mapping = :ascii) -> new_string * * :include: doc/string/downcase.rdoc * From bd64cf00a2e9c295b854c8a4bbb79672bfa1654a Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 21 Nov 2025 14:23:04 +0000 Subject: [PATCH 223/367] [DOC] Tweaks for String#capitalize --- doc/string/capitalize.rdoc | 28 ++++++++++++++++++++++++++++ string.c | 23 ++--------------------- 2 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 doc/string/capitalize.rdoc diff --git a/doc/string/capitalize.rdoc b/doc/string/capitalize.rdoc new file mode 100644 index 00000000000000..9b26c0215342db --- /dev/null +++ b/doc/string/capitalize.rdoc @@ -0,0 +1,28 @@ +Returns a string containing the characters in +self+, +each with possibly changed case: + +- The first character made uppercase. +- All other characters are made lowercase. + +Examples: + + 'hello'.capitalize # => "Hello" + 'HELLO'.capitalize # => "Hello" + 'straße'.capitalize # => "Straße" # Lowercase 'ß' not changed. + 'STRAẞE'.capitalize # => "Straße" # Uppercase 'ẞ' downcased to 'ß'. + 'привет'.capitalize # => "Привет" + 'ПРИВЕТ'.capitalize # => "Привет" + +Some characters (and some character sets) do not have upcase and downcase versions; +see {Case Mapping}[rdoc-ref:case_mapping.rdoc]: + + s = '1, 2, 3, ...' + s.capitalize == s # => true + s = 'こんにちは' + s.capitalize == s # => true + +The casing is affected by the given +mapping+, +which may be +:ascii+, +:fold+, or +:turkic+; +see {Case Mappings}[rdoc-ref:case_mapping.rdoc@Case+Mappings]. + +Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. diff --git a/string.c b/string.c index ce90c07d158d80..9327306384a232 100644 --- a/string.c +++ b/string.c @@ -8118,29 +8118,10 @@ rb_str_capitalize_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * capitalize(mapping = :ascii) -> string + * capitalize(mapping = :ascii) -> new_string * - * Returns a string containing the characters in +self+, - * each with possibly changed case: + * :include: doc/string/capitalize.rdoc * - * - The first character is upcased. - * - All other characters are downcased. - * - * Examples: - * - * 'hello world'.capitalize # => "Hello world" - * 'HELLO WORLD'.capitalize # => "Hello world" - * - * Some characters do not have upcase and downcase, and so are not changed; - * see {Case Mapping}[rdoc-ref:case_mapping.rdoc]: - * - * '1, 2, 3, ...'.capitalize # => "1, 2, 3, ..." - * - * The casing is affected by the given +mapping+, - * which may be +:ascii+, +:fold+, or +:turkic+; - * see {Case Mappings}[rdoc-ref:case_mapping.rdoc@Case+Mappings]. - * - * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE From 21f9a647fb7405e0a22656ed6405f86811a22a1f Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Thu, 4 Dec 2025 15:57:46 -0600 Subject: [PATCH 224/367] [ruby/stringio] [DOC] Class doc for StringIO (https://github.com/ruby/stringio/pull/178) https://github.com/ruby/stringio/commit/6449251678 --- doc/stringio/stringio.md | 692 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 683 insertions(+), 9 deletions(-) diff --git a/doc/stringio/stringio.md b/doc/stringio/stringio.md index 345fc5f20707df..aebfa6d6f1f4c2 100644 --- a/doc/stringio/stringio.md +++ b/doc/stringio/stringio.md @@ -1,18 +1,39 @@ -\IO streams for strings, with access similar to -{IO}[rdoc-ref:IO]; -see {IO}[rdoc-ref:IO]. +\Class \StringIO supports accessing a string as a stream, +similar in some ways to [class IO][io class]. -### About the Examples +You can create a \StringIO instance using: + +- StringIO.new: returns a new \StringIO object containing the given string. +- StringIO.open: passes a new \StringIO object to the given block. + +Like an \IO stream, a \StringIO stream has certain properties: + +- **Read/write mode**: whether the stream may be read, written, appended to, etc.; + see [Read/Write Mode][read/write mode]. +- **Data mode**: text-only or binary; + see [Data Mode][data mode]. +- **Encodings**: internal and external encodings; + see [Encodings][encodings]. +- **Position**: where in the stream the next read or write is to occur; + see [Position][position]. +- **Line number**: a special, line-oriented, "position" (different from the position mentioned above); + see [Line Number][line number]. +- **Open/closed**: whether the stream is open or closed, for reading or writing. + see [Open/Closed Streams][open/closed streams]. +- **BOM**: byte mark order; + see [Byte Order Mark][bom (byte order mark)]. + +## About the Examples Examples on this page assume that \StringIO has been required: -``` +```ruby require 'stringio' ``` -And that these constants have been defined: +And that this constant has been defined: -``` +```ruby TEXT = <'r': read-only | No | Anywhere | Error | +| 'w': write-only | Yes | Error | Anywhere | +| 'a': append-only | No | Error | End only | +| 'r+': read/write | No | Anywhere | Anywhere | +| 'w+': read-write | Yes | Anywhere | Anywhere | +| 'a+': read/append | No | Anywhere | End only | + +Each section below describes a read/write mode. + +Any of the modes may be given as a string or as file constants; +example: + +```ruby +strio = StringIO.new('foo', 'a') +strio = StringIO.new('foo', File::WRONLY | File::APPEND) +``` + +#### `'r'`: Read-Only + +Mode specified as one of: + +- String: `'r'`. +- Constant: `File::RDONLY`. + +Initial state: + +```ruby +strio = StringIO.new('foobarbaz', 'r') +strio.pos # => 0 # Beginning-of-stream. +strio.string # => "foobarbaz" # Not cleared. +``` + +May be read anywhere: + +```ruby +strio.gets(3) # => "foo" +strio.gets(3) # => "bar" +strio.pos = 9 +strio.gets(3) # => nil +``` + +May not be written: + +```ruby +strio.write('foo') # Raises IOError: not opened for writing +``` + +#### `'w'`: Write-Only + +Mode specified as one of: + +- String: `'w'`. +- Constant: `File::WRONLY`. + +Initial state: + +```ruby +strio = StringIO.new('foo', 'w') +strio.pos # => 0 # Beginning of stream. +strio.string # => "" # Initially cleared. +``` + +May be written anywhere (even past end-of-stream): + +```ruby +strio.write('foobar') +strio.string # => "foobar" +strio.rewind +strio.write('FOO') +strio.string # => "FOObar" +strio.pos = 3 +strio.write('BAR') +strio.string # => "FOOBAR" +strio.pos = 9 +strio.write('baz') +strio.string # => "FOOBAR\u0000\u0000\u0000baz" # Null-padded. +``` + +May not be read: + +```ruby +strio.read # Raises IOError: not opened for reading +``` + +#### `'a'`: Append-Only + +Mode specified as one of: + +- String: `'a'`. +- Constant: `File::WRONLY | File::APPEND`. + +Initial state: + +```ruby +strio = StringIO.new('foo', 'a') +strio.pos # => 0 # Beginning-of-stream. +strio.string # => "foo" # Not cleared. +``` + +May be written only at the end; position does not affect writing: + +```ruby +strio.write('bar') +strio.string # => "foobar" +strio.write('baz') +strio.string # => "foobarbaz" +strio.pos = 400 +strio.write('bat') +strio.string # => "foobarbazbat" +``` + +May not be read: + +```ruby +strio.gets # Raises IOError: not opened for reading +``` + +#### `'r+'`: Read/Write + +Mode specified as one of: + +- String: `'r+'`. +- Constant: `File::RDRW`. + +Initial state: + +```ruby +strio = StringIO.new('foobar', 'r+') +strio.pos # => 0 # Beginning-of-stream. +strio.string # => "foobar" # Not cleared. +``` + +May be written anywhere (even past end-of-stream): + +```ruby +strio.write('FOO') +strio.string # => "FOObar" +strio.write('BAR') +strio.string # => "FOOBAR" +strio.write('BAZ') +strio.string # => "FOOBARBAZ" +strio.pos = 12 +strio.write('BAT') +strio.string # => "FOOBARBAZ\u0000\u0000\u0000BAT" # Null padded. +``` + +May be read anywhere: + +```ruby +strio.pos = 0 +strio.gets(3) # => "FOO" +strio.pos = 6 +strio.gets(3) # => "BAZ" +strio.pos = 400 +strio.gets(3) # => nil +``` + +#### `'w+'`: Read/Write (Initially Clear) + +Mode specified as one of: + +- String: `'w+'`. +- Constant: `File::RDWR | File::TRUNC`. + +Initial state: + +```ruby +strio = StringIO.new('foo', 'w+') +strio.pos # => 0 # Beginning-of-stream. +strio.string # => "" # Truncated. +``` + +May be written anywhere (even past end-of-stream): + +```ruby +strio.write('foobar') +strio.string # => "foobar" +strio.rewind +strio.write('FOO') +strio.string # => "FOObar" +strio.write('BAR') +strio.string # => "FOOBAR" +strio.write('BAZ') +strio.string # => "FOOBARBAZ" +strio.pos = 12 +strio.write('BAT') +strio.string # => "FOOBARBAZ\u0000\u0000\u0000BAT" # Null-padded. +``` + +May be read anywhere: + +```ruby +strio.rewind +strio.gets(3) # => "FOO" +strio.gets(3) # => "BAR" +strio.pos = 12 +strio.gets(3) # => "BAT" +strio.pos = 400 +strio.gets(3) # => nil +``` + +#### `'a+'`: Read/Append + +Mode specified as one of: + +- String: `'a+'`. +- Constant: `File::RDWR | File::APPEND`. + +Initial state: + +```ruby +strio = StringIO.new('foo', 'a+') +strio.pos # => 0 # Beginning-of-stream. +strio.string # => "foo" # Not cleared. +``` + +May be written only at the end; #rewind; position does not affect writing: + +```ruby +strio.write('bar') +strio.string # => "foobar" +strio.write('baz') +strio.string # => "foobarbaz" +strio.pos = 400 +strio.write('bat') +strio.string # => "foobarbazbat" +``` + +May be read anywhere: + +```ruby +strio.rewind +strio.gets(3) # => "foo" +strio.gets(3) # => "bar" +strio.pos = 9 +strio.gets(3) # => "bat" +strio.pos = 400 +strio.gets(3) # => nil +``` + +### Data Mode + +To specify whether the stream is to be treated as text or as binary data, +either of the following may be suffixed to any of the string read/write modes above: + +- `'t'`: Text; + initializes the encoding as Encoding::UTF_8. +- `'b'`: Binary; + initializes the encoding as Encoding::ASCII_8BIT. + +If neither is given, the stream defaults to text data. + +Examples: + +```ruby +strio = StringIO.new('foo', 'rt') +strio.external_encoding # => # +data = "\u9990\u9991\u9992\u9993\u9994" +strio = StringIO.new(data, 'rb') +strio.external_encoding # => # +``` + +When the data mode is specified, the read/write mode may not be omitted: + +```ruby +StringIO.new(data, 'b') # Raises ArgumentError: invalid access mode b +``` + +A text stream may be changed to binary by calling instance method #binmode; +a binary stream may not be changed to text. + +### Encodings + +A stream has an encoding; see the [encodings document][encodings document]. + +The initial encoding for a new or re-opened stream depends on its [data mode][data mode]: -RUSSIAN = 'тест' -DATA = "\u9990\u9991\u9992\u9993\u9994" +- Text: `Encoding::UTF_8`. +- Binary: `Encoding::ASCII_8BIT`. + +These instance methods are relevant: + +- #external_encoding: returns the current encoding of the stream as an `Encoding` object. +- #internal_encoding: returns +nil+; a stream does not have an internal encoding. +- #set_encoding: sets the encoding for the stream. +- #set_encoding_by_bom: sets the encoding for the stream to the stream's BOM (byte order mark). + +Examples: + +```ruby +strio = StringIO.new('foo', 'rt') # Text mode. +strio.external_encoding # => # +data = "\u9990\u9991\u9992\u9993\u9994" +strio = StringIO.new(data, 'rb') # Binary mode. +strio.external_encoding # => # +strio = StringIO.new('foo') +strio.external_encoding # => # +strio.set_encoding('US-ASCII') +strio.external_encoding # => # +``` + +### Position + +A stream has a _position_, and integer offset (in bytes) into the stream. +The initial position of a stream is zero. + +#### Getting and Setting the Position + +Each of these methods initializes (to zero) the position of a new or re-opened stream: + +- ::new: returns a new stream. +- ::open: passes a new stream to the block. +- #reopen: re-initializes the stream. + +Each of these methods queries, gets, or sets the position, without otherwise changing the stream: + +- #eof?: returns whether the position is at end-of-stream. +- #pos: returns the position. +- #pos=: sets the position. +- #rewind: sets the position to zero. +- #seek: sets the position. + +Examples: + +```ruby +strio = StringIO.new('foobar') +strio.pos # => 0 +strio.pos = 3 +strio.pos # => 3 +strio.eof? # => false +strio.rewind +strio.pos # => 0 +strio.seek(0, IO::SEEK_END) +strio.pos # => 6 +strio.eof? # => true +``` + +#### Position Before and After Reading + +Except for #pread, a stream reading method (see [Basic Reading][basic reading]) +begins reading at the current position. + +Except for #pread, a read method advances the position past the read substring. + +Examples: + +```ruby +strio = StringIO.new(TEXT) +strio.string # => "First line\nSecond line\n\nFourth line\nFifth line\n" +strio.pos # => 0 +strio.getc # => "F" +strio.pos # => 1 +strio.gets # => "irst line\n" +strio.pos # => 11 +strio.pos = 24 +strio.gets # => "Fourth line\n" +strio.pos # => 36 + +strio = StringIO.new('тест') # Four 2-byte characters. +strio.pos = 0 # At first byte of first character. +strio.read # => "тест" +strio.pos = 1 # At second byte of first character. +strio.read # => "\x82ест" +strio.pos = 2 # At first of second character. +strio.read # => "ест" + +strio = StringIO.new(TEXT) +strio.pos = 15 +a = [] +strio.each_line {|line| a.push(line) } +a # => ["nd line\n", "\n", "Fourth line\n", "Fifth line\n"] +strio.pos # => 47 ## End-of-stream. +``` + +#### Position Before and After Writing + +Each of these methods begins writing at the current position, +and advances the position to the end of the written substring: + +- #putc: writes the given character. +- #write: writes the given objects as strings. +- [Kernel#puts][kernel#puts]: writes given objects as strings, each followed by newline. + +Examples: + +```ruby +strio = StringIO.new('foo') +strio.pos # => 0 +strio.putc('b') +strio.string # => "boo" +strio.pos # => 1 +strio.write('r') +strio.string # => "bro" +strio.pos # => 2 +strio.puts('ew') +strio.string # => "brew\n" +strio.pos # => 5 +strio.pos = 8 +strio.write('foo') +strio.string # => "brew\n\u0000\u0000\u0000foo" +strio.pos # => 11 +``` + +Each of these methods writes _before_ the current position, and decrements the position +so that the written data is next to be read: + +- #ungetbyte: unshifts the given byte. +- #ungetc: unshifts the given character. + +Examples: + +```ruby +strio = StringIO.new('foo') +strio.pos = 2 +strio.ungetc('x') +strio.pos # => 1 +strio.string # => "fxo" +strio.ungetc('x') +strio.pos # => 0 +strio.string # => "xxo" +``` + +This method does not affect the position: + +- #truncate: truncates the stream's string to the given size. + +Examples: + +```ruby +strio = StringIO.new('foobar') +strio.pos # => 0 +strio.truncate(3) +strio.string # => "foo" +strio.pos # => 0 +strio.pos = 500 +strio.truncate(0) +strio.string # => "" +strio.pos # => 500 +``` + +### Line Number + +A stream has a line number, which initially is zero: + +- Method #lineno returns the line number. +- Method #lineno= sets the line number. + +The line number can be affected by reading (but never by writing); +in general, the line number is incremented each time the record separator (default: `"\n"`) is read. + +Examples: + +```ruby +strio = StringIO.new(TEXT) +strio.string # => "First line\nSecond line\n\nFourth line\nFifth line\n" +strio.lineno # => 0 +strio.gets # => "First line\n" +strio.lineno # => 1 +strio.getc # => "S" +strio.lineno # => 1 +strio.gets # => "econd line\n" +strio.lineno # => 2 +strio.gets # => "\n" +strio.lineno # => 3 +strio.gets # => "Fourth line\n" +strio.lineno # => 4 +``` + +Setting the position does not affect the line number: + +```ruby +strio.pos = 0 +strio.lineno # => 4 +strio.gets # => "First line\n" +strio.pos # => 11 +strio.lineno # => 5 +``` + +And setting the line number does not affect the position: + +```ruby +strio.lineno = 10 +strio.pos # => 11 +strio.gets # => "Second line\n" +strio.lineno # => 11 +strio.pos # => 23 ``` + +### Open/Closed Streams + +A new stream is open for either reading or writing, and may be open for both; +see [Read/Write Mode][read/write mode]. + +Each of these methods initializes the read/write mode for a new or re-opened stream: + +- ::new: returns a new stream. +- ::open: passes a new stream to the block. +- #reopen: re-initializes the stream. + +Other relevant methods: + +- #close: closes the stream for both reading and writing. +- #close_read: closes the stream for reading. +- #close_write: closes the stream for writing. +- #closed?: returns whether the stream is closed for both reading and writing. +- #closed_read?: returns whether the stream is closed for reading. +- #closed_write?: returns whether the stream is closed for writing. + +### BOM (Byte Order Mark) + +The string provided for ::new, ::open, or #reopen +may contain an optional [BOM][bom] (byte order mark) at the beginning of the string; +the BOM can affect the stream's encoding. + +The BOM (if provided): + +- Is stored as part of the stream's string. +- Does _not_ immediately affect the encoding. +- Is _initially_ considered part of the stream. + +```ruby +utf8_bom = "\xEF\xBB\xBF" +string = utf8_bom + 'foo' +string.bytes # => [239, 187, 191, 102, 111, 111] +strio.string.bytes.take(3) # => [239, 187, 191] # The BOM. +strio = StringIO.new(string, 'rb') +strio.string.bytes # => [239, 187, 191, 102, 111, 111] # BOM is part of the stored string. +strio.external_encoding # => # # Default for a binary stream. +strio.gets # => "\xEF\xBB\xBFfoo" # BOM is part of the stream. +``` + +You can call instance method #set_encoding_by_bom to "activate" the stored BOM; +after doing so the BOM: + +- Is _still_ stored as part of the stream's string. +- _Determines_ (and may have changed) the stream's encoding. +- Is _no longer_ considered part of the stream. + +```ruby +strio.set_encoding_by_bom +strio.string.bytes # => [239, 187, 191, 102, 111, 111] # BOM is still part of the stored string. +strio.external_encoding # => # # The new encoding. +strio.rewind # => 0 +strio.gets # => "foo" # BOM is not part of the stream. +``` + +## Basic Stream \IO + +### Basic Reading + +You can read from the stream using these instance methods: + +- #getbyte: reads and returns the next byte. +- #getc: reads and returns the next character. +- #gets: reads and returns all or part of the next line. +- #read: reads and returns all or part of the remaining data in the stream. +- #readlines: reads the remaining data the stream and returns an array of its lines. +- [Kernel#readline][kernel#readline]: like #gets, but raises an exception if at end-of-stream. + +You can iterate over the stream using these instance methods: + +- #each_byte: reads each remaining byte, passing it to the block. +- #each_char: reads each remaining character, passing it to the block. +- #each_codepoint: reads each remaining codepoint, passing it to the block. +- #each_line: reads all or part of each remaining line, passing the read string to the block + +This instance method is useful in a multi-threaded application: + +- #pread: reads and returns all or part of the stream. + +### Basic Writing + +You can write to the stream, advancing the position, using these instance methods: + +- #putc: writes a given character. +- #write: writes the given objects as strings. +- [Kernel#puts][kernel#puts] writes given objects as strings, each followed by newline. + +You can "unshift" to the stream using these instance methods; +each writes _before_ the current position, and decrements the position +so that the written data is next to be read. + +- #ungetbyte: unshifts the given byte. +- #ungetc: unshifts the given character. + +One more writing method: + +- #truncate: truncates the stream's string to the given size. + +## Line \IO + +Reading: + +- #gets: reads and returns the next line. +- [Kernel#readline][kernel#readline]: like #gets, but raises an exception if at end-of-stream. +- #readlines: reads the remaining data the stream and returns an array of its lines. +- #each_line: reads each remaining line, passing it to the block + +Writing: + +- [Kernel#puts][kernel#puts]: writes given objects, each followed by newline. + +## Character \IO + +Reading: + +- #each_char: reads each remaining character, passing it to the block. +- #getc: reads and returns the next character. + +Writing: + +- #putc: writes the given character. +- #ungetc.: unshifts the given character. + +## Byte \IO + +Reading: + +- #each_byte: reads each remaining byte, passing it to the block. +- #getbyte: reads and returns the next byte. + +Writing: + +- #ungetbyte: unshifts the given byte. + +## Codepoint \IO + +Reading: + +- #each_codepoint: reads each remaining codepoint, passing it to the block. + +[bom]: https://en.wikipedia.org/wiki/Byte_order_mark +[encodings document]: https://docs.ruby-lang.org/en/master/encodings_rdoc.html +[io class]: https://docs.ruby-lang.org/en/master/IO.html +[kernel#puts]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-puts +[kernel#readline]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-readline + +[basic reading]: rdoc-ref:StringIO@Basic+Reading +[basic writing]: rdoc-ref:StringIO@Basic+Writing +[bom (byte order mark)]: rdoc-ref:StringIO@BOM+-28Byte+Order+Mark-29 +[data mode]: rdoc-ref:StringIO@Data+Mode +[encodings]: rdoc-ref:StringIO@Encodings +[end-of-stream]: rdoc-ref:StringIO@End-of-Stream +[line number]: rdoc-ref:StringIO@Line+Number +[open/closed streams]: rdoc-ref:StringIO@Open-2FClosed+Streams +[position]: rdoc-ref:StringIO@Position +[read/write mode]: rdoc-ref:StringIO@Read-2FWrite+Mode From d490247df22fc1e800a6590fba52c5801b94302e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 17:30:14 +0900 Subject: [PATCH 225/367] [DOC] Link global variables to command line options --- doc/language/globals.md | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/doc/language/globals.md b/doc/language/globals.md index b22363f1da1b71..716e62a7df87d5 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -176,7 +176,7 @@ No \English. ### `$/` (Input Record Separator) An input record separator, initially newline. -Set by the command-line option `-0`. +Set by the [command-line option `-0`]. Setting to non-nil value by other than the command-line option is deprecated. @@ -188,7 +188,9 @@ Aliased as `$-0`. ### `$\` (Output Record Separator) An output record separator, initially `nil`. -Copied from `$/` when the command-line option `-l` is given. + +Copied from `$/` when the [command-line option `-l`] is +given. Setting to non-nil value by other than the command-line option is deprecated. @@ -328,8 +330,8 @@ The value returned by method ARGF.filename. ### `$DEBUG` -Initially `true` if command-line option `-d` or `--debug` is given, -otherwise initially `false`; +Initially `true` if [command-line option `-d`] or +[`--debug`][command-line option `-d`] is given, otherwise initially `false`; may be set to either value in the running program. When `true`, prints each raised exception to `$stderr`. @@ -338,8 +340,8 @@ Aliased as `$-d`. ### `$VERBOSE` -Initially `true` if command-line option `-v` or `-w` is given, -otherwise initially `false`; +Initially `true` if [command-line option `-v`] or +[`-w`][command-line option `-w`] is given, otherwise initially `false`; may be set to either value, or to `nil`, in the running program. When `true`, enables Ruby warnings. @@ -353,7 +355,7 @@ Aliased as `$-v` and `$-w`. ### `$-F` The default field separator in String#split; must be a String or a -Regexp, and can be set with command-line option `-F`. +Regexp, and can be set with [command-line option `-F`]. Setting to non-nil value by other than the command-line option is deprecated. @@ -362,28 +364,28 @@ Aliased as `$;`. ### `$-a` -Whether command-line option `-a` was given; read-only. +Whether [command-line option `-a`] was given; read-only. ### `$-i` -Contains the extension given with command-line option `-i`, +Contains the extension given with [command-line option `-i`], or `nil` if none. An alias of ARGF.inplace_mode. ### `$-l` -Whether command-line option `-l` was set; read-only. +Whether [command-line option `-l`] was set; read-only. ### `$-p` -Whether command-line option `-p` was given; read-only. +Whether [command-line option `-p`] was given; read-only. ### `$F` -If the command line option `-a` is given, the array obtained by -splitting `$_` by `$-F` is assigned at the start of each `-l`/`-p` -loop. +If the [command-line option `-a`] is given, the array +obtained by splitting `$_` by `$-F` is assigned at the start of each +`-l`/`-p` loop. ## Deprecated @@ -594,3 +596,14 @@ Output: "Bar\n" "Baz\n" ``` + + +[command-line option `-0`]: rdoc-ref:language/options.md@0-3A+Set+-24-2F+-28Input+Record+Separator-29 +[command-line option `-F`]: rdoc-ref:language/options.md@F-3A+Set+Input+Field+Separator +[command-line option `-a`]: rdoc-ref:language/options.md@a-3A+Split+Input+Lines+into+Fields +[command-line option `-d`]: rdoc-ref:language/options.md@d-3A+Set+-24DEBUG+to+true +[command-line option `-i`]: rdoc-ref:language/options.md@i-3A+Set+ARGF+In-Place+Mode +[command-line option `-l`]: rdoc-ref:language/options.md@l-3A+Set+Output+Record+Separator-3B+Chop+Lines +[command-line option `-p`]: rdoc-ref:language/options.md@p-3A+-n-2C+with+Printing +[command-line option `-v`]: rdoc-ref:language/options.md@v-3A+Print+Version-3B+Set+-24VERBOSE +[command-line option `-w`]: rdoc-ref:language/options.md@w-3A+Synonym+for+-W1 From 7259c18c3aa89ce282a5f7ebad68c62980c605d5 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Dec 2025 23:00:13 -0800 Subject: [PATCH 226/367] [DOC] Update NEWS about ZJIT (#15426) --- NEWS.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index b44df19ca2b9b2..3468f479596293 100644 --- a/NEWS.md +++ b/NEWS.md @@ -383,18 +383,19 @@ A lot of work has gone into making Ractors more stable, performant, and usable. ## JIT +* ZJIT + * Introduce an experimental method-based JIT compiler. + To enable `--zjit` support, build Ruby with Rust 1.85.0 or later. + * As of Ruby 4.0.0, ZJIT is faster than the interpreter, but not yet as fast as YJIT. + We encourage experimentation with ZJIT, but advise against deploying it in production for now. + * Our goal is to make ZJIT faster than YJIT and production-ready in Ruby 4.1. * YJIT - * YJIT stats + * `RubyVM::YJIT.runtime_stats` * `ratio_in_yjit` no longer works in the default build. Use `--enable-yjit=stats` on `configure` to enable it on `--yjit-stats`. * Add `invalidate_everything` to default stats, which is incremented when every code is invalidated by TracePoint. * Add `mem_size:` and `call_threshold:` options to `RubyVM::YJIT.enable`. -* ZJIT - * Add an experimental method-based JIT compiler. - Use `--enable-zjit` on `configure` to enable the `--zjit` support. - * As of Ruby 4.0.0-preview1, ZJIT is not yet ready for speeding up most benchmarks. - Please refrain from evaluating ZJIT just yet. Stay tuned for the Ruby 4.0 release. * RJIT * `--rjit` is removed. We will move the implementation of the third-party JIT API to the [ruby/rjit](https://github.com/ruby/rjit) repository. From 180020e1e53b96a3080f3ac731cb32bbbdee31d5 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 6 Dec 2025 20:35:08 +1300 Subject: [PATCH 227/367] Fix `io_pwrite` fiber scheduler hook. (#15428) Fix io_pwrite fiber scheduler hook. --- io.c | 13 ++++++++++--- scheduler.c | 8 ++++++-- vm_dump.c | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/io.c b/io.c index 549e7e1317ef30..91537895d2b3a5 100644 --- a/io.c +++ b/io.c @@ -6254,6 +6254,14 @@ internal_pwrite_func(void *_arg) { struct prdwr_internal_arg *arg = _arg; + return (VALUE)pwrite(arg->fd, arg->buf, arg->count, arg->offset); +} + +static VALUE +pwrite_internal_call(VALUE _arg) +{ + struct prdwr_internal_arg *arg = (struct prdwr_internal_arg *)_arg; + VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_pwrite_memory(scheduler, arg->io->self, arg->offset, arg->buf, arg->count, 0); @@ -6263,8 +6271,7 @@ internal_pwrite_func(void *_arg) } } - - return (VALUE)pwrite(arg->fd, arg->buf, arg->count, arg->offset); + return rb_io_blocking_region_wait(arg->io, internal_pwrite_func, arg, RUBY_IO_WRITABLE); } /* @@ -6316,7 +6323,7 @@ rb_io_pwrite(VALUE io, VALUE str, VALUE offset) arg.buf = RSTRING_PTR(tmp); arg.count = (size_t)RSTRING_LEN(tmp); - n = (ssize_t)rb_io_blocking_region_wait(fptr, internal_pwrite_func, &arg, RUBY_IO_WRITABLE); + n = (ssize_t)pwrite_internal_call((VALUE)&arg); if (n < 0) rb_sys_fail_path(fptr->pathv); rb_str_tmp_frozen_release(str, tmp); diff --git a/scheduler.c b/scheduler.c index 8351fc9945fd8f..3e45cbe83e1d44 100644 --- a/scheduler.c +++ b/scheduler.c @@ -460,12 +460,14 @@ rb_fiber_scheduler_current_for_threadptr(rb_thread_t *thread) } } -VALUE -rb_fiber_scheduler_current(void) +VALUE rb_fiber_scheduler_current(void) { + RUBY_ASSERT(ruby_thread_has_gvl_p()); + return rb_fiber_scheduler_current_for_threadptr(GET_THREAD()); } +// This function is allowed to be called without holding the GVL. VALUE rb_fiber_scheduler_current_for_thread(VALUE thread) { return rb_fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread)); @@ -929,6 +931,8 @@ fiber_scheduler_io_pwrite(VALUE _argument) { VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset) { + + if (!rb_respond_to(scheduler, id_io_pwrite)) { return RUBY_Qundef; } diff --git a/vm_dump.c b/vm_dump.c index c1a8d707359aa0..e2b4804ab0d583 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -1436,7 +1436,7 @@ rb_vm_bugreport(const void *ctx, FILE *errout) "---------------------------------------------------\n"); kprintf("Total ractor count: %u\n", vm->ractor.cnt); kprintf("Ruby thread count for this ractor: %u\n", rb_ec_ractor_ptr(ec)->threads.cnt); - if (rb_fiber_scheduler_get() != Qnil) { + if (ec->thread_ptr->scheduler != Qnil) { kprintf("Note that the Fiber scheduler is enabled\n"); } kputs("\n"); From 42f5654b69244b5892ff2c2eba8d838064d2cd9f Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 6 Dec 2025 21:44:14 +1300 Subject: [PATCH 228/367] Yield to scheduler if interrupts are pending. (#14700) --- ext/-test-/scheduler/extconf.rb | 2 + ext/-test-/scheduler/scheduler.c | 88 +++++++++++++++ include/ruby/fiber/scheduler.h | 8 ++ scheduler.c | 20 ++++ .../test_interrupt_with_scheduler.rb | 55 +++++++++ test/fiber/scheduler.rb | 104 ++++++++++-------- thread.c | 13 ++- 7 files changed, 242 insertions(+), 48 deletions(-) create mode 100644 ext/-test-/scheduler/extconf.rb create mode 100644 ext/-test-/scheduler/scheduler.c create mode 100644 test/-ext-/scheduler/test_interrupt_with_scheduler.rb diff --git a/ext/-test-/scheduler/extconf.rb b/ext/-test-/scheduler/extconf.rb new file mode 100644 index 00000000000000..159699bd8e3ac1 --- /dev/null +++ b/ext/-test-/scheduler/extconf.rb @@ -0,0 +1,2 @@ +# frozen_string_literal: false +create_makefile("-test-/scheduler") diff --git a/ext/-test-/scheduler/scheduler.c b/ext/-test-/scheduler/scheduler.c new file mode 100644 index 00000000000000..f3badceb92fcc6 --- /dev/null +++ b/ext/-test-/scheduler/scheduler.c @@ -0,0 +1,88 @@ +#include "ruby/ruby.h" +#include "ruby/thread.h" +#include "ruby/fiber/scheduler.h" + +/* + * Test extension for reproducing the gRPC interrupt handling bug. + * + * This reproduces the exact issue from grpc/grpc commit 69f229e (June 2025): + * https://github.com/grpc/grpc/commit/69f229edd1d79ab7a7dfda98e3aef6fd807adcad + * + * The bug occurs when: + * 1. A fiber scheduler uses Thread.handle_interrupt(::SignalException => :never) + * (like Async::Scheduler does) + * 2. Native code uses rb_thread_call_without_gvl in a retry loop that checks + * the interrupted flag and retries (like gRPC's completion queue) + * 3. A signal (SIGINT/SIGTERM) is sent + * 4. The unblock_func sets interrupted=1, but Thread.handle_interrupt defers the signal + * 5. The loop sees interrupted=1 and retries without yielding to the scheduler + * 6. The deferred interrupt never gets processed -> infinite hang + * + * The fix is in vm_check_ints_blocking() in thread.c, which should yield to + * the fiber scheduler when interrupts are pending, allowing the scheduler to + * detect Thread.pending_interrupt? and exit its run loop. + */ + +struct blocking_state { + volatile int interrupted; +}; + +static void +unblock_callback(void *argument) +{ + struct blocking_state *blocking_state = (struct blocking_state *)argument; + blocking_state->interrupted = 1; +} + +static void * +blocking_operation(void *argument) +{ + struct blocking_state *blocking_state = (struct blocking_state *)argument; + + while (true) { + struct timeval tv = {1, 0}; // 1 second timeout. + + int result = select(0, NULL, NULL, NULL, &tv); + + if (result == -1 && errno == EINTR) { + blocking_state->interrupted = 1; + return NULL; + } + + // Otherwise, timeout -> loop again. + } + + return NULL; +} + +static VALUE +scheduler_blocking_loop(VALUE self) +{ + struct blocking_state blocking_state = { + .interrupted = 0, + }; + + while (true) { + blocking_state.interrupted = 0; + + rb_thread_call_without_gvl( + blocking_operation, &blocking_state, + unblock_callback, &blocking_state + ); + + // The bug: When interrupted, loop retries without yielding to scheduler. + // With Thread.handle_interrupt(:never), this causes an infinite hang, + // because the deferred interrupt never gets a chance to be processed. + } while (blocking_state.interrupted); + + return Qnil; +} + +void +Init_scheduler(void) +{ + VALUE mBug = rb_define_module("Bug"); + VALUE mScheduler = rb_define_module_under(mBug, "Scheduler"); + + rb_define_module_function(mScheduler, "blocking_loop", scheduler_blocking_loop, 0); +} diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index b06884f596665e..537a3a7bb27a5c 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -167,6 +167,14 @@ VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE duration); */ VALUE rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv); +/** + * Yield to the scheduler, to be resumed on the next scheduling cycle. + * + * @param[in] scheduler Target scheduler. + * @return What `scheduler.yield` returns. + */ +VALUE rb_fiber_scheduler_yield(VALUE scheduler); + /* Description TBW */ #if 0 VALUE rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message); diff --git a/scheduler.c b/scheduler.c index 3e45cbe83e1d44..63c22b55aaa8d5 100644 --- a/scheduler.c +++ b/scheduler.c @@ -28,6 +28,8 @@ static ID id_scheduler_close; static ID id_block; static ID id_unblock; +static ID id_yield; + static ID id_timeout_after; static ID id_kernel_sleep; static ID id_process_wait; @@ -321,6 +323,7 @@ Init_Fiber_Scheduler(void) id_block = rb_intern_const("block"); id_unblock = rb_intern_const("unblock"); + id_yield = rb_intern_const("yield"); id_timeout_after = rb_intern_const("timeout_after"); id_kernel_sleep = rb_intern_const("kernel_sleep"); @@ -538,6 +541,23 @@ rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv) return rb_funcallv(scheduler, id_kernel_sleep, argc, argv); } +/** + * Document-method: Fiber::Scheduler#yield + * call-seq: yield + * + * Yield to the scheduler, to be resumed on the next scheduling cycle. + */ +VALUE +rb_fiber_scheduler_yield(VALUE scheduler) +{ + // First try to call the scheduler's yield method, if it exists: + VALUE result = rb_check_funcall(scheduler, id_yield, 0, NULL); + if (!UNDEF_P(result)) return result; + + // Otherwise, we can emulate yield by sleeping for 0 seconds: + return rb_fiber_scheduler_kernel_sleep(scheduler, RB_INT2NUM(0)); +} + #if 0 /* * Document-method: Fiber::Scheduler#timeout_after diff --git a/test/-ext-/scheduler/test_interrupt_with_scheduler.rb b/test/-ext-/scheduler/test_interrupt_with_scheduler.rb new file mode 100644 index 00000000000000..3f9a7f55a07d11 --- /dev/null +++ b/test/-ext-/scheduler/test_interrupt_with_scheduler.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true +require 'test/unit' +require 'timeout' +require_relative '../../fiber/scheduler' + +class TestSchedulerInterruptHandling < Test::Unit::TestCase + def setup + pend("No fork support") unless Process.respond_to?(:fork) + require '-test-/scheduler' + end + + # Test without Thread.handle_interrupt - should work regardless of fix + def test_without_handle_interrupt_signal_works + IO.pipe do |input, output| + pid = fork do + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + Signal.trap(:INT) do + ::Thread.current.raise(Interrupt) + end + + Fiber.schedule do + # Yield to the scheduler: + sleep(0) + + output.puts "ready" + Bug::Scheduler.blocking_loop + end + end + + output.close + assert_equal "ready\n", input.gets + + sleep 0.1 # Ensure the child is in the blocking loop + $stderr.puts "Sending interrupt" + Process.kill(:INT, pid) + + reaper = Thread.new do + Process.waitpid2(pid) + end + + unless reaper.join(1) + Process.kill(:KILL, pid) + end + + _, status = reaper.value + + # It should be interrupted (not killed): + assert_not_equal 0, status.exitstatus + assert_equal true, status.signaled? + assert_equal Signal.list["INT"], status.termsig + end + end +end diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 2401cb30d34563..60261d69e2213f 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -65,69 +65,79 @@ def next_timeout end end - def run - # $stderr.puts [__method__, Fiber.current].inspect - + def run_once readable = writable = nil - while @readable.any? or @writable.any? or @waiting.any? or @blocking.any? - # May only handle file descriptors up to 1024... - begin - readable, writable = IO.select(@readable.keys + [@urgent.first], @writable.keys, [], next_timeout) - rescue IOError - # Ignore - this can happen if the IO is closed while we are waiting. - end + begin + readable, writable = IO.select(@readable.keys + [@urgent.first], @writable.keys, [], next_timeout) + rescue IOError + # Ignore - this can happen if the IO is closed while we are waiting. + end - # puts "readable: #{readable}" if readable&.any? - # puts "writable: #{writable}" if writable&.any? + # puts "readable: #{readable}" if readable&.any? + # puts "writable: #{writable}" if writable&.any? - selected = {} + selected = {} - readable&.each do |io| - if fiber = @readable.delete(io) - @writable.delete(io) if @writable[io] == fiber - selected[fiber] = IO::READABLE - elsif io == @urgent.first - @urgent.first.read_nonblock(1024) - end + readable&.each do |io| + if fiber = @readable.delete(io) + @writable.delete(io) if @writable[io] == fiber + selected[fiber] = IO::READABLE + elsif io == @urgent.first + @urgent.first.read_nonblock(1024) end + end - writable&.each do |io| - if fiber = @writable.delete(io) - @readable.delete(io) if @readable[io] == fiber - selected[fiber] = selected.fetch(fiber, 0) | IO::WRITABLE - end + writable&.each do |io| + if fiber = @writable.delete(io) + @readable.delete(io) if @readable[io] == fiber + selected[fiber] = selected.fetch(fiber, 0) | IO::WRITABLE end + end - selected.each do |fiber, events| - fiber.transfer(events) - end + selected.each do |fiber, events| + fiber.transfer(events) + end - if @waiting.any? - time = current_time - waiting, @waiting = @waiting, {} - - waiting.each do |fiber, timeout| - if fiber.alive? - if timeout <= time - fiber.transfer - else - @waiting[fiber] = timeout - end + if @waiting.any? + time = current_time + waiting, @waiting = @waiting, {} + + waiting.each do |fiber, timeout| + if fiber.alive? + if timeout <= time + fiber.transfer + else + @waiting[fiber] = timeout end end end + end - if @ready.any? - ready = nil + if @ready.any? + ready = nil - @lock.synchronize do - ready, @ready = @ready, [] - end + @lock.synchronize do + ready, @ready = @ready, [] + end - ready.each do |fiber| - fiber.transfer if fiber.alive? - end + ready.each do |fiber| + fiber.transfer if fiber.alive? + end + end + end + + def run + # $stderr.puts [__method__, Fiber.current].inspect + + # Use Thread.handle_interrupt like Async::Scheduler does + # This defers signal processing, which is the root cause of the gRPC bug + # See: https://github.com/socketry/async/blob/main/lib/async/scheduler.rb + Thread.handle_interrupt(::SignalException => :never) do + while @readable.any? or @writable.any? or @waiting.any? or @blocking.any? + run_once + + break if Thread.pending_interrupt? end end end diff --git a/thread.c b/thread.c index 7c88c979029a10..0beb84a5b4575b 100644 --- a/thread.c +++ b/thread.c @@ -221,7 +221,18 @@ vm_check_ints_blocking(rb_execution_context_t *ec) th->pending_interrupt_queue_checked = 0; RUBY_VM_SET_INTERRUPT(ec); } - return rb_threadptr_execute_interrupts(th, 1); + + int result = rb_threadptr_execute_interrupts(th, 1); + + // When a signal is received, we yield to the scheduler as soon as possible: + if (result || RUBY_VM_INTERRUPTED(ec)) { + VALUE scheduler = rb_fiber_scheduler_current(); + if (scheduler != Qnil) { + rb_fiber_scheduler_yield(scheduler); + } + } + + return result; } int From aae85926504e448637c336aaf41b2c1ed1a6b78b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 6 Dec 2025 11:09:27 +0100 Subject: [PATCH 229/367] [ruby/timeout] Test that Timeout does not expose extra constants https://github.com/ruby/timeout/commit/4de4b4759c --- test/test_timeout.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_timeout.rb b/test/test_timeout.rb index d6ae0c9b50b0ec..3be9013f7abbc4 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -7,7 +7,10 @@ class TestTimeout < Test::Unit::TestCase def test_public_methods assert_equal [:timeout], Timeout.private_instance_methods(false) assert_equal [], Timeout.public_instance_methods(false) + assert_equal [:timeout], Timeout.singleton_class.public_instance_methods(false) + + assert_equal [:Error, :ExitException, :VERSION], Timeout.constants.sort end def test_work_is_done_in_same_thread_as_caller From f4f5f0a009f6335ad13b8651bf43a216442c49a7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Dec 2025 19:32:14 +0900 Subject: [PATCH 230/367] Add error case tests for `File.path` - for non-String argument - for NUL-contained argument - for ASCII-incompatible argument --- spec/ruby/core/file/path_spec.rb | 41 +++++++++++++++++++++++++++++++ test/ruby/test_file_exhaustive.rb | 21 +++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/spec/ruby/core/file/path_spec.rb b/spec/ruby/core/file/path_spec.rb index dfa0c4ec027c99..726febcc2bd0eb 100644 --- a/spec/ruby/core/file/path_spec.rb +++ b/spec/ruby/core/file/path_spec.rb @@ -37,4 +37,45 @@ path.should_receive(:to_path).and_return("abc") File.path(path).should == "abc" end + + it "raises TypeError when #to_path result is not a string" do + path = mock("path") + path.should_receive(:to_path).and_return(nil) + -> { File.path(path) }.should raise_error TypeError + + path = mock("path") + path.should_receive(:to_path).and_return(42) + -> { File.path(path) }.should raise_error TypeError + end + + it "raises ArgumentError for string argument contains NUL character" do + -> { File.path("\0") }.should raise_error ArgumentError + -> { File.path("a\0") }.should raise_error ArgumentError + -> { File.path("a\0c") }.should raise_error ArgumentError + end + + it "raises ArgumentError when #to_path result contains NUL character" do + path = mock("path") + path.should_receive(:to_path).and_return("\0") + -> { File.path(path) }.should raise_error ArgumentError + + path = mock("path") + path.should_receive(:to_path).and_return("a\0") + -> { File.path(path) }.should raise_error ArgumentError + + path = mock("path") + path.should_receive(:to_path).and_return("a\0c") + -> { File.path(path) }.should raise_error ArgumentError + end + + it "raises Encoding::CompatibilityError for ASCII-incompatible string argument" do + path = "abc".encode(Encoding::UTF_32BE) + -> { File.path(path) }.should raise_error Encoding::CompatibilityError + end + + it "raises Encoding::CompatibilityError when #to_path result is ASCII-incompatible" do + path = mock("path") + path.should_receive(:to_path).and_return("abc".encode(Encoding::UTF_32BE)) + -> { File.path(path) }.should raise_error Encoding::CompatibilityError + end end diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index 222578be269581..394dc47603f782 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -204,12 +204,21 @@ def test_path end conv_error = ->(method, msg = "converting with #{method}") { - o = Struct.new(method).new(42) - assert_raise(TypeError, msg) {File.path(o)} - o = Struct.new(method).new("abc".encode(Encoding::UTF_32BE)) - assert_raise(Encoding::CompatibilityError, msg) {File.path(o)} - o = Struct.new(method).new("\0") - assert_raise(ArgumentError, msg) {File.path(o)} + test = ->(&new) do + o = new.(42) + assert_raise(TypeError, msg) {File.path(o)} + + o = new.("abc".encode(Encoding::UTF_32BE)) + assert_raise(Encoding::CompatibilityError, msg) {File.path(o)} + + ["\0", "a\0", "a\0c"].each do |path| + o = new.(path) + assert_raise(ArgumentError, msg) {File.path(o)} + end + end + + test.call(&:itself) + test.call(&Struct.new(method).method(:new)) } conv_error[:to_path] From 0346206d3eab2a8e659be0dd52aea6fc7b0ebb06 Mon Sep 17 00:00:00 2001 From: Steven Johnstone Date: Sat, 6 Dec 2025 00:37:53 +0000 Subject: [PATCH 231/367] [ruby/prism] Avoid out-of-bounds reads Fixes https://github.com/ruby/prism/pull/3790. https://github.com/ruby/prism/commit/173ccb84ad --- prism/prism.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 77ac74192e6f66..93392da3499fbe 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12762,7 +12762,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0)); } - if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { + if (peek_at(parser, call->message_loc.start) == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) { pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION); } @@ -16118,7 +16118,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag static void parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) { // Skip this capture if it starts with an underscore. - if (*location->start == '_') return; + if (peek_at(parser, location->start) == '_') return; if (pm_constant_id_list_includes(captures, capture)) { pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE); From e7f9abdc910bbb37d04e8e65e9bad5f36fa074e1 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 5 Dec 2025 22:09:00 -0500 Subject: [PATCH 232/367] Sync doc/stringio in sync_default_gems.rb --- tool/sync_default_gems.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index c2f352d797ffc8..811e72fc008eff 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -266,6 +266,7 @@ def lib((upstream, branch), gemspec_in_subdir: false) ["ext/stringio", "ext/stringio"], ["test/stringio", "test/stringio"], ["stringio.gemspec", "ext/stringio/stringio.gemspec"], + ["doc/stringio", "doc/stringio"], ], exclude: [ "ext/stringio/README.md", ]), From 240afe50a1d3790d1570ad7bbc16f03bc8512b47 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 00:38:25 +0900 Subject: [PATCH 233/367] Suppress noisy outputs Fix up ruby/ruby#14700. --- test/-ext-/scheduler/test_interrupt_with_scheduler.rb | 4 +++- tool/sync_default_gems.rb | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/-ext-/scheduler/test_interrupt_with_scheduler.rb b/test/-ext-/scheduler/test_interrupt_with_scheduler.rb index 3f9a7f55a07d11..42a109b98b51a6 100644 --- a/test/-ext-/scheduler/test_interrupt_with_scheduler.rb +++ b/test/-ext-/scheduler/test_interrupt_with_scheduler.rb @@ -13,6 +13,8 @@ def setup def test_without_handle_interrupt_signal_works IO.pipe do |input, output| pid = fork do + STDERR.reopen(output) + scheduler = Scheduler.new Fiber.set_scheduler scheduler @@ -33,7 +35,7 @@ def test_without_handle_interrupt_signal_works assert_equal "ready\n", input.gets sleep 0.1 # Ensure the child is in the blocking loop - $stderr.puts "Sending interrupt" + # $stderr.puts "Sending interrupt" Process.kill(:INT, pid) reaper = Thread.new do diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 811e72fc008eff..c2f352d797ffc8 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -266,7 +266,6 @@ def lib((upstream, branch), gemspec_in_subdir: false) ["ext/stringio", "ext/stringio"], ["test/stringio", "test/stringio"], ["stringio.gemspec", "ext/stringio/stringio.gemspec"], - ["doc/stringio", "doc/stringio"], ], exclude: [ "ext/stringio/README.md", ]), From 7319db44fc835d04616369b7b61c33e57960dcde Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Dec 2025 17:53:54 +0900 Subject: [PATCH 234/367] [ruby/pathname] Define `to_path` alias directly The constant `TO_PATH` was defined for 1.9 compatibility but the code for it was dropped 10 years ago. https://github.com/ruby/pathname/commit/95ad4ceb19 --- pathname_builtin.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 1dedf5e08df546..d1b2947107a837 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -195,9 +195,6 @@ class Pathname # :stopdoc: - # to_path is implemented so Pathname objects are usable with File.open, etc. - TO_PATH = :to_path - SAME_PATHS = if File::FNM_SYSCASE.nonzero? # Avoid #zero? here because #casecmp can return nil. proc {|a, b| a.casecmp(b) == 0} @@ -264,7 +261,7 @@ def to_s end # to_path is implemented so Pathname objects are usable with File.open, etc. - alias_method TO_PATH, :to_s + alias to_path to_s def inspect # :nodoc: "#<#{self.class}:#{@path}>" From b675deeeb175b6434bf6abaa747ca3a471eb18ad Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Dec 2025 18:10:44 +0900 Subject: [PATCH 235/367] [ruby/pathname] Use `File.path` for conversion to path name This method has been defined since 1.9, as the standard conversion procedure. https://github.com/ruby/pathname/commit/8f582dc65d --- pathname_builtin.rb | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index d1b2947107a837..0dee1446c07746 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -212,17 +212,7 @@ class Pathname # If +path+ contains a NUL character (\0), an ArgumentError is raised. # def initialize(path) - unless String === path - path = path.to_path if path.respond_to? :to_path - path = path.to_str if path.respond_to? :to_str - raise TypeError, "Pathname.new requires a String, #to_path or #to_str" unless String === path - end - - if path.include?("\0") - raise ArgumentError, "pathname contains \\0: #{path.inspect}" - end - - @path = path.dup + @path = File.path(path).dup end def freeze From da3b7d5ee3afb5ae8e97a906d114b56242a5ff27 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 6 Dec 2025 18:13:58 +0900 Subject: [PATCH 236/367] [ruby/pathname] Freeze and hide internal constants https://github.com/ruby/pathname/commit/60f5d58d73 --- pathname_builtin.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 0dee1446c07746..3d78619c9e4041 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -201,6 +201,8 @@ class Pathname else proc {|a, b| a == b} end + SAME_PATHS.freeze + private_constant :SAME_PATHS attr_reader :path protected :path @@ -307,6 +309,9 @@ def sub_ext(repl) SEPARATOR_LIST = Regexp.quote File::SEPARATOR SEPARATOR_PAT = /#{SEPARATOR_LIST}/ end + SEPARATOR_LIST.freeze + SEPARATOR_PAT.freeze + private_constant :SEPARATOR_LIST, :SEPARATOR_LIST if File.dirname('A:') == 'A:.' # DOSish drive letter # Regexp that matches an absolute path. @@ -314,6 +319,7 @@ def sub_ext(repl) else ABSOLUTE_PATH = /\A#{SEPARATOR_PAT}/ end + ABSOLUTE_PATH.freeze private_constant :ABSOLUTE_PATH # :startdoc: From 2e828dd98feeec5bab5ea85f0661638524004a01 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sat, 6 Dec 2025 10:37:56 -0500 Subject: [PATCH 237/367] Fix strict aliasing warning in ruby_swap128_int The following warnings are emitted. We can use type punning to prevent strict aliasing violations. io_buffer.c:1935:23: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 1935 | rb_uint128_t u = *(rb_uint128_t*)&x; | ^~~~~~~~~~~~~~~~~ io_buffer.c:1937:13: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 1937 | return *(rb_int128_t*)&swapped; | --- io_buffer.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/io_buffer.c b/io_buffer.c index 0d2cbdb4b7e704..b81527ff71147a 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -1928,13 +1928,19 @@ ruby_swap128_uint(rb_uint128_t x) return result; } +union uint128_int128_conversion { + rb_uint128_t uint128; + rb_int128_t int128; +}; + static inline rb_int128_t ruby_swap128_int(rb_int128_t x) { - // Cast to unsigned, swap, then cast back - rb_uint128_t u = *(rb_uint128_t*)&x; - rb_uint128_t swapped = ruby_swap128_uint(u); - return *(rb_int128_t*)&swapped; + union uint128_int128_conversion conversion = { + .int128 = x + }; + conversion.uint128 = ruby_swap128_uint(conversion.uint128); + return conversion.int128; } #define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ From 9dfb7bd7d44ac99bc4d7233cef00e0e6e0743905 Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Fri, 5 Dec 2025 13:14:45 +0100 Subject: [PATCH 238/367] [ruby/openssl] const correct ossl_bin2hex() This helper only reads from its in parameter. Making that const avoids a couple of casts in an upcoming change. https://github.com/ruby/openssl/commit/970d5764e3 --- ext/openssl/ossl.c | 2 +- ext/openssl/ossl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index a19ff23b107c88..5fd6bff98b7bbe 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -122,7 +122,7 @@ ossl_buf2str(char *buf, int len) } void -ossl_bin2hex(unsigned char *in, char *out, size_t inlen) +ossl_bin2hex(const unsigned char *in, char *out, size_t inlen) { const char *hex = "0123456789abcdef"; size_t i; diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 93deafb4b68363..0b479a7200a1f4 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -131,7 +131,7 @@ do{\ * Convert binary string to hex string. The caller is responsible for * ensuring out has (2 * len) bytes of capacity. */ -void ossl_bin2hex(unsigned char *in, char *out, size_t len); +void ossl_bin2hex(const unsigned char *in, char *out, size_t len); /* * Our default PEM callback From d3aa7b889f572237467156f3c6bc3c68ef45e9c4 Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Fri, 5 Dec 2025 13:29:59 +0100 Subject: [PATCH 239/367] [ruby/openssl] Convert ossl_ocsp.c to opaque ASN1_STRING OpenSSL plans to make asn1_string_st opaque, the struct underlying most ASN.1 types such as ASN1_*STRING, ASN1_ENUMERATED, ASN1_INTEGER, etc. Most of ruby/openssl's C code can be straigtforwardly converted to use accessors available since OpenSS https://github.com/ruby/openssl/commit/374262435a --- ext/openssl/ossl_ocsp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index 97ab24c347b556..93d8bc85671eac 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -1550,8 +1550,9 @@ ossl_ocspcid_get_issuer_name_hash(VALUE self) GetOCSPCertId(self, id); OCSP_id_get0_info(&name_hash, NULL, NULL, NULL, id); - ret = rb_str_new(NULL, name_hash->length * 2); - ossl_bin2hex(name_hash->data, RSTRING_PTR(ret), name_hash->length); + ret = rb_str_new(NULL, ASN1_STRING_length(name_hash) * 2); + ossl_bin2hex(ASN1_STRING_get0_data(name_hash), RSTRING_PTR(ret), + ASN1_STRING_length(name_hash)); return ret; } @@ -1573,8 +1574,9 @@ ossl_ocspcid_get_issuer_key_hash(VALUE self) GetOCSPCertId(self, id); OCSP_id_get0_info(NULL, NULL, &key_hash, NULL, id); - ret = rb_str_new(NULL, key_hash->length * 2); - ossl_bin2hex(key_hash->data, RSTRING_PTR(ret), key_hash->length); + ret = rb_str_new(NULL, ASN1_STRING_length(key_hash) * 2); + ossl_bin2hex(ASN1_STRING_get0_data(key_hash), RSTRING_PTR(ret), + ASN1_STRING_length(key_hash)); return ret; } From 98c151b0e55e25217334a94c17102ea8382027f2 Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Fri, 5 Dec 2025 13:44:18 +0100 Subject: [PATCH 240/367] [ruby/openssl] Convert some of ossl_asn1.c to opaque ASN1_STRING This uses the normal accessors but leaves out BIT STRINGS, which will need compat implementations for ASN1_BIT_STRING_get_length() and ASN1_BIT_STRING_set1() for older libcryptos. https://github.com/openssl/openssl/issues/29184 https://github.com/openssl/openssl/issues/29185 https://github.com/ruby/openssl/commit/ba3d1cc5c2 --- ext/openssl/ossl_asn1.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index b21a79484ccc4a..dc72df5a63fe3e 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -114,7 +114,8 @@ ossl_time_split(VALUE time, time_t *sec, int *days) VALUE asn1str_to_str(const ASN1_STRING *str) { - return rb_str_new((const char *)str->data, str->length); + return rb_str_new((const char *)ASN1_STRING_get0_data(str), + ASN1_STRING_length(str)); } /* @@ -129,7 +130,7 @@ asn1integer_to_num(const ASN1_INTEGER *ai) if (!ai) { ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); } - if (ai->type == V_ASN1_ENUMERATED) + if (ASN1_STRING_type(ai) == V_ASN1_ENUMERATED) /* const_cast: workaround for old OpenSSL */ bn = ASN1_ENUMERATED_to_BN((ASN1_ENUMERATED *)ai, NULL); else From a07997bf8124b8aac516f8f70388e86fd24f4a2b Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Fri, 5 Dec 2025 13:52:04 +0100 Subject: [PATCH 241/367] [ruby/openssl] Convert ossl_ns_spki.c to opaque ASN1_STRING https://github.com/ruby/openssl/commit/0941ebbda5 --- ext/openssl/ossl_ns_spki.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index 1d1498824620a9..8440c2ee82b909 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -230,13 +230,12 @@ ossl_spki_get_challenge(VALUE self) NETSCAPE_SPKI *spki; GetSPKI(self, spki); - if (spki->spkac->challenge->length <= 0) { + if (ASN1_STRING_length(spki->spkac->challenge) <= 0) { OSSL_Debug("Challenge.length <= 0?"); return rb_str_new(0, 0); } - return rb_str_new((const char *)spki->spkac->challenge->data, - spki->spkac->challenge->length); + return asn1str_to_str(spki->spkac->challenge); } /* From 38ad0806d7270926ff6fc5c23092aa3e822f386b Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Fri, 5 Dec 2025 13:54:49 +0100 Subject: [PATCH 242/367] [ruby/openssl] Convert ossl_ts.c to opaque ASN1_STRING https://github.com/ruby/openssl/commit/8945f379b3 --- ext/openssl/ossl_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index 615eedc016a0f4..b31a854a63e9ac 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -259,7 +259,7 @@ ossl_ts_req_get_msg_imprint(VALUE self) mi = TS_REQ_get_msg_imprint(req); hashed_msg = TS_MSG_IMPRINT_get_msg(mi); - ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length); + ret = asn1str_to_str(hashed_msg); return ret; } @@ -470,7 +470,7 @@ ossl_ts_req_to_der(VALUE self) ossl_raise(eTimestampError, "Message imprint missing algorithm"); hashed_msg = TS_MSG_IMPRINT_get_msg(mi); - if (!hashed_msg->length) + if (!ASN1_STRING_length(hashed_msg)) ossl_raise(eTimestampError, "Message imprint missing hashed message"); return asn1_to_der((void *)req, (int (*)(void *, unsigned char **))i2d_TS_REQ); @@ -981,7 +981,7 @@ ossl_ts_token_info_get_msg_imprint(VALUE self) GetTSTokenInfo(self, info); mi = TS_TST_INFO_get_msg_imprint(info); hashed_msg = TS_MSG_IMPRINT_get_msg(mi); - ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length); + ret = asn1str_to_str(hashed_msg); return ret; } From 8d3da814c03e06ce331e7022b87500943b57fa4e Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Fri, 5 Dec 2025 14:15:08 +0100 Subject: [PATCH 243/367] [ruby/openssl] Convert ossl_x509ext.c to opaque ASN1_STRING https://github.com/ruby/openssl/commit/a41cf28bab --- ext/openssl/ossl_x509ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c index 85e2eff7a275c6..ef66ecc3fe8390 100644 --- a/ext/openssl/ossl_x509ext.c +++ b/ext/openssl/ossl_x509ext.c @@ -402,7 +402,7 @@ ossl_x509ext_get_value_der(VALUE obj) if ((value = X509_EXTENSION_get_data(ext)) == NULL) ossl_raise(eX509ExtError, NULL); - return rb_str_new((const char *)value->data, value->length); + return asn1str_to_str(value); } static VALUE From 1f0d41aa4d1a3b36e9640e5e8e64c030c71ed613 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sat, 6 Dec 2025 12:07:08 -0500 Subject: [PATCH 244/367] [ruby/date] Call rb_gc_register_mark_object after object allocation It's possible that both half_days_in_day and day_in_nanoseconds are Ruby objects, which means that creating day_in_nanoseconds may trigger GC. Since half_days_in_day is not registered as a mark object until after day_in_nanoseconds is allocated, the GC may reclaim half_days_in_day. We can see this crash: ruby(rb_print_backtrace+0xb) [0x63a373c0] vm_dump.c:1105 ruby(rb_vm_bugreport) vm_dump.c:1450 ruby(rb_assert_failure_detail+0xdb) [0x6371d3a2] error.c:1216 ruby(RB_FL_TEST_RAW+0x0) [0x6371d3d5] error.c:1192 ruby(rb_assert_failure) (null):0 ruby(rb_gc_impl_writebarrier+0xb4) [0x636f01e4] gc/default/default.c:6103 ruby(pin_array_list_append+0x72) [0x638f9787] include/ruby/internal/gc.h:788 ruby(rb_vm_register_global_object) vm.c:4713 ruby(rb_gc_register_mark_object+0x3a) [0x6374144a] gc.c:3449 .ext/i686-linux-gnu/date_core.so(Init_date_core+0x204) [0xdbec86c4] ext/date/date_core.c:9511 .ext/i686-linux-gnu/date_core.so(Init_date_core) (null):0 ruby(dln_load_and_init+0x71) [0x6392c541] dln.c:521 ruby(dln_load_feature+0xd2) [0x6392c7d2] dln.c:566 ruby(load_ext+0xc3) [0x637931b3] load.c:1210 ruby(rb_vm_pop_frame+0x0) [0x638f80cd] vm.c:3120 ruby(rb_vm_call_cfunc_in_box) vm.c:3122 ruby(rb_long2num_inline+0x0) [0x637956f8] load.c:1353 ruby(require_internal) load.c:1354 ruby(rb_require_string_internal+0x60) [0x63795fa1] load.c:1457 ruby(rb_require_string) load.c:1443 https://github.com/ruby/date/commit/cbec5948e0 --- ext/date/date_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/date/date_core.c b/ext/date/date_core.c index cf8ea3c0a44990..6bcf272b62d8b0 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -9496,6 +9496,7 @@ Init_date_core(void) sym_zone = ID2SYM(rb_intern_const("zone")); half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2)); + rb_gc_register_mark_object(half_days_in_day); #if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS day_in_nanoseconds = LONG2NUM((long)DAY_IN_SECONDS * @@ -9507,8 +9508,6 @@ Init_date_core(void) day_in_nanoseconds = f_mul(INT2FIX(DAY_IN_SECONDS), INT2FIX(SECOND_IN_NANOSECONDS)); #endif - - rb_gc_register_mark_object(half_days_in_day); rb_gc_register_mark_object(day_in_nanoseconds); positive_inf = +INFINITY; From 47c0dae188162798c50fdb2580d852ceec89f2ec Mon Sep 17 00:00:00 2001 From: Theo Buehler Date: Sat, 6 Dec 2025 17:58:56 +0100 Subject: [PATCH 245/367] [ruby/openssl] asn1integer_to_num: don't cast away const ASN1_ENUMERATED_to_BN() has been const-correct for a long time in all supported libcrytos, so we can remove this workaround. https://github.com/ruby/openssl/commit/d0f36a7c65 --- ext/openssl/ossl_asn1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index dc72df5a63fe3e..628140a75e3ea4 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -131,8 +131,7 @@ asn1integer_to_num(const ASN1_INTEGER *ai) ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); } if (ASN1_STRING_type(ai) == V_ASN1_ENUMERATED) - /* const_cast: workaround for old OpenSSL */ - bn = ASN1_ENUMERATED_to_BN((ASN1_ENUMERATED *)ai, NULL); + bn = ASN1_ENUMERATED_to_BN(ai, NULL); else bn = ASN1_INTEGER_to_BN(ai, NULL); From 87bc106b8790d5beaccc46555538424fccb1f50f Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Sat, 6 Dec 2025 17:37:40 +0000 Subject: [PATCH 246/367] [ruby/stringio] [DOC] Change link to on-page https://github.com/ruby/stringio/commit/a7c118d786 --- ext/stringio/stringio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 5556426fdc387a..9240929646d5ea 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -1888,7 +1888,7 @@ strio_truncate(VALUE self, VALUE len) * external_encoding -> encoding or nil * * Returns an Encoding object that represents the encoding of the string; - * see {Encoding}[rdoc-ref:Encoding]: + * see {Encodings}[rdoc-ref:StringIO@Encodings]: * * strio = StringIO.new('foo') * strio.external_encoding # => # From 588347a088625b5c16eedbc5f3a7a1189a427e25 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sat, 6 Dec 2025 11:47:34 -0500 Subject: [PATCH 247/367] Fix id2ref for multi-Ractor The id2ref table needs to be under a VM lock to ensure there are no race conditions. The following script crashes: o = Object.new ObjectSpace._id2ref(o.object_id) 10.times.map do Ractor.new do 10_000.times do a = Object.new a.object_id end end end.map(&:value) With: [BUG] Object ID seen, but not in _id2ref table: object_id=2800 object=T_OBJECT ruby 4.0.0dev (2025-12-06T15:15:43Z ractor-id2ref-fix e7f9abdc91) +PRISM [x86_64-linux] -- Control frame information ----------------------------------------------- c:0001 p:---- s:0003 e:000002 l:y b:---- DUMMY [FINISH] -- Threading information --------------------------------------------------- Total ractor count: 5 Ruby thread count for this ractor: 1 -- C level backtrace information ------------------------------------------- miniruby(rb_print_backtrace+0x14) [0x6047d09b2dff] vm_dump.c:1105 miniruby(rb_vm_bugreport) vm_dump.c:1450 miniruby(rb_bug_without_die_internal+0x5f) [0x6047d066bf57] error.c:1098 miniruby(rb_bug) error.c:1116 miniruby(rb_gc_get_ractor_newobj_cache+0x0) [0x6047d066c8dd] gc.c:2052 miniruby(gc_sweep_plane+0xad) [0x6047d079276d] gc/default/default.c:3513 miniruby(gc_sweep_page) gc/default/default.c:3605 miniruby(gc_sweep_step) gc/default/default.c:3886 miniruby(gc_sweep+0x1ba) [0x6047d0794cfa] gc/default/default.c:4154 miniruby(gc_start+0xbf2) [0x6047d0796742] gc/default/default.c:6519 miniruby(heap_prepare+0xcc) [0x6047d079748c] gc/default/default.c:2090 miniruby(heap_next_free_page) gc/default/default.c:2305 miniruby(newobj_cache_miss) gc/default/default.c:2412 miniruby(newobj_alloc+0xd) [0x6047d0798ff5] gc/default/default.c:2436 miniruby(rb_gc_impl_new_obj) gc/default/default.c:2515 miniruby(newobj_of) gc.c:996 miniruby(rb_wb_protected_newobj_of) gc.c:1046 miniruby(str_alloc_embed+0x28) [0x6047d08fda18] string.c:1019 miniruby(str_enc_new) string.c:1069 miniruby(prep_io+0x5) [0x6047d07cda14] io.c:9305 miniruby(prep_stdio) io.c:9347 miniruby(rb_io_prep_stdin) io.c:9365 miniruby(thread_start_func_2+0x77c) [0x6047d093a55c] thread.c:679 miniruby(thread_sched_lock_+0x0) [0x6047d093aacd] thread_pthread.c:2241 miniruby(co_start) thread_pthread_mn.c:469 --- bootstraptest/test_ractor.rb | 17 +++++++++++++++++ gc.c | 4 +++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index bec742da4be0d5..af739ba4bc7fd7 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1116,6 +1116,23 @@ class C end.value RUBY +# Inserting into the id2ref table should be Ractor-safe +assert_equal 'ok', <<~'RUBY' + # Force all calls to Kernel#object_id to insert into the id2ref table + ObjectSpace._id2ref(Object.new.object_id) + + 10.times.map do + Ractor.new do + 10_000.times do + a = Object.new + a.object_id + end + end + end.map(&:value) + + :ok +RUBY + # Ractor.make_shareable(obj) assert_equal 'true', <<~'RUBY', frozen_string_literal: false class C diff --git a/gc.c b/gc.c index 4f5f041fb348fb..c660d1491f3a6d 100644 --- a/gc.c +++ b/gc.c @@ -1905,7 +1905,9 @@ object_id0(VALUE obj) RUBY_ASSERT(rb_shape_obj_has_id(obj)); if (RB_UNLIKELY(id2ref_tbl)) { - st_insert(id2ref_tbl, (st_data_t)id, (st_data_t)obj); + RB_VM_LOCKING() { + st_insert(id2ref_tbl, (st_data_t)id, (st_data_t)obj); + } } return id; } From f298beb2d973debe7ac080aecd28df543678adcf Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 17:20:50 -0600 Subject: [PATCH 248/367] [ruby/stringio] [DOC] Tweaks for StringIO#lineno (https://github.com/ruby/stringio/pull/191) https://github.com/ruby/stringio/commit/f2a2a5a99e --- ext/stringio/stringio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 9240929646d5ea..68986c1a683eba 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -747,7 +747,7 @@ strio_copy(VALUE copy, VALUE orig) * lineno -> current_line_number * * Returns the current line number in +self+; - * see {Line Number}[rdoc-ref:IO@Line+Number]. + * see {Line Number}[rdoc-ref:StringIO@Line+Number]. */ static VALUE strio_get_lineno(VALUE self) From c9fe3cba39fda954e532424c2c082da915320da9 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 17:21:14 -0600 Subject: [PATCH 249/367] [ruby/stringio] [DOC] Tweaks for StringIO#lineno= (https://github.com/ruby/stringio/pull/192) https://github.com/ruby/stringio/commit/8b1ee03cbe --- ext/stringio/stringio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 68986c1a683eba..c135603a28e5f5 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -760,7 +760,7 @@ strio_get_lineno(VALUE self) * lineno = new_line_number -> new_line_number * * Sets the current line number in +self+ to the given +new_line_number+; - * see {Line Number}[rdoc-ref:IO@Line+Number]. + * see {Line Number}[rdoc-ref:StringIO@Line+Number]. */ static VALUE strio_set_lineno(VALUE self, VALUE lineno) From 82577ac09005cf949e125ebe9bb4561002768e5a Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 17:21:26 -0600 Subject: [PATCH 250/367] [ruby/stringio] [DOC] Tweaks for StringIO#pos (https://github.com/ruby/stringio/pull/193) https://github.com/ruby/stringio/commit/90728bbbca --- ext/stringio/stringio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index c135603a28e5f5..69642f8139a81e 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -835,7 +835,7 @@ strio_reopen(int argc, VALUE *argv, VALUE self) * pos -> stream_position * * Returns the current position (in bytes); - * see {Position}[rdoc-ref:IO@Position]. + * see {Position}[rdoc-ref:StringIO@Position]. */ static VALUE strio_get_pos(VALUE self) From 33837abb81510d25088375bbfd74baba16ea2ed1 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 17:21:38 -0600 Subject: [PATCH 251/367] [ruby/stringio] [DOC] Tweaks for StringIO#pos= (https://github.com/ruby/stringio/pull/194) https://github.com/ruby/stringio/commit/3cef1e0e5f --- ext/stringio/stringio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 69642f8139a81e..56fb044a3a299f 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -848,7 +848,7 @@ strio_get_pos(VALUE self) * pos = new_position -> new_position * * Sets the current position (in bytes); - * see {Position}[rdoc-ref:IO@Position]. + * see {Position}[rdoc-ref:StringIO@Position]. */ static VALUE strio_set_pos(VALUE self, VALUE pos) From fb80f84fc7aac043db63e27948a54ce94fcf0da3 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 2 Dec 2025 21:43:29 -0500 Subject: [PATCH 252/367] [DOC] Fix formatting in docs for String#[]= --- doc/string/aset.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/string/aset.rdoc b/doc/string/aset.rdoc index e06680d16c5435..cac3b67ef580dd 100644 --- a/doc/string/aset.rdoc +++ b/doc/string/aset.rdoc @@ -42,7 +42,7 @@ searches for a substring of size +length+ characters (as available) beginning at character offset specified by +start+. If argument +start+ is non-negative, -the offset is +start': +the offset is +start+: s = 'hello' s[0, 1] = 'foo' # => "foo" From 68eab91b147dee98873b54e97296a36301191d72 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 13:44:19 +0900 Subject: [PATCH 253/367] Allow to sync pathname manually Still development of the gem continues, sync as possible manually. --- tool/sync_default_gems.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index c2f352d797ffc8..9d62e954ce11f1 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -194,6 +194,12 @@ def lib((upstream, branch), gemspec_in_subdir: false) optparse: lib("ruby/optparse", gemspec_in_subdir: true).tap { it.mappings << ["doc/optparse", "doc/optparse"] }, + pathname: repo("ruby/pathname", [ + ["ext/pathname/pathname.c", "pathname.c"], + ["lib/pathname_builtin.rb", "pathname_builtin.rb"], + ["lib/pathname.rb", "lib/pathname.rb"], + ["test/pathname", "test/pathname"], + ]), pp: lib("ruby/pp"), prettyprint: lib("ruby/prettyprint"), prism: repo(["ruby/prism", "main"], [ From 941e70ab38d01d067b7bbbcdf8553893a9ca8b49 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 5 Dec 2025 22:09:00 -0500 Subject: [PATCH 254/367] Sync doc/stringio in sync_default_gems.rb --- tool/sync_default_gems.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 9d62e954ce11f1..780f923b55598e 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -272,6 +272,7 @@ def lib((upstream, branch), gemspec_in_subdir: false) ["ext/stringio", "ext/stringio"], ["test/stringio", "test/stringio"], ["stringio.gemspec", "ext/stringio/stringio.gemspec"], + ["doc/stringio", "doc/stringio"], ], exclude: [ "ext/stringio/README.md", ]), From eafaff9443daebba1f4e1a5b2a217b993f066360 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 8 Dec 2025 00:55:13 +1300 Subject: [PATCH 255/367] Re-introduce support for `io_close` hook. (#15434) --- io.c | 22 ++++++++++++---------- test/fiber/scheduler.rb | 7 +++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/io.c b/io.c index 91537895d2b3a5..e0e7d40548ee69 100644 --- a/io.c +++ b/io.c @@ -5551,18 +5551,9 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl) fptr->stdio_file = 0; fptr->mode &= ~(FMODE_READABLE|FMODE_WRITABLE); - // wait for blocking operations to ensure they do not hit EBADF: + // Wait for blocking operations to ensure they do not hit EBADF: rb_thread_io_close_wait(fptr); - // Disable for now. - // if (!done && fd >= 0) { - // VALUE scheduler = rb_fiber_scheduler_current(); - // if (scheduler != Qnil) { - // VALUE result = rb_fiber_scheduler_io_close(scheduler, fptr->self); - // if (!UNDEF_P(result)) done = 1; - // } - // } - if (!done && stdio_file) { // stdio_file is deallocated anyway even if fclose failed. if ((maygvl_fclose(stdio_file, noraise) < 0) && NIL_P(error)) { @@ -5574,6 +5565,15 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl) done = 1; } + VALUE scheduler = rb_fiber_scheduler_current(); + if (!done && fd >= 0 && scheduler != Qnil) { + VALUE result = rb_fiber_scheduler_io_close(scheduler, RB_INT2NUM(fd)); + + if (!UNDEF_P(result)) { + done = RTEST(result); + } + } + if (!done && fd >= 0) { // fptr->fd may be closed even if close fails. POSIX doesn't specify it. // We assumes it is closed. @@ -5724,10 +5724,12 @@ io_close_fptr(VALUE io) if (!fptr) return 0; if (fptr->fd < 0) return 0; + // This guards against multiple threads closing the same IO object: if (rb_thread_io_close_interrupt(fptr)) { /* calls close(fptr->fd): */ fptr_finalize_flush(fptr, FALSE, KEEPGVL); } + rb_io_fptr_cleanup(fptr, FALSE); return fptr; } diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 60261d69e2213f..029c5043dc1b14 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -255,6 +255,13 @@ def io_select(...) end.value end + # This hook is invoked by `IO#close`. Using a separate IO object + # demonstrates that the close operation is asynchronous. + def io_close(descriptor) + Fiber.blocking{IO.for_fd(descriptor.to_i).close} + return true + end + # This hook is invoked by `Kernel#sleep` and `Thread::Mutex#sleep`. def kernel_sleep(duration = nil) # $stderr.puts [__method__, duration, Fiber.current].inspect From 4080abecd6f7b2ce6f7e6a6d1801d3d9fcfa9a58 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 18:16:02 +0900 Subject: [PATCH 256/367] Ignore distclean failures Just clean the directory if it exists and is empty. --- prism/srcs.mk | 2 +- prism/srcs.mk.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prism/srcs.mk b/prism/srcs.mk index 167aa4dbe07e89..022662a00b5f30 100644 --- a/prism/srcs.mk +++ b/prism/srcs.mk @@ -14,7 +14,7 @@ prism/$(HAVE_BASERUBY:yes=.srcs.mk.time): \ distclean-prism-srcs:: $(RM) prism/.srcs.mk.time - $(RMDIR) prism + $(RMDIRS) prism || $(NULLCMD) distclean-srcs-local:: distclean-prism-srcs diff --git a/prism/srcs.mk.in b/prism/srcs.mk.in index b67ce993709ac3..cc263fd1b4ef3f 100644 --- a/prism/srcs.mk.in +++ b/prism/srcs.mk.in @@ -22,7 +22,7 @@ prism/$(HAVE_BASERUBY:yes=.srcs.mk.time): \ distclean-prism-srcs:: $(RM) prism/.srcs.mk.time - $(RMDIR) prism + $(RMDIRS) prism || $(NULLCMD) distclean-srcs-local:: distclean-prism-srcs From f2b250c150b947c3b2341796b2ce44dd94002055 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 00:06:21 +0900 Subject: [PATCH 257/367] [ruby/pathname] Define private method `same_paths?` for Ractor-safety https://github.com/ruby/pathname/commit/d33d18e5e2 --- pathname_builtin.rb | 12 +++++------- test/pathname/test_ractor.rb | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 3d78619c9e4041..d9c5d5b6bad75d 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -195,14 +195,12 @@ class Pathname # :stopdoc: - SAME_PATHS = if File::FNM_SYSCASE.nonzero? + if File::FNM_SYSCASE.nonzero? # Avoid #zero? here because #casecmp can return nil. - proc {|a, b| a.casecmp(b) == 0} + private def same_paths?(a, b) a.casecmp(b) == 0 end else - proc {|a, b| a == b} + private def same_paths?(a, b) a == b end end - SAME_PATHS.freeze - private_constant :SAME_PATHS attr_reader :path protected :path @@ -836,12 +834,12 @@ def relative_path_from(base_directory) base_prefix, basename = r base_names.unshift basename if basename != '.' end - unless SAME_PATHS[dest_prefix, base_prefix] + unless same_paths?(dest_prefix, base_prefix) raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" end while !dest_names.empty? && !base_names.empty? && - SAME_PATHS[dest_names.first, base_names.first] + same_paths?(dest_names.first, base_names.first) dest_names.shift base_names.shift end diff --git a/test/pathname/test_ractor.rb b/test/pathname/test_ractor.rb index f06b7501f3448b..737e4a4111d65c 100644 --- a/test/pathname/test_ractor.rb +++ b/test/pathname/test_ractor.rb @@ -20,6 +20,11 @@ class Ractor x.join(Pathname("b"), Pathname("c")) end assert_equal(Pathname("a/b/c"), r.value) + + r = Ractor.new Pathname("a") do |a| + Pathname("b").relative_path_from(a) + end + assert_equal(Pathname("../b"), r.value) end; end end From 806f2d3533ec5bbf6b4d598b36b1c9de7e786659 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 11:34:11 +0900 Subject: [PATCH 258/367] [ruby/pathname] [DOC] Pathname#freeze https://github.com/ruby/pathname/commit/4580540a2b --- pathname_builtin.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index d9c5d5b6bad75d..17299d42913a93 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -215,6 +215,9 @@ def initialize(path) @path = File.path(path).dup end + # + # Freze self. + # def freeze super @path.freeze From a8a188e1fc8568185c2ca460b5a2e800e9cac4bd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 12:29:41 +0900 Subject: [PATCH 259/367] [ruby/pathname] Add more tests for `Pathname#initialize` https://github.com/ruby/pathname/commit/a2edd25bc1 --- test/pathname/test_pathname.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb index e2d6a09fb2f0c0..10ab1542205fa7 100644 --- a/test/pathname/test_pathname.rb +++ b/test/pathname/test_pathname.rb @@ -485,6 +485,13 @@ def test_initialize p2 = Pathname.new(p1) assert_equal(p1, p2) + obj = Object.new + assert_raise(TypeError) { Pathname.new(obj) } + + obj = Object.new + def obj.to_path; "a/path"; end + assert_equal("a/path", Pathname.new(obj).to_s) + obj = Object.new def obj.to_str; "a/b"; end assert_equal("a/b", Pathname.new(obj).to_s) @@ -494,6 +501,10 @@ def test_initialize_nul assert_raise(ArgumentError) { Pathname.new("a\0") } end + def test_initialize_encoding + assert_raise(Encoding::CompatibilityError) { Pathname.new("a".encode(Encoding::UTF_32BE)) } + end + def test_global_constructor p = Pathname.new('a') assert_equal(p, Pathname('a')) From db6071b5216ac58976f8ab8181ff6eab02dfa8be Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 7 Dec 2025 12:31:42 +0900 Subject: [PATCH 260/367] [ruby/pathname] Raise the previous message Fix ruby/pathname#75. https://github.com/ruby/pathname/commit/5ba967b274 --- pathname_builtin.rb | 2 ++ test/pathname/test_pathname.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pathname_builtin.rb b/pathname_builtin.rb index 17299d42913a93..878603d4d69cd0 100644 --- a/pathname_builtin.rb +++ b/pathname_builtin.rb @@ -213,6 +213,8 @@ class Pathname # def initialize(path) @path = File.path(path).dup + rescue TypeError => e + raise e.class, "Pathname.new requires a String, #to_path or #to_str", cause: nil end # diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb index 10ab1542205fa7..3114d1458d229d 100644 --- a/test/pathname/test_pathname.rb +++ b/test/pathname/test_pathname.rb @@ -486,7 +486,7 @@ def test_initialize assert_equal(p1, p2) obj = Object.new - assert_raise(TypeError) { Pathname.new(obj) } + assert_raise_with_message(TypeError, /#to_path or #to_str/) { Pathname.new(obj) } obj = Object.new def obj.to_path; "a/path"; end From 379d22ce8418448ade3d410e5c76dd2ca0c7a8cf Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Sun, 7 Dec 2025 16:35:04 +0000 Subject: [PATCH 261/367] Bump RDoc version to 6.17.0 (#15439) --- gems/bundled_gems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index 180e1dffcd4dae..bdda3311d0eb07 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -39,7 +39,7 @@ ostruct 0.6.3 https://github.com/ruby/ostruct pstore 0.2.0 https://github.com/ruby/pstore benchmark 0.5.0 https://github.com/ruby/benchmark logger 1.7.0 https://github.com/ruby/logger -rdoc 6.16.1 https://github.com/ruby/rdoc +rdoc 6.17.0 https://github.com/ruby/rdoc win32ole 1.9.2 https://github.com/ruby/win32ole irb 1.15.3 https://github.com/ruby/irb reline 0.6.3 https://github.com/ruby/reline From a4d142137332e449c2e037fa123a9d477d22093a Mon Sep 17 00:00:00 2001 From: git Date: Sun, 7 Dec 2025 16:35:47 +0000 Subject: [PATCH 262/367] [DOC] Update bundled gems list at 379d22ce8418448ade3d410e5c76dd --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 3468f479596293..24a36475360569 100644 --- a/NEWS.md +++ b/NEWS.md @@ -213,7 +213,7 @@ The following bundled gems are promoted from default gems. * pstore 0.2.0 * benchmark 0.5.0 * logger 1.7.0 -* rdoc 6.16.1 +* rdoc 6.17.0 * win32ole 1.9.2 * irb 1.15.3 * reline 0.6.3 From 4f900c35bc01e12b948703b9e4b4f5d0e803f073 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sun, 7 Dec 2025 10:24:08 -0500 Subject: [PATCH 263/367] Output ivar length for T_OBJECT in obj_info --- gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index c660d1491f3a6d..5e3e44e726704d 100644 --- a/gc.c +++ b/gc.c @@ -4841,11 +4841,11 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU APPEND_F("(too_complex) len:%zu", hash_len); } else { - APPEND_F("(embed) len:%d", ROBJECT_FIELDS_CAPACITY(obj)); + APPEND_F("(embed) len:%d capa:%d", RSHAPE_LEN(RBASIC_SHAPE_ID(obj)), ROBJECT_FIELDS_CAPACITY(obj)); } } else { - APPEND_F("len:%d ptr:%p", ROBJECT_FIELDS_CAPACITY(obj), (void *)ROBJECT_FIELDS(obj)); + APPEND_F("len:%d capa:%d ptr:%p", RSHAPE_LEN(RBASIC_SHAPE_ID(obj)), ROBJECT_FIELDS_CAPACITY(obj), (void *)ROBJECT_FIELDS(obj)); } } break; From f2eece71990fd79a5d7e24999f8df5c1121253c6 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 7 Dec 2025 14:58:28 +0900 Subject: [PATCH 264/367] Remove the internal-only attribute from ruby_reset_timezone() The #ifdef is currently not taken because include/ruby/backward.h is not included at this point. The attribute is unnecessary in an internal header, so remove it. --- internal/time.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/time.h b/internal/time.h index e21b3574f6db7a..ad8133ed799c19 100644 --- a/internal/time.h +++ b/internal/time.h @@ -28,9 +28,6 @@ struct timeval rb_time_timeval(VALUE); RUBY_SYMBOL_EXPORT_BEGIN /* time.c (export) */ void ruby_reset_leap_second_info(void); -#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY -RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() -#endif void ruby_reset_timezone(const char *); RUBY_SYMBOL_EXPORT_END From be882278f22444e7d27db091bbd5f8bf63e882c2 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 7 Dec 2025 14:51:38 +0900 Subject: [PATCH 265/367] Move RBIMPL_ATTR_DEPRECATED_* macros to the appropriate header file Move these macros from include/ruby/backward.h to include/ruby/internal/attr/deprecated.h, alongside the other similar macros. include/ruby/internal/intern/vm.h cannot currently use them because include/ruby/backward.h is included too late. --- include/ruby/backward.h | 4 ---- include/ruby/internal/attr/deprecated.h | 7 +++++++ include/ruby/internal/intern/vm.h | 2 -- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/ruby/backward.h b/include/ruby/backward.h index f804c2c36e9425..3a0fda9ec56dda 100644 --- a/include/ruby/backward.h +++ b/include/ruby/backward.h @@ -11,10 +11,6 @@ #include "ruby/internal/interpreter.h" #include "ruby/backward/2/attributes.h" -#define RBIMPL_ATTR_DEPRECATED_SINCE(ver) RBIMPL_ATTR_DEPRECATED(("since " #ver)) -#define RBIMPL_ATTR_DEPRECATED_INTERNAL(ver) RBIMPL_ATTR_DEPRECATED(("since "#ver", also internal")) -#define RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() RBIMPL_ATTR_DEPRECATED(("only for internal use")) - RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() void rb_clear_constant_cache(void); /* from version.c */ diff --git a/include/ruby/internal/attr/deprecated.h b/include/ruby/internal/attr/deprecated.h index e1bbdbd15ad0de..9c9dea1df2794a 100644 --- a/include/ruby/internal/attr/deprecated.h +++ b/include/ruby/internal/attr/deprecated.h @@ -72,4 +72,11 @@ # define RBIMPL_ATTR_DEPRECATED_EXT(msg) RBIMPL_ATTR_DEPRECATED(msg) #endif +#define RBIMPL_ATTR_DEPRECATED_SINCE(ver) \ + RBIMPL_ATTR_DEPRECATED(("since " #ver)) +#define RBIMPL_ATTR_DEPRECATED_INTERNAL(ver) \ + RBIMPL_ATTR_DEPRECATED(("since "#ver", also internal")) +#define RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() \ + RBIMPL_ATTR_DEPRECATED(("only for internal use")) + #endif /* RBIMPL_ATTR_DEPRECATED_H */ diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h index 779f77fe91fcb5..0d25a9cdb041fc 100644 --- a/include/ruby/internal/intern/vm.h +++ b/include/ruby/internal/intern/vm.h @@ -95,7 +95,6 @@ VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv); */ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat); -#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL /** * This API is practically a variant of rb_proc_call_kw() now. Historically * when there still was a concept called `$SAFE`, this was an API for that. @@ -112,7 +111,6 @@ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int k */ RBIMPL_ATTR_DEPRECATED_INTERNAL(4.0) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat); -#endif /** * Identical to rb_funcallv(), except it takes Ruby's array instead of C's. From 4655b174d5fa71b69781c56701be63a02215b12f Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 8 Dec 2025 12:05:54 +0900 Subject: [PATCH 266/367] [ruby/timeout] v0.5.0 https://github.com/ruby/timeout/commit/837d5aac73 --- lib/timeout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 36cd0f915bc7ff..1640542d6f806a 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -20,7 +20,7 @@ module Timeout # The version - VERSION = "0.4.4" + VERSION = "0.5.0" # Internal error raised to when a timeout is triggered. class ExitException < Exception From fbc5bb91207f759c69c3a460bd8bb39915b152d8 Mon Sep 17 00:00:00 2001 From: git Date: Mon, 8 Dec 2025 03:07:35 +0000 Subject: [PATCH 267/367] Update default gems list at 4655b174d5fa71b69781c56701be63 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 24a36475360569..95c02be9513306 100644 --- a/NEWS.md +++ b/NEWS.md @@ -254,7 +254,7 @@ The following default gems are updated. * resolv 0.6.3 * stringio 3.1.9.dev * strscan 3.1.6.dev -* timeout 0.4.4 +* timeout 0.5.0 * uri 1.1.1 * weakref 0.1.4 * zlib 3.2.2 From 6a1f5b68cbd90031d849265b663986c5b8dd39dd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 11:57:24 +0900 Subject: [PATCH 268/367] Make `ruby_reset_timezone` internal It is used only in hash.c, when `ENV['TZ']` is set. --- internal/time.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/time.h b/internal/time.h index ad8133ed799c19..224d485b2e1ce3 100644 --- a/internal/time.h +++ b/internal/time.h @@ -28,7 +28,8 @@ struct timeval rb_time_timeval(VALUE); RUBY_SYMBOL_EXPORT_BEGIN /* time.c (export) */ void ruby_reset_leap_second_info(void); -void ruby_reset_timezone(const char *); RUBY_SYMBOL_EXPORT_END +void ruby_reset_timezone(const char *); + #endif /* INTERNAL_TIME_H */ From a82aa08fe0112aefd35e28dc5ca3f9ea9238ae17 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 12:04:11 +0900 Subject: [PATCH 269/367] Make `ruby_reset_leap_second_info` internal It is exported only for the extension library to test, but the method is no longer used since 29e31e72fb5a14194a78ec974c4ba56c33ad8d45. --- ext/-test-/time/leap_second.c | 15 --------------- internal/time.h | 1 - test/ruby/test_time_tz.rb | 1 - time.c | 2 ++ 4 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 ext/-test-/time/leap_second.c diff --git a/ext/-test-/time/leap_second.c b/ext/-test-/time/leap_second.c deleted file mode 100644 index ee7011fa97e983..00000000000000 --- a/ext/-test-/time/leap_second.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "ruby.h" -#include "internal/time.h" - -static VALUE -bug_time_s_reset_leap_second_info(VALUE klass) -{ - ruby_reset_leap_second_info(); - return Qnil; -} - -void -Init_time_leap_second(VALUE klass) -{ - rb_define_singleton_method(klass, "reset_leap_second_info", bug_time_s_reset_leap_second_info, 0); -} diff --git a/internal/time.h b/internal/time.h index 224d485b2e1ce3..1f3505f5bc7685 100644 --- a/internal/time.h +++ b/internal/time.h @@ -27,7 +27,6 @@ struct timeval rb_time_timeval(VALUE); RUBY_SYMBOL_EXPORT_BEGIN /* time.c (export) */ -void ruby_reset_leap_second_info(void); RUBY_SYMBOL_EXPORT_END void ruby_reset_timezone(const char *); diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index f66cd9bec2eedc..473c3cabcb4d50 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -1,6 +1,5 @@ # frozen_string_literal: false require 'test/unit' -require '-test-/time' class TestTimeTZ < Test::Unit::TestCase has_right_tz = true diff --git a/time.c b/time.c index 2a121d5fcc8f74..b2eed2daf3b85c 100644 --- a/time.c +++ b/time.c @@ -746,6 +746,8 @@ get_tzname(int dst) } #endif +static void ruby_reset_leap_second_info(void); + void ruby_reset_timezone(const char *val) { From 1de15815a8b85f02ba73d9f7c30322530b471b5f Mon Sep 17 00:00:00 2001 From: yoshoku Date: Thu, 4 Dec 2025 09:04:11 +0900 Subject: [PATCH 270/367] [ruby/rubygems] Fix native extension loading in newgem template for RHEL-based systems Add fallback to `require` when `require_relative` fails to load native extensions. This addresses an issue on RHEL-based Linux distributions where Ruby scripts and built native extension shared libraries are installed in separate directories. https://github.com/ruby/rubygems/commit/68599bd107 --- lib/bundler/templates/newgem/lib/newgem.rb.tt | 2 +- spec/bundler/commands/newgem_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt index caf6e32f4abf30..3aedee0d25e92b 100644 --- a/lib/bundler/templates/newgem/lib/newgem.rb.tt +++ b/lib/bundler/templates/newgem/lib/newgem.rb.tt @@ -2,7 +2,7 @@ require_relative "<%= File.basename(config[:namespaced_path]) %>/version" <%- if config[:ext] -%> -require_relative "<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>" +require "<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>" <%- end -%> <%- config[:constant_array].each_with_index do |c, i| -%> diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index 1d158726bed6a0..06c226f9e56a17 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -1681,6 +1681,13 @@ def create_temporary_dir(dir) expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist end + it "generates native extension loading code" do + expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb").read).to include(<<~RUBY) + require_relative "test_gem/version" + require "#{gem_name}/#{gem_name}" + RUBY + end + it "includes rake-compiler, but no Rust related changes" do expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"') From ced333677fb97f2fc720dfb86213ea416b3ebcf4 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Mon, 8 Dec 2025 17:47:07 +0900 Subject: [PATCH 271/367] fix SEGV on clang-16/18 Maybe because of TLS/coroutine problem, CI fails on clang-16/18 ``` 1) Failure: TestTimeout#test_ractor [/tmp/ruby/src/trunk_clang_18/test/test_timeout.rb:288]: pid 307341 killed by SIGSEGV (signal 11) (core dumped) | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98: [BUG] Segmentation fault at 0x0000000000000030 | ruby 4.0.0dev (2025-12-07T16:51:02Z master 4f900c35bc) +PRISM [x86_64-linux] | | -- Control frame information ----------------------------------------------- | c:0006 p:---- s:0026 e:000025 l:y b:---- CFUNC :sleep | c:0005 p:---- s:0023 e:000022 l:y b:---- CFUNC :wait | c:0004 p:0020 s:0017 e:000016 l:n b:---- BLOCK /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98 [FINISH] | c:0003 p:---- s:0014 e:000013 l:y b:---- CFUNC :synchronize | c:0002 p:0072 s:0010 e:000009 l:n b:---- BLOCK /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:96 [FINISH] | c:0001 p:---- s:0003 e:000002 l:y b:---- DUMMY [FINISH] | | -- Ruby level backtrace information ---------------------------------------- | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:96:in 'block in create_timeout_thread' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:96:in 'synchronize' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98:in 'block (2 levels) in create_timeout_thread' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98:in 'wait' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98:in 'sleep' | | -- Threading information --------------------------------------------------- | Total ractor count: 3 | Ruby thread count for this ractor: 2 | | -- Machine register context ------------------------------------------------ | RIP: 0x0000602b1e08a5b5 RBP: 0x000071c65facd130 RSP: 0x000071c6258842e0 | RAX: 0x0000000000000000 RBX: 0x000000006935f7c4 RCX: 0x0000000000000000 | RDX: 0x0000602b1e520c20 RDI: 0x000071c620012480 RSI: 0x000071c620012480 | R8: 0x0000000000000000 R9: 0x0000000000000000 R10: 0x0000000000000000 | R11: 0x0000000000000000 R12: 0x000071c65fa2f640 R13: 0x000071c65fb66e48 | R14: 0x0000000000000000 R15: 0xfdccaa3270000002 EFL: 0x0000000000010202 | | -- C level backtrace information ------------------------------------------- | /tmp/ruby/build/trunk_clang_18/ruby(rb_print_backtrace+0x14) [0x602b1e31a6ea] /tmp/ruby/src/trunk_clang_18/vm_dump.c:1105 | /tmp/ruby/build/trunk_clang_18/ruby(rb_vm_bugreport) /tmp/ruby/src/trunk_clang_18/vm_dump.c:1450 | /tmp/ruby/build/trunk_clang_18/ruby(rb_bug_for_fatal_signal+0x15c) [0x602b1e2d960c] /tmp/ruby/src/trunk_clang_18/error.c:1131 | /tmp/ruby/build/trunk_clang_18/ruby(sigsegv+0x5a) [0x602b1e05528a] /tmp/ruby/src/trunk_clang_18/signal.c:948 | /lib/x86_64-linux-gnu/libc.so.6(0x71c65fd46320) [0x71c65fd46320] | /tmp/ruby/build/trunk_clang_18/ruby(vm_check_ints_blocking+0x0) [0x602b1e08a5b5] /tmp/ruby/src/trunk_clang_18/vm_core.h:2097 | /tmp/ruby/build/trunk_clang_18/ruby(rb_current_execution_context) /tmp/ruby/src/trunk_clang_18/thread_sync.c:617 | /tmp/ruby/build/trunk_clang_18/ruby(rb_mutex_sleep) /tmp/ruby/src/trunk_clang_18/thread_sync.c:617 ``` This patch introduces workaround by acquiring EC before swithcing coroutine. --- thread_sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/thread_sync.c b/thread_sync.c index 6cc23f7d87b32d..8967e24e341bad 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -599,6 +599,8 @@ mutex_sleep_begin(VALUE _arguments) VALUE rb_mutex_sleep(VALUE self, VALUE timeout) { + rb_execution_context_t *ec = GET_EC(); + if (!NIL_P(timeout)) { // Validate the argument: rb_time_interval(timeout); @@ -614,7 +616,7 @@ rb_mutex_sleep(VALUE self, VALUE timeout) VALUE woken = rb_ensure(mutex_sleep_begin, (VALUE)&arguments, mutex_lock_uninterruptible, self); - RUBY_VM_CHECK_INTS_BLOCKING(GET_EC()); + RUBY_VM_CHECK_INTS_BLOCKING(ec); if (!woken) return Qnil; time_t end = time(0) - beg; return TIMET2NUM(end); From 159430e8b6a2c6b5a5a9c926676082fcaef7535e Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Mon, 8 Dec 2025 17:59:57 +0900 Subject: [PATCH 272/367] ignore Thread creation error on resource limited environment. ``` stderr output is not empty bootstraptest.test_ractor.rb_2446_1412.rb:23:in 'Ractor.new': can't create Thread: Cannot allocate memory (ThreadError) ``` --- bootstraptest/test_ractor.rb | 56 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index af739ba4bc7fd7..9042f945f7f4f4 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -2444,35 +2444,45 @@ def call_test(obj) # When creating bmethods in Ractors, they should only be usable from their # defining ractor, even if it is GC'd assert_equal 'ok', <<~'RUBY' -CLASSES = 1000.times.map { Class.new }.freeze -# This would be better to run in parallel, but there's a bug with lambda -# creation and YJIT causing crashes in dev mode -ractors = CLASSES.map do |klass| - Ractor.new(klass) do |klass| - Ractor.receive - klass.define_method(:foo) {} +begin + CLASSES = 1000.times.map { Class.new }.freeze + + # This would be better to run in parallel, but there's a bug with lambda + # creation and YJIT causing crashes in dev mode + ractors = CLASSES.map do |klass| + Ractor.new(klass) do |klass| + Ractor.receive + klass.define_method(:foo) {} + end end -end -ractors.each do |ractor| - ractor << nil - ractor.join -end + ractors.each do |ractor| + ractor << nil + ractor.join + end -ractors.clear -GC.start + ractors.clear + GC.start -any = 1000.times.map do - Ractor.new do - CLASSES.any? do |klass| - begin - klass.new.foo - true - rescue RuntimeError - false + any = 1000.times.map do + Ractor.new do + CLASSES.any? do |klass| + begin + klass.new.foo + true + rescue RuntimeError + false + end end end + end.map(&:value).none? && :ok +rescue ThreadError => e + # ignore limited memory machine + if /can\'t create Thread/ =~ e.message + :ok + else + raise end -end.map(&:value).none? && :ok +end RUBY From 27d60e29845cce3159856ef25d8be533ef402e3c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 17:59:21 +0900 Subject: [PATCH 273/367] [ruby/resolv] Fix warnings on cygwin https://github.com/ruby/resolv/commit/075e76f997 --- ext/win32/resolv/resolv.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/win32/resolv/resolv.c b/ext/win32/resolv/resolv.c index 066856dd984df0..4c56c61d7de579 100644 --- a/ext/win32/resolv/resolv.c +++ b/ext/win32/resolv/resolv.c @@ -21,7 +21,7 @@ w32error_make_error(DWORD e) FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer), NULL)) { - snprintf(buffer, sizeof(buffer), "Unknown Error %u", (unsigned long)e); + snprintf(buffer, sizeof(buffer), "Unknown Error %lu", (unsigned long)e); } p = buffer; while ((p = strpbrk(p, "\r\n")) != NULL) { @@ -149,7 +149,6 @@ reg_each_key(VALUE self) { WCHAR wname[256]; HKEY hkey = DATA_PTR(self); - rb_encoding *utf8 = rb_utf8_encoding(); VALUE k = TypedData_Wrap_Struct(CLASS_OF(self), &hkey_type, NULL); DWORD i, e, n; for (i = 0; n = numberof(wname), (e = RegEnumKeyExW(hkey, i, wname, &n, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS; i++) { @@ -187,7 +186,7 @@ reg_value(VALUE self, VALUE name) case REG_DWORD: case REG_DWORD_BIG_ENDIAN: { DWORD d; - if (size != sizeof(d)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", size); + if (size != sizeof(d)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_REG_DWORD, &type, &d, &size)); if (type == REG_DWORD_BIG_ENDIAN) d = swap_dw(d); return ULONG2NUM(d); @@ -195,12 +194,12 @@ reg_value(VALUE self, VALUE name) case REG_QWORD: { QWORD q; - if (size != sizeof(q)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", size); + if (size != sizeof(q)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_REG_QWORD, &type, &q, &size)); return ULL2NUM(q); } case REG_SZ: case REG_MULTI_SZ: case REG_EXPAND_SZ: - if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", size); + if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); buffer = ALLOCV_N(char, value_buffer, size); break; default: @@ -211,7 +210,6 @@ reg_value(VALUE self, VALUE name) switch (type) { case REG_MULTI_SZ: { const WCHAR *w = (WCHAR *)buffer; - rb_encoding *utf8 = rb_utf8_encoding(); result = rb_ary_new(); size /= sizeof(WCHAR); size -= 1; From 66b2cc3dabbedca1331507f8b4f2b274d0a47570 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 18:29:06 +0900 Subject: [PATCH 274/367] [ruby/resolv] Check the second RegGetValue type https://github.com/ruby/resolv/commit/3678de9e30 --- ext/win32/resolv/resolv.c | 49 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/ext/win32/resolv/resolv.c b/ext/win32/resolv/resolv.c index 4c56c61d7de579..b2d377df9fffc6 100644 --- a/ext/win32/resolv/resolv.c +++ b/ext/win32/resolv/resolv.c @@ -182,6 +182,15 @@ reg_value(VALUE self, VALUE name) e = RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type, NULL, &size); if (e == ERROR_FILE_NOT_FOUND) return Qnil; w32error_check(e); +# define get_value_2nd(data, dsize) do { \ + DWORD type2 = type; \ + w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type2, data, dsize)); \ + if (type != type2) { \ + rb_raise(rb_eRuntimeError, "registry value type changed %lu -> %lu", \ + (unsigned long)type, (unsigned long)type2); \ + } \ + } while (0) + switch (type) { case REG_DWORD: case REG_DWORD_BIG_ENDIAN: { @@ -201,30 +210,30 @@ reg_value(VALUE self, VALUE name) case REG_SZ: case REG_MULTI_SZ: case REG_EXPAND_SZ: if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); buffer = ALLOCV_N(char, value_buffer, size); + get_value_2nd(buffer, &size); + if (type == REG_MULTI_SZ) { + const WCHAR *w = (WCHAR *)buffer; + result = rb_ary_new(); + size /= sizeof(WCHAR); + size -= 1; + for (size_t i = 0; i < size; ++i) { + int n = lstrlenW(w+i); + rb_ary_push(result, wchar_to_utf8(w+i, n)); + i += n; + } + } + else { + result = wchar_to_utf8((WCHAR *)buffer, lstrlenW((WCHAR *)buffer)); + } + ALLOCV_END(value_buffer); break; default: result = rb_str_new(0, size); - buffer = RSTRING_PTR(result); - } - w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type, buffer, &size)); - switch (type) { - case REG_MULTI_SZ: { - const WCHAR *w = (WCHAR *)buffer; - result = rb_ary_new(); - size /= sizeof(WCHAR); - size -= 1; - for (size_t i = 0; i < size; ++i) { - int n = lstrlenW(w+i); - rb_ary_push(result, wchar_to_utf8(w+i, n)); - i += n; - } - return result; - } - case REG_SZ: case REG_EXPAND_SZ: - return wchar_to_utf8((WCHAR *)buffer, lstrlenW((WCHAR *)buffer)); - default: - return result; + get_value_2nd(RSTRING_PTR(result), &size); + rb_str_set_len(result, size); + break; } + return result; } void From 956f8d490cde0bb6941f21d263287bd3a141348c Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Mon, 8 Dec 2025 16:36:45 +0000 Subject: [PATCH 275/367] ZJIT: Avoid redundant SP save in codegen (#15448) --- zjit/src/codegen.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 8c5fdc816d6450..73c092f72091c5 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -790,7 +790,7 @@ fn gen_ccall_with_frame( // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP // to account for the receiver and arguments (and block arguments if any) - gen_prepare_call_with_gc(asm, state, false); + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, caller_stack_size); gen_spill_stack(jit, asm, state); gen_spill_locals(jit, asm, state); @@ -875,7 +875,7 @@ fn gen_ccall_variadic( // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP // to account for the receiver and arguments (like gen_ccall_with_frame does) - gen_prepare_call_with_gc(asm, state, false); + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, caller_stack_size); gen_spill_stack(jit, asm, state); gen_spill_locals(jit, asm, state); @@ -1304,8 +1304,8 @@ fn gen_send_without_block_direct( gen_stack_overflow_check(jit, asm, state, stack_growth); // Save cfp->pc and cfp->sp for the caller frame - gen_prepare_call_with_gc(asm, state, false); - // Special SP math. Can't use gen_prepare_non_leaf_call + // Can't use gen_prepare_non_leaf_call because we need special SP math. + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, state.stack().len() - args.len() - 1); // -1 for receiver gen_spill_locals(jit, asm, state); @@ -2008,6 +2008,18 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso } } +/// Save only the PC to CFP. Use this when you need to call gen_save_sp() +/// immediately after with a custom stack size (e.g., gen_ccall_with_frame +/// adjusts SP to exclude receiver and arguments). +fn gen_save_pc_for_gc(asm: &mut Assembler, state: &FrameState) { + let opcode: usize = state.get_opcode().try_into().unwrap(); + let next_pc: *const VALUE = unsafe { state.pc.offset(insn_len(opcode) as isize) }; + + gen_incr_counter(asm, Counter::vm_write_pc_count); + asm_comment!(asm, "save PC to CFP"); + asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(next_pc)); +} + /// Save the current PC on the CFP as a preparation for calling a C function /// that may allocate objects and trigger GC. Use gen_prepare_non_leaf_call() /// if it may raise exceptions or call arbitrary methods. @@ -2017,13 +2029,7 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso /// However, to avoid marking uninitialized stack slots, this also updates SP, /// which may have cfp->sp for a past frame or a past non-leaf call. fn gen_prepare_call_with_gc(asm: &mut Assembler, state: &FrameState, leaf: bool) { - let opcode: usize = state.get_opcode().try_into().unwrap(); - let next_pc: *const VALUE = unsafe { state.pc.offset(insn_len(opcode) as isize) }; - - gen_incr_counter(asm, Counter::vm_write_pc_count); - asm_comment!(asm, "save PC to CFP"); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(next_pc)); - + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, state.stack_size()); if leaf { asm.expect_leaf_ccall(state.stack_size()); From fd45496f919f202dd30a6a76e7e24fc07abbedc0 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 8 Dec 2025 11:59:27 -0500 Subject: [PATCH 276/367] Update ZJIT docs (#15449) --- NEWS.md | 2 +- doc/jit/zjit.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 95c02be9513306..033fc2ea76da1b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -384,7 +384,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. ## JIT * ZJIT - * Introduce an experimental method-based JIT compiler. + * Introduce an [experimental method-based JIT compiler](https://docs.ruby-lang.org/en/master/jit/zjit_md.html). To enable `--zjit` support, build Ruby with Rust 1.85.0 or later. * As of Ruby 4.0.0, ZJIT is faster than the interpreter, but not yet as fast as YJIT. We encourage experimentation with ZJIT, but advise against deploying it in production for now. diff --git a/doc/jit/zjit.md b/doc/jit/zjit.md index 7434d44b9d7d60..1e5a36fd5e6f04 100644 --- a/doc/jit/zjit.md +++ b/doc/jit/zjit.md @@ -1,7 +1,49 @@ # ZJIT: ADVANCED RUBY JIT PROTOTYPE +ZJIT is a method-based just-in-time (JIT) compiler for Ruby. It uses profile +information from the interpreter to guide optimization in the compiler. + +ZJIT is currently supported for macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. +This project is open source and falls under the same license as CRuby. + +## Current Limitations + +ZJIT may not be suitable for certain applications. It currently only supports macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. ZJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information. +You can change how much executable memory is allocated using [ZJIT's command-line options](rdoc-ref:@Command-Line+Options). + ## Build Instructions +### For normal use + +To build ZJIT on macOS: + +```bash +./autogen.sh + +./configure \ + --enable-zjit \ + --prefix="$HOME"/.rubies/ruby-zjit \ + --disable-install-doc \ + --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" + +make -j miniruby +``` + +To build ZJIT on Linux: + +```bash +./autogen.sh + +./configure \ + --enable-zjit \ + --prefix="$HOME"/.rubies/ruby-zjit \ + --disable-install-doc + +make -j miniruby +``` + +### For development + To build ZJIT on macOS: ```bash @@ -47,6 +89,35 @@ make zjit-bindgen ## Documentation +### Command-Line Options + +See `ruby --help` for ZJIT-specific command-line options: + +``` +$ ruby --help +... +ZJIT options: + --zjit-mem-size=num + Max amount of memory that ZJIT can use in MiB (default: 128). + --zjit-call-threshold=num + Number of calls to trigger JIT (default: 30). + --zjit-num-profiles=num + Number of profiled calls before JIT (default: 5). + --zjit-stats[=quiet] + Enable collecting ZJIT statistics (=quiet to suppress output). + --zjit-disable Disable ZJIT for lazily enabling it with RubyVM::ZJIT.enable. + --zjit-perf Dump ISEQ symbols into /tmp/perf-{}.map for Linux perf. + --zjit-log-compiled-iseqs=path + Log compiled ISEQs to the file. The file will be truncated. + --zjit-trace-exits[=counter] + Record source on side-exit. `Counter` picks specific counter. + --zjit-trace-exits-sample-rate=num + Frequency at which to record side exits. Must be `usize`. +$ +``` + +### Source level documentation + You can generate and open the source level documentation in your browser using: ```bash From bd752290e743e3465286b3c656c8e31869f1c4fc Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Tue, 9 Dec 2025 00:50:32 +0900 Subject: [PATCH 277/367] [ruby/timeout] Revert "Exclude constantly-failing test on x86_64-darwin" This reverts commit https://github.com/ruby/timeout/commit/45816b1b2602. https://github.com/ruby/timeout/commit/b54f91e9dd --- test/test_timeout.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 3be9013f7abbc4..51666b73d8e52a 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -302,6 +302,5 @@ def test_ractor assert_equal :ok, r end; - end if defined?(::Ractor) && RUBY_VERSION >= '4.0' && !RUBY_PLATFORM.include?('x86_64-darwin') - # Exclude on x86_64-darwin as it failed 4 times out of 4 tries in the CI of ruby/ruby-dev-builder + end if defined?(::Ractor) && RUBY_VERSION >= '4.0' end From e61b79b384307ae3358c13d015be3550db7c0b53 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 14:02:08 +0900 Subject: [PATCH 278/367] Fix a typo in the deprecation warning message --- include/ruby/internal/fl_type.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h index da8670a8086abf..9bbcb9d2b82433 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -260,7 +260,7 @@ ruby_fl_type { RUBY_FL_EXIVAR #if defined(RBIMPL_HAVE_ENUM_ATTRIBUTE) - RBIMPL_ATTR_DEPRECATED(("FL_EXIVAR is an outdated implementation detail, it shoudl be used.")) + RBIMPL_ATTR_DEPRECATED(("FL_EXIVAR is an outdated implementation detail, it should not be used.")) #elif defined(_MSC_VER) # pragma deprecated(RUBY_FL_EXIVAR) #endif From 90a9b64238eca659df9d59d35f6cf59fa3851119 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Thu, 4 Dec 2025 16:13:59 -0800 Subject: [PATCH 279/367] Use rb_current_ec_noinline in ractor_{lock,unlock} We're seeing an occasional crash on CI because this ends up inlined all the way into ractor_wait_receive. On llvm (possibly other compilers) the thread local address of ec ends up cached (not the value of ec, the address ec is read from). So if we are migrated to another native thread, that may be invalid. Using rb_current_ec_noinline avoids this problems. It would be good to adjust this code so that ec (or current ractor) is calculated once and then passed through to both lock and unlock. --- ractor.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ractor.c b/ractor.c index 9fec70a1dfab71..f65a4f79c1f8e8 100644 --- a/ractor.c +++ b/ractor.c @@ -72,15 +72,17 @@ ractor_lock(rb_ractor_t *r, const char *file, int line) ASSERT_ractor_unlocking(r); rb_native_mutex_lock(&r->sync.lock); - if (rb_current_execution_context(false)) { - VM_ASSERT(!GET_RACTOR()->malloc_gc_disabled); - GET_RACTOR()->malloc_gc_disabled = true; + const rb_execution_context_t *ec = rb_current_ec_noinline(); + if (ec) { + rb_ractor_t *cr = rb_ec_ractor_ptr(ec); + VM_ASSERT(!cr->malloc_gc_disabled); + cr->malloc_gc_disabled = true; } #if RACTOR_CHECK_MODE > 0 - if (rb_current_execution_context(false) != NULL) { - rb_ractor_t *cr = rb_current_ractor_raw(false); - r->sync.locked_by = cr ? rb_ractor_self(cr) : Qundef; + if (ec != NULL) { + rb_ractor_t *cr = rb_ec_ractor_ptr(ec); + r->sync.locked_by = rb_ractor_self(cr); } #endif @@ -105,9 +107,11 @@ ractor_unlock(rb_ractor_t *r, const char *file, int line) r->sync.locked_by = Qnil; #endif - if (rb_current_execution_context(false)) { - VM_ASSERT(GET_RACTOR()->malloc_gc_disabled); - GET_RACTOR()->malloc_gc_disabled = false; + const rb_execution_context_t *ec = rb_current_ec_noinline(); + if (ec) { + rb_ractor_t *cr = rb_ec_ractor_ptr(ec); + VM_ASSERT(cr->malloc_gc_disabled); + cr->malloc_gc_disabled = false; } rb_native_mutex_unlock(&r->sync.lock); From 66bda731901d4588c9df1a671022231ea0d03216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Mon, 8 Dec 2025 22:58:35 +0100 Subject: [PATCH 280/367] Remove unused local variables in test/ruby/test_io_buffer.rb (#15451) --- test/ruby/test_io_buffer.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 9ff22b4bb356eb..cc7998842313e8 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -852,9 +852,6 @@ def test_integer_endianness_swapping assert_equal value, result_be, "#{be_type}: round-trip failed" # Verify byte patterns are different when endianness differs from host - le_bytes = buffer.get_string(0, buffer_size) - be_bytes = buffer.get_string(buffer_size, buffer_size) - if host_is_le # On little-endian host: le_type should match host, be_type should be swapped # So the byte patterns should be different (unless value is symmetric) From 55ea3ec00f5166423cd7dcd67e220cd264a766f6 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sun, 7 Dec 2025 12:43:44 -0500 Subject: [PATCH 281/367] Fix strict aliasing warning in rb_int128_to_numeric If we don't have uint128, then rb_int128_to_numeric emits a strict aliasing warning: numeric.c:3641:39: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 3641 | return rb_uint128_to_numeric(*(rb_uint128_t*)&n); | ^~~~~~~~~~~~~~~~~ --- internal/numeric.h | 5 +++++ io_buffer.c | 5 ----- numeric.c | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/internal/numeric.h b/internal/numeric.h index 75181a7f168620..d3905f048c2ab5 100644 --- a/internal/numeric.h +++ b/internal/numeric.h @@ -164,6 +164,11 @@ union rb_int128 { }; typedef union rb_int128 rb_int128_t; +union uint128_int128_conversion { + rb_uint128_t uint128; + rb_int128_t int128; +}; + // Conversion functions for 128-bit integers: rb_uint128_t rb_numeric_to_uint128(VALUE x); rb_int128_t rb_numeric_to_int128(VALUE x); diff --git a/io_buffer.c b/io_buffer.c index b81527ff71147a..55f1933194ef7b 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -1928,11 +1928,6 @@ ruby_swap128_uint(rb_uint128_t x) return result; } -union uint128_int128_conversion { - rb_uint128_t uint128; - rb_int128_t int128; -}; - static inline rb_int128_t ruby_swap128_int(rb_int128_t x) { diff --git a/numeric.c b/numeric.c index d9e837644ffa05..1942a6164f616a 100644 --- a/numeric.c +++ b/numeric.c @@ -3638,7 +3638,10 @@ rb_int128_to_numeric(rb_int128_t n) } else { // Positive value - return rb_uint128_to_numeric(*(rb_uint128_t*)&n); + union uint128_int128_conversion conversion = { + .int128 = n + }; + return rb_uint128_to_numeric(conversion.uint128); } #endif } From ca8630b6363a706a43bfdb5a3359addcfb616d19 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 8 Dec 2025 15:33:54 +0900 Subject: [PATCH 282/367] [ruby/rubygems] Extract and generate only bundler bin files instead of full installation. https://github.com/ruby/rubygems/commit/a70e573973 --- lib/rubygems/commands/setup_command.rb | 6 ++++-- test/rubygems/test_gem_commands_setup_command.rb | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 8fcece5d5ce252..175599967cf62d 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -402,8 +402,10 @@ def install_default_bundler_gem(bin_dir) install_dir: default_dir, wrappers: true ) - installer.install - File.delete installer.spec_file + # We need to install only executable and default spec files. + # lib/bundler.rb and lib/bundler/* are available under the site_ruby directory. + installer.extract_bin + installer.generate_bin installer.write_default_spec ensure FileUtils.rm_f built_gem diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb index dfd951268dc962..b33e05ab28dc55 100644 --- a/test/rubygems/test_gem_commands_setup_command.rb +++ b/test/rubygems/test_gem_commands_setup_command.rb @@ -31,6 +31,7 @@ def setup gemspec = util_spec "bundler", "9.9.9" do |s| s.bindir = "exe" s.executables = ["bundle", "bundler"] + s.files = ["lib/bundler.rb"] end File.open "bundler/bundler.gemspec", "w" do |io| @@ -222,6 +223,9 @@ def test_install_default_bundler_gem assert_path_exist "#{Gem.dir}/gems/bundler-#{bundler_version}" assert_path_exist "#{Gem.dir}/gems/bundler-audit-1.0.0" + + assert_path_exist "#{Gem.dir}/gems/bundler-#{bundler_version}/exe/bundle" + assert_path_not_exist "#{Gem.dir}/gems/bundler-#{bundler_version}/lib/bundler.rb" end def test_install_default_bundler_gem_with_default_gems_not_installed_at_default_dir From 09f8b8e3ad46683f1166b5dc1d5ef01cacfae3e4 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 11:40:32 +0100 Subject: [PATCH 283/367] Test that Ractor.shareable_proc keeps the original Proc intact --- bootstraptest/test_ractor.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 9042f945f7f4f4..98fa7c276741b8 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -167,6 +167,25 @@ [pr1.call == self, pr2.call == nil] } +# Ractor.shareable_proc keeps the original Proc intact +assert_equal '[SyntaxError, [Object, 43, 43], Binding]', %q{ + a = 42 + pr1 = Proc.new do + [self.class, eval("a"), binding.local_variable_get(:a)] + end + a += 1 + pr2 = Ractor.shareable_proc(&pr1) + + r = [] + begin + pr2.call + rescue SyntaxError + r << SyntaxError + end + + r << pr1.call << pr1.binding.class +} + # Ractor::IsolationError cases assert_equal '3', %q{ ok = 0 From 39a3b88659a04729a7eec30bbc5ea804a565b9eb Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 11:42:12 +0100 Subject: [PATCH 284/367] Fix some descriptions in bootstraptest/test_ractor.rb --- bootstraptest/test_ractor.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 98fa7c276741b8..75286e2d0c941e 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -150,14 +150,14 @@ Ractor.shareable_lambda{ a }.call } -# Ractor.make_shareable issue for locals in proc [Bug #18023] +# Ractor.shareable_proc issue for locals in proc [Bug #18023] assert_equal '[:a, :b, :c, :d, :e]', %q{ v1, v2, v3, v4, v5 = :a, :b, :c, :d, :e closure = Proc.new { [v1, v2, v3, v4, v5] } Ractor.shareable_proc(&closure).call } -# Ractor.make_shareable makes a copy of given Proc +# Ractor.shareable_proc makes a copy of given Proc assert_equal '[true, true]', %q{ pr1 = Proc.new do self From 4cb3a61b5ec7714f57a7f43409ccc74746e7df6e Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 11:47:03 +0100 Subject: [PATCH 285/367] Fix Ractor test to not depend on the previous test --- bootstraptest/test_ractor.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 75286e2d0c941e..17b1c05173c7bb 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -200,9 +200,9 @@ begin cond = false - a = 1 - a = 2 if cond - Ractor.shareable_proc{a} + b = 1 + b = 2 if cond + Ractor.shareable_proc{b} rescue Ractor::IsolationError => e ok += 1 end From 007a70a15c2911845f83872b83d39eeca7f0f607 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 12:03:44 +0100 Subject: [PATCH 286/367] Test that Ractor.make_shareable mutates the original Proc --- bootstraptest/test_ractor.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 17b1c05173c7bb..81dc2d6b8d04be 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -186,6 +186,37 @@ r << pr1.call << pr1.binding.class } +# Ractor.make_shareable mutates the original Proc +# This is the current behavior, it's currently considered safe enough +# because in most cases it would raise anyway due to not-shared self or not-shared captured variable value +assert_equal '[[42, 42], Binding, true, SyntaxError, "Can\'t create Binding from isolated Proc"]', %q{ + a = 42 + pr1 = nil.instance_exec do + Proc.new do + [eval("a"), binding.local_variable_get(:a)] + end + end + + r = [pr1.call, pr1.binding.class] + + pr2 = Ractor.make_shareable(pr1) + r << pr1.equal?(pr2) + + begin + pr1.call + rescue SyntaxError + r << SyntaxError + end + + begin + r << pr1.binding + rescue ArgumentError + r << $!.message + end + + r +} + # Ractor::IsolationError cases assert_equal '3', %q{ ok = 0 From 55668d748457e7d9db4db93c050e80afe2e9bb47 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Fri, 5 Dec 2025 16:13:23 -0800 Subject: [PATCH 287/367] Only globally clear the flag being cleared This solution is not quite correct because it doesn't solve multiple Ractors subscribing to the same event, but this will avoid unrelated events clobbering the flags for other events. This however will work corretly for subscribing to global ObjectSpace GC events. --- vm_trace.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/vm_trace.c b/vm_trace.c index 58424008fd4f0e..3a445892761521 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -127,9 +127,15 @@ update_global_event_hook(rb_event_flag_t prev_events, rb_event_flag_t new_events rb_clear_bf_ccs(); } - ruby_vm_event_flags = new_events; - ruby_vm_event_enabled_global_flags |= new_events; - rb_objspace_set_event_hook(new_events); + // FIXME: Which flags are enabled globally comes from multiple lists, one + // per-ractor and a global list. + // This incorrectly assumes the lists have mutually exclusive flags set. + // This is true for the global (objspace) events, but not for ex. multiple + // Ractors listening for the same iseq events. + rb_event_flag_t new_events_global = (ruby_vm_event_flags & ~prev_events) | new_events; + ruby_vm_event_flags = new_events_global; + ruby_vm_event_enabled_global_flags |= new_events_global; + rb_objspace_set_event_hook(new_events_global); // Invalidate JIT code as needed if (first_time_iseq_events_p || enable_c_call || enable_c_return) { From de94f88c6820c94b69df3343c6050cdb8cc0610e Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Wed, 3 Dec 2025 00:06:03 -0800 Subject: [PATCH 288/367] Register internal tracepoints globally Internal tracepoints only make sense to run globally, and they already took completely different paths. --- test/objspace/test_objspace.rb | 27 +++++++++++++++++++++++++ test/objspace/test_ractor.rb | 28 ++++++++++++++++++++++++++ vm.c | 2 ++ vm_core.h | 27 +++++++++++++++++++++++-- vm_trace.c | 36 +++++++++++++++++++++++++++------- 5 files changed, 111 insertions(+), 9 deletions(-) diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index e35fc0c14ecfe5..ea3c6aa7192146 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -288,6 +288,33 @@ def test_trace_object_allocations_gc_stress assert true # success end + def test_trace_object_allocations_with_other_tracepoint + # Test that ObjectSpace.trace_object_allocations isn't changed by changes + # to another tracepoint + line_tp = TracePoint.new(:line) { } + + ObjectSpace.trace_object_allocations_start + + obj1 = Object.new; line1 = __LINE__ + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(obj1) + assert_equal line1, ObjectSpace.allocation_sourceline(obj1) + + line_tp.enable + + obj2 = Object.new; line2 = __LINE__ + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(obj2) + assert_equal line2, ObjectSpace.allocation_sourceline(obj2) + + line_tp.disable + + obj3 = Object.new; line3 = __LINE__ + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(obj3) + assert_equal line3, ObjectSpace.allocation_sourceline(obj3) + ensure + ObjectSpace.trace_object_allocations_stop + ObjectSpace.trace_object_allocations_clear + end + def test_trace_object_allocations_compaction omit "compaction is not supported on this platform" unless GC.respond_to?(:compact) diff --git a/test/objspace/test_ractor.rb b/test/objspace/test_ractor.rb index eb3044cda3c3c3..996d83fbd214dd 100644 --- a/test/objspace/test_ractor.rb +++ b/test/objspace/test_ractor.rb @@ -52,4 +52,32 @@ def fin ractors.each(&:join) RUBY end + + def test_trace_object_allocations_with_ractor_tracepoint + # Test that ObjectSpace.trace_object_allocations works globally across all Ractors + assert_ractor(<<~'RUBY', require: 'objspace') + ObjectSpace.trace_object_allocations do + obj1 = Object.new; line1 = __LINE__ + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(obj1) + assert_equal line1, ObjectSpace.allocation_sourceline(obj1) + + r = Ractor.new { + obj = Object.new; line = __LINE__ + [line, obj] + } + + obj2 = Object.new; line2 = __LINE__ + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(obj2) + assert_equal line2, ObjectSpace.allocation_sourceline(obj2) + + expected_line, ractor_obj = r.value + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(ractor_obj) + assert_equal expected_line, ObjectSpace.allocation_sourceline(ractor_obj) + + obj3 = Object.new; line3 = __LINE__ + assert_equal __FILE__, ObjectSpace.allocation_sourcefile(obj3) + assert_equal line3, ObjectSpace.allocation_sourceline(obj3) + end + RUBY + end end diff --git a/vm.c b/vm.c index b15ee0ba236b57..f832fe401f124b 100644 --- a/vm.c +++ b/vm.c @@ -3314,6 +3314,8 @@ rb_vm_mark(void *ptr) rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd); + rb_hook_list_mark(&vm->global_hooks); + rb_id_table_foreach_values(vm->negative_cme_table, vm_mark_negative_cme, NULL); rb_mark_tbl_no_pin(vm->overloaded_cme_table); for (i=0; ihooks; } +static inline rb_hook_list_t * +rb_vm_global_hooks(const rb_execution_context_t *ec) +{ + return &rb_ec_vm_ptr(ec)->global_hooks; +} + +static inline rb_hook_list_t * +rb_ec_hooks(const rb_execution_context_t *ec, rb_event_flag_t event) +{ + // Should be a single bit set + VM_ASSERT(event != 0 && ((event - 1) & event) == 0); + + if (event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK) { + return rb_vm_global_hooks(ec); + } + else { + return rb_ec_ractor_hooks(ec); + } +} + #define EXEC_EVENT_HOOK(ec_, flag_, self_, id_, called_id_, klass_, data_) \ - EXEC_EVENT_HOOK_ORIG(ec_, rb_ec_ractor_hooks(ec_), flag_, self_, id_, called_id_, klass_, data_, 0) + EXEC_EVENT_HOOK_ORIG(ec_, rb_ec_hooks(ec_, flag_), flag_, self_, id_, called_id_, klass_, data_, 0) #define EXEC_EVENT_HOOK_AND_POP_FRAME(ec_, flag_, self_, id_, called_id_, klass_, data_) \ - EXEC_EVENT_HOOK_ORIG(ec_, rb_ec_ractor_hooks(ec_), flag_, self_, id_, called_id_, klass_, data_, 1) + EXEC_EVENT_HOOK_ORIG(ec_, rb_ec_hooks(ec_, flag_), flag_, self_, id_, called_id_, klass_, data_, 1) static inline void rb_exec_event_hook_script_compiled(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE eval_script) diff --git a/vm_trace.c b/vm_trace.c index 3a445892761521..b2fc436a96b210 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -194,7 +194,17 @@ hook_list_connect(VALUE list_owner, rb_hook_list_t *list, rb_event_hook_t *hook, static void connect_event_hook(const rb_execution_context_t *ec, rb_event_hook_t *hook) { - rb_hook_list_t *list = rb_ec_ractor_hooks(ec); + rb_hook_list_t *list; + + /* internal events are VM-global, non-internal are ractor-local */ + VM_ASSERT(!(hook->events & RUBY_INTERNAL_EVENT_OBJSPACE_MASK) || !(hook->events & ~RUBY_INTERNAL_EVENT_OBJSPACE_MASK)); + + if (hook->events & RUBY_INTERNAL_EVENT_OBJSPACE_MASK) { + list = rb_vm_global_hooks(ec); + } + else { + list = rb_ec_ractor_hooks(ec); + } hook_list_connect(Qundef, list, hook, TRUE); } @@ -278,11 +288,9 @@ clean_hooks_check(rb_hook_list_t *list) #define MATCH_ANY_FILTER_TH ((rb_thread_t *)1) -/* if func is 0, then clear all funcs */ static int -remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data) +remove_event_hook_from_list(rb_hook_list_t *list, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data) { - rb_hook_list_t *list = rb_ec_ractor_hooks(ec); int ret = 0; rb_event_hook_t *hook = list->hooks; @@ -303,6 +311,18 @@ remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th return ret; } +/* if func is 0, then clear all funcs */ +static int +remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data) +{ + int ret = 0; + + ret += remove_event_hook_from_list(rb_ec_ractor_hooks(ec), filter_th, func, data); + ret += remove_event_hook_from_list(rb_vm_global_hooks(ec), filter_th, func, data); + + return ret; +} + static int rb_threadptr_remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data) { @@ -427,8 +447,10 @@ rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p) { rb_execution_context_t *ec = trace_arg->ec; - if (UNLIKELY(trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) { - if (ec->trace_arg && (ec->trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) { + if (UNLIKELY(trace_arg->event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK)) { + VM_ASSERT(hooks == rb_vm_global_hooks(ec)); + + if (ec->trace_arg && (ec->trace_arg->event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK)) { /* skip hooks because this thread doing INTERNAL_EVENT */ } else { @@ -436,7 +458,7 @@ rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p) ec->trace_arg = trace_arg; /* only global hooks */ - exec_hooks_unprotected(ec, rb_ec_ractor_hooks(ec), trace_arg); + exec_hooks_unprotected(ec, hooks, trace_arg); ec->trace_arg = prev_trace_arg; } } From 576acb950232a8d979b94117050f577505d8a167 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sat, 6 Dec 2025 16:02:49 +0900 Subject: [PATCH 289/367] Remove `FORWARD_ARGS_WITH_RUBY2_KEYWORDS` check Because `FORWARD_ARGS_WITH_RUBY2_KEYWORDS` definition was removed by 4f77d8d3289ece0e3537d9273a5c745120bff59a. --- parse.y | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/parse.y b/parse.y index 32431434de9734..b700302e7b615f 100644 --- a/parse.y +++ b/parse.y @@ -6385,11 +6385,7 @@ f_args : f_arg ',' f_opt_arg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) args_forward : tBDOT3 { -#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS - $$ = 0; -#else $$ = idFWD_KWREST; -#endif /*% ripper: args_forward! %*/ } ; @@ -14434,11 +14430,7 @@ new_args(struct parser_params *p, rb_node_args_aux_t *pre_args, rb_node_opt_arg_ args->opt_args = opt_args; -#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS - args->ruby2_keywords = args->forwarding; -#else args->ruby2_keywords = 0; -#endif nd_set_loc(RNODE(tail), loc); @@ -15049,9 +15041,7 @@ static void add_forwarding_args(struct parser_params *p) { arg_var(p, idFWD_REST); -#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS arg_var(p, idFWD_KWREST); -#endif arg_var(p, idFWD_BLOCK); arg_var(p, idFWD_ALL); } @@ -15094,15 +15084,11 @@ static NODE * new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc, const YYLTYPE *argsloc) { NODE *rest = NEW_LVAR(idFWD_REST, loc); -#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS NODE *kwrest = list_append(p, NEW_LIST(0, loc), NEW_LVAR(idFWD_KWREST, loc)); -#endif rb_node_block_pass_t *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), argsloc, &NULL_LOC); NODE *args = leading ? rest_arg_append(p, leading, rest, argsloc) : NEW_SPLAT(rest, loc, &NULL_LOC); block->forwarding = TRUE; -#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS args = arg_append(p, args, new_hash(p, kwrest, loc), argsloc); -#endif return arg_blk_pass(args, block); } From 056997cbcdd38b062518fe54e311c964f8bfd98f Mon Sep 17 00:00:00 2001 From: yui-knk Date: Tue, 9 Dec 2025 09:05:45 +0900 Subject: [PATCH 290/367] Remove needless `ruby2_keywords` field from `struct rb_args_info` `ruby2_keywords` is set only to be `0` in parse.y. However `args->ruby2_keywords` is initialized with `0` by `MEMZERO` in `rb_node_args_new` function and `body->param.flags.ruby2_keywords` is initialized with `0` by `ZALLOC` in `rb_iseq_constant_body_alloc` function, so `args->ruby2_keywords` does nothing for `body->param.flags.ruby2_keywords`. --- compile.c | 1 - parse.y | 2 -- rubyparser.h | 1 - 3 files changed, 4 deletions(-) diff --git a/compile.c b/compile.c index e6665c7e1d966f..a5d821eb810cf1 100644 --- a/compile.c +++ b/compile.c @@ -2106,7 +2106,6 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG); - body->param.flags.ruby2_keywords = args->ruby2_keywords; body->param.lead_num = arg_size = (int)args->pre_args_num; if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE; debugs(" - argc: %d\n", body->param.lead_num); diff --git a/parse.y b/parse.y index b700302e7b615f..cb73ea2ef026bb 100644 --- a/parse.y +++ b/parse.y @@ -14430,8 +14430,6 @@ new_args(struct parser_params *p, rb_node_args_aux_t *pre_args, rb_node_opt_arg_ args->opt_args = opt_args; - args->ruby2_keywords = 0; - nd_set_loc(RNODE(tail), loc); return tail; diff --git a/rubyparser.h b/rubyparser.h index ee4fe2e44da03d..5af8f6db62c308 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -782,7 +782,6 @@ struct rb_args_info { struct RNode_OPT_ARG *opt_args; unsigned int no_kwarg: 1; - unsigned int ruby2_keywords: 1; unsigned int forwarding: 1; }; From fab94ecd344b5804f9a1e6b38082049f37bacbd3 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 13:04:34 +0100 Subject: [PATCH 291/367] [ruby/rubygems] Fix the config suggestion in the warning for `$ bundle` * `install_or_cli_help` does not exist for older Bundler like Bundler 2 and so results in a confusing error on Bundler 2: ``` $ bundle Could not find command "". ``` * See https://github.com/ruby/rubygems/pull/9136/files#r2592366837 * Merge the behavior of `install_or_cli_help` in `install`. https://github.com/ruby/rubygems/commit/9b4819ae18 --- lib/bundler/cli.rb | 17 +++++++++++------ spec/bundler/bundler/cli_spec.rb | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 36ce04eb2a5d05..1321c56ed740c1 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -120,12 +120,14 @@ def cli_help self.class.send(:class_options_help, shell) end - desc "install_or_cli_help", "Tries to run bundle install but prints a summary of bundler commands if there is no Gemfile", hide: true + desc "install_or_cli_help", "Deprecated alias of install", hide: true def install_or_cli_help + Bundler.ui.warn <<~MSG + `bundle install_or_cli_help` is a deprecated alias of `bundle install`. + It might be called due to the 'default_cli_command' being set to 'install_or_cli_help', + if so fix that by running `bundle config set default_cli_command install --global`. + MSG invoke_other_command("install") - rescue GemfileNotFound => error - Bundler.ui.error error.message, wrap: true - invoke_other_command("cli_help") end def self.default_command(meth = nil) @@ -136,12 +138,12 @@ def self.default_command(meth = nil) In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, - or you can continue to use the current behavior with `bundle config set default_cli_command install_or_cli_help --global`. + or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. MSG end - Bundler.settings[:default_cli_command] || "install_or_cli_help" + Bundler.settings[:default_cli_command] || "install" end class_option "no-color", type: :boolean, desc: "Disable colorization in output" @@ -287,6 +289,9 @@ def install Bundler.settings.temporary(no_install: false) do Install.new(options).run end + rescue GemfileNotFound => error + invoke_other_command("cli_help") + raise error # re-raise to show the error and get a failing exit status end map aliases_for("install") diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index 4503cea6a0058c..7bc8fd3d36adaf 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -100,10 +100,11 @@ def out_with_macos_man_workaround end it "runs bundle install when default_cli_command set to install" do - bundle "config set default_cli_command install_or_cli_help" + bundle "config set default_cli_command install" bundle "", raise_on_error: false expect(out).to_not include("In a future version of Bundler") expect(err).to include("Could not locate Gemfile") + expect(exitstatus).to_not be_zero end end From 19172d64ebe909f185e28b1d8368a8a920f94a8b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 12:24:03 +0100 Subject: [PATCH 292/367] [ruby/rubygems] Fix indentation of the info message for default_cli_command * It looked like: In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. Bundler version 4.0.0 (2025-12-08 commit https://github.com/ruby/rubygems/commit/9b4819ae18) * And now looks like: In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. Bundler version 4.0.0 (2025-12-08 commit https://github.com/ruby/rubygems/commit/9b4819ae18) https://github.com/ruby/rubygems/commit/979dada8f3 --- lib/bundler/cli.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 1321c56ed740c1..17d8c42e6ecdc5 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -134,12 +134,13 @@ def self.default_command(meth = nil) return super if meth unless Bundler.settings[:default_cli_command] - Bundler.ui.info <<-MSG + Bundler.ui.info <<~MSG In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. + MSG end From 12c16f9dedb38af8111809229495e28e8d37b569 Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Mon, 8 Dec 2025 17:39:34 +0100 Subject: [PATCH 293/367] [ruby/rubygems] Fix Bundler removing executables after creating them When running a fresh `bundle install` with gems that contains executables, Bundler will generate binstubs but soon after will remove them. This is a regression introduced in https://github.com/ruby/rubygems/commit/573ffad3ea4a. This results in doing `bundle install && bundle exec foo` to raise an error saying `foo` couldn't be found. This issue only appears if `BUNDLE_CLEAN` is set. At the end of the installation process, when Bundler has installed gems and generated binstubs, it runs the cleanup. 1. It [detects](https://github.com/ruby/rubygems/blob/4f8aa3b40cded3465bb2cd761e9ce7f8673b7fcb/bundler/lib/bundler/runtime.rb#L182) the executable for the current specs 2. Any existing executables not detected is then removed https://github.com/ruby/rubygems/blob/4f8aa3b40cded3465bb2cd761e9ce7f8673b7fcb/bundler/lib/bundler/runtime.rb#L194. The issue being that 1. now returns an empty array where as it should return the executables of the gems from the current bundle. The problem is in https://github.com/ruby/rubygems/commit/573ffad3ea4a where we removed the `executables` method from the `EndpointSpecification`. When Bundler reads the lockfile, it creates a `EndpointSpecification` object for each spec. At this point, the EndpointSpecification doesn't know about the `executables` of a gem. Once Bundler fetches the `gemspec` from the remote, it swaps the the "spec" with the real one and from here knows what executables the gem has. Reintroduce the `executables` method and the `bindir` in the EndpointSpecification class. From what I'm seeing, the removal of those wasn't needed to resolve the issue where Bundler remembers CLI flags. This is probably an oversight. https://github.com/ruby/rubygems/commit/b47f6b0247 --- lib/bundler/endpoint_specification.rb | 22 ++++++++++++++++++++++ spec/bundler/commands/install_spec.rb | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index 95c6deea26bf4e..c06684657d598b 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -60,6 +60,28 @@ def load_paths end end + # needed for binstubs + def executables + if @remote_specification + @remote_specification.executables + elsif _local_specification + _local_specification.executables + else + super + end + end + + # needed for bundle clean + def bindir + if @remote_specification + @remote_specification.bindir + elsif _local_specification + _local_specification.bindir + else + super + end + end + # needed for post_install_messages during install def post_install_message if @remote_specification diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 3dc8aa0dc01757..41b3a865cd9dfd 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -1885,6 +1885,25 @@ def run expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables) end + it "prevents removing binstubs when BUNDLE_CLEAN is set" do + build_repo4 do + build_gem "kamal", "4.0.6" do |s| + s.executables = ["kamal"] + end + end + + gemfile = <<~G + source "https://gem.repo4" + gem "kamal" + G + + install_gemfile(gemfile, env: { "BUNDLE_CLEAN" => "true", "BUNDLE_PATH" => "vendor/bundle" }) + + expected_executables = [vendored_gems("bin/kamal").to_s] + expected_executables << vendored_gems("bin/kamal.bat").to_s if Gem.win_platform? + expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables) + end + it "preserves lockfile versions conservatively" do build_repo4 do build_gem "mypsych", "4.0.6" do |s| From 71354a9879c5e68b215469ef1b17eeb1ba308ada Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 08:26:28 -0500 Subject: [PATCH 294/367] [ruby/prism] Fix hash pattern location when missing nodes https://github.com/ruby/prism/commit/0ad30561e2 --- prism/prism.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 93392da3499fbe..0cb8c9057054d1 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -4096,8 +4096,8 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme if (elements->size > 0) { if (rest) { - start = elements->nodes[0]->location.start; - end = rest->location.end; + start = MIN(rest->location.start, elements->nodes[0]->location.start); + end = MAX(rest->location.end, elements->nodes[elements->size - 1]->location.end); } else { start = elements->nodes[0]->location.start; end = elements->nodes[elements->size - 1]->location.end; @@ -4117,11 +4117,7 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme .closing_loc = { 0 } }; - pm_node_t *element; - PM_NODE_LIST_FOREACH(elements, index, element) { - pm_node_list_append(&node->elements, element); - } - + pm_node_list_concat(&node->elements, elements); return node; } From cbf39c3be5b71b7321cfba768417427e7c3bf4ad Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 08:37:52 -0500 Subject: [PATCH 295/367] [ruby/prism] Fix up call target node when invalid When there is an invalid syntax tree, we need to make sure to fill in the required call operator location. https://github.com/ruby/prism/commit/937313d7f0 --- prism/prism.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/prism/prism.c b/prism/prism.c index 0cb8c9057054d1..7f1d782f35cfa6 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -3077,6 +3077,17 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { .message_loc = target->message_loc }; + /* It is possible to get here where we have parsed an invalid syntax tree + * where the call operator was not present. In that case we will have a + * problem because it is a required location. In this case we need to fill + * it in with a fake location so that the syntax tree remains valid. */ + if (node->call_operator_loc.start == NULL) { + node->call_operator_loc = (pm_location_t) { + .start = target->base.location.start, + .end = target->base.location.start + }; + } + // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. From 268cbb29c7f69ea5a6d45973091a6e5f5aa89403 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 10:07:39 -0500 Subject: [PATCH 296/367] [ruby/prism] Fully handle unreferencing a block exit If a block exit has a further block exit in its subtree, we need to keep recursing. https://github.com/ruby/prism/commit/855d81a4a8 --- prism/prism.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index 7f1d782f35cfa6..ace4b0b1abcf2c 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12540,7 +12540,10 @@ pm_node_unreference_each(const pm_node_t *node, void *data) { ); } parser->current_block_exits->size--; - return false; + + /* Note returning true here because these nodes could have + * arguments that are themselves block exits. */ + return true; } index++; From 59314911a13d10eab2afbca62081311d702a1373 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 10:18:42 -0500 Subject: [PATCH 297/367] [ruby/prism] Nested heredoc with newline terminator When you have a heredoc interpolated into another heredoc where the inner heredoc is terminated by a newline, you need to avoid adding the newline character a second time. https://github.com/ruby/prism/commit/8eeb5f358b --- prism/prism.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index ace4b0b1abcf2c..20037b5b9f6b6c 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12031,7 +12031,10 @@ parser_lex(pm_parser_t *parser) { // string content. if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) { const uint8_t *end = parser->current.end; - pm_newline_list_append(&parser->newline_list, end); + + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, end); + } // Here we want the buffer to only // include up to the backslash. From 25f277abe481b9109d59d28a2eee3f9998a9ad3a Mon Sep 17 00:00:00 2001 From: hi Date: Fri, 5 Dec 2025 23:56:32 +0900 Subject: [PATCH 298/367] Fix typos in gc.c and gc.rb --- gc.c | 2 +- gc.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index 5e3e44e726704d..79eec5d96bf7af 100644 --- a/gc.c +++ b/gc.c @@ -1816,7 +1816,7 @@ id2ref_tbl_mark(void *data) // It's very unlikely, but if enough object ids were generated, keys may be T_BIGNUM rb_mark_set(table); } - // We purposedly don't mark values, as they are weak references. + // We purposely don't mark values, as they are weak references. // rb_gc_obj_free_vm_weak_references takes care of cleaning them up. } diff --git a/gc.rb b/gc.rb index ccad5ef2c1dbfa..59adcbc62f64d6 100644 --- a/gc.rb +++ b/gc.rb @@ -37,7 +37,7 @@ module GC # interleaved with program execution both before the method returns and afterward; # therefore sweeping may not be completed before the return. # - # Note that these keword arguments are implementation- and version-dependent, + # Note that these keyword arguments are implementation- and version-dependent, # are not guaranteed to be future-compatible, # and may be ignored in some implementations. def self.start full_mark: true, immediate_mark: true, immediate_sweep: true From 79c57d747f7d1e6253a6df7b624a9930cb8a523c Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 9 Dec 2025 17:22:03 +0900 Subject: [PATCH 299/367] Fixed by `misspell -w -error -source=text` --- concurrent_set.c | 2 +- lib/erb/util.rb | 2 +- lib/net/http.rb | 2 +- string.c | 2 +- zjit.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/concurrent_set.c b/concurrent_set.c index e48d9a46e89d3f..3aa61507aaa7d2 100644 --- a/concurrent_set.c +++ b/concurrent_set.c @@ -129,7 +129,7 @@ concurrent_set_try_resize_without_locking(VALUE old_set_obj, VALUE *set_obj_ptr) new_capacity = old_capacity; } - // May cause GC and therefore deletes, so must hapen first. + // May cause GC and therefore deletes, so must happen first. VALUE new_set_obj = rb_concurrent_set_new(old_set->funcs, new_capacity); struct concurrent_set *new_set = RTYPEDDATA_GET_DATA(new_set_obj); diff --git a/lib/erb/util.rb b/lib/erb/util.rb index adeb695aa81fec..d7d69eb4f159a3 100644 --- a/lib/erb/util.rb +++ b/lib/erb/util.rb @@ -9,7 +9,7 @@ require 'cgi/escape' # Load or define ERB::Escape#html_escape. -# We don't build the C extention 'cgi/escape' for JRuby, TruffleRuby, and WASM. +# We don't build the C extension 'cgi/escape' for JRuby, TruffleRuby, and WASM. # miniruby (used by CRuby build scripts) also fails to load erb/escape.so. begin require 'erb/escape' diff --git a/lib/net/http.rb b/lib/net/http.rb index 8a4ff481872abc..3c061286121791 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1323,7 +1323,7 @@ def response_body_encoding=(value) # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. attr_writer :proxy_pass - # Sets wheter the proxy uses SSL; + # Sets whether the proxy uses SSL; # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. attr_writer :proxy_use_ssl diff --git a/string.c b/string.c index 9327306384a232..52d1f28cc1443f 100644 --- a/string.c +++ b/string.c @@ -3979,7 +3979,7 @@ rb_str_append_as_bytes(int argc, VALUE *argv, VALUE str) clear_cr: // If no fast path was hit, we clear the coderange. - // append_as_bytes is predominently meant to be used in + // append_as_bytes is predominantly meant to be used in // buffering situation, hence it's likely the coderange // will never be scanned, so it's not worth spending time // precomputing the coderange except for simple and common diff --git a/zjit.c b/zjit.c index d62c5c58c6334c..7731bc908f6ab9 100644 --- a/zjit.c +++ b/zjit.c @@ -103,7 +103,7 @@ rb_zjit_exit_locations_dict(VALUE *zjit_raw_samples, int *zjit_line_samples, int // Loop through the length of samples_len and add data to the // frames hash. Also push the current value onto the raw_samples - // and line_samples arrary respectively. + // and line_samples array respectively. for (int o = 0; o < num; o++) { rb_zjit_add_frame(frames, zjit_raw_samples[idx]); rb_ary_push(raw_samples, SIZET2NUM(zjit_raw_samples[idx])); From 99133a66f3e5c64c2c2f5dbca0d1be816254f636 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 13:11:55 +0100 Subject: [PATCH 300/367] [ruby/net-http] Check whether TCPSocket#initialize supports open_timeout once and without exceptions * See discussion in https://github.com/ruby/net-http/pull/224 * This check is known to work on at least CRuby, TruffleRuby and JRuby. * Exceptions show up with `ruby -d`/`$DEBUG == true` and would show for every Net::HTTP instance. https://github.com/ruby/net-http/commit/8c76f92779 --- lib/net/http.rb | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 3c061286121791..c63bdddcad621e 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1772,26 +1772,24 @@ def connect end private :connect + tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters + TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]] + tcp_socket_parameters.include?([:key, :open_timeout]) + else + # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize + # See discussion in https://github.com/ruby/net-http/pull/224 + Socket.method(:tcp).parameters.include?([:key, :open_timeout]) + end + private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + def timeouted_connect(conn_addr, conn_port) - if @tcpsocket_supports_open_timeout == nil || @tcpsocket_supports_open_timeout == true - # Try to use built-in open_timeout in TCPSocket.open if: - # - The current Ruby runtime is known to support it, or - # - It is unknown whether the current Ruby runtime supports it (so we'll try). - begin - sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) - @tcpsocket_supports_open_timeout = true - return sock - rescue ArgumentError => e - raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)')) - @tcpsocket_supports_open_timeout = false - end + if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + else + Timeout.timeout(@open_timeout, Net::OpenTimeout) { + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + } end - - # This Ruby runtime is known not to support `TCPSocket(open_timeout:)`. - # Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue. - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } end private :timeouted_connect From ee6784f2899364f9bfd8d605b1bea7d78777a116 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 20 Oct 2025 13:30:46 +0900 Subject: [PATCH 301/367] Refine `copy_ext_file` - Define the error constants. - Use system calls to copy files if available. - Simplify fallback copying. - Copy without stdio buffering. --- box.c | 192 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 108 insertions(+), 84 deletions(-) diff --git a/box.c b/box.c index 3fbc4e9fe23a14..86424dedc8b1e8 100644 --- a/box.c +++ b/box.c @@ -20,6 +20,13 @@ #include +#ifdef HAVE_SYS_SENDFILE_H +# include +#endif +#ifdef HAVE_COPYFILE_H +#include +#endif + VALUE rb_cBox = 0; VALUE rb_cBoxEntry = 0; VALUE rb_mBoxLoader = 0; @@ -518,10 +525,21 @@ sprint_ext_filename(char *str, size_t size, long box_id, const char *prefix, con return snprintf(str, size, "%s%s%sp%"PRI_PIDT_PREFIX"u_%ld_%s", tmp_dir, DIRSEP, prefix, getpid(), box_id, basename); } -#ifdef _WIN32 +enum copy_error_type { + COPY_ERROR_NONE, + COPY_ERROR_SRC_OPEN, + COPY_ERROR_DST_OPEN, + COPY_ERROR_SRC_READ, + COPY_ERROR_DST_WRITE, + COPY_ERROR_SRC_STAT, + COPY_ERROR_DST_CHMOD, + COPY_ERROR_SYSERR +}; + static const char * -copy_ext_file_error(char *message, size_t size) +copy_ext_file_error(char *message, size_t size, int copy_retvalue, const char *src_path, const char *dst_path) { +#ifdef _WIN32 int error = GetLastError(); char *p = message; size_t len = snprintf(message, size, "%d: ", error); @@ -536,120 +554,130 @@ copy_ext_file_error(char *message, size_t size) if (*p == '\n' || *p == '\r') *p = ' '; } - return message; -} #else -static const char * -copy_ext_file_error(char *message, size_t size, int copy_retvalue, const char *src_path, const char *dst_path) -{ switch (copy_retvalue) { - case 1: + case COPY_ERROR_SRC_OPEN: snprintf(message, size, "can't open the extension path: %s", src_path); break; - case 2: + case COPY_ERROR_DST_OPEN: snprintf(message, size, "can't open the file to write: %s", dst_path); break; - case 3: + case COPY_ERROR_SRC_READ: snprintf(message, size, "failed to read the extension path: %s", src_path); break; - case 4: + case COPY_ERROR_DST_WRITE: snprintf(message, size, "failed to write the extension path: %s", dst_path); break; - case 5: + case COPY_ERROR_SRC_STAT: snprintf(message, size, "failed to stat the extension path to copy permissions: %s", src_path); break; - case 6: + case COPY_ERROR_DST_CHMOD: snprintf(message, size, "failed to set permissions to the copied extension path: %s", dst_path); break; + case COPY_ERROR_SYSERR: + snprintf(message, size, "failed to copy the extension: %s", strerror(errno)); + break; + case COPY_ERROR_NONE: /* shouldn't be called */ default: rb_bug("unknown return value of copy_ext_file: %d", copy_retvalue); } +#endif return message; } + +#ifndef _WIN32 +static enum copy_error_type +copy_stream(int src_fd, int dst_fd) +{ + char buffer[1024]; + ssize_t rsize; + + while ((rsize = read(src_fd, buffer, sizeof(buffer))) != 0) { + if (rsize < 0) return COPY_ERROR_SRC_READ; + for (size_t written = 0; written < (size_t)rsize;) { + ssize_t wsize = write(dst_fd, buffer+written, rsize-written); + if (wsize < 0) return COPY_ERROR_DST_WRITE; + written += (size_t)wsize; + } + } + return COPY_ERROR_NONE; +} #endif -static int +static enum copy_error_type copy_ext_file(const char *src_path, const char *dst_path) { #if defined(_WIN32) - int rvalue; - WCHAR *w_src = rb_w32_mbstr_to_wstr(CP_UTF8, src_path, -1, NULL); WCHAR *w_dst = rb_w32_mbstr_to_wstr(CP_UTF8, dst_path, -1, NULL); if (!w_src || !w_dst) { + free(w_src); + free(w_dst); rb_memerror(); } - rvalue = CopyFileW(w_src, w_dst, FALSE) ? 0 : 1; + enum copy_error_type rvalue = CopyFileW(w_src, w_dst, TRUE) ? + COPY_ERROR_NONE : COPY_ERROR_SYSERR; free(w_src); free(w_dst); return rvalue; #else - FILE *src, *dst; - char buffer[1024]; - size_t read = 0, wrote, written = 0; - size_t maxread = sizeof(buffer); - int eof = 0; - int clean_read = 1; - int retvalue = 0; - - src = fopen(src_path, "rb"); - if (!src) { - return 1; +# ifdef O_BINARY + const int bin = O_BINARY; +# else + const int bin = 0; +# endif + const int src_fd = open(src_path, O_RDONLY|bin); + if (src_fd < 0) return COPY_ERROR_SRC_OPEN; + + struct stat src_st; + if (fstat(src_fd, &src_st)) { + close(src_fd); + return COPY_ERROR_SRC_STAT; } - dst = fopen(dst_path, "wb"); - if (!dst) { - return 2; + + const int dst_fd = open(dst_path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|bin, S_IRWXU); + if (dst_fd < 0) { + close(src_fd); + return COPY_ERROR_DST_OPEN; } - while (!eof) { - if (clean_read) { - read = fread(buffer, 1, sizeof(buffer), src); - written = 0; - } - if (read > 0) { - wrote = fwrite(buffer+written, 1, read-written, dst); - if (wrote < read-written) { - if (ferror(dst)) { - retvalue = 4; - break; - } - else { // partial write - clean_read = 0; - written += wrote; - } - } - else { // Wrote the entire buffer to dst, next read is clean one - clean_read = 1; - } - } - if (read < maxread) { - if (clean_read && feof(src)) { - // If it's not clean, buffer should have bytes not written yet. - eof = 1; - } - else if (ferror(src)) { - retvalue = 3; - // Writes could be partial/dirty, but this load is failure anyway - break; - } - } + + enum copy_error_type ret = COPY_ERROR_NONE; + + if (fchmod(dst_fd, src_st.st_mode & 0777)) { + ret = COPY_ERROR_DST_CHMOD; + goto done; } - fclose(src); - fclose(dst); -#if defined(__CYGWIN__) - // On Cygwin, CopyFile-like operations may strip executable bits. - // Explicitly match destination file permissions to source. - if (retvalue == 0) { - struct stat st; - if (stat(src_path, &st) != 0) { - retvalue = 5; - } - else if (chmod(dst_path, st.st_mode & 0777) != 0) { - retvalue = 6; - } + + const size_t count_max = (SIZE_MAX >> 1) + 1; + (void)count_max; + +# ifdef HAVE_COPY_FILE_RANGE + for (;;) { + ssize_t written = copy_file_range(src_fd, NULL, dst_fd, NULL, count_max, 0); + if (written == 0) goto done; + if (written < 0) break; } -#endif - return retvalue; +# endif +# ifdef HAVE_FCOPYFILE + if (fcopyfile(src_fd, dst_fd, NULL, COPYFILE_DATA) == 0) { + goto done; + } +# endif +# ifdef USE_SENDFILE + for (;;) { + ssize_t written = sendfile(src_fd, dst_fd, NULL count_max); + if (written == 0) goto done; + if (written < 0) break; + } +# endif + ret = copy_stream(src_fd, dst_fd); + + done: + close(src_fd); + if (dst_fd >= 0) close(dst_fd); + if (ret != COPY_ERROR_NONE) unlink(dst_path); + return ret; #endif } @@ -700,7 +728,7 @@ VALUE rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path) { char ext_path[MAXPATHLEN], fname2[MAXPATHLEN], basename[MAXPATHLEN]; - int copy_error, wrote; + int wrote; const char *src_path = RSTRING_PTR(path), *fname_ptr = RSTRING_PTR(fname); rb_box_t *box = rb_get_box_t(box_value); @@ -711,14 +739,10 @@ rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path) if (wrote >= (int)sizeof(ext_path)) { rb_bug("Extension file path in the box was too long"); } - copy_error = copy_ext_file(src_path, ext_path); + enum copy_error_type copy_error = copy_ext_file(src_path, ext_path); if (copy_error) { char message[1024]; -#if defined(_WIN32) - copy_ext_file_error(message, sizeof(message)); -#else copy_ext_file_error(message, sizeof(message), copy_error, src_path, ext_path); -#endif rb_raise(rb_eLoadError, "can't prepare the extension file for Ruby Box (%s from %s): %s", ext_path, src_path, message); } // TODO: register the path to be clean-uped From 8d1eafa7a7289141338a6ace6570e6850e529347 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 12 Nov 2025 00:37:27 +0900 Subject: [PATCH 302/367] Remove duplicate path names in error message --- box.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/box.c b/box.c index 86424dedc8b1e8..e7065c1c298815 100644 --- a/box.c +++ b/box.c @@ -537,7 +537,7 @@ enum copy_error_type { }; static const char * -copy_ext_file_error(char *message, size_t size, int copy_retvalue, const char *src_path, const char *dst_path) +copy_ext_file_error(char *message, size_t size, int copy_retvalue) { #ifdef _WIN32 int error = GetLastError(); @@ -557,25 +557,25 @@ copy_ext_file_error(char *message, size_t size, int copy_retvalue, const char *s #else switch (copy_retvalue) { case COPY_ERROR_SRC_OPEN: - snprintf(message, size, "can't open the extension path: %s", src_path); + strlcpy(message, "can't open the extension path", size); break; case COPY_ERROR_DST_OPEN: - snprintf(message, size, "can't open the file to write: %s", dst_path); + strlcpy(message, "can't open the file to write", size); break; case COPY_ERROR_SRC_READ: - snprintf(message, size, "failed to read the extension path: %s", src_path); + strlcpy(message, "failed to read the extension path", size); break; case COPY_ERROR_DST_WRITE: - snprintf(message, size, "failed to write the extension path: %s", dst_path); + strlcpy(message, "failed to write the extension path", size); break; case COPY_ERROR_SRC_STAT: - snprintf(message, size, "failed to stat the extension path to copy permissions: %s", src_path); + strlcpy(message, "failed to stat the extension path to copy permissions", size); break; case COPY_ERROR_DST_CHMOD: - snprintf(message, size, "failed to set permissions to the copied extension path: %s", dst_path); + strlcpy(message, "failed to set permissions to the copied extension path", size); break; case COPY_ERROR_SYSERR: - snprintf(message, size, "failed to copy the extension: %s", strerror(errno)); + strlcpy(message, strerror(errno), size); break; case COPY_ERROR_NONE: /* shouldn't be called */ default: @@ -742,8 +742,8 @@ rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path) enum copy_error_type copy_error = copy_ext_file(src_path, ext_path); if (copy_error) { char message[1024]; - copy_ext_file_error(message, sizeof(message), copy_error, src_path, ext_path); - rb_raise(rb_eLoadError, "can't prepare the extension file for Ruby Box (%s from %s): %s", ext_path, src_path, message); + copy_ext_file_error(message, sizeof(message), copy_error); + rb_raise(rb_eLoadError, "can't prepare the extension file for Ruby Box (%s from %"PRIsVALUE"): %s", ext_path, path, message); } // TODO: register the path to be clean-uped return rb_str_new_cstr(ext_path); From 875c4c7dfdf9f9271fde47d6616249724af4d014 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 9 Dec 2025 13:51:04 +0900 Subject: [PATCH 303/367] [ruby/rubygems] Bump Bundler version to 4.0.1 (cherry picked from commit https://github.com/ruby/rubygems/commit/26c1db5a65a8) https://github.com/ruby/rubygems/commit/bbb5b767d0 --- lib/bundler/version.rb | 2 +- spec/bundler/realworld/fixtures/tapioca/Gemfile.lock | 2 +- spec/bundler/realworld/fixtures/warbler/Gemfile.lock | 2 +- tool/bundler/dev_gems.rb.lock | 2 +- tool/bundler/rubocop_gems.rb.lock | 2 +- tool/bundler/standard_gems.rb.lock | 2 +- tool/bundler/test_gems.rb.lock | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 8cdc3067366a8e..411829b28a3d35 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "4.0.0".freeze + VERSION = "4.0.1".freeze def self.bundler_major_version @bundler_major_version ||= gem_version.segments.first diff --git a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock index 92b16c76064450..866a77125bcaed 100644 --- a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock @@ -46,4 +46,4 @@ DEPENDENCIES tapioca BUNDLED WITH - 4.0.0 + 4.0.1 diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock index 90090e5fbfdc7e..67c887c84c2554 100644 --- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock @@ -36,4 +36,4 @@ DEPENDENCIES warbler! BUNDLED WITH - 4.0.0 + 4.0.1 diff --git a/tool/bundler/dev_gems.rb.lock b/tool/bundler/dev_gems.rb.lock index 4cc6655e43d631..45062e42253876 100644 --- a/tool/bundler/dev_gems.rb.lock +++ b/tool/bundler/dev_gems.rb.lock @@ -129,4 +129,4 @@ CHECKSUMS turbo_tests (2.2.5) sha256=3fa31497d12976d11ccc298add29107b92bda94a90d8a0a5783f06f05102509f BUNDLED WITH - 4.0.0 + 4.0.1 diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index 77bdc4b6f90a2a..b45717d4e3eaca 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -156,4 +156,4 @@ CHECKSUMS unicode-emoji (4.1.0) sha256=4997d2d5df1ed4252f4830a9b6e86f932e2013fbff2182a9ce9ccabda4f325a5 BUNDLED WITH - 4.0.0 + 4.0.1 diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index 354a57d660e99b..9fff0eb0fcdc89 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -176,4 +176,4 @@ CHECKSUMS unicode-emoji (4.1.0) sha256=4997d2d5df1ed4252f4830a9b6e86f932e2013fbff2182a9ce9ccabda4f325a5 BUNDLED WITH - 4.0.0 + 4.0.1 diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index b11a9607255aa1..ac581de02e7fd5 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -103,4 +103,4 @@ CHECKSUMS tilt (2.6.1) sha256=35a99bba2adf7c1e362f5b48f9b581cce4edfba98117e34696dde6d308d84770 BUNDLED WITH - 4.0.0 + 4.0.1 From 0da74e0aa0189b1d2ec9dadf7b580f9bcd6adee7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 9 Dec 2025 13:51:05 +0900 Subject: [PATCH 304/367] [ruby/rubygems] Bump Rubygems version to 4.0.1 (cherry picked from commit https://github.com/ruby/rubygems/commit/f3e5ebf5afe7) https://github.com/ruby/rubygems/commit/583b0222ad --- lib/rubygems.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems.rb b/lib/rubygems.rb index eaeffc7a54d00b..55e727425fbe4d 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,7 +9,7 @@ require "rbconfig" module Gem - VERSION = "4.0.0" + VERSION = "4.0.1" end require_relative "rubygems/defaults" From 4b8e48a362e28fe3b89694dbb0d3ee1c36368583 Mon Sep 17 00:00:00 2001 From: git Date: Tue, 9 Dec 2025 12:10:58 +0000 Subject: [PATCH 305/367] Update default gems list at 0da74e0aa0189b1d2ec9dadf7b580f [ci skip] --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 033fc2ea76da1b..e9dfa948a634a9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -232,8 +232,8 @@ The following default gem is added. The following default gems are updated. -* RubyGems 4.0.0 -* bundler 4.0.0 +* RubyGems 4.0.1 +* bundler 4.0.1 * date 3.5.0 * digest 3.2.1 * english 0.8.1 From 5ae2bd240f0adea46358f0a95bc26276d9f0cc31 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 9 Dec 2025 21:29:36 +0900 Subject: [PATCH 306/367] [DOC] Add Ruby::Box on NEWS --- NEWS.md | 8 ++++++++ doc/language/box.md | 8 +++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index e9dfa948a634a9..784b92b717d1c5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -160,6 +160,12 @@ Note: We're only listing outstanding class updates. Ruby-related constants. This module was reserved in Ruby 3.4 and is now officially defined. [[Feature #20884]] +* Ruby::Box + + * A new (experimental) feature to provide separation about definitions. + For the detail of "Ruby Box", see [doc/language/box.md](doc/language/box.md). + [[Feature #21311]] [[Misc #21385]] + * Set * `Set` is now a core class, instead of an autoloaded stdlib class. @@ -427,8 +433,10 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #21262]: https://bugs.ruby-lang.org/issues/21262 [Feature #21275]: https://bugs.ruby-lang.org/issues/21275 [Feature #21287]: https://bugs.ruby-lang.org/issues/21287 +[Feature #21311]: https://bugs.ruby-lang.org/issues/21311 [Feature #21347]: https://bugs.ruby-lang.org/issues/21347 [Feature #21360]: https://bugs.ruby-lang.org/issues/21360 +[Misc #21385]: https://bugs.ruby-lang.org/issues/21385 [Feature #21389]: https://bugs.ruby-lang.org/issues/21389 [Feature #21390]: https://bugs.ruby-lang.org/issues/21390 [Feature #21459]: https://bugs.ruby-lang.org/issues/21459 diff --git a/doc/language/box.md b/doc/language/box.md index ed62cbd072dcff..928c98eb3c32bb 100644 --- a/doc/language/box.md +++ b/doc/language/box.md @@ -1,6 +1,6 @@ # Ruby Box - Ruby's in-process separation of Classes and Modules -Ruby Box is designed to provide separated spaces in a Ruby process, to isolate applications and libraries. +Ruby Box is designed to provide separated spaces in a Ruby process, to isolate application codes, libraries and monkey patches. ## Known issues @@ -12,13 +12,11 @@ Ruby Box is designed to provide separated spaces in a Ruby process, to isolate a ## TODOs * Add the loaded namespace on iseq to check if another namespace tries running the iseq (add a field only when VM_CHECK_MODE?) -* Delete per-box extension files (.so) lazily or process exit -* Collect `rb_classext_t` entries for a box when GC collects the box +* Delete per-box extension files (.so) lazily or process exit (on Windows) * Assign its own TOPLEVEL_BINDING in boxes * Fix calling `warn` in boxes to refer `$VERBOSE` and `Warning.warn` in the box -* Make an internal data container `Ruby::Box::Entry` invisible +* Make an internal data container class `Ruby::Box::Entry` invisible * More test cases about `$LOAD_PATH` and `$LOADED_FEATURES` -* Return classpath and nesting without the namespace prefix in the namespace itself [#21316](https://bugs.ruby-lang.org/issues/21316), [#21318](https://bugs.ruby-lang.org/issues/21318) ## How to use From edca81a1bb72a9dc54a37766d2c80790dec13884 Mon Sep 17 00:00:00 2001 From: Abrar Habib <86024593+dddictionary@users.noreply.github.com> Date: Tue, 9 Dec 2025 07:41:09 -0500 Subject: [PATCH 307/367] ZJIT: Add codegen for FixnumDiv (#15452) Fixes https://github.com/Shopify/ruby/issues/902 This pull request adds code generation for dividing fixnums. Testing confirms the normal case, flooring, and side-exiting on division by zero. --- jit.c | 6 ++++++ test/ruby/test_zjit.rb | 31 +++++++++++++++++++++++++++++++ yjit.c | 6 ------ yjit/bindgen/src/main.rs | 2 +- yjit/src/cruby.rs | 2 +- yjit/src/cruby_bindings.inc.rs | 2 +- zjit/bindgen/src/main.rs | 1 + zjit/src/codegen.rs | 12 +++++++++++- zjit/src/cruby_bindings.inc.rs | 1 + zjit/src/hir.rs | 1 + zjit/src/stats.rs | 2 ++ 11 files changed, 56 insertions(+), 10 deletions(-) diff --git a/jit.c b/jit.c index ae592b2205c0be..ff44ac5b2e565d 100644 --- a/jit.c +++ b/jit.c @@ -761,6 +761,12 @@ rb_jit_fix_mod_fix(VALUE recv, VALUE obj) return rb_fix_mod_fix(recv, obj); } +VALUE +rb_jit_fix_div_fix(VALUE recv, VALUE obj) +{ + return rb_fix_div_fix(recv, obj); +} + // YJIT/ZJIT need this function to never allocate and never raise VALUE rb_yarv_str_eql_internal(VALUE str1, VALUE str2) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 03d2461fa0a0af..cc5143aee52b0b 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -960,6 +960,37 @@ def test(n) = C * n }, call_threshold: 2, insns: [:opt_mult] end + def test_fixnum_div + assert_compiles '12', %q{ + C = 48 + def test(n) = C / n + test(4) + test(4) + }, call_threshold: 2, insns: [:opt_div] + end + + def test_fixnum_floor + assert_compiles '0', %q{ + C = 3 + def test(n) = C / n + test(4) + test(4) + }, call_threshold: 2, insns: [:opt_div] + end + + def test_fixnum_div_zero + assert_runs '"divided by 0"', %q{ + def test(n) + n / 0 + rescue ZeroDivisionError => e + e.message + end + + test(0) + test(0) + }, call_threshold: 2, insns: [:opt_div] + end + def test_opt_not assert_compiles('[true, true, false]', <<~RUBY, insns: [:opt_not]) def test(obj) = !obj diff --git a/yjit.c b/yjit.c index 16debf6eca5555..6d909a0da61ee3 100644 --- a/yjit.c +++ b/yjit.c @@ -292,12 +292,6 @@ rb_yjit_rb_ary_subseq_length(VALUE ary, long beg) return rb_ary_subseq(ary, beg, len); } -VALUE -rb_yjit_fix_div_fix(VALUE recv, VALUE obj) -{ - return rb_fix_div_fix(recv, obj); -} - // Return non-zero when `obj` is an array and its last item is a // `ruby2_keywords` hash. We don't support this kind of splat. size_t diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 8d11b4216ef0b0..06c475f3c8b493 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -368,7 +368,7 @@ fn main() { .allowlist_function("rb_str_neq_internal") .allowlist_function("rb_yarv_ary_entry_internal") .allowlist_function("rb_yjit_ruby2_keywords_splat_p") - .allowlist_function("rb_yjit_fix_div_fix") + .allowlist_function("rb_jit_fix_div_fix") .allowlist_function("rb_jit_fix_mod_fix") .allowlist_function("rb_FL_TEST") .allowlist_function("rb_FL_TEST_RAW") diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 5562f73be26dab..6e6a1810c67dda 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -197,7 +197,7 @@ pub use rb_get_cikw_keywords_idx as get_cikw_keywords_idx; pub use rb_get_call_data_ci as get_call_data_ci; pub use rb_yarv_str_eql_internal as rb_str_eql_internal; pub use rb_yarv_ary_entry_internal as rb_ary_entry_internal; -pub use rb_yjit_fix_div_fix as rb_fix_div_fix; +pub use rb_jit_fix_div_fix as rb_fix_div_fix; pub use rb_jit_fix_mod_fix as rb_fix_mod_fix; pub use rb_FL_TEST as FL_TEST; pub use rb_FL_TEST_RAW as FL_TEST_RAW; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index b9e5fb3ab12843..d2347671bbacb8 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1174,7 +1174,6 @@ extern "C" { pub fn rb_str_neq_internal(str1: VALUE, str2: VALUE) -> VALUE; pub fn rb_ary_unshift_m(argc: ::std::os::raw::c_int, argv: *mut VALUE, ary: VALUE) -> VALUE; pub fn rb_yjit_rb_ary_subseq_length(ary: VALUE, beg: ::std::os::raw::c_long) -> VALUE; - pub fn rb_yjit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE; pub fn rb_yjit_ruby2_keywords_splat_p(obj: VALUE) -> usize; pub fn rb_yjit_splat_varg_checks( sp: *mut VALUE, @@ -1312,6 +1311,7 @@ extern "C" { end: *mut ::std::os::raw::c_void, ); pub fn rb_jit_fix_mod_fix(recv: VALUE, obj: VALUE) -> VALUE; + pub fn rb_jit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE; pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; pub fn rb_jit_str_concat_codepoint(str_: VALUE, codepoint: VALUE); } diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 30e85149744e22..7b968d14bbb678 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -279,6 +279,7 @@ fn main() { .allowlist_function("rb_jit_mark_unused") .allowlist_function("rb_jit_get_page_size") .allowlist_function("rb_jit_array_len") + .allowlist_function("rb_jit_fix_div_fix") .allowlist_function("rb_jit_iseq_builtin_attrs") .allowlist_function("rb_jit_str_concat_codepoint") .allowlist_function("rb_zjit_iseq_inspect") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 73c092f72091c5..642991a4381265 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -396,6 +396,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::FixnumAdd { left, right, state } => gen_fixnum_add(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), Insn::FixnumSub { left, right, state } => gen_fixnum_sub(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), Insn::FixnumMult { left, right, state } => gen_fixnum_mult(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), + Insn::FixnumDiv { left, right, state } => gen_fixnum_div(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), Insn::FixnumEq { left, right } => gen_fixnum_eq(asm, opnd!(left), opnd!(right)), Insn::FixnumNeq { left, right } => gen_fixnum_neq(asm, opnd!(left), opnd!(right)), Insn::FixnumLt { left, right } => gen_fixnum_lt(asm, opnd!(left), opnd!(right)), @@ -484,7 +485,6 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::ArrayHash { elements, state } => gen_opt_newarray_hash(jit, asm, opnds!(elements), &function.frame_state(*state)), &Insn::IsA { val, class } => gen_is_a(asm, opnd!(val), opnd!(class)), &Insn::ArrayMax { state, .. } - | &Insn::FixnumDiv { state, .. } | &Insn::Throw { state, .. } => return Err(state), }; @@ -1704,6 +1704,16 @@ fn gen_fixnum_mult(jit: &mut JITState, asm: &mut Assembler, left: lir::Opnd, rig asm.add(out_val, Opnd::UImm(1)) } +/// Compile Fixnum / Fixnum +fn gen_fixnum_div(jit: &mut JITState, asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd, state: &FrameState) -> lir::Opnd { + gen_prepare_leaf_call_with_gc(asm, state); + + // Side exit if rhs is 0 + asm.cmp(right, Opnd::from(VALUE::fixnum_from_usize(0))); + asm.je(side_exit(jit, state, FixnumDivByZero)); + asm_ccall!(asm, rb_jit_fix_div_fix, left, right) +} + /// Compile Fixnum == Fixnum fn gen_fixnum_eq(asm: &mut Assembler, left: lir::Opnd, right: lir::Opnd) -> lir::Opnd { asm.cmp(left, right); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index aed35c3c636846..c436e20087ebbe 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -2198,6 +2198,7 @@ unsafe extern "C" { start: *mut ::std::os::raw::c_void, end: *mut ::std::os::raw::c_void, ); + pub fn rb_jit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE; pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; pub fn rb_jit_str_concat_codepoint(str_: VALUE, codepoint: VALUE); pub fn rb_jit_shape_capacity(shape_id: shape_id_t) -> attr_index_t; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 4323b145feb1b6..abf48e04d64be6 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -501,6 +501,7 @@ pub enum SideExitReason { BlockParamProxyNotIseqOrIfunc, StackOverflow, FixnumModByZero, + FixnumDivByZero, BoxFixnumOverflow, } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 7a076b7cda2550..2272404b690b37 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -183,6 +183,7 @@ make_counters! { exit_fixnum_mult_overflow, exit_fixnum_lshift_overflow, exit_fixnum_mod_by_zero, + exit_fixnum_div_by_zero, exit_box_fixnum_overflow, exit_guard_type_failure, exit_guard_type_not_failure, @@ -492,6 +493,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { FixnumMultOverflow => exit_fixnum_mult_overflow, FixnumLShiftOverflow => exit_fixnum_lshift_overflow, FixnumModByZero => exit_fixnum_mod_by_zero, + FixnumDivByZero => exit_fixnum_div_by_zero, BoxFixnumOverflow => exit_box_fixnum_overflow, GuardType(_) => exit_guard_type_failure, GuardTypeNot(_) => exit_guard_type_not_failure, From c8441e8db52fe260545b83ffbe5278aff242bd14 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 20:53:50 -0500 Subject: [PATCH 308/367] ZJIT: Clean up opt_newarray_send --- zjit/src/hir.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index abf48e04d64be6..7b1374f9f467ff 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -5252,20 +5252,25 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { YARVINSN_opt_newarray_send => { let count = get_arg(pc, 0).as_usize(); let method = get_arg(pc, 1).as_u32(); - let elements = state.stack_pop_n(count)?; let (bop, insn) = match method { - VM_OPT_NEWARRAY_SEND_MAX => (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }), - VM_OPT_NEWARRAY_SEND_HASH => (BOP_HASH, Insn::ArrayHash { elements, state: exit_id }), + VM_OPT_NEWARRAY_SEND_MAX => { + let elements = state.stack_pop_n(count)?; + (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }) + } + VM_OPT_NEWARRAY_SEND_HASH => { + let elements = state.stack_pop_n(count)?; + (BOP_HASH, Insn::ArrayHash { elements, state: exit_id }) + } VM_OPT_NEWARRAY_SEND_INCLUDE_P => { - let target = elements[elements.len() - 1]; - let array_elements = elements[..elements.len() - 1].to_vec(); - (BOP_INCLUDE_P, Insn::ArrayInclude { elements: array_elements, target, state: exit_id }) - }, + let target = state.stack_pop()?; + let elements = state.stack_pop_n(count - 1)?; + (BOP_INCLUDE_P, Insn::ArrayInclude { elements, target, state: exit_id }) + } _ => { // Unknown opcode; side-exit into the interpreter fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledNewarraySend(method) }); break; // End the block - }, + } }; if !unsafe { rb_BASIC_OP_UNREDEFINED_P(bop, ARRAY_REDEFINED_OP_FLAG) } { // If the basic operation is already redefined, we cannot optimize it. From cb9510f539e12f7c58bf2e74186c6a48b62fec3c Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 3 Dec 2025 21:16:27 -0500 Subject: [PATCH 309/367] ZJIT: Support opt_newarray_send with PACK_BUFFER --- test/ruby/test_zjit.rb | 28 +++++++++++++++++++++++++ zjit/src/codegen.rs | 29 ++++++++++++++++++++++++++ zjit/src/hir.rs | 30 +++++++++++++++++++++++++++ zjit/src/hir/tests.rs | 46 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index cc5143aee52b0b..ff9d45a0832835 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -1129,6 +1129,34 @@ def test(x) }, insns: [:opt_duparray_send], call_threshold: 1 end + def test_opt_newarray_send_pack_buffer + assert_compiles '["ABC", "ABC", "ABC", "ABC"]', %q{ + def test(num, buffer) + [num].pack('C', buffer:) + end + buf = "" + [test(65, buf), test(66, buf), test(67, buf), buf] + }, insns: [:opt_newarray_send], call_threshold: 1 + end + + def test_opt_newarray_send_pack_buffer_redefined + assert_compiles '["b", "A"]', %q{ + class Array + alias_method :old_pack, :pack + def pack(fmt, buffer: nil) + old_pack(fmt, buffer: buffer) + "b" + end + end + + def test(num, buffer) + [num].pack('C', buffer:) + end + buf = "" + [test(65, buf), buf] + }, insns: [:opt_newarray_send], call_threshold: 1 + end + def test_opt_newarray_send_hash assert_compiles 'Integer', %q{ def test(x) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 642991a4381265..43fdc2f06e0a5e 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -481,6 +481,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::WriteBarrier { recv, val } => no_output!(gen_write_barrier(asm, opnd!(recv), opnd!(val), function.type_of(val))), &Insn::IsBlockGiven => gen_is_block_given(jit, asm), Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)), + Insn::ArrayPackBuffer { elements, fmt, buffer, state } => gen_array_pack_buffer(jit, asm, opnds!(elements), opnd!(fmt), opnd!(buffer), &function.frame_state(*state)), &Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)), Insn::ArrayHash { elements, state } => gen_opt_newarray_hash(jit, asm, opnds!(elements), &function.frame_state(*state)), &Insn::IsA { val, class } => gen_is_a(asm, opnd!(val), opnd!(class)), @@ -1548,6 +1549,34 @@ fn gen_array_include( ) } +fn gen_array_pack_buffer( + jit: &JITState, + asm: &mut Assembler, + elements: Vec, + fmt: Opnd, + buffer: Opnd, + state: &FrameState, +) -> lir::Opnd { + gen_prepare_non_leaf_call(jit, asm, state); + + let array_len: c_long = elements.len().try_into().expect("Unable to fit length of elements into c_long"); + + // After gen_prepare_non_leaf_call, the elements are spilled to the Ruby stack. + // The elements are at the bottom of the virtual stack, followed by the fmt, followed by the buffer. + // Get a pointer to the first element on the Ruby stack. + let stack_bottom = state.stack().len() - elements.len() - 2; + let elements_ptr = asm.lea(Opnd::mem(64, SP, stack_bottom as i32 * SIZEOF_VALUE_I32)); + + unsafe extern "C" { + fn rb_vm_opt_newarray_pack_buffer(ec: EcPtr, num: c_long, elts: *const VALUE, fmt: VALUE, buffer: VALUE) -> VALUE; + } + asm_ccall!( + asm, + rb_vm_opt_newarray_pack_buffer, + EC, array_len.into(), elements_ptr, fmt, buffer + ) +} + fn gen_dup_array_include( jit: &JITState, asm: &mut Assembler, diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 7b1374f9f467ff..523a757b30782e 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -675,6 +675,7 @@ pub enum Insn { ArrayHash { elements: Vec, state: InsnId }, ArrayMax { elements: Vec, state: InsnId }, ArrayInclude { elements: Vec, target: InsnId, state: InsnId }, + ArrayPackBuffer { elements: Vec, fmt: InsnId, buffer: InsnId, state: InsnId }, DupArrayInclude { ary: VALUE, target: InsnId, state: InsnId }, /// Extend `left` with the elements from `right`. `left` and `right` must both be `Array`. ArrayExtend { left: InsnId, right: InsnId, state: InsnId }, @@ -1122,6 +1123,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } write!(f, " | {target}") } + Insn::ArrayPackBuffer { elements, fmt, buffer, .. } => { + write!(f, "ArrayPackBuffer ")?; + for element in elements { + write!(f, "{element}, ")?; + } + write!(f, "fmt: {fmt}, buf: {buffer}") + } Insn::DupArrayInclude { ary, target, .. } => { write!(f, "DupArrayInclude {} | {}", ary.print(self.ptr_map), target) } @@ -1990,6 +1998,7 @@ impl Function { &ArrayLength { array } => ArrayLength { array: find!(array) }, &ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) }, &ArrayInclude { ref elements, target, state } => ArrayInclude { elements: find_vec!(elements), target: find!(target), state: find!(state) }, + &ArrayPackBuffer { ref elements, fmt, buffer, state } => ArrayPackBuffer { elements: find_vec!(elements), fmt: find!(fmt), buffer: find!(buffer), state: find!(state) }, &DupArrayInclude { ary, target, state } => DupArrayInclude { ary, target: find!(target), state: find!(state) }, &ArrayHash { ref elements, state } => ArrayHash { elements: find_vec!(elements), state }, &SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state }, @@ -2144,6 +2153,7 @@ impl Function { Insn::FixnumBitCheck { .. } => types::BoolExact, Insn::ArrayMax { .. } => types::BasicObject, Insn::ArrayInclude { .. } => types::BoolExact, + Insn::ArrayPackBuffer { .. } => types::String, Insn::DupArrayInclude { .. } => types::BoolExact, Insn::ArrayHash { .. } => types::Fixnum, Insn::GetGlobal { .. } => types::BasicObject, @@ -3658,6 +3668,12 @@ impl Function { worklist.push_back(target); worklist.push_back(state); } + &Insn::ArrayPackBuffer { ref elements, fmt, buffer, state } => { + worklist.extend(elements); + worklist.push_back(fmt); + worklist.push_back(buffer); + worklist.push_back(state); + } &Insn::DupArrayInclude { target, state, .. } => { worklist.push_back(target); worklist.push_back(state); @@ -4417,6 +4433,14 @@ impl Function { } Ok(()) } + Insn::ArrayPackBuffer { ref elements, fmt, buffer, .. } => { + self.assert_subtype(insn_id, fmt, types::BasicObject)?; + self.assert_subtype(insn_id, buffer, types::BasicObject)?; + for &element in elements { + self.assert_subtype(insn_id, element, types::BasicObject)?; + } + Ok(()) + } // Instructions with a Vec of Ruby objects Insn::InvokeBlock { ref args, .. } | Insn::NewArray { elements: ref args, .. } @@ -5266,6 +5290,12 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let elements = state.stack_pop_n(count - 1)?; (BOP_INCLUDE_P, Insn::ArrayInclude { elements, target, state: exit_id }) } + VM_OPT_NEWARRAY_SEND_PACK_BUFFER => { + let buffer = state.stack_pop()?; + let fmt = state.stack_pop()?; + let elements = state.stack_pop_n(count - 2)?; + (BOP_PACK, Insn::ArrayPackBuffer { elements, fmt, buffer, state: exit_id }) + } _ => { // Unknown opcode; side-exit into the interpreter fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::UnhandledNewarraySend(method) }); diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 76be941fe529f9..84e48ed843fa2b 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2154,7 +2154,51 @@ pub mod hir_build_tests { v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v37:StringExact = StringCopy v36 v39:BasicObject = GetLocal :buf, l0, EP@3 - SideExit UnhandledNewarraySend(PACK_BUFFER) + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK) + v42:String = ArrayPackBuffer v15, v16, fmt: v37, buf: v39 + PatchPoint NoEPEscape(test) + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_opt_newarray_send_pack_buffer_redefined() { + eval(r#" + class Array + def pack(fmt, buffer: nil) = 5 + end + def test(a,b) + sum = a+b + buf = "" + [a,b].pack 'C', buffer: buf + buf + end + "#); + assert_contains_opcode("test", YARVINSN_opt_newarray_send); + assert_snapshot!(hir_string("test"), @r" + fn test@:6: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :a, l0, SP@7 + v3:BasicObject = GetLocal :b, l0, SP@6 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + v29:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v30:StringExact = StringCopy v29 + v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v37:StringExact = StringCopy v36 + v39:BasicObject = GetLocal :buf, l0, EP@3 + SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_PACK)) "); } From e8568bbcf20fafcb82d8b537b99762528dfbdc3e Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 9 Dec 2025 23:03:41 +0900 Subject: [PATCH 310/367] [DOC] Update Ruby Box documents (known issues) --- doc/language/box.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/doc/language/box.md b/doc/language/box.md index 928c98eb3c32bb..dc4b3201b4eea4 100644 --- a/doc/language/box.md +++ b/doc/language/box.md @@ -5,14 +5,14 @@ Ruby Box is designed to provide separated spaces in a Ruby process, to isolate a ## Known issues * Experimental warning is shown when ruby starts with `RUBY_BOX=1` (specify `-W:no-experimental` option to hide it) -* `bundle install` may fail -* `require 'active_support'` may fail -* A wrong current namespace detection happens sometimes in the root namespace +* Installing native extensions may fail under `RUBY_BOX=1` because of stack level too deep in extconf.rb +* `require 'active_support/core_ext'` may fail under `RUBY_BOX=1` +* Defined methods in a box may not be referred by built-in methods written in Ruby ## TODOs * Add the loaded namespace on iseq to check if another namespace tries running the iseq (add a field only when VM_CHECK_MODE?) -* Delete per-box extension files (.so) lazily or process exit (on Windows) +* Delete per-box extension files (.dll) lazily or process exit (on Windows) * Assign its own TOPLEVEL_BINDING in boxes * Fix calling `warn` in boxes to refer `$VERBOSE` and `Warning.warn` in the box * Make an internal data container class `Ruby::Box::Entry` invisible @@ -258,6 +258,16 @@ Ruby Box works in file scope. One `.rb` file runs in a single box. Once a file is loaded in a box `box`, all methods/procs defined/created in the file run in `box`. +### Utility methods + +Several methods are available for trying/testing Ruby Box. + +* `Ruby::Box.current` returns the current box +* `Ruby::Box.enabled?` returns true/false to represent `RUBY_BOX=1` is specified or not +* `Ruby::Box.root` returns the root box +* `Ruby::Box.main` returns the main box +* `Ruby::Box#eval` evaluates a Ruby code (String) in the receiver box, just like calling `#load` with a file + ## Implementation details #### ISeq inline method/constant cache @@ -294,6 +304,10 @@ It is a breaking change. Users can define methods using `Ruby::Box.root.eval(...)`, but it's clearly not ideal API. +#### Assigning values to global variables used by builtin methods + +Similar to monkey patching methods, global variables assigned in a box is separated from the root box. Methods defined in the root box referring a global variable can't find the re-assigned one. + #### Context of `$LOAD_PATH` and `$LOADED_FEATURES` Global variables `$LOAD_PATH` and `$LOADED_FEATURES` control `require` method behaviors. So those variables are determined by the loading box instead of the current box. From 573896a40ac25bd9febb2bbc0502b43ef36f9b9b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 8 Nov 2025 11:05:17 +0900 Subject: [PATCH 311/367] Box: remove copied extension files --- box.c | 60 ++++++++++++++++++++++++++++++++++++++++--- internal/box.h | 13 +++++++++- load.c | 16 ++++-------- test/ruby/test_box.rb | 4 --- 4 files changed, 74 insertions(+), 19 deletions(-) diff --git a/box.c b/box.c index e7065c1c298815..0ce8d0aee02f0f 100644 --- a/box.c +++ b/box.c @@ -62,6 +62,7 @@ bool ruby_box_crashed = false; // extern, changed only in vm.c VALUE rb_resolve_feature_path(VALUE klass, VALUE fname); static VALUE rb_box_inspect(VALUE obj); +static void cleanup_all_local_extensions(VALUE libmap); void rb_box_init_done(void) @@ -274,6 +275,8 @@ box_entry_free(void *ptr) st_foreach(box->classext_cow_classes, free_classext_for_box, (st_data_t)box); } + cleanup_all_local_extensions(box->ruby_dln_libmap); + box_root_free(ptr); xfree(ptr); } @@ -724,8 +727,57 @@ escaped_basename(const char *path, const char *fname, char *rvalue, size_t rsize } } +static void +box_ext_cleanup_mark(void *p) +{ + rb_gc_mark((VALUE)p); +} + +static void +box_ext_cleanup_free(void *p) +{ + VALUE path = (VALUE)p; + unlink(RSTRING_PTR(path)); +} + +static const rb_data_type_t box_ext_cleanup_type = { + "box_ext_cleanup", + {box_ext_cleanup_mark, box_ext_cleanup_free}, + .flags = RUBY_TYPED_FREE_IMMEDIATELY, +}; + +void +rb_box_cleanup_local_extension(VALUE cleanup) +{ + void *p = DATA_PTR(cleanup); + DATA_PTR(cleanup) = NULL; +#ifndef _WIN32 + if (p) box_ext_cleanup_free(p); +#endif +} + +static int +cleanup_local_extension_i(VALUE key, VALUE value, VALUE arg) +{ +#if defined(_WIN32) + HMODULE h = (HMODULE)NUM2SVALUE(value); + WCHAR module_path[MAXPATHLEN]; + DWORD len = GetModuleFileNameW(h, module_path, numberof(module_path)); + + FreeLibrary(h); + if (len > 0 && len < numberof(module_path)) DeleteFileW(module_path); +#endif + return ST_DELETE; +} + +static void +cleanup_all_local_extensions(VALUE libmap) +{ + rb_hash_foreach(libmap, cleanup_local_extension_i, 0); +} + VALUE -rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path) +rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path, VALUE *cleanup) { char ext_path[MAXPATHLEN], fname2[MAXPATHLEN], basename[MAXPATHLEN]; int wrote; @@ -739,14 +791,16 @@ rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path) if (wrote >= (int)sizeof(ext_path)) { rb_bug("Extension file path in the box was too long"); } + VALUE new_path = rb_str_new_cstr(ext_path); + *cleanup = TypedData_Wrap_Struct(0, &box_ext_cleanup_type, NULL); enum copy_error_type copy_error = copy_ext_file(src_path, ext_path); if (copy_error) { char message[1024]; copy_ext_file_error(message, sizeof(message), copy_error); rb_raise(rb_eLoadError, "can't prepare the extension file for Ruby Box (%s from %"PRIsVALUE"): %s", ext_path, path, message); } - // TODO: register the path to be clean-uped - return rb_str_new_cstr(ext_path); + DATA_PTR(*cleanup) = (void *)new_path; + return new_path; } // TODO: delete it just after dln_load? or delay it? diff --git a/internal/box.h b/internal/box.h index c341f046d3e147..72263cc9dc5e5d 100644 --- a/internal/box.h +++ b/internal/box.h @@ -3,6 +3,16 @@ #include "ruby/ruby.h" /* for VALUE */ +#if SIZEOF_VALUE <= SIZEOF_LONG +# define SVALUE2NUM(x) LONG2NUM((long)(x)) +# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LONG(x) +#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG +# define SVALUE2NUM(x) LL2NUM((LONG_LONG)(x)) +# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LL(x) +#else +# error Need integer for VALUE +#endif + /** * @author Ruby developers * @copyright This file is a part of the programming language Ruby. @@ -75,7 +85,8 @@ void rb_box_gc_update_references(void *ptr); rb_box_t * rb_get_box_t(VALUE ns); VALUE rb_get_box_object(rb_box_t *ns); -VALUE rb_box_local_extension(VALUE box, VALUE fname, VALUE path); +VALUE rb_box_local_extension(VALUE box, VALUE fname, VALUE path, VALUE *cleanup); +void rb_box_cleanup_local_extension(VALUE cleanup); void rb_initialize_main_box(void); void rb_box_init_done(void); diff --git a/load.c b/load.c index 5a0697a2626707..466517f465b098 100644 --- a/load.c +++ b/load.c @@ -27,16 +27,6 @@ #define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0) #define IS_DLEXT(e) (strcmp((e), DLEXT) == 0) -#if SIZEOF_VALUE <= SIZEOF_LONG -# define SVALUE2NUM(x) LONG2NUM((long)(x)) -# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LONG(x) -#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG -# define SVALUE2NUM(x) LL2NUM((LONG_LONG)(x)) -# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LL(x) -#else -# error Need integer for VALUE -#endif - enum { loadable_ext_rb = (0+ /* .rb extension is the first in both tables */ 1) /* offset by rb_find_file_ext() */ @@ -1203,11 +1193,15 @@ load_ext(VALUE path, VALUE fname) { VALUE loaded = path; const rb_box_t *box = rb_loading_box(); + VALUE cleanup = 0; if (BOX_USER_P(box)) { - loaded = rb_box_local_extension(box->box_object, fname, path); + loaded = rb_box_local_extension(box->box_object, fname, path, &cleanup); } rb_scope_visibility_set(METHOD_VISI_PUBLIC); void *handle = dln_load_feature(RSTRING_PTR(loaded), RSTRING_PTR(fname)); + if (cleanup) { + rb_box_cleanup_local_extension(cleanup); + } RB_GC_GUARD(loaded); RB_GC_GUARD(fname); return (VALUE)handle; diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb index c52c7564ae682e..2e4e07a86ad7e3 100644 --- a/test/ruby/test_box.rb +++ b/test/ruby/test_box.rb @@ -697,10 +697,6 @@ def test_root_and_main_methods assert !$LOADED_FEATURES.include?("/tmp/barbaz") assert !Object.const_defined?(:FooClass) end; - ensure - tmp = ENV["TMPDIR"] || ENV["TMP"] || Etc.systmpdir || "/tmp" - pat = "_ruby_ns_*."+RbConfig::CONFIG["DLEXT"] - File.unlink(*Dir.glob(pat, base: tmp).map {|so| "#{tmp}/#{so}"}) end def test_basic_box_detections From 07e85e1dba3e15c714044660f731a90ce86c8c24 Mon Sep 17 00:00:00 2001 From: Satoshi Tagomori Date: Tue, 2 Dec 2025 22:22:01 +0900 Subject: [PATCH 312/367] Box: add a test case about deleting .so/.dll files --- test/ruby/test_box.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb index 2e4e07a86ad7e3..1daf5d57d5a9e3 100644 --- a/test/ruby/test_box.rb +++ b/test/ruby/test_box.rb @@ -823,4 +823,17 @@ def test_mark_box_object_referred_only_from_binding assert_equal 42, b.eval('1+2') end; end + + def test_loaded_extension_deleted_in_user_box + require 'tmpdir' + Dir.mktmpdir do |tmpdir| + env = ENV_ENABLE_BOX.merge({'TMPDIR'=>tmpdir}) + assert_separately([env], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) + begin; + require "json" + tmpdirname = ENV['TMPDIR'] + assert_empty(Dir.children(tmpdirname)) + end; + end + end end From 1e6a479520cb13f927399c97d363558b3beeaea7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 3 Dec 2025 10:41:39 +0900 Subject: [PATCH 313/367] Box: relax the condition of clean up It is impossible to delete DLLs being loaded on Windows. I guess that unnamed (no accessible path on the filesystem) files are not allowed essentially. The only way is to relax the condition, such as no files are left after the process terminated, probably. --- test/ruby/test_box.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb index 1daf5d57d5a9e3..5d06b60cd77005 100644 --- a/test/ruby/test_box.rb +++ b/test/ruby/test_box.rb @@ -828,12 +828,11 @@ def test_loaded_extension_deleted_in_user_box require 'tmpdir' Dir.mktmpdir do |tmpdir| env = ENV_ENABLE_BOX.merge({'TMPDIR'=>tmpdir}) - assert_separately([env], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) + assert_ruby_status([env], "#{<<~"begin;"}\n#{<<~'end;'}") begin; require "json" - tmpdirname = ENV['TMPDIR'] - assert_empty(Dir.children(tmpdirname)) end; + assert_empty(Dir.children(tmpdir)) end end end From 1933f1291a788369b252be80b0d452716d35831a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 9 Dec 2025 23:44:06 +0900 Subject: [PATCH 314/367] [DOC] Clear one of known issues of Ruby Box --- doc/language/box.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/language/box.md b/doc/language/box.md index dc4b3201b4eea4..aebce7188b3179 100644 --- a/doc/language/box.md +++ b/doc/language/box.md @@ -12,7 +12,6 @@ Ruby Box is designed to provide separated spaces in a Ruby process, to isolate a ## TODOs * Add the loaded namespace on iseq to check if another namespace tries running the iseq (add a field only when VM_CHECK_MODE?) -* Delete per-box extension files (.dll) lazily or process exit (on Windows) * Assign its own TOPLEVEL_BINDING in boxes * Fix calling `warn` in boxes to refer `$VERBOSE` and `Warning.warn` in the box * Make an internal data container class `Ruby::Box::Entry` invisible From 7ecfb1b2324d9488d4bd94801ed8a694d3c1ee0b Mon Sep 17 00:00:00 2001 From: YO4 Date: Mon, 8 Dec 2025 21:41:02 +0900 Subject: [PATCH 315/367] [ruby/resolv] use domain suffix from 'Domain' instead of 'NV Domain' 'NV Domain' does not change results of `powershell -command Get-DnsClientGlobalSetting`. 'Domain' do this. https://github.com/ruby/resolv/commit/d49e3d5b84 --- ext/win32/lib/win32/resolv.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/win32/lib/win32/resolv.rb b/ext/win32/lib/win32/resolv.rb index 9a74a753517ab3..8d631a21403f4c 100644 --- a/ext/win32/lib/win32/resolv.rb +++ b/ext/win32/lib/win32/resolv.rb @@ -63,13 +63,13 @@ def get_info if add_search = search.nil? search = [] - nvdom = params.value('NV Domain') + domain = params.value('Domain') - if nvdom and !nvdom.empty? - search = [ nvdom ] + if domain and !domain.empty? + search = [ domain ] udmnd = params.value('UseDomainNameDevolution') if udmnd&.nonzero? - if /^\w+\./ =~ nvdom + if /^\w+\./ =~ domain devo = $' end end From 76d845aa7a0fa07aa3e733528cbd7e48feccfd59 Mon Sep 17 00:00:00 2001 From: Randy Stauner Date: Tue, 18 Nov 2025 16:19:49 -0700 Subject: [PATCH 316/367] ZJIT: Test additional arg passing scenarios --- test/ruby/test_zjit.rb | 75 ++++++++++++++++++++++++++++++++++++++- zjit/src/hir/opt_tests.rs | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index ff9d45a0832835..18e10f52068362 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -555,7 +555,8 @@ def test_send_kwarg def test(a:, b:) = [a, b] def entry = test(b: 2, a: 1) # change order entry - } + entry + }, call_threshold: 2 end def test_send_kwarg_optional @@ -567,6 +568,15 @@ def entry = test }, call_threshold: 2 end + def test_send_kwarg_optional_too_many + assert_compiles '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]', %q{ + def test(a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10) = [a, b, c, d, e, f, g, h, i, j] + def entry = test + entry + entry + }, call_threshold: 2 + end + def test_send_kwarg_required_and_optional assert_compiles '[3, 2]', %q{ def test(a:, b: 2) = [a, b] @@ -585,6 +595,28 @@ def entry = test(a: 3) }, call_threshold: 2 end + def test_send_kwarg_to_ccall + assert_compiles '["a", "b", "c"]', %q{ + def test(s) = s.each_line(chomp: true).to_a + def entry = test(%(a\nb\nc)) + entry + entry + }, call_threshold: 2 + end + + def test_send_kwarg_and_block_to_ccall + assert_compiles '["a", "b", "c"]', %q{ + def test(s) + a = [] + s.each_line(chomp: true) { |l| a << l } + a + end + def entry = test(%(a\nb\nc)) + entry + entry + }, call_threshold: 2 + end + def test_send_kwrest assert_compiles '{a: 3}', %q{ def test(**kwargs) = kwargs @@ -594,6 +626,47 @@ def entry = test(a: 3) }, call_threshold: 2 end + def test_send_req_kwreq + assert_compiles '[1, 3]', %q{ + def test(a, c:) = [a, c] + def entry = test(1, c: 3) + entry + entry + }, call_threshold: 2 + end + + def test_send_req_opt_kwreq_kwopt + assert_compiles '[[1, 2, 3, 4], [-1, -2, -3, -4]]', %q{ + def test(a, b = 2, c:, d: 4) = [a, b, c, d] + def entry = [test(1, c: 3), test(-1, -2, d: -4, c: -3)] # specify all, change kw order + entry + entry + }, call_threshold: 2 + end + + def test_send_unexpected_keyword + assert_compiles ':error', %q{ + def test(a: 1) = a*2 + def entry + test(z: 2) + rescue ArgumentError + :error + end + + entry + entry + }, call_threshold: 2 + end + + def test_send_all_arg_types + assert_compiles '[:req, :opt, :post, :kwr, :kwo, true]', %q{ + def test(a, b = :opt, c, d:, e: :kwo) = [a, b, c, d, e, block_given?] + def entry = test(:req, :post, d: :kwr) {} + entry + entry + }, call_threshold: 2 + end + def test_send_ccall_variadic_with_different_receiver_classes assert_compiles '[true, true]', %q{ def test(obj) = obj.start_with?("a") diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 5646af804a2d0f..81a2cea80608de 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -2862,6 +2862,70 @@ mod hir_opt_tests { "); } + #[test] + fn dont_optimize_ccall_with_kwarg() { + eval(" + def test = sprintf('%s', a: 1) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v12:StringExact = StringCopy v11 + v14:Fixnum[1] = Const Value(1) + IncrCounter complex_arg_pass_caller_kwarg + v16:BasicObject = SendWithoutBlock v6, :sprintf, v12, v14 + CheckInterrupts + Return v16 + "); + } + + #[test] + fn dont_optimize_ccall_with_block_and_kwarg() { + eval(" + def test(s) + a = [] + s.each_line(chomp: true) { |l| a << l } + a + end + test %(a\nb\nc) + test %() + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@5 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + v16:ArrayExact = NewArray + SetLocal :a, l0, EP@3, v16 + v22:TrueClass = Const Value(true) + IncrCounter complex_arg_pass_caller_kwarg + v24:BasicObject = Send v11, 0x1000, :each_line, v22 + v25:BasicObject = GetLocal :s, l0, EP@4 + v26:BasicObject = GetLocal :a, l0, EP@3 + v30:BasicObject = GetLocal :a, l0, EP@3 + CheckInterrupts + Return v30 + "); + } + #[test] fn dont_replace_get_constant_path_with_empty_ic() { eval(" From c42f4d80eabc6b9b4117253a2ba2ee0b9526f253 Mon Sep 17 00:00:00 2001 From: Randy Stauner Date: Fri, 14 Nov 2025 14:50:39 -0700 Subject: [PATCH 317/367] ZJIT: Handle caller_kwarg in direct send when all keyword params are required --- test/ruby/test_zjit.rb | 18 ++++ zjit/src/codegen.rs | 31 ++++++- zjit/src/hir.rs | 134 +++++++++++++++++++++++++++-- zjit/src/hir/opt_tests.rs | 176 +++++++++++++++++++++++++++++++++++--- zjit/src/stats.rs | 13 ++- 5 files changed, 351 insertions(+), 21 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 18e10f52068362..81d940407063f1 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -617,6 +617,15 @@ def entry = test(%(a\nb\nc)) }, call_threshold: 2 end + def test_send_kwarg_with_too_many_args_to_c_call + assert_compiles '"a b c d {kwargs: :e}"', %q{ + def test(a:, b:, c:, d:, e:) = sprintf("%s %s %s %s %s", a, b, c, d, kwargs: e) + def entry = test(e: :e, d: :d, c: :c, a: :a, b: :b) + entry + entry + }, call_threshold: 2 + end + def test_send_kwrest assert_compiles '{a: 3}', %q{ def test(**kwargs) = kwargs @@ -635,6 +644,15 @@ def entry = test(1, c: 3) }, call_threshold: 2 end + def test_send_req_opt_kwreq + assert_compiles '[[1, 2, 3], [-1, -2, -3]]', %q{ + def test(a, b = 2, c:) = [a, b, c] + def entry = [test(1, c: 3), test(-1, -2, c: -3)] # specify all, change kw order + entry + entry + }, call_threshold: 2 + end + def test_send_req_opt_kwreq_kwopt assert_compiles '[[1, 2, 3, 4], [-1, -2, -3, -4]]', %q{ def test(a, b = 2, c:, d: 4) = [a, b, c, d] diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 43fdc2f06e0a5e..8c5946e6a6329b 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -384,6 +384,9 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio // Give up SendWithoutBlockDirect for 6+ args since asm.ccall() doesn't support it. Insn::SendWithoutBlockDirect { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => // +1 for self gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::TooManyArgsForLir), + // Give up SendWithoutBlockDirect for 5+ args (plus 1 arg for keyword bits) since asm.ccall() doesn't support it. + Insn::SendWithoutBlockDirect { cd, state, args, iseq, .. } if args.len() + 2 > C_ARG_OPNDS.len() && unsafe { rb_get_iseq_flags_has_kw(*iseq) } => // +1 for self +1 for keyword bits + gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::TooManyArgsForLir), Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state)), &Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason), @@ -1337,6 +1340,20 @@ fn gen_send_without_block_direct( specval, }); + // Write "keyword_bits" to the callee's frame if the callee accepts keywords. + // This is a synthetic local/parameter that the callee reads via checkkeyword to determine + // which optional keyword arguments need their defaults evaluated. + if unsafe { rb_get_iseq_flags_has_kw(iseq) } { + let keyword = unsafe { rb_get_iseq_body_param_keyword(iseq) }; + let bits_start = unsafe { (*keyword).bits_start } as usize; + // Currently we only support required keywords, so all bits are 0 (all keywords specified). + // TODO: When supporting optional keywords, calculate actual unspecified_bits here. + let unspecified_bits = VALUE::fixnum_from_usize(0); + let bits_offset = (state.stack().len() - args.len() + bits_start) * SIZEOF_VALUE; + asm_comment!(asm, "write keyword bits to callee frame"); + asm.store(Opnd::mem(64, SP, bits_offset as i32), unspecified_bits.into()); + } + asm_comment!(asm, "switch to new SP register"); let sp_offset = (state.stack().len() + local_size - args.len() + VM_ENV_DATA_SIZE.to_usize()) * SIZEOF_VALUE; let new_sp = asm.add(SP, sp_offset.into()); @@ -1351,13 +1368,23 @@ fn gen_send_without_block_direct( let mut c_args = vec![recv]; c_args.extend(&args); + if unsafe { rb_get_iseq_flags_has_kw(iseq) } { + // Currently we only get to this point if all the accepted keyword args are required. + let unspecified_bits = 0; + // For each optional keyword that isn't passed we would `unspecified_bits |= (0x01 << idx)`. + c_args.push(VALUE::fixnum_from_usize(unspecified_bits).into()); + } + let params = unsafe { iseq.params() }; let num_optionals_passed = if params.flags.has_opt() != 0 { // See vm_call_iseq_setup_normal_opt_start in vm_inshelper.c let lead_num = params.lead_num as u32; let opt_num = params.opt_num as u32; - assert!(args.len() as u32 <= lead_num + opt_num); - let num_optionals_passed = args.len() as u32 - lead_num; + let keyword = params.keyword; + let kw_req_num = if keyword.is_null() { 0 } else { unsafe { (*keyword).required_num } } as u32; + let req_num = lead_num + kw_req_num; + assert!(args.len() as u32 <= req_num + opt_num); + let num_optionals_passed = args.len() as u32 - req_num; num_optionals_passed } else { 0 diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 523a757b30782e..442f0500c4650f 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -616,6 +616,10 @@ pub enum SendFallbackReason { SendWithoutBlockNotOptimizedMethodTypeOptimized(OptimizedMethodType), SendWithoutBlockBopRedefined, SendWithoutBlockOperandsNotFixnum, + SendWithoutBlockDirectKeywordMismatch, + SendWithoutBlockDirectOptionalKeywords, + SendWithoutBlockDirectKeywordCountMismatch, + SendWithoutBlockDirectMissingKeyword, SendPolymorphic, SendMegamorphic, SendNoProfiles, @@ -632,6 +636,8 @@ pub enum SendFallbackReason { /// The call has at least one feature on the caller or callee side that the optimizer does not /// support. ComplexArgPass, + /// Caller has keyword arguments but callee doesn't expect them; need to convert to hash. + UnexpectedKeywordArgs, /// Initial fallback reason for every instruction, which should be mutated to /// a more actionable reason when an attempt to specialize the instruction fails. Uncategorized(ruby_vminsn_type), @@ -1570,11 +1576,22 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq use Counter::*; if 0 != params.flags.has_rest() { count_failure(complex_arg_pass_param_rest) } if 0 != params.flags.has_post() { count_failure(complex_arg_pass_param_post) } - if 0 != params.flags.has_kw() { count_failure(complex_arg_pass_param_kw) } - if 0 != params.flags.has_kwrest() { count_failure(complex_arg_pass_param_kwrest) } if 0 != params.flags.has_block() { count_failure(complex_arg_pass_param_block) } if 0 != params.flags.forwardable() { count_failure(complex_arg_pass_param_forwardable) } + if 0 != params.flags.has_kwrest() { count_failure(complex_arg_pass_param_kwrest) } + if 0 != params.flags.has_kw() { + let keyword = params.keyword; + if !keyword.is_null() { + let num = unsafe { (*keyword).num }; + let required_num = unsafe { (*keyword).required_num }; + // Only support required keywords for now (no optional keywords) + if num != required_num { + count_failure(complex_arg_pass_param_kw_opt) + } + } + } + if !can_send { function.set_dynamic_send_reason(send_insn, ComplexArgPass); return false; @@ -1583,9 +1600,12 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq // Because we exclude e.g. post parameters above, they are also excluded from the sum below. let lead_num = params.lead_num; let opt_num = params.opt_num; + let keyword = params.keyword; + let kw_req_num = if keyword.is_null() { 0 } else { unsafe { (*keyword).required_num } }; + let req_num = lead_num + kw_req_num; can_send = c_int::try_from(args.len()) .as_ref() - .map(|argc| (lead_num..=lead_num + opt_num).contains(argc)) + .map(|argc| (req_num..=req_num + opt_num).contains(argc)) .unwrap_or(false); if !can_send { function.set_dynamic_send_reason(send_insn, ArgcParamMismatch); @@ -2299,6 +2319,72 @@ impl Function { } } + /// Reorder keyword arguments to match the callee's expectation. + /// + /// Returns Ok with reordered arguments if successful, or Err with the fallback reason if not. + fn reorder_keyword_arguments( + &self, + args: &[InsnId], + kwarg: *const rb_callinfo_kwarg, + iseq: IseqPtr, + ) -> Result, SendFallbackReason> { + let callee_keyword = unsafe { rb_get_iseq_body_param_keyword(iseq) }; + if callee_keyword.is_null() { + // Caller is passing kwargs but callee doesn't expect them. + return Err(SendWithoutBlockDirectKeywordMismatch); + } + + let caller_kw_count = unsafe { get_cikw_keyword_len(kwarg) } as usize; + let callee_kw_count = unsafe { (*callee_keyword).num } as usize; + let callee_kw_required = unsafe { (*callee_keyword).required_num } as usize; + let callee_kw_table = unsafe { (*callee_keyword).table }; + + // For now, only handle the case where all keywords are required. + if callee_kw_count != callee_kw_required { + return Err(SendWithoutBlockDirectOptionalKeywords); + } + if caller_kw_count != callee_kw_count { + return Err(SendWithoutBlockDirectKeywordCountMismatch); + } + + // The keyword arguments are the last arguments in the args vector. + let kw_args_start = args.len() - caller_kw_count; + + // Build a mapping from caller keywords to their positions. + let mut caller_kw_order: Vec = Vec::with_capacity(caller_kw_count); + for i in 0..caller_kw_count { + let sym = unsafe { get_cikw_keywords_idx(kwarg, i as i32) }; + let id = unsafe { rb_sym2id(sym) }; + caller_kw_order.push(id); + } + + // Reorder keyword arguments to match callee expectation. + let mut reordered_kw_args: Vec = Vec::with_capacity(callee_kw_count); + for i in 0..callee_kw_count { + let expected_id = unsafe { *callee_kw_table.add(i) }; + + // Find where this keyword is in the caller's order + let mut found = false; + for (j, &caller_id) in caller_kw_order.iter().enumerate() { + if caller_id == expected_id { + reordered_kw_args.push(args[kw_args_start + j]); + found = true; + break; + } + } + + if !found { + // Required keyword not provided by caller which will raise an ArgumentError. + return Err(SendWithoutBlockDirectMissingKeyword); + } + } + + // Replace the keyword arguments with the reordered ones. + let mut processed_args = args[..kw_args_start].to_vec(); + processed_args.extend(reordered_kw_args); + Ok(processed_args) + } + /// Resolve the receiver type for method dispatch optimization. /// /// Takes the receiver's Type, receiver HIR instruction, and ISEQ instruction index. @@ -2514,6 +2600,7 @@ impl Function { cme = unsafe { rb_aliased_callable_method_entry(cme) }; def_type = unsafe { get_cme_def_type(cme) }; } + if def_type == VM_METHOD_TYPE_ISEQ { // TODO(max): Allow non-iseq; cache cme // Only specialize positional-positional calls @@ -2529,7 +2616,21 @@ impl Function { if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } - let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { recv, cd, cme, iseq, args, state }); + + let kwarg = unsafe { rb_vm_ci_kwarg(ci) }; + let processed_args = if !kwarg.is_null() { + match self.reorder_keyword_arguments(&args, kwarg, iseq) { + Ok(reordered) => reordered, + Err(reason) => { + self.set_dynamic_send_reason(insn_id, reason); + self.push_insn_id(block, insn_id); continue; + } + } + } else { + args.clone() + }; + + let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { recv, cd, cme, iseq, args: processed_args, state }); self.make_equal_to(insn_id, send_direct); } else if def_type == VM_METHOD_TYPE_BMETHOD { let procv = unsafe { rb_get_def_bmethod_proc((*cme).def) }; @@ -2564,7 +2665,21 @@ impl Function { if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } - let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { recv, cd, cme, iseq, args, state }); + + let kwarg = unsafe { rb_vm_ci_kwarg(ci) }; + let processed_args = if !kwarg.is_null() { + match self.reorder_keyword_arguments(&args, kwarg, iseq) { + Ok(reordered) => reordered, + Err(reason) => { + self.set_dynamic_send_reason(insn_id, reason); + self.push_insn_id(block, insn_id); continue; + } + } + } else { + args.clone() + }; + + let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { recv, cd, cme, iseq, args: processed_args, state }); self.make_equal_to(insn_id, send_direct); } else if def_type == VM_METHOD_TYPE_IVAR && args.is_empty() { // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. @@ -3106,7 +3221,7 @@ impl Function { // When seeing &block argument, fall back to dynamic dispatch for now // TODO: Support block forwarding - if unspecializable_call_type(ci_flags) { + if unspecializable_c_call_type(ci_flags) { fun.count_complex_call_features(block, ci_flags); fun.set_dynamic_send_reason(send_insn_id, ComplexArgPass); return Err(()); @@ -5018,9 +5133,14 @@ fn unhandled_call_type(flags: u32) -> Result<(), CallType> { Ok(()) } +/// If a given call to a c func uses overly complex arguments, then we won't specialize. +fn unspecializable_c_call_type(flags: u32) -> bool { + ((flags & VM_CALL_KWARG) != 0) || + unspecializable_call_type(flags) +} + /// If a given call uses overly complex arguments, then we won't specialize. fn unspecializable_call_type(flags: u32) -> bool { - ((flags & VM_CALL_KWARG) != 0) || ((flags & VM_CALL_ARGS_SPLAT) != 0) || ((flags & VM_CALL_ARGS_BLOCKARG) != 0) } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 81a2cea80608de..a6099f47bedcc9 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -2761,10 +2761,10 @@ mod hir_opt_tests { } #[test] - fn dont_specialize_call_to_iseq_with_kw() { + fn specialize_call_to_iseq_with_multiple_required_kw() { eval(" - def foo(a:) = 1 - def test = foo(a: 1) + def foo(a:, b:) = [a, b] + def test = foo(a: 1, b: 2) test test "); @@ -2779,7 +2779,162 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) - IncrCounter complex_arg_pass_caller_kwarg + v13:Fixnum[2] = Const Value(2) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 + CheckInterrupts + Return v23 + "); + } + + #[test] + fn specialize_call_to_iseq_with_required_kw_reorder() { + eval(" + def foo(a:, b:, c:) = [a, b, c] + def test = foo(c: 3, a: 1, b: 2) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:Fixnum[3] = Const Value(3) + v13:Fixnum[1] = Const Value(1) + v15:Fixnum[2] = Const Value(2) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v25:BasicObject = SendWithoutBlockDirect v24, :foo (0x1038), v13, v15, v11 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn specialize_call_to_iseq_with_positional_and_required_kw_reorder() { + eval(" + def foo(x, a:, b:) = [x, a, b] + def test = foo(0, b: 2, a: 1) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:Fixnum[0] = Const Value(0) + v13:Fixnum[2] = Const Value(2) + v15:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v25:BasicObject = SendWithoutBlockDirect v24, :foo (0x1038), v11, v15, v13 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn dont_specialize_call_with_positional_and_optional_kw() { + eval(" + def foo(x, a: 1) = [x, a] + def test = foo(0, a: 2) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:Fixnum[0] = Const Value(0) + v13:Fixnum[2] = Const Value(2) + IncrCounter complex_arg_pass_param_kw_opt + v15:BasicObject = SendWithoutBlock v6, :foo, v11, v13 + CheckInterrupts + Return v15 + "); + } + + #[test] + fn specialize_call_with_pos_optional_and_req_kw() { + eval(" + def foo(r, x = 2, a:, b:) = [x, a] + def test = [foo(1, a: 3, b: 4), foo(1, 2, b: 4, a: 3)] # with and without the optional, change kw order + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:Fixnum[1] = Const Value(1) + v13:Fixnum[3] = Const Value(3) + v15:Fixnum[4] = Const Value(4) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v37:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v38:BasicObject = SendWithoutBlockDirect v37, :foo (0x1038), v11, v13, v15 + v20:Fixnum[1] = Const Value(1) + v22:Fixnum[2] = Const Value(2) + v24:Fixnum[4] = Const Value(4) + v26:Fixnum[3] = Const Value(3) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Object@0x1000) + v41:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v42:BasicObject = SendWithoutBlockDirect v41, :foo (0x1038), v20, v22, v26, v24 + v30:ArrayExact = NewArray v38, v42 + CheckInterrupts + Return v30 + "); + } + + #[test] + fn test_send_call_to_iseq_with_optional_kw() { + eval(" + def foo(a: 1) = a + def test = foo(a: 2) + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:Fixnum[2] = Const Value(2) + IncrCounter complex_arg_pass_param_kw_opt v13:BasicObject = SendWithoutBlock v6, :foo, v11 CheckInterrupts Return v13 @@ -2805,7 +2960,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) - IncrCounter complex_arg_pass_caller_kwarg + IncrCounter complex_arg_pass_param_kwrest v13:BasicObject = SendWithoutBlock v6, :foo, v11 CheckInterrupts Return v13 @@ -2813,7 +2968,7 @@ mod hir_opt_tests { } #[test] - fn dont_specialize_call_to_iseq_with_param_kw() { + fn dont_specialize_call_to_iseq_with_optional_param_kw() { eval(" def foo(int: 1) = int + 1 def test = foo @@ -2830,7 +2985,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - IncrCounter complex_arg_pass_param_kw + IncrCounter complex_arg_pass_param_kw_opt v11:BasicObject = SendWithoutBlock v6, :foo CheckInterrupts Return v11 @@ -2882,7 +3037,6 @@ mod hir_opt_tests { v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v11 v14:Fixnum[1] = Const Value(1) - IncrCounter complex_arg_pass_caller_kwarg v16:BasicObject = SendWithoutBlock v6, :sprintf, v12, v14 CheckInterrupts Return v16 @@ -3180,8 +3334,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(Hash@0x1008, new@0x1009, cme:0x1010) v46:HashExact = ObjectAllocClass Hash:VALUE(0x1008) - IncrCounter complex_arg_pass_param_kw IncrCounter complex_arg_pass_param_block + IncrCounter complex_arg_pass_param_kw_opt v20:BasicObject = SendWithoutBlock v46, :initialize CheckInterrupts CheckInterrupts @@ -9028,9 +9182,9 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_param_rest - IncrCounter complex_arg_pass_param_kw - IncrCounter complex_arg_pass_param_kwrest IncrCounter complex_arg_pass_param_block + IncrCounter complex_arg_pass_param_kwrest + IncrCounter complex_arg_pass_param_kw_opt v13:BasicObject = SendWithoutBlock v6, :fancy, v11 CheckInterrupts Return v13 diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 2272404b690b37..7d54f40c8d5dbd 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -222,6 +222,10 @@ make_counters! { send_fallback_too_many_args_for_lir, send_fallback_send_without_block_bop_redefined, send_fallback_send_without_block_operands_not_fixnum, + send_fallback_send_without_block_direct_keyword_mismatch, + send_fallback_send_without_block_direct_optional_keywords, + send_fallback_send_without_block_direct_keyword_count_mismatch, + send_fallback_send_without_block_direct_missing_keyword, send_fallback_send_polymorphic, send_fallback_send_megamorphic, send_fallback_send_no_profiles, @@ -231,6 +235,8 @@ make_counters! { // The call has at least one feature on the caller or callee side // that the optimizer does not support. send_fallback_one_or_more_complex_arg_pass, + // Caller has keyword arguments but callee doesn't expect them. + send_fallback_unexpected_keyword_args, send_fallback_bmethod_non_iseq_proc, send_fallback_obj_to_string_not_string, send_fallback_send_cfunc_variadic, @@ -347,7 +353,7 @@ make_counters! { // Unsupported parameter features complex_arg_pass_param_rest, complex_arg_pass_param_post, - complex_arg_pass_param_kw, + complex_arg_pass_param_kw_opt, complex_arg_pass_param_kwrest, complex_arg_pass_param_block, complex_arg_pass_param_forwardable, @@ -546,12 +552,17 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter TooManyArgsForLir => send_fallback_too_many_args_for_lir, SendWithoutBlockBopRedefined => send_fallback_send_without_block_bop_redefined, SendWithoutBlockOperandsNotFixnum => send_fallback_send_without_block_operands_not_fixnum, + SendWithoutBlockDirectKeywordMismatch => send_fallback_send_without_block_direct_keyword_mismatch, + SendWithoutBlockDirectOptionalKeywords => send_fallback_send_without_block_direct_optional_keywords, + SendWithoutBlockDirectKeywordCountMismatch=> send_fallback_send_without_block_direct_keyword_count_mismatch, + SendWithoutBlockDirectMissingKeyword => send_fallback_send_without_block_direct_missing_keyword, SendPolymorphic => send_fallback_send_polymorphic, SendMegamorphic => send_fallback_send_megamorphic, SendNoProfiles => send_fallback_send_no_profiles, SendCfuncVariadic => send_fallback_send_cfunc_variadic, SendCfuncArrayVariadic => send_fallback_send_cfunc_array_variadic, ComplexArgPass => send_fallback_one_or_more_complex_arg_pass, + UnexpectedKeywordArgs => send_fallback_unexpected_keyword_args, ArgcParamMismatch => send_fallback_argc_param_mismatch, BmethodNonIseqProc => send_fallback_bmethod_non_iseq_proc, SendNotOptimizedMethodType(_) => send_fallback_send_not_optimized_method_type, From f0b288adf7b3550dc5be6a02316c5822ff47ca82 Mon Sep 17 00:00:00 2001 From: Randy Stauner Date: Fri, 5 Dec 2025 19:57:08 -0700 Subject: [PATCH 318/367] ZJIT: Put keyword bits in callee frame rather than c_args --- zjit/src/codegen.rs | 10 ---------- zjit/src/hir.rs | 17 +++++++++++++++++ zjit/src/hir/opt_tests.rs | 3 ++- zjit/src/hir/tests.rs | 15 ++++++++++----- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 8c5946e6a6329b..05b704d66a4bb4 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -384,9 +384,6 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio // Give up SendWithoutBlockDirect for 6+ args since asm.ccall() doesn't support it. Insn::SendWithoutBlockDirect { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => // +1 for self gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::TooManyArgsForLir), - // Give up SendWithoutBlockDirect for 5+ args (plus 1 arg for keyword bits) since asm.ccall() doesn't support it. - Insn::SendWithoutBlockDirect { cd, state, args, iseq, .. } if args.len() + 2 > C_ARG_OPNDS.len() && unsafe { rb_get_iseq_flags_has_kw(*iseq) } => // +1 for self +1 for keyword bits - gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::TooManyArgsForLir), Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state)), &Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason), @@ -1368,13 +1365,6 @@ fn gen_send_without_block_direct( let mut c_args = vec![recv]; c_args.extend(&args); - if unsafe { rb_get_iseq_flags_has_kw(iseq) } { - // Currently we only get to this point if all the accepted keyword args are required. - let unspecified_bits = 0; - // For each optional keyword that isn't passed we would `unspecified_bits |= (0x01 << idx)`. - c_args.push(VALUE::fixnum_from_usize(unspecified_bits).into()); - } - let params = unsafe { iseq.params() }; let num_optionals_passed = if params.flags.has_opt() != 0 { // See vm_call_iseq_setup_normal_opt_start in vm_inshelper.c diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 442f0500c4650f..6d1fe70e271869 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6239,12 +6239,29 @@ fn compile_jit_entry_state(fun: &mut Function, jit_entry_block: BlockId, jit_ent let lead_num: usize = params.lead_num.try_into().expect("iseq param lead_num >= 0"); let passed_opt_num = jit_entry_idx; + // If the iseq has keyword parameters, the keyword bits local will be appended to the local table. + let kw_bits_idx: Option = if unsafe { rb_get_iseq_flags_has_kw(iseq) } { + let keyword = unsafe { rb_get_iseq_body_param_keyword(iseq) }; + if !keyword.is_null() { + Some(unsafe { (*keyword).bits_start } as usize) + } else { + None + } + } else { + None + }; + let self_param = fun.push_insn(jit_entry_block, Insn::Param); let mut entry_state = FrameState::new(iseq); for local_idx in 0..num_locals(iseq) { if (lead_num + passed_opt_num..lead_num + opt_num).contains(&local_idx) { // Omitted optionals are locals, so they start as nils before their code run entry_state.locals.push(fun.push_insn(jit_entry_block, Insn::Const { val: Const::Value(Qnil) })); + } else if Some(local_idx) == kw_bits_idx { + // We currently only support required keywords so the unspecified bits will always be zero. + // TODO: Make this a parameter when we start writing anything other than zero. + let unspecified_bits = VALUE::fixnum_from_usize(0); + entry_state.locals.push(fun.push_insn(jit_entry_block, Insn::Const { val: Const::Value(unspecified_bits) })); } else if local_idx < param_size { entry_state.locals.push(fun.push_insn(jit_entry_block, Insn::Param)); } else { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index a6099f47bedcc9..0a0737469371d7 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -626,8 +626,9 @@ mod hir_opt_tests { v2:BasicObject = GetLocal :k, l0, SP@5 v3:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + bb1(v6:BasicObject, v7:BasicObject): EntryPoint JIT(0) + v8:Fixnum[0] = Const Value(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): CheckInterrupts diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 84e48ed843fa2b..7ef7671fa444f6 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -3000,8 +3000,9 @@ pub mod hir_build_tests { v3:BasicObject = GetLocal :exception, l0, SP@5 v4:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3, v4) - bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject): + bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject): EntryPoint JIT(0) + v10:Fixnum[0] = Const Value(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): v19:Float = InvokeBuiltin rb_f_float, v12, v13, v14 @@ -3050,8 +3051,9 @@ pub mod hir_build_tests { v5:BasicObject = GetLocal :block, l0, SP@5 v6:NilClass = Const Value(nil) Jump bb2(v1, v2, v3, v4, v5, v6) - bb1(v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject, v13:BasicObject): + bb1(v9:BasicObject, v10:BasicObject, v11:BasicObject, v13:BasicObject): EntryPoint JIT(0) + v12:Fixnum[0] = Const Value(0) v14:NilClass = Const Value(nil) Jump bb2(v9, v10, v11, v12, v13, v14) bb2(v16:BasicObject, v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:NilClass): @@ -3112,8 +3114,9 @@ pub mod hir_build_tests { v4:BasicObject = GetLocal :immediate_sweep, l0, SP@5 v5:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject): + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject): EntryPoint JIT(0) + v12:Fixnum[0] = Const Value(0) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:BasicObject): v25:FalseClass = Const Value(false) @@ -3532,8 +3535,9 @@ pub mod hir_build_tests { v2:BasicObject = GetLocal :kw, l0, SP@5 v3:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): + bb1(v6:BasicObject, v7:BasicObject): EntryPoint JIT(0) + v8:Fixnum[0] = Const Value(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v15:BasicObject = GetLocal , l0, EP@3 @@ -3605,8 +3609,9 @@ pub mod hir_build_tests { v34:BasicObject = GetLocal :k33, l0, SP@5 v35:BasicObject = GetLocal , l0, SP@4 Jump bb2(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) - bb1(v38:BasicObject, v39:BasicObject, v40:BasicObject, v41:BasicObject, v42:BasicObject, v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:BasicObject, v51:BasicObject, v52:BasicObject, v53:BasicObject, v54:BasicObject, v55:BasicObject, v56:BasicObject, v57:BasicObject, v58:BasicObject, v59:BasicObject, v60:BasicObject, v61:BasicObject, v62:BasicObject, v63:BasicObject, v64:BasicObject, v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject, v69:BasicObject, v70:BasicObject, v71:BasicObject, v72:BasicObject): + bb1(v38:BasicObject, v39:BasicObject, v40:BasicObject, v41:BasicObject, v42:BasicObject, v43:BasicObject, v44:BasicObject, v45:BasicObject, v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:BasicObject, v51:BasicObject, v52:BasicObject, v53:BasicObject, v54:BasicObject, v55:BasicObject, v56:BasicObject, v57:BasicObject, v58:BasicObject, v59:BasicObject, v60:BasicObject, v61:BasicObject, v62:BasicObject, v63:BasicObject, v64:BasicObject, v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject, v69:BasicObject, v70:BasicObject, v71:BasicObject): EntryPoint JIT(0) + v72:Fixnum[0] = Const Value(0) Jump bb2(v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63, v64, v65, v66, v67, v68, v69, v70, v71, v72) bb2(v74:BasicObject, v75:BasicObject, v76:BasicObject, v77:BasicObject, v78:BasicObject, v79:BasicObject, v80:BasicObject, v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject, v86:BasicObject, v87:BasicObject, v88:BasicObject, v89:BasicObject, v90:BasicObject, v91:BasicObject, v92:BasicObject, v93:BasicObject, v94:BasicObject, v95:BasicObject, v96:BasicObject, v97:BasicObject, v98:BasicObject, v99:BasicObject, v100:BasicObject, v101:BasicObject, v102:BasicObject, v103:BasicObject, v104:BasicObject, v105:BasicObject, v106:BasicObject, v107:BasicObject, v108:BasicObject): SideExit TooManyKeywordParameters From 98390d9360b8b8c82f798f51567587882c4e5c00 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 9 Dec 2025 17:39:15 +0100 Subject: [PATCH 319/367] Don't declare `rbimpl_check_typeddata` as pure [Bug #21771] It may raise so it's incorrect and can lead to the compiler optimizing the call out. --- include/ruby/internal/core/rtypeddata.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index ebcac2c8469db6..aed4bd89b893f6 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -607,7 +607,6 @@ RTYPEDDATA_TYPE(VALUE obj) return (const struct rb_data_type_struct *)(RTYPEDDATA(obj)->type & TYPED_DATA_PTR_MASK); } -RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * @private From bd0d08b6d20e4145e472578d47164fcce14c0abf Mon Sep 17 00:00:00 2001 From: Randy Stauner Date: Tue, 9 Dec 2025 13:29:58 -0700 Subject: [PATCH 320/367] ZJIT: Show send fallback reason in HIR dump (#15454) This adds comments to the hir dump output like this: v13:BasicObject = SendWithoutBlock v6, :test, v11 # SendFallbackReason: Complex argument passing --- zjit/src/hir.rs | 51 ++++++++++++++-- zjit/src/hir/opt_tests.rs | 74 +++++++++++------------ zjit/src/hir/tests.rs | 124 +++++++++++++++++++------------------- 3 files changed, 144 insertions(+), 105 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 6d1fe70e271869..4e597db936c345 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -643,6 +643,40 @@ pub enum SendFallbackReason { Uncategorized(ruby_vminsn_type), } +impl Display for SendFallbackReason { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SendWithoutBlockPolymorphic => write!(f, "SendWithoutBlock: polymorphic call site"), + SendWithoutBlockMegamorphic => write!(f, "SendWithoutBlock: megamorphic call site"), + SendWithoutBlockNoProfiles => write!(f, "SendWithoutBlock: no profile data available"), + SendWithoutBlockCfuncNotVariadic => write!(f, "SendWithoutBlock: C function is not variadic"), + SendWithoutBlockCfuncArrayVariadic => write!(f, "SendWithoutBlock: C function expects array variadic"), + SendWithoutBlockNotOptimizedMethodType(method_type) => write!(f, "SendWithoutBlock: unsupported method type {:?}", method_type), + SendWithoutBlockNotOptimizedMethodTypeOptimized(opt_type) => write!(f, "SendWithoutBlock: unsupported optimized method type {:?}", opt_type), + SendWithoutBlockBopRedefined => write!(f, "SendWithoutBlock: basic operation was redefined"), + SendWithoutBlockOperandsNotFixnum => write!(f, "SendWithoutBlock: operands are not fixnums"), + SendWithoutBlockDirectKeywordMismatch => write!(f, "SendWithoutBlockDirect: keyword mismatch"), + SendWithoutBlockDirectOptionalKeywords => write!(f, "SendWithoutBlockDirect: optional keywords"), + SendWithoutBlockDirectKeywordCountMismatch => write!(f, "SendWithoutBlockDirect: keyword count mismatch"), + SendWithoutBlockDirectMissingKeyword => write!(f, "SendWithoutBlockDirect: missing keyword"), + SendPolymorphic => write!(f, "Send: polymorphic call site"), + SendMegamorphic => write!(f, "Send: megamorphic call site"), + SendNoProfiles => write!(f, "Send: no profile data available"), + SendCfuncVariadic => write!(f, "Send: C function is variadic"), + SendCfuncArrayVariadic => write!(f, "Send: C function expects array variadic"), + SendNotOptimizedMethodType(method_type) => write!(f, "Send: unsupported method type {:?}", method_type), + CCallWithFrameTooManyArgs => write!(f, "CCallWithFrame: too many arguments"), + ObjToStringNotString => write!(f, "ObjToString: result is not a string"), + TooManyArgsForLir => write!(f, "Too many arguments for LIR"), + BmethodNonIseqProc => write!(f, "Bmethod: Proc object is not defined by an ISEQ"), + ArgcParamMismatch => write!(f, "Argument count does not match parameter count"), + ComplexArgPass => write!(f, "Complex argument passing"), + UnexpectedKeywordArgs => write!(f, "Unexpected Keyword Args"), + Uncategorized(insn) => write!(f, "Uncategorized({})", insn_name(*insn as usize)), + } + } +} + /// An instruction in the SSA IR. The output of an instruction is referred to by the index of /// the instruction ([`InsnId`]). SSA form enables this, and [`UnionFind`] ([`Function::find`]) /// helps with editing. @@ -1203,11 +1237,12 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::Jump(target) => { write!(f, "Jump {target}") } Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}") } Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}") } - Insn::SendWithoutBlock { recv, cd, args, .. } => { + Insn::SendWithoutBlock { recv, cd, args, reason, .. } => { write!(f, "SendWithoutBlock {recv}, :{}", ruby_call_method_name(*cd))?; for arg in args { write!(f, ", {arg}")?; } + write!(f, " # SendFallbackReason: {reason}")?; Ok(()) } Insn::SendWithoutBlockDirect { recv, cd, iseq, args, .. } => { @@ -1217,7 +1252,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } - Insn::Send { recv, cd, args, blockiseq, .. } => { + Insn::Send { recv, cd, args, blockiseq, reason, .. } => { // For tests, we want to check HIR snippets textually. Addresses change // between runs, making tests fail. Instead, pick an arbitrary hex value to // use as a "pointer" so we can check the rest of the HIR. @@ -1225,27 +1260,31 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { for arg in args { write!(f, ", {arg}")?; } + write!(f, " # SendFallbackReason: {reason}")?; Ok(()) } - Insn::SendForward { recv, cd, args, blockiseq, .. } => { + Insn::SendForward { recv, cd, args, blockiseq, reason, .. } => { write!(f, "SendForward {recv}, {:p}, :{}", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd))?; for arg in args { write!(f, ", {arg}")?; } + write!(f, " # SendFallbackReason: {reason}")?; Ok(()) } - Insn::InvokeSuper { recv, blockiseq, args, .. } => { + Insn::InvokeSuper { recv, blockiseq, args, reason, .. } => { write!(f, "InvokeSuper {recv}, {:p}", self.ptr_map.map_ptr(blockiseq))?; for arg in args { write!(f, ", {arg}")?; } + write!(f, " # SendFallbackReason: {reason}")?; Ok(()) } - Insn::InvokeBlock { args, .. } => { + Insn::InvokeBlock { args, reason, .. } => { write!(f, "InvokeBlock")?; for arg in args { write!(f, ", {arg}")?; } + write!(f, " # SendFallbackReason: {reason}")?; Ok(()) } Insn::InvokeBuiltin { bf, args, leaf, .. } => { @@ -2044,7 +2083,7 @@ impl Function { /// Update DynamicSendReason for the instruction at insn_id fn set_dynamic_send_reason(&mut self, insn_id: InsnId, dynamic_send_reason: SendFallbackReason) { use Insn::*; - if get_option!(stats) { + if get_option!(stats) || get_option!(dump_hir_opt).is_some() || cfg!(test) { match self.insns.get_mut(insn_id.0).unwrap() { Send { reason, .. } | SendForward { reason, .. } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 0a0737469371d7..3e2db528158090 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -812,7 +812,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = SendWithoutBlock v6, :foo + v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: SendWithoutBlock: unsupported method type Null CheckInterrupts Return v11 "); @@ -2444,7 +2444,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) v12:Fixnum[0] = Const Value(0) - v14:BasicObject = SendWithoutBlock v10, :itself, v12 + v14:BasicObject = SendWithoutBlock v10, :itself, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -2670,7 +2670,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = Send v6, 0x1000, :foo + v11:BasicObject = Send v6, 0x1000, :foo # SendFallbackReason: Send: unsupported method type Iseq CheckInterrupts Return v11 "); @@ -2702,7 +2702,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:NilClass): v13:Fixnum[1] = Const Value(1) SetLocal :a, l0, EP@3, v13 - v19:BasicObject = Send v8, 0x1000, :foo + v19:BasicObject = Send v8, 0x1000, :foo # SendFallbackReason: Send: unsupported method type Iseq v20:BasicObject = GetLocal :a, l0, EP@3 v24:BasicObject = GetLocal :a, l0, EP@3 CheckInterrupts @@ -2730,7 +2730,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_param_rest - v13:BasicObject = SendWithoutBlock v6, :foo, v11 + v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -2755,7 +2755,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[10] = Const Value(10) IncrCounter complex_arg_pass_param_post - v13:BasicObject = SendWithoutBlock v6, :foo, v11 + v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -2871,7 +2871,7 @@ mod hir_opt_tests { v11:Fixnum[0] = Const Value(0) v13:Fixnum[2] = Const Value(2) IncrCounter complex_arg_pass_param_kw_opt - v15:BasicObject = SendWithoutBlock v6, :foo, v11, v13 + v15:BasicObject = SendWithoutBlock v6, :foo, v11, v13 # SendFallbackReason: Complex argument passing CheckInterrupts Return v15 "); @@ -2936,7 +2936,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[2] = Const Value(2) IncrCounter complex_arg_pass_param_kw_opt - v13:BasicObject = SendWithoutBlock v6, :foo, v11 + v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -2962,7 +2962,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_param_kwrest - v13:BasicObject = SendWithoutBlock v6, :foo, v11 + v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -2987,7 +2987,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): IncrCounter complex_arg_pass_param_kw_opt - v11:BasicObject = SendWithoutBlock v6, :foo + v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: Complex argument passing CheckInterrupts Return v11 "); @@ -3012,7 +3012,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): IncrCounter complex_arg_pass_param_kwrest - v11:BasicObject = SendWithoutBlock v6, :foo + v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: Complex argument passing CheckInterrupts Return v11 "); @@ -3038,7 +3038,7 @@ mod hir_opt_tests { v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v11 v14:Fixnum[1] = Const Value(1) - v16:BasicObject = SendWithoutBlock v6, :sprintf, v12, v14 + v16:BasicObject = SendWithoutBlock v6, :sprintf, v12, v14 # SendFallbackReason: Complex argument passing CheckInterrupts Return v16 "); @@ -3072,7 +3072,7 @@ mod hir_opt_tests { SetLocal :a, l0, EP@3, v16 v22:TrueClass = Const Value(true) IncrCounter complex_arg_pass_caller_kwarg - v24:BasicObject = Send v11, 0x1000, :each_line, v22 + v24:BasicObject = Send v11, 0x1000, :each_line, v22 # SendFallbackReason: Complex argument passing v25:BasicObject = GetLocal :s, l0, EP@4 v26:BasicObject = GetLocal :a, l0, EP@3 v30:BasicObject = GetLocal :a, l0, EP@3 @@ -3337,7 +3337,7 @@ mod hir_opt_tests { v46:HashExact = ObjectAllocClass Hash:VALUE(0x1008) IncrCounter complex_arg_pass_param_block IncrCounter complex_arg_pass_param_kw_opt - v20:BasicObject = SendWithoutBlock v46, :initialize + v20:BasicObject = SendWithoutBlock v46, :initialize # SendFallbackReason: Complex argument passing CheckInterrupts CheckInterrupts Return v46 @@ -3541,7 +3541,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): GuardBlockParamProxy l0 v15:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) - v17:BasicObject = Send v8, 0x1008, :tap, v15 + v17:BasicObject = Send v8, 0x1008, :tap, v15 # SendFallbackReason: Uncategorized(send) CheckInterrupts Return v17 "); @@ -4018,7 +4018,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 - v14:BasicObject = SendWithoutBlock v22, :freeze + v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v14 "); @@ -4041,7 +4041,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:HashExact = NewHash v12:NilClass = Const Value(nil) - v14:BasicObject = SendWithoutBlock v10, :freeze, v12 + v14:BasicObject = SendWithoutBlock v10, :freeze, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -4111,7 +4111,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 - v14:BasicObject = SendWithoutBlock v22, :freeze + v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v14 "); @@ -4134,7 +4134,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:ArrayExact = NewArray v12:NilClass = Const Value(nil) - v14:BasicObject = SendWithoutBlock v10, :freeze, v12 + v14:BasicObject = SendWithoutBlock v10, :freeze, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -4205,7 +4205,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 - v15:BasicObject = SendWithoutBlock v23, :freeze + v15:BasicObject = SendWithoutBlock v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -4229,7 +4229,7 @@ mod hir_opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 v13:NilClass = Const Value(nil) - v15:BasicObject = SendWithoutBlock v11, :freeze, v13 + v15:BasicObject = SendWithoutBlock v11, :freeze, v13 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v15 "); @@ -4300,7 +4300,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 - v15:BasicObject = SendWithoutBlock v23, :-@ + v15:BasicObject = SendWithoutBlock v23, :-@ # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -4805,7 +4805,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[100] = Const Value(100) - v13:BasicObject = SendWithoutBlock v6, :identity, v11 + v13:BasicObject = SendWithoutBlock v6, :identity, v11 # SendFallbackReason: Bmethod: Proc object is not defined by an ISEQ CheckInterrupts Return v13 "); @@ -4828,7 +4828,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = Send v6, 0x1000, :bmethod + v11:BasicObject = Send v6, 0x1000, :bmethod # SendFallbackReason: Send: unsupported method type Bmethod CheckInterrupts Return v11 "); @@ -5667,7 +5667,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = SendWithoutBlock v6, :foo + v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v11 "); @@ -5710,7 +5710,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v14:BasicObject = SendWithoutBlock v9, :foo + v14:BasicObject = SendWithoutBlock v9, :foo # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v14 "); @@ -5844,7 +5844,7 @@ mod hir_opt_tests { GuardBlockParamProxy l0 v16:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) IncrCounter complex_arg_pass_caller_blockarg - v18:BasicObject = Send v13, 0x1008, :map, v16 + v18:BasicObject = Send v13, 0x1008, :map, v16 # SendFallbackReason: Complex argument passing CheckInterrupts Return v18 "); @@ -5870,7 +5870,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = Send v6, 0x1000, :foo + v11:BasicObject = Send v6, 0x1000, :foo # SendFallbackReason: Send: unsupported method type Iseq CheckInterrupts Return v11 "); @@ -7589,7 +7589,7 @@ mod hir_opt_tests { v12:Fixnum[3] = Const Value(3) v14:Fixnum[3] = Const Value(3) v16:Fixnum[3] = Const Value(3) - v18:BasicObject = SendWithoutBlock v10, :foo, v12, v14, v16 + v18:BasicObject = SendWithoutBlock v10, :foo, v12, v14, v16 # SendFallbackReason: Argument count does not match parameter count CheckInterrupts Return v18 "); @@ -7639,7 +7639,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[0] = Const Value(0) - v12:BasicObject = SendWithoutBlock v10, :foo + v12:BasicObject = SendWithoutBlock v10, :foo # SendFallbackReason: Argument count does not match parameter count CheckInterrupts Return v12 "); @@ -7662,7 +7662,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:Fixnum[4] = Const Value(4) v12:Fixnum[1] = Const Value(1) - v14:BasicObject = SendWithoutBlock v10, :succ, v12 + v14:BasicObject = SendWithoutBlock v10, :succ, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -7816,7 +7816,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:BasicObject = SendWithoutBlock v11, :^ + v17:BasicObject = SendWithoutBlock v11, :^ # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v17 "); @@ -8491,17 +8491,17 @@ mod hir_opt_tests { v13:ArrayExact = NewArray v19:ArrayExact = ToArray v13 IncrCounter complex_arg_pass_caller_splat - v21:BasicObject = SendWithoutBlock v8, :foo, v19 + v21:BasicObject = SendWithoutBlock v8, :foo, v19 # SendFallbackReason: Complex argument passing v25:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v26:StringExact = StringCopy v25 PatchPoint NoEPEscape(test) v31:ArrayExact = ToArray v13 IncrCounter complex_arg_pass_caller_splat - v33:BasicObject = SendWithoutBlock v26, :display, v31 + v33:BasicObject = SendWithoutBlock v26, :display, v31 # SendFallbackReason: Complex argument passing PatchPoint NoEPEscape(test) v41:ArrayExact = ToArray v13 IncrCounter complex_arg_pass_caller_splat - v43:BasicObject = SendWithoutBlock v8, :itself, v41 + v43:BasicObject = SendWithoutBlock v8, :itself, v41 # SendFallbackReason: Complex argument passing CheckInterrupts Return v43 "); @@ -9186,7 +9186,7 @@ mod hir_opt_tests { IncrCounter complex_arg_pass_param_block IncrCounter complex_arg_pass_param_kwrest IncrCounter complex_arg_pass_param_kw_opt - v13:BasicObject = SendWithoutBlock v6, :fancy, v11 + v13:BasicObject = SendWithoutBlock v6, :fancy, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -9210,7 +9210,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): IncrCounter complex_arg_pass_param_forwardable - v11:BasicObject = SendWithoutBlock v6, :forwardable + v11:BasicObject = SendWithoutBlock v6, :forwardable # SendFallbackReason: Complex argument passing CheckInterrupts Return v11 "); diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 7ef7671fa444f6..49f092337e9816 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -524,7 +524,7 @@ pub mod hir_build_tests { bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) v12:Fixnum[2] = Const Value(2) - v15:BasicObject = SendWithoutBlock v10, :+, v12 + v15:BasicObject = SendWithoutBlock v10, :+, v12 # SendFallbackReason: Uncategorized(opt_plus) CheckInterrupts Return v15 "); @@ -777,11 +777,11 @@ pub mod hir_build_tests { SetLocal :l1, l1, EP@3, v10 v15:BasicObject = GetLocal :l1, l1, EP@3 v17:BasicObject = GetLocal :l2, l2, EP@4 - v20:BasicObject = SendWithoutBlock v15, :+, v17 + v20:BasicObject = SendWithoutBlock v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) SetLocal :l2, l2, EP@4, v20 v25:BasicObject = GetLocal :l2, l2, EP@4 v27:BasicObject = GetLocal :l3, l3, EP@5 - v30:BasicObject = SendWithoutBlock v25, :+, v27 + v30:BasicObject = SendWithoutBlock v25, :+, v27 # SendFallbackReason: Uncategorized(opt_plus) SetLocal :l3, l3, EP@5, v30 CheckInterrupts Return v30 @@ -1102,7 +1102,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :+, v12 + v19:BasicObject = SendWithoutBlock v11, :+, v12 # SendFallbackReason: Uncategorized(opt_plus) CheckInterrupts Return v19 "); @@ -1127,7 +1127,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :-, v12 + v19:BasicObject = SendWithoutBlock v11, :-, v12 # SendFallbackReason: Uncategorized(opt_minus) CheckInterrupts Return v19 "); @@ -1152,7 +1152,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :*, v12 + v19:BasicObject = SendWithoutBlock v11, :*, v12 # SendFallbackReason: Uncategorized(opt_mult) CheckInterrupts Return v19 "); @@ -1177,7 +1177,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :/, v12 + v19:BasicObject = SendWithoutBlock v11, :/, v12 # SendFallbackReason: Uncategorized(opt_div) CheckInterrupts Return v19 "); @@ -1202,7 +1202,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :%, v12 + v19:BasicObject = SendWithoutBlock v11, :%, v12 # SendFallbackReason: Uncategorized(opt_mod) CheckInterrupts Return v19 "); @@ -1227,7 +1227,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :==, v12 + v19:BasicObject = SendWithoutBlock v11, :==, v12 # SendFallbackReason: Uncategorized(opt_eq) CheckInterrupts Return v19 "); @@ -1252,7 +1252,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :!=, v12 + v19:BasicObject = SendWithoutBlock v11, :!=, v12 # SendFallbackReason: Uncategorized(opt_neq) CheckInterrupts Return v19 "); @@ -1277,7 +1277,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :<, v12 + v19:BasicObject = SendWithoutBlock v11, :<, v12 # SendFallbackReason: Uncategorized(opt_lt) CheckInterrupts Return v19 "); @@ -1302,7 +1302,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :<=, v12 + v19:BasicObject = SendWithoutBlock v11, :<=, v12 # SendFallbackReason: Uncategorized(opt_le) CheckInterrupts Return v19 "); @@ -1327,7 +1327,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :>, v12 + v19:BasicObject = SendWithoutBlock v11, :>, v12 # SendFallbackReason: Uncategorized(opt_gt) CheckInterrupts Return v19 "); @@ -1368,7 +1368,7 @@ pub mod hir_build_tests { bb4(v26:BasicObject, v27:BasicObject, v28:BasicObject): PatchPoint NoEPEscape(test) v34:Fixnum[0] = Const Value(0) - v37:BasicObject = SendWithoutBlock v28, :>, v34 + v37:BasicObject = SendWithoutBlock v28, :>, v34 # SendFallbackReason: Uncategorized(opt_gt) CheckInterrupts v40:CBool = Test v37 IfTrue v40, bb3(v26, v27, v28) @@ -1379,9 +1379,9 @@ pub mod hir_build_tests { bb3(v53:BasicObject, v54:BasicObject, v55:BasicObject): PatchPoint NoEPEscape(test) v62:Fixnum[1] = Const Value(1) - v65:BasicObject = SendWithoutBlock v54, :+, v62 + v65:BasicObject = SendWithoutBlock v54, :+, v62 # SendFallbackReason: Uncategorized(opt_plus) v70:Fixnum[1] = Const Value(1) - v73:BasicObject = SendWithoutBlock v55, :-, v70 + v73:BasicObject = SendWithoutBlock v55, :-, v70 # SendFallbackReason: Uncategorized(opt_minus) Jump bb4(v53, v65, v73) "); } @@ -1405,7 +1405,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :>=, v12 + v19:BasicObject = SendWithoutBlock v11, :>=, v12 # SendFallbackReason: Uncategorized(opt_ge) CheckInterrupts Return v19 "); @@ -1472,7 +1472,7 @@ pub mod hir_build_tests { bb2(v6:BasicObject): v11:Fixnum[2] = Const Value(2) v13:Fixnum[3] = Const Value(3) - v15:BasicObject = SendWithoutBlock v6, :bar, v11, v13 + v15:BasicObject = SendWithoutBlock v6, :bar, v11, v13 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -1500,7 +1500,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v14:BasicObject = Send v9, 0x1000, :each + v14:BasicObject = Send v9, 0x1000, :each # SendFallbackReason: Uncategorized(send) v15:BasicObject = GetLocal :a, l0, EP@3 CheckInterrupts Return v14 @@ -1559,7 +1559,7 @@ pub mod hir_build_tests { v18:StringExact = StringCopy v17 v20:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) v21:StringExact = StringCopy v20 - v23:BasicObject = SendWithoutBlock v6, :unknown_method, v12, v15, v18, v21 + v23:BasicObject = SendWithoutBlock v6, :unknown_method, v12, v15, v18, v21 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v23 "); @@ -1582,7 +1582,7 @@ pub mod hir_build_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v15:ArrayExact = ToArray v9 - v17:BasicObject = SendWithoutBlock v8, :foo, v15 + v17:BasicObject = SendWithoutBlock v8, :foo, v15 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v17 "); @@ -1604,7 +1604,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = Send v8, 0x1000, :foo, v9 + v15:BasicObject = Send v8, 0x1000, :foo, v9 # SendFallbackReason: Uncategorized(send) CheckInterrupts Return v15 "); @@ -1627,7 +1627,7 @@ pub mod hir_build_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - v16:BasicObject = SendWithoutBlock v8, :foo, v14 + v16:BasicObject = SendWithoutBlock v8, :foo, v14 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v16 "); @@ -1649,7 +1649,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v8, :foo, v9 + v15:BasicObject = SendWithoutBlock v8, :foo, v9 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -1672,7 +1672,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = InvokeSuper v6, 0x1000 + v11:BasicObject = InvokeSuper v6, 0x1000 # SendFallbackReason: Uncategorized(invokesuper) CheckInterrupts Return v11 "); @@ -1693,7 +1693,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = InvokeSuper v6, 0x1000 + v11:BasicObject = InvokeSuper v6, 0x1000 # SendFallbackReason: Uncategorized(invokesuper) CheckInterrupts Return v11 "); @@ -1715,7 +1715,7 @@ pub mod hir_build_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:NilClass = Const Value(nil) - v13:BasicObject = InvokeSuper v6, 0x1000, v11 + v13:BasicObject = InvokeSuper v6, 0x1000, v11 # SendFallbackReason: Uncategorized(invokesuper) CheckInterrupts Return v13 "); @@ -1782,12 +1782,12 @@ pub mod hir_build_tests { v14:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) v16:HashExact = NewHash PatchPoint NoEPEscape(test) - v21:BasicObject = SendWithoutBlock v14, :core#hash_merge_kwd, v16, v9 + v21:BasicObject = SendWithoutBlock v14, :core#hash_merge_kwd, v16, v9 # SendFallbackReason: Uncategorized(opt_send_without_block) v23:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) v26:StaticSymbol[:b] = Const Value(VALUE(0x1008)) v28:Fixnum[1] = Const Value(1) - v30:BasicObject = SendWithoutBlock v23, :core#hash_merge_ptr, v21, v26, v28 - v32:BasicObject = SendWithoutBlock v8, :foo, v30 + v30:BasicObject = SendWithoutBlock v23, :core#hash_merge_ptr, v21, v26, v28 # SendFallbackReason: Uncategorized(opt_send_without_block) + v32:BasicObject = SendWithoutBlock v8, :foo, v30 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v32 "); @@ -1812,7 +1812,7 @@ pub mod hir_build_tests { v15:ArrayExact = ToNewArray v9 v17:Fixnum[1] = Const Value(1) ArrayPush v15, v17 - v21:BasicObject = SendWithoutBlock v8, :foo, v15 + v21:BasicObject = SendWithoutBlock v8, :foo, v15 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v21 "); @@ -1834,7 +1834,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendForward v8, 0x1000, :foo, v9 + v15:BasicObject = SendForward v8, 0x1000, :foo, v9 # SendFallbackReason: Uncategorized(sendforward) CheckInterrupts Return v15 "); @@ -1891,11 +1891,11 @@ pub mod hir_build_tests { v16:CBool = IsMethodCFunc v11, :new IfFalse v16, bb3(v6, v13, v11) v18:HeapBasicObject = ObjectAlloc v11 - v20:BasicObject = SendWithoutBlock v18, :initialize + v20:BasicObject = SendWithoutBlock v18, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Jump bb4(v6, v18, v20) bb3(v24:BasicObject, v25:NilClass, v26:BasicObject): - v29:BasicObject = SendWithoutBlock v26, :new + v29:BasicObject = SendWithoutBlock v26, :new # SendFallbackReason: Uncategorized(opt_send_without_block) Jump bb4(v24, v29, v25) bb4(v32:BasicObject, v33:BasicObject, v34:BasicObject): CheckInterrupts @@ -2008,7 +2008,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) SideExit UnhandledNewarraySend(MIN) "); } @@ -2040,13 +2040,13 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_HASH) v32:Fixnum = ArrayHash v15, v16 PatchPoint NoEPEscape(test) v39:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v40:ArrayExact = ArrayDup v39 - v42:BasicObject = SendWithoutBlock v14, :puts, v40 + v42:BasicObject = SendWithoutBlock v14, :puts, v40 # SendFallbackReason: Uncategorized(opt_send_without_block) PatchPoint NoEPEscape(test) CheckInterrupts Return v32 @@ -2082,7 +2082,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_HASH)) "); } @@ -2114,7 +2114,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) v31:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v32:StringExact = StringCopy v31 SideExit UnhandledNewarraySend(PACK) @@ -2148,7 +2148,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) v29:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v30:StringExact = StringCopy v29 v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -2192,7 +2192,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) v29:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v30:StringExact = StringCopy v29 v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -2229,13 +2229,13 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_INCLUDE_P) v33:BoolExact = ArrayInclude v15, v16 | v16 PatchPoint NoEPEscape(test) v40:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v41:ArrayExact = ArrayDup v40 - v43:BasicObject = SendWithoutBlock v14, :puts, v41 + v43:BasicObject = SendWithoutBlock v14, :puts, v41 # SendFallbackReason: Uncategorized(opt_send_without_block) PatchPoint NoEPEscape(test) CheckInterrupts Return v33 @@ -2276,7 +2276,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 + v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_INCLUDE_P)) "); } @@ -2355,7 +2355,7 @@ pub mod hir_build_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - v21:BasicObject = SendWithoutBlock v18, :length + v21:BasicObject = SendWithoutBlock v18, :length # SendFallbackReason: Uncategorized(opt_length) CheckInterrupts Return v21 "); @@ -2380,7 +2380,7 @@ pub mod hir_build_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - v21:BasicObject = SendWithoutBlock v18, :size + v21:BasicObject = SendWithoutBlock v18, :size # SendFallbackReason: Uncategorized(opt_size) CheckInterrupts Return v21 "); @@ -2685,7 +2685,7 @@ pub mod hir_build_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v16:NilClass = Const Value(nil) v20:Fixnum[1] = Const Value(1) - v24:BasicObject = SendWithoutBlock v11, :[]=, v12, v20 + v24:BasicObject = SendWithoutBlock v11, :[]=, v12, v20 # SendFallbackReason: Uncategorized(opt_aset) CheckInterrupts Return v20 "); @@ -2709,7 +2709,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :[], v12 + v19:BasicObject = SendWithoutBlock v11, :[], v12 # SendFallbackReason: Uncategorized(opt_aref) CheckInterrupts Return v19 "); @@ -2732,7 +2732,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :empty? + v15:BasicObject = SendWithoutBlock v9, :empty? # SendFallbackReason: Uncategorized(opt_empty_p) CheckInterrupts Return v15 "); @@ -2755,7 +2755,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :succ + v15:BasicObject = SendWithoutBlock v9, :succ # SendFallbackReason: Uncategorized(opt_succ) CheckInterrupts Return v15 "); @@ -2779,7 +2779,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :&, v12 + v19:BasicObject = SendWithoutBlock v11, :&, v12 # SendFallbackReason: Uncategorized(opt_and) CheckInterrupts Return v19 "); @@ -2803,7 +2803,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :|, v12 + v19:BasicObject = SendWithoutBlock v11, :|, v12 # SendFallbackReason: Uncategorized(opt_or) CheckInterrupts Return v19 "); @@ -2826,7 +2826,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :! + v15:BasicObject = SendWithoutBlock v9, :! # SendFallbackReason: Uncategorized(opt_not) CheckInterrupts Return v15 "); @@ -2850,7 +2850,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :=~, v12 + v19:BasicObject = SendWithoutBlock v11, :=~, v12 # SendFallbackReason: Uncategorized(opt_regexpmatch2) CheckInterrupts Return v19 "); @@ -2880,7 +2880,7 @@ pub mod hir_build_tests { v12:BasicObject = PutSpecialObject CBase v14:StaticSymbol[:aliased] = Const Value(VALUE(0x1008)) v16:StaticSymbol[:__callee__] = Const Value(VALUE(0x1010)) - v18:BasicObject = SendWithoutBlock v10, :core#set_method_alias, v12, v14, v16 + v18:BasicObject = SendWithoutBlock v10, :core#set_method_alias, v12, v14, v16 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v18 "); @@ -2980,7 +2980,7 @@ pub mod hir_build_tests { CheckInterrupts v16:CBool = IsNil v9 IfTrue v16, bb3(v8, v9, v9) - v19:BasicObject = SendWithoutBlock v9, :itself + v19:BasicObject = SendWithoutBlock v9, :itself # SendFallbackReason: Uncategorized(opt_send_without_block) Jump bb3(v8, v9, v19) bb3(v21:BasicObject, v22:BasicObject, v23:BasicObject): CheckInterrupts @@ -3065,7 +3065,7 @@ pub mod hir_build_tests { v35:CBool[true] = Test v32 IfFalse v35, bb3(v16, v17, v18, v19, v20, v25) PatchPoint NoEPEscape(open) - v42:BasicObject = InvokeBlock, v25 + v42:BasicObject = InvokeBlock, v25 # SendFallbackReason: Uncategorized(invokeblock) v45:BasicObject = InvokeBuiltin dir_s_close, v16, v25 CheckInterrupts Return v42 @@ -3190,12 +3190,12 @@ pub mod hir_build_tests { v13:NilClass = Const Value(nil) v16:Fixnum[0] = Const Value(0) v18:Fixnum[1] = Const Value(1) - v21:BasicObject = SendWithoutBlock v9, :[], v16, v18 + v21:BasicObject = SendWithoutBlock v9, :[], v16, v18 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts v25:CBool = Test v21 IfTrue v25, bb3(v8, v9, v13, v9, v16, v18, v21) v29:Fixnum[2] = Const Value(2) - v32:BasicObject = SendWithoutBlock v9, :[]=, v16, v18, v29 + v32:BasicObject = SendWithoutBlock v9, :[]=, v16, v18, v29 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v29 bb3(v38:BasicObject, v39:BasicObject, v40:NilClass, v41:BasicObject, v42:Fixnum[0], v43:Fixnum[1], v44:BasicObject): @@ -3398,7 +3398,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v10:BasicObject = InvokeBlock + v10:BasicObject = InvokeBlock # SendFallbackReason: Uncategorized(invokeblock) CheckInterrupts Return v10 "); @@ -3423,7 +3423,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v18:BasicObject = InvokeBlock, v11, v12 + v18:BasicObject = InvokeBlock, v11, v12 # SendFallbackReason: Uncategorized(invokeblock) CheckInterrupts Return v18 "); @@ -3547,7 +3547,7 @@ pub mod hir_build_tests { IfTrue v19, bb3(v10, v11, v12) v22:Fixnum[1] = Const Value(1) v24:Fixnum[1] = Const Value(1) - v27:BasicObject = SendWithoutBlock v22, :+, v24 + v27:BasicObject = SendWithoutBlock v22, :+, v24 # SendFallbackReason: Uncategorized(opt_plus) PatchPoint NoEPEscape(test) Jump bb3(v10, v27, v12) bb3(v32:BasicObject, v33:BasicObject, v34:BasicObject): From 6409715212d22699bd2751a363b050a5d8b94b83 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Tue, 2 Dec 2025 17:34:36 -0800 Subject: [PATCH 321/367] Fix allocationless anonymous splat keyword argument check Previously, if an argument splat and keywords are provided by the caller, it did not check whether the method/proc accepted keywords before avoiding the allocation. This is incorrect, because if the method/proc does not accept keywords, the keywords passed by the caller are added as a positional argument, so there must be an allocation to avoid mutating the positional splat argument. Add a check that if the caller passes keywords, the method/proc must accept keywords in order to optimize. If the caller passes a keyword splat, either the method/proc must accept keywords, or the keyword splat must be empty in order to optimize. If keywords are explicitly disallowed via `**nil`, the optimization should be skipped, because the array is mutated before the ArgumentError exception is raised. In addition to a test for the correct behavior, add an allocation test for a method that accepts an anonymous splat without keywords. Fixes [Bug #21757] --- test/ruby/test_allocation.rb | 53 ++++++++++++++++++++++++++++++++++++ test/ruby/test_call.rb | 29 ++++++++++++++++++++ vm_args.c | 26 ++++++++++++++---- 3 files changed, 102 insertions(+), 6 deletions(-) diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb index 6ade391c951848..90d7c04f9b0a2b 100644 --- a/test/ruby/test_allocation.rb +++ b/test/ruby/test_allocation.rb @@ -527,6 +527,59 @@ def self.splat_and_keyword_splat(*b, **kw#{block}); end RUBY end + def test_anonymous_splat_parameter + only_block = block.empty? ? block : block[2..] + check_allocations(<<~RUBY) + def self.anon_splat(*#{block}); end + + check_allocations(1, 1, "anon_splat(1, a: 2#{block})") + check_allocations(1, 1, "anon_splat(1, *empty_array, a: 2#{block})") + check_allocations(1, 1, "anon_splat(1, a:2, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, **empty_hash, a: 2#{block})") + + check_allocations(1, 0, "anon_splat(1, **nil#{block})") + check_allocations(1, 0, "anon_splat(1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, **hash1#{block})") + check_allocations(1, 1, "anon_splat(1, *empty_array, **hash1#{block})") + check_allocations(1, 1, "anon_splat(1, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, **empty_hash, **hash1#{block})") + + check_allocations(1, 0, "anon_splat(1, *empty_array#{block})") + check_allocations(1, 0, "anon_splat(1, *empty_array, *empty_array, **empty_hash#{block})") + + check_allocations(1, 1, "anon_splat(*array1, a: 2#{block})") + + check_allocations(0, 0, "anon_splat(*nil, **nill#{block})") + check_allocations(0, 0, "anon_splat(*array1, **nill#{block})") + check_allocations(0, 0, "anon_splat(*array1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(*array1, **hash1#{block})") + check_allocations(1, 1, "anon_splat(*array1, *empty_array, **hash1#{block})") + + check_allocations(1, 0, "anon_splat(*array1, *empty_array#{block})") + check_allocations(1, 0, "anon_splat(*array1, *empty_array, **empty_hash#{block})") + + check_allocations(1, 1, "anon_splat(*array1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(*array1, *empty_array, **hash1, **empty_hash#{block})") + + check_allocations(0, 0, "anon_splat(#{only_block})") + check_allocations(1, 1, "anon_splat(a: 2#{block})") + check_allocations(0, 0, "anon_splat(**empty_hash#{block})") + + check_allocations(1, 1, "anon_splat(1, *empty_array, a: 2, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(1, *empty_array, **hash1, **empty_hash#{block})") + check_allocations(1, 1, "anon_splat(*array1, **empty_hash, a: 2#{block})") + check_allocations(1, 1, "anon_splat(*array1, **hash1, **empty_hash#{block})") + + unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? + check_allocations(0, 0, "anon_splat(*array1, **nil#{block})") + check_allocations(1, 0, "anon_splat(*r2k_empty_array#{block})") + check_allocations(1, 1, "anon_splat(*r2k_array#{block})") + check_allocations(1, 0, "anon_splat(*r2k_empty_array1#{block})") + check_allocations(1, 1, "anon_splat(*r2k_array1#{block})") + end + RUBY + end + def test_anonymous_splat_and_anonymous_keyword_splat_parameters only_block = block.empty? ? block : block[2..] check_allocations(<<~RUBY) diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb index 7843f3b476e6c9..1b30ed34d8826f 100644 --- a/test/ruby/test_call.rb +++ b/test/ruby/test_call.rb @@ -423,6 +423,35 @@ def self.s(*, kw: 0, **kws) [*->(*a){a}.call(*), kw, kws] end assert_equal([1, 2, {}], s(*r2ka)) end + def test_anon_splat_mutated_bug_21757 + args = [1, 2] + kw = {bug: true} + + def self.m(*); end + m(*args, bug: true) + assert_equal(2, args.length) + + proc = ->(*) { } + proc.(*args, bug: true) + assert_equal(2, args.length) + + def self.m2(*); end + m2(*args, **kw) + assert_equal(2, args.length) + + proc = ->(*) { } + proc.(*args, **kw) + assert_equal(2, args.length) + + def self.m3(*, **nil); end + assert_raise(ArgumentError) { m3(*args, bug: true) } + assert_equal(2, args.length) + + proc = ->(*, **nil) { } + assert_raise(ArgumentError) { proc.(*args, bug: true) } + assert_equal(2, args.length) + end + def test_kwsplat_block_eval_order def self.t(**kw, &b) [kw, b] end diff --git a/vm_args.c b/vm_args.c index 64ed88d0e1dcce..8d4042e35566ae 100644 --- a/vm_args.c +++ b/vm_args.c @@ -638,12 +638,26 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co given_argc == ISEQ_BODY(iseq)->param.lead_num + (kw_flag ? 2 : 1) && !ISEQ_BODY(iseq)->param.flags.has_opt && !ISEQ_BODY(iseq)->param.flags.has_post && - !ISEQ_BODY(iseq)->param.flags.ruby2_keywords && - (!kw_flag || - !ISEQ_BODY(iseq)->param.flags.has_kw || - !ISEQ_BODY(iseq)->param.flags.has_kwrest || - !ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg)) { - args->rest_dupped = true; + !ISEQ_BODY(iseq)->param.flags.ruby2_keywords) { + if (kw_flag) { + if (ISEQ_BODY(iseq)->param.flags.has_kw || + ISEQ_BODY(iseq)->param.flags.has_kwrest) { + args->rest_dupped = true; + } + else if (kw_flag & VM_CALL_KW_SPLAT) { + VALUE kw_hash = locals[args->argc - 1]; + if (kw_hash == Qnil || + (RB_TYPE_P(kw_hash, T_HASH) && RHASH_EMPTY_P(kw_hash))) { + args->rest_dupped = true; + } + } + + } + else if (!ISEQ_BODY(iseq)->param.flags.has_kw && + !ISEQ_BODY(iseq)->param.flags.has_kwrest && + !ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg) { + args->rest_dupped = true; + } } } From 1e7cf7b2bc1f9b356b2e980e1e18548618da6363 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 10 Oct 2025 16:32:43 -0700 Subject: [PATCH 322/367] Fix refinement modification of method visibility in superclass Previously, this didn't work correctly, resulting in a SystemStackError. This fixes the issue by finding the related superclass method entry, and updating the orig_me in the refinement method to point to the superclass method. Fixes [Bug #21446] --- test/ruby/test_refinement.rb | 23 +++++++++++++++++++++++ vm_method.c | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index bdc6667b8e81fe..209e55294b1889 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -1933,6 +1933,29 @@ module PublicCows end; end + def test_public_in_refine_for_method_in_superclass + assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}") + begin; + bug21446 = '[ruby-core:122558] [Bug #21446]' + + class CowSuper + private + def moo() "Moo"; end + end + class Cow < CowSuper + end + + module PublicCows + refine(Cow) { + public :moo + } + end + + using PublicCows + assert_equal("Moo", Cow.new.moo, bug21446) + end; + end + module SuperToModule class Parent end diff --git a/vm_method.c b/vm_method.c index c4f391b5afe38a..dbc5ad97eded92 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1281,6 +1281,7 @@ check_override_opt_method(VALUE klass, VALUE mid) } } +static inline rb_method_entry_t* search_method0(VALUE klass, ID id, VALUE *defined_class_ptr, bool skip_refined); /* * klass->method_table[mid] = method_entry(defined_class, visi, def) * @@ -1321,7 +1322,12 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil if (RB_TYPE_P(klass, T_MODULE) && FL_TEST(klass, RMODULE_IS_REFINEMENT)) { VALUE refined_class = rb_refinement_module_get_refined_class(klass); + bool search_superclass = type == VM_METHOD_TYPE_ZSUPER && !lookup_method_table(refined_class, mid); rb_add_refined_method_entry(refined_class, mid); + if (search_superclass) { + rb_method_entry_t *me = lookup_method_table(refined_class, mid); + me->def->body.refined.orig_me = search_method0(refined_class, mid, NULL, true); + } } if (type == VM_METHOD_TYPE_REFINED) { rb_method_entry_t *old_me = lookup_method_table(RCLASS_ORIGIN(klass), mid); From 76fb0d244b95a23116bfe72bb2422395c3a76477 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sat, 22 Nov 2025 19:51:46 -0800 Subject: [PATCH 323/367] Use actual class instead of singleton class in frozen error message With the following code: ```ruby object = [] object.singleton_class object.freeze object.instance_variable_set(:@a, 42) ``` The previous error message was: ``` can't modify frozen #>: [] ``` With this change, the error message is: ``` can't modify frozen Array: [] ``` Since we show the inspect value of the affected object, I think including the singleton class instead of the actual class if it exists makes the error message harder to understand. --- error.c | 2 +- test/ruby/test_frozen.rb | 16 ++++++++++++++++ test/ruby/test_zjit.rb | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/error.c b/error.c index ed66e6c8488d0f..e1a01b985aae16 100644 --- a/error.c +++ b/error.c @@ -4163,7 +4163,7 @@ rb_error_frozen_object(VALUE frozen_obj) rb_yjit_lazy_push_frame(GET_EC()->cfp->pc); VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ", - CLASS_OF(frozen_obj)); + rb_obj_class(frozen_obj)); VALUE exc = rb_exc_new_str(rb_eFrozenError, mesg); rb_ivar_set(exc, id_recv, frozen_obj); diff --git a/test/ruby/test_frozen.rb b/test/ruby/test_frozen.rb index 2918a2afd822c7..6721cb112863f3 100644 --- a/test/ruby/test_frozen.rb +++ b/test/ruby/test_frozen.rb @@ -27,4 +27,20 @@ def test_setting_ivar_on_frozen_string_with_ivars str.freeze assert_raise(FrozenError) { str.instance_variable_set(:@b, 1) } end + + def test_setting_ivar_on_frozen_string_with_singleton_class + str = "str" + str.singleton_class + str.freeze + assert_raise_with_message(FrozenError, "can't modify frozen String: \"str\"") { str.instance_variable_set(:@a, 1) } + end + + class A + freeze + end + + def test_setting_ivar_on_frozen_class + assert_raise_with_message(FrozenError, "can't modify frozen Class: TestFrozen::A") { A.instance_variable_set(:@a, 1) } + assert_raise_with_message(FrozenError, "can't modify frozen Class: #") { A.singleton_class.instance_variable_set(:@a, 1) } + end end diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 81d940407063f1..49b3616425ecd3 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -2057,7 +2057,7 @@ def self.test = @@x = 42 end def test_setclassvariable_raises - assert_compiles '"can\'t modify frozen #: Foo"', %q{ + assert_compiles '"can\'t modify frozen Class: Foo"', %q{ class Foo def self.test = @@x = 42 freeze From e436dba9fee4c75104212ad0fd16ab7f753985c4 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sat, 22 Nov 2025 19:58:43 -0800 Subject: [PATCH 324/367] Use rb_error_frozen_object in rb_class_modify_check This provides information on the class of the frozen object. It also results in a much simpler implementation. Fixes [Bug #21374] --- eval.c | 32 +------------------ spec/ruby/core/exception/frozen_error_spec.rb | 4 ++- spec/ruby/language/def_spec.rb | 9 ++++-- test/ruby/test_class.rb | 2 +- test/ruby/test_module.rb | 6 ++-- 5 files changed, 14 insertions(+), 39 deletions(-) diff --git a/eval.c b/eval.c index 4fbb0e799735d4..ee5bc43f9cc4c1 100644 --- a/eval.c +++ b/eval.c @@ -428,40 +428,10 @@ rb_class_modify_check(VALUE klass) rb_class_set_initialized(klass); } if (OBJ_FROZEN(klass)) { - const char *desc; - if (RCLASS_SINGLETON_P(klass)) { - desc = "object"; klass = RCLASS_ATTACHED_OBJECT(klass); - if (!SPECIAL_CONST_P(klass)) { - switch (BUILTIN_TYPE(klass)) { - case T_MODULE: - case T_ICLASS: - desc = "Module"; - break; - case T_CLASS: - desc = "Class"; - break; - default: - break; - } - } - } - else { - switch (BUILTIN_TYPE(klass)) { - case T_MODULE: - case T_ICLASS: - desc = "module"; - break; - case T_CLASS: - desc = "class"; - break; - default: - Check_Type(klass, T_CLASS); - UNREACHABLE; - } } - rb_frozen_error_raise(klass, "can't modify frozen %s: %"PRIsVALUE, desc, klass); + rb_error_frozen_object(klass); } } diff --git a/spec/ruby/core/exception/frozen_error_spec.rb b/spec/ruby/core/exception/frozen_error_spec.rb index 51eb79cacef6ae..1cf35496d40d11 100644 --- a/spec/ruby/core/exception/frozen_error_spec.rb +++ b/spec/ruby/core/exception/frozen_error_spec.rb @@ -26,9 +26,11 @@ def o.x; end object = Object.new object.freeze + msg_class = RUBY_VERSION >= "4" ? "Object" : "object" + -> { def object.x; end - }.should raise_error(FrozenError, "can't modify frozen object: #{object}") + }.should raise_error(FrozenError, "can't modify frozen #{msg_class}: #{object}") object = [].freeze -> { object << nil }.should raise_error(FrozenError, "can't modify frozen Array: []") diff --git a/spec/ruby/language/def_spec.rb b/spec/ruby/language/def_spec.rb index 296d4787d01c45..5cc36d61f0a927 100644 --- a/spec/ruby/language/def_spec.rb +++ b/spec/ruby/language/def_spec.rb @@ -97,7 +97,8 @@ def foo(a); end def foo; end end }.should raise_error(FrozenError) { |e| - e.message.should == "can't modify frozen module: #{e.receiver}" + msg_class = RUBY_VERSION >= "4" ? "Module" : "module" + e.message.should == "can't modify frozen #{msg_class}: #{e.receiver}" } -> { @@ -106,7 +107,8 @@ def foo; end def foo; end end }.should raise_error(FrozenError){ |e| - e.message.should == "can't modify frozen class: #{e.receiver}" + msg_class = RUBY_VERSION >= "4" ? "Class" : "class" + e.message.should == "can't modify frozen #{msg_class}: #{e.receiver}" } end end @@ -283,7 +285,8 @@ def obj.==(other) it "raises FrozenError with the correct class name" do obj = Object.new obj.freeze - -> { def obj.foo; end }.should raise_error(FrozenError, "can't modify frozen object: #{obj}") + msg_class = RUBY_VERSION >= "4" ? "Object" : "object" + -> { def obj.foo; end }.should raise_error(FrozenError, "can't modify frozen #{msg_class}: #{obj}") obj = Object.new c = obj.singleton_class diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 1faecd0cb25e13..22078514ad5cd3 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -601,7 +601,7 @@ def test_singleton_class_of_frozen_object obj = Object.new c = obj.singleton_class obj.freeze - assert_raise_with_message(FrozenError, /frozen object/) { + assert_raise_with_message(FrozenError, /frozen Object/) { c.class_eval {def f; end} } end diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 62b2a7164fb4ff..3a47c2551a813e 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -3016,17 +3016,17 @@ def test_frozen_visibility bug11532 = '[ruby-core:70828] [Bug #11532]' c = Class.new {const_set(:A, 1)}.freeze - assert_raise_with_message(FrozenError, /frozen class/, bug11532) { + assert_raise_with_message(FrozenError, /frozen Class/, bug11532) { c.class_eval {private_constant :A} } c = Class.new {const_set(:A, 1); private_constant :A}.freeze - assert_raise_with_message(FrozenError, /frozen class/, bug11532) { + assert_raise_with_message(FrozenError, /frozen Class/, bug11532) { c.class_eval {public_constant :A} } c = Class.new {const_set(:A, 1)}.freeze - assert_raise_with_message(FrozenError, /frozen class/, bug11532) { + assert_raise_with_message(FrozenError, /frozen Class/, bug11532) { c.class_eval {deprecate_constant :A} } end From 29c29c2b7e972359ab83038c5dc27a7e53ae65c7 Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Tue, 9 Dec 2025 18:14:49 -0500 Subject: [PATCH 325/367] ZJIT: Add dump to file for --zjit-stats (#15414) * ZJIT: Add dump to file for --zjit-stats * ZJIT: Rename --zjit-stats=quiet to --zjit-stats-quiet --- zjit.c | 1 + zjit.rb | 11 +++++++++++ zjit/src/options.rs | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/zjit.c b/zjit.c index 7731bc908f6ab9..75cca281a45a91 100644 --- a/zjit.c +++ b/zjit.c @@ -315,6 +315,7 @@ VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key); VALUE rb_zjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self); VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self); VALUE rb_zjit_print_stats_p(rb_execution_context_t *ec, VALUE self); +VALUE rb_zjit_get_stats_file_path_p(rb_execution_context_t *ec, VALUE self); VALUE rb_zjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self); VALUE rb_zjit_get_exit_locations(rb_execution_context_t *ec, VALUE self); diff --git a/zjit.rb b/zjit.rb index d128adead6fe39..e2aa55f764eb9a 100644 --- a/zjit.rb +++ b/zjit.rb @@ -10,6 +10,9 @@ module RubyVM::ZJIT # Blocks that are called when YJIT is enabled @jit_hooks = [] # Avoid calling a Ruby method here to avoid interfering with compilation tests + if Primitive.rb_zjit_get_stats_file_path_p + at_exit { print_stats_file } + end if Primitive.rb_zjit_print_stats_p at_exit { print_stats } end @@ -333,6 +336,14 @@ def print_stats $stderr.write stats_string end + # Print ZJIT stats to file + def print_stats_file + filename = Primitive.rb_zjit_get_stats_file_path_p + File.open(filename, "wb") do |file| + file.write stats_string + end + end + def dump_locations # :nodoc: return unless trace_exit_locations_enabled? diff --git a/zjit/src/options.rs b/zjit/src/options.rs index b7e2c71cefcd65..ea9dc3249b82ff 100644 --- a/zjit/src/options.rs +++ b/zjit/src/options.rs @@ -51,6 +51,9 @@ pub struct Options { /// Print stats on exit (when stats is also true) pub print_stats: bool, + /// Print stats to file on exit (when stats is also true) + pub print_stats_file: Option, + /// Enable debug logging pub debug: bool, @@ -103,6 +106,7 @@ impl Default for Options { num_profiles: DEFAULT_NUM_PROFILES, stats: false, print_stats: false, + print_stats_file: None, debug: false, disable: false, disable_hir_opt: false, @@ -131,7 +135,10 @@ pub const ZJIT_OPTIONS: &[(&str, &str)] = &[ "Number of calls to trigger JIT (default: 30)."), ("--zjit-num-profiles=num", "Number of profiled calls before JIT (default: 5)."), - ("--zjit-stats[=quiet]", "Enable collecting ZJIT statistics (=quiet to suppress output)."), + ("--zjit-stats-quiet", + "Collect ZJIT stats and suppress output."), + ("--zjit-stats[=file]", + "Collect ZJIT stats (=file to write to a file)."), ("--zjit-disable", "Disable ZJIT for lazily enabling it with RubyVM::ZJIT.enable."), ("--zjit-perf", "Dump ISEQ symbols into /tmp/perf-{}.map for Linux perf."), @@ -303,13 +310,28 @@ fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> { Err(_) => return None, }, + + ("stats-quiet", _) => { + options.stats = true; + options.print_stats = false; + } + ("stats", "") => { options.stats = true; options.print_stats = true; } - ("stats", "quiet") => { + ("stats", path) => { + // Truncate the file if it exists + std::fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path) + .map_err(|e| eprintln!("Failed to open file '{}': {}", path, e)) + .ok(); + let canonical_path = std::fs::canonicalize(opt_val).unwrap_or_else(|_| opt_val.into()); options.stats = true; - options.print_stats = false; + options.print_stats_file = Some(canonical_path); } ("trace-exits", exits) => { @@ -488,3 +510,14 @@ pub extern "C" fn rb_zjit_print_stats_p(_ec: EcPtr, _self: VALUE) -> VALUE { Qfalse } } + +/// Return path if stats should be printed at exit to a specified file, else Qnil. +#[unsafe(no_mangle)] +pub extern "C" fn rb_zjit_get_stats_file_path_p(_ec: EcPtr, _self: VALUE) -> VALUE { + if let Some(opts) = unsafe { OPTIONS.as_ref() } { + if let Some(ref path) = opts.print_stats_file { + return rust_str_to_ruby(path.as_os_str().to_str().unwrap()); + } + } + Qnil +} From 264c469bc2cd6a9320cb1545d83ebda69e421f49 Mon Sep 17 00:00:00 2001 From: Yuji Teshima <36704166+yujiteshima@users.noreply.github.com> Date: Wed, 10 Dec 2025 08:28:18 +0900 Subject: [PATCH 326/367] Fix typo in thread_pthread.c [ci skip] (#15465) Fix typo in thread_pthread.c: threre -> there --- thread_pthread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thread_pthread.c b/thread_pthread.c index 93b32e55c0f72b..0a19f7f0310af1 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -1459,7 +1459,7 @@ rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr) vm->ractor.sync.lock_owner = cr; } - // do not release ractor_sched_lock and threre is no newly added (resumed) thread + // do not release ractor_sched_lock and there is no newly added (resumed) thread // thread_sched_setup_running_threads } From f9eb0d8da2cb570c6cf7754e4078ed0de266a52c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 10 Dec 2025 09:22:09 +0900 Subject: [PATCH 327/367] Use `ruby_version_is` As the markers for spec/mspec/tool/remove_old_guards.rb. --- spec/ruby/core/exception/frozen_error_spec.rb | 2 +- spec/ruby/language/def_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/ruby/core/exception/frozen_error_spec.rb b/spec/ruby/core/exception/frozen_error_spec.rb index 1cf35496d40d11..af2e92566192eb 100644 --- a/spec/ruby/core/exception/frozen_error_spec.rb +++ b/spec/ruby/core/exception/frozen_error_spec.rb @@ -26,7 +26,7 @@ def o.x; end object = Object.new object.freeze - msg_class = RUBY_VERSION >= "4" ? "Object" : "object" + msg_class = ruby_version_is("4.0") ? "Object" : "object" -> { def object.x; end diff --git a/spec/ruby/language/def_spec.rb b/spec/ruby/language/def_spec.rb index 5cc36d61f0a927..0cf1790791a0fb 100644 --- a/spec/ruby/language/def_spec.rb +++ b/spec/ruby/language/def_spec.rb @@ -97,7 +97,7 @@ def foo(a); end def foo; end end }.should raise_error(FrozenError) { |e| - msg_class = RUBY_VERSION >= "4" ? "Module" : "module" + msg_class = ruby_version_is("4.0") ? "Module" : "module" e.message.should == "can't modify frozen #{msg_class}: #{e.receiver}" } @@ -107,7 +107,7 @@ def foo; end def foo; end end }.should raise_error(FrozenError){ |e| - msg_class = RUBY_VERSION >= "4" ? "Class" : "class" + msg_class = ruby_version_is("4.0") ? "Class" : "class" e.message.should == "can't modify frozen #{msg_class}: #{e.receiver}" } end @@ -285,7 +285,7 @@ def obj.==(other) it "raises FrozenError with the correct class name" do obj = Object.new obj.freeze - msg_class = RUBY_VERSION >= "4" ? "Object" : "object" + msg_class = ruby_version_is("4.0") ? "Object" : "object" -> { def obj.foo; end }.should raise_error(FrozenError, "can't modify frozen #{msg_class}: #{obj}") obj = Object.new From 3bb97e7707a0be8c371cb9c704cb1e21062e1fc6 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Wed, 10 Dec 2025 04:32:34 +0900 Subject: [PATCH 328/367] `_RUBY_DEBUG_LOG` usable anywhere even if `USE_RUBY_DEBUG_LOG=0`. It becomes `fprintf(stderr, ...)`. --- debug.c | 18 ++++++++++++++++++ vm_debug.h | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/debug.c b/debug.c index b92faa8f369398..4daee2bd1cbd0d 100644 --- a/debug.c +++ b/debug.c @@ -708,4 +708,22 @@ ruby_debug_log_dump(const char *fname, unsigned int n) fclose(fp); } } + +#else + +#undef ruby_debug_log +void +ruby_debug_log(const char *file, int line, const char *func_name, const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "[%s:%d] %s: ", file, line, func_name); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + #endif // #if USE_RUBY_DEBUG_LOG diff --git a/vm_debug.h b/vm_debug.h index d0bc81574a31b5..cf80232f3a5eb0 100644 --- a/vm_debug.h +++ b/vm_debug.h @@ -88,6 +88,10 @@ void ruby_debug_log(const char *file, int line, const char *func_name, const cha void ruby_debug_log_print(unsigned int n); bool ruby_debug_log_filter(const char *func_name, const char *file_name); +// convenient macro to log even if the USE_RUBY_DEBUG_LOG macro is not specified. +// You can use this macro for temporary usage (you should not commit it). +#define _RUBY_DEBUG_LOG(...) ruby_debug_log(__FILE__, __LINE__, RUBY_FUNCTION_NAME_STRING, "" __VA_ARGS__) + #if RBIMPL_COMPILER_IS(GCC) && defined(__OPTIMIZE__) # define ruby_debug_log(...) \ RB_GNUC_EXTENSION_BLOCK( \ @@ -97,10 +101,6 @@ bool ruby_debug_log_filter(const char *func_name, const char *file_name); RBIMPL_WARNING_POP()) #endif -// convenient macro to log even if the USE_RUBY_DEBUG_LOG macro is not specified. -// You can use this macro for temporary usage (you should not commit it). -#define _RUBY_DEBUG_LOG(...) ruby_debug_log(__FILE__, __LINE__, RUBY_FUNCTION_NAME_STRING, "" __VA_ARGS__) - #if USE_RUBY_DEBUG_LOG # define RUBY_DEBUG_LOG_ENABLED(func_name, file_name) \ (ruby_debug_log_mode && ruby_debug_log_filter(func_name, file_name)) From 3636277dc5837bcedcd5ef43d49423194064a676 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 10 Dec 2025 12:09:50 +0900 Subject: [PATCH 329/367] Add `NUM2PTR` and `PTR2NUM` macros These macros have been defined here and there, so collect them. --- box.c | 2 +- compile.c | 2 -- ext/-test-/fatal/invalid.c | 6 ------ gc.c | 7 +------ include/ruby/internal/arithmetic/intptr_t.h | 12 ++++++++++++ internal/box.h | 10 ---------- load.c | 4 ++-- spec/ruby/optional/capi/ext/digest_spec.c | 2 ++ yjit.c | 2 -- zjit.c | 2 -- 10 files changed, 18 insertions(+), 31 deletions(-) diff --git a/box.c b/box.c index 0ce8d0aee02f0f..7907e0ff632a1e 100644 --- a/box.c +++ b/box.c @@ -760,7 +760,7 @@ static int cleanup_local_extension_i(VALUE key, VALUE value, VALUE arg) { #if defined(_WIN32) - HMODULE h = (HMODULE)NUM2SVALUE(value); + HMODULE h = (HMODULE)NUM2PTR(value); WCHAR module_path[MAXPATHLEN]; DWORD len = GetModuleFileNameW(h, module_path, numberof(module_path)); diff --git a/compile.c b/compile.c index a5d821eb810cf1..bcf22243cfc7af 100644 --- a/compile.c +++ b/compile.c @@ -610,8 +610,6 @@ branch_coverage_valid_p(rb_iseq_t *iseq, int first_line) return 1; } -#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) - static VALUE setup_branch(const rb_code_location_t *loc, const char *type, VALUE structure, VALUE key) { diff --git a/ext/-test-/fatal/invalid.c b/ext/-test-/fatal/invalid.c index 393465416a0f6c..6fd970b181191c 100644 --- a/ext/-test-/fatal/invalid.c +++ b/ext/-test-/fatal/invalid.c @@ -1,11 +1,5 @@ #include -#if SIZEOF_LONG == SIZEOF_VOIDP -# define NUM2PTR(x) NUM2ULONG(x) -#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP -# define NUM2PTR(x) NUM2ULL(x) -#endif - static VALUE invalid_call(VALUE obj, VALUE address) { diff --git a/gc.c b/gc.c index 79eec5d96bf7af..5f0f2307c8c9fd 100644 --- a/gc.c +++ b/gc.c @@ -2122,14 +2122,9 @@ rb_gc_obj_free_vm_weak_references(VALUE obj) static VALUE id2ref(VALUE objid) { -#if SIZEOF_LONG == SIZEOF_VOIDP -#define NUM2PTR(x) NUM2ULONG(x) -#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP -#define NUM2PTR(x) NUM2ULL(x) -#endif objid = rb_to_int(objid); if (FIXNUM_P(objid) || rb_big_size(objid) <= SIZEOF_VOIDP) { - VALUE ptr = NUM2PTR(objid); + VALUE ptr = (VALUE)NUM2PTR(objid); if (SPECIAL_CONST_P(ptr)) { if (ptr == Qtrue) return Qtrue; if (ptr == Qfalse) return Qfalse; diff --git a/include/ruby/internal/arithmetic/intptr_t.h b/include/ruby/internal/arithmetic/intptr_t.h index a354f4469cdf95..70090f88e6b37c 100644 --- a/include/ruby/internal/arithmetic/intptr_t.h +++ b/include/ruby/internal/arithmetic/intptr_t.h @@ -32,6 +32,18 @@ #define rb_int_new rb_int2inum /**< @alias{rb_int2inum} */ #define rb_uint_new rb_uint2inum /**< @alias{rb_uint2inum} */ +// These definitions are same as fiddle/conversions.h +#if SIZEOF_VOIDP <= SIZEOF_LONG +# define PTR2NUM(x) (LONG2NUM((long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULONG(x))) +#elif SIZEOF_VOIDP <= SIZEOF_LONG_LONG +# define PTR2NUM(x) (LL2NUM((LONG_LONG)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULL(x))) +#else +// should have been an error in ruby/internal/value.h +# error Need integer for VALUE +#endif + RBIMPL_SYMBOL_EXPORT_BEGIN() /** diff --git a/internal/box.h b/internal/box.h index 72263cc9dc5e5d..b62b6a9bc946f2 100644 --- a/internal/box.h +++ b/internal/box.h @@ -3,16 +3,6 @@ #include "ruby/ruby.h" /* for VALUE */ -#if SIZEOF_VALUE <= SIZEOF_LONG -# define SVALUE2NUM(x) LONG2NUM((long)(x)) -# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LONG(x) -#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG -# define SVALUE2NUM(x) LL2NUM((LONG_LONG)(x)) -# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LL(x) -#else -# error Need integer for VALUE -#endif - /** * @author Ruby developers * @copyright This file is a part of the programming language Ruby. diff --git a/load.c b/load.c index 466517f465b098..144f095b04d2d3 100644 --- a/load.c +++ b/load.c @@ -1345,7 +1345,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa reset_ext_config = true; ext_config_push(th, &prev_ext_config); handle = rb_vm_call_cfunc_in_box(box->top_self, load_ext, path, fname, path, box); - rb_hash_aset(box->ruby_dln_libmap, path, SVALUE2NUM((SIGNED_VALUE)handle)); + rb_hash_aset(box->ruby_dln_libmap, path, PTR2NUM(handle)); break; } result = TAG_RETURN; @@ -1666,7 +1666,7 @@ rb_ext_resolve_symbol(const char* fname, const char* symbol) if (NIL_P(handle)) { return NULL; } - return dln_symbol((void *)NUM2SVALUE(handle), symbol); + return dln_symbol(NUM2PTR(handle), symbol); } void diff --git a/spec/ruby/optional/capi/ext/digest_spec.c b/spec/ruby/optional/capi/ext/digest_spec.c index 9993238cf227d3..65c8defa20adce 100644 --- a/spec/ruby/optional/capi/ext/digest_spec.c +++ b/spec/ruby/optional/capi/ext/digest_spec.c @@ -135,7 +135,9 @@ VALUE digest_spec_context_size(VALUE self, VALUE meta) { return SIZET2NUM(algo->ctx_size); } +#ifndef PTR2NUM #define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) +#endif VALUE digest_spec_context(VALUE self, VALUE digest) { return PTR2NUM(context); diff --git a/yjit.c b/yjit.c index 6d909a0da61ee3..f3c256093eda0b 100644 --- a/yjit.c +++ b/yjit.c @@ -64,8 +64,6 @@ STATIC_ASSERT(pointer_tagging_scheme, USE_FLONUM); // The "_yjit_" part is for trying to be informative. We might want different // suffixes for symbols meant for Rust and symbols meant for broader CRuby. -# define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) - // For a given raw_sample (frame), set the hash with the caller's // name, file, and line number. Return the hash with collected frame_info. static void diff --git a/zjit.c b/zjit.c index 75cca281a45a91..df44821fe54643 100644 --- a/zjit.c +++ b/zjit.c @@ -35,8 +35,6 @@ enum zjit_struct_offsets { ISEQ_BODY_OFFSET_PARAM = offsetof(struct rb_iseq_constant_body, param) }; -#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) - // For a given raw_sample (frame), set the hash with the caller's // name, file, and line number. Return the hash with collected frame_info. static void From df4fc0f7fcda6c552084ea0638c7185b4a98c939 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 14:07:11 +0900 Subject: [PATCH 330/367] [ruby/psych] v5.3.0 https://github.com/ruby/psych/commit/d8053b0d16 --- ext/psych/lib/psych/versions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb index 3202b10296f549..2c2319e7a38e67 100644 --- a/ext/psych/lib/psych/versions.rb +++ b/ext/psych/lib/psych/versions.rb @@ -2,7 +2,7 @@ module Psych # The version of Psych you are using - VERSION = '5.2.6' + VERSION = '5.3.0' if RUBY_ENGINE == 'jruby' DEFAULT_SNAKEYAML_VERSION = '2.9'.freeze From e4786376d68fc62a0bfcdd4d0e644de0e41e0d4d Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 05:09:13 +0000 Subject: [PATCH 331/367] Update default gems list at df4fc0f7fcda6c552084ea0638c718 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 784b92b717d1c5..a92df627636bd0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -256,7 +256,7 @@ The following default gems are updated. * optparse 0.8.0 * pp 0.6.3 * prism 1.6.0 -* psych 5.2.6 +* psych 5.3.0 * resolv 0.6.3 * stringio 3.1.9.dev * strscan 3.1.6.dev From 814f23747b5fd7b0d5fb6cd8e45833ec39482858 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 14:11:45 +0900 Subject: [PATCH 332/367] [ruby/resolv] v0.7.0 https://github.com/ruby/resolv/commit/a0e89bbe48 --- lib/resolv.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resolv.rb b/lib/resolv.rb index b98c7ecdd2aa32..0e62aaf8510496 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -35,7 +35,7 @@ class Resolv # The version string - VERSION = "0.6.3" + VERSION = "0.7.0" ## # Looks up the first IP address for +name+. From 238e69d1258663e3385853dca36719c08e089f06 Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 05:13:39 +0000 Subject: [PATCH 333/367] Update default gems list at 814f23747b5fd7b0d5fb6cd8e45833 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index a92df627636bd0..8ea6e93b1b5acc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -257,7 +257,7 @@ The following default gems are updated. * pp 0.6.3 * prism 1.6.0 * psych 5.3.0 -* resolv 0.6.3 +* resolv 0.7.0 * stringio 3.1.9.dev * strscan 3.1.6.dev * timeout 0.5.0 From ec862b41dc9aaa2a22d80961b62417a347bc84ec Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 9 Dec 2025 21:18:03 -0800 Subject: [PATCH 334/367] ZJIT: Prohibit ZJIT support with USE_FLONUM=0 (#15471) --- .github/workflows/compilers.yml | 2 +- zjit.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 5e54d39e9fa6b9..8c0ca54e0b100b 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -244,7 +244,7 @@ jobs: - { uses: './.github/actions/compilers', name: 'RGENGC_CHECK_MODE', with: { cppflags: '-DRGENGC_CHECK_MODE' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'VM_CHECK_MODE', with: { cppflags: '-DVM_CHECK_MODE' }, timeout-minutes: 5 } - { uses: './.github/actions/compilers', name: 'USE_EMBED_CI=0', with: { cppflags: '-DUSE_EMBED_CI=0' }, timeout-minutes: 5 } - - { uses: './.github/actions/compilers', name: 'USE_FLONUM=0', with: { cppflags: '-DUSE_FLONUM=0', append_configure: '--disable-yjit' }, timeout-minutes: 5 } + - { uses: './.github/actions/compilers', name: 'USE_FLONUM=0', with: { cppflags: '-DUSE_FLONUM=0', append_configure: '--disable-yjit --disable-zjit' }, timeout-minutes: 5 } compileX: name: 'omnibus compilations, #10' diff --git a/zjit.c b/zjit.c index df44821fe54643..05fb3e1f028ff6 100644 --- a/zjit.c +++ b/zjit.c @@ -31,6 +31,10 @@ #include +// This build config impacts the pointer tagging scheme and we only want to +// support one scheme for simplicity. +STATIC_ASSERT(pointer_tagging_scheme, USE_FLONUM); + enum zjit_struct_offsets { ISEQ_BODY_OFFSET_PARAM = offsetof(struct rb_iseq_constant_body, param) }; From 5f444cba4741b2ff0e1e95f4a179329b1ebc74a2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 14:21:59 +0900 Subject: [PATCH 335/367] [ruby/ipaddr] v1.2.8 https://github.com/ruby/ipaddr/commit/93ef50bc04 --- lib/ipaddr.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 513b7778a92f0b..725ff309d27f48 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -41,7 +41,7 @@ class IPAddr # The version string - VERSION = "1.2.7" + VERSION = "1.2.8" # 32 bit mask for IPv4 IN4MASK = 0xffffffff From ab80d05fef77c20abb77b08b0c14882a8772ecfb Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 05:23:39 +0000 Subject: [PATCH 336/367] Update default gems list at 5f444cba4741b2ff0e1e95f4a17932 [ci skip] --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 8ea6e93b1b5acc..0c2682d8b96aa3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -250,6 +250,7 @@ The following default gems are updated. * io-console 0.8.1 * io-nonblock 0.3.2 * io-wait 0.4.0.dev +* ipaddr 1.2.8 * json 2.17.1 * net-http 0.8.0 * openssl 4.0.0.pre From 4523a905327d8438f845f5a75822225ccd041beb Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 14:28:10 +0900 Subject: [PATCH 337/367] [ruby/date] v3.5.1 https://github.com/ruby/date/commit/1d0aadc295 --- ext/date/lib/date.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb index b33f6e65f466b0..0cb763017f22c0 100644 --- a/ext/date/lib/date.rb +++ b/ext/date/lib/date.rb @@ -4,7 +4,7 @@ require 'date_core' class Date - VERSION = "3.5.0" # :nodoc: + VERSION = "3.5.1" # :nodoc: # call-seq: # infinite? -> false From 74376fefbb2d79d9c2df355445689058fab828e6 Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 05:30:06 +0000 Subject: [PATCH 338/367] Update default gems list at 4523a905327d8438f845f5a7582222 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 0c2682d8b96aa3..8c1d15142f71b4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -240,7 +240,7 @@ The following default gems are updated. * RubyGems 4.0.1 * bundler 4.0.1 -* date 3.5.0 +* date 3.5.1 * digest 3.2.1 * english 0.8.1 * erb 6.0.0 From bbee62abbd26e3bf526dbbfddd17d72b81402a72 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 14:48:32 +0900 Subject: [PATCH 339/367] We don't need to check the latest release of pathname Pathname is now embedded class of Ruby --- tool/sync_default_gems.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 780f923b55598e..6945c6cdce52a6 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -427,7 +427,7 @@ def sync_default_gems(gem) end def check_prerelease_version(gem) - return if ["rubygems", "mmtk", "cgi"].include?(gem) + return if ["rubygems", "mmtk", "cgi", "pathname"].include?(gem) require "net/https" require "json" From 842f91aec09e419481af6358657e06973f2410c2 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 23:15:47 -0600 Subject: [PATCH 340/367] [ruby/stringio] [DOC] Tweaks for StringIO#getc (https://github.com/ruby/stringio/pull/189) https://github.com/ruby/stringio/commit/e3d16d30ed --- doc/stringio/getc.rdoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/stringio/getc.rdoc b/doc/stringio/getc.rdoc index c021789c911b8d..b2ab46843c8466 100644 --- a/doc/stringio/getc.rdoc +++ b/doc/stringio/getc.rdoc @@ -12,9 +12,9 @@ Returns +nil+ if at end-of-stream: Returns characters, not bytes: - strio = StringIO.new('тест') - strio.getc # => "т" - strio.getc # => "е" + strio = StringIO.new('Привет') + strio.getc # => "П" + strio.getc # => "р" strio = StringIO.new('こんにちは') strio.getc # => "こ" @@ -31,4 +31,4 @@ in other cases that need not be true: strio.pos = 5 # => 5 # At third byte of second character; returns byte. strio.getc # => "\x93" -Related: StringIO.getbyte. +Related: #getbyte, #putc, #ungetc. From 668fe01182df185c3592061e22087b6454132fec Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 5 Dec 2025 16:07:45 +0000 Subject: [PATCH 341/367] [ruby/stringio] [DOC] Fix link https://github.com/ruby/stringio/commit/e2d24ae8d7 --- doc/stringio/stringio.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/stringio/stringio.md b/doc/stringio/stringio.md index aebfa6d6f1f4c2..8931d1c30c7d79 100644 --- a/doc/stringio/stringio.md +++ b/doc/stringio/stringio.md @@ -324,7 +324,7 @@ a binary stream may not be changed to text. ### Encodings -A stream has an encoding; see the [encodings document][encodings document]. +A stream has an encoding; see [Encodings][encodings document]. The initial encoding for a new or re-opened stream depends on its [data mode][data mode]: @@ -683,7 +683,7 @@ Reading: - #each_codepoint: reads each remaining codepoint, passing it to the block. [bom]: https://en.wikipedia.org/wiki/Byte_order_mark -[encodings document]: https://docs.ruby-lang.org/en/master/encodings_rdoc.html +[encodings document]: https://docs.ruby-lang.org/en/master/language/encodings_rdoc.html [io class]: https://docs.ruby-lang.org/en/master/IO.html [kernel#puts]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-puts [kernel#readline]: https://docs.ruby-lang.org/en/master/Kernel.html#method-i-readline From f623fcc7d069cdfcaf25285c986ed995530a686f Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 17:18:18 -0600 Subject: [PATCH 342/367] [ruby/stringio] [DOC] Tweaks for StringIO.getbyte (https://github.com/ruby/stringio/pull/188) https://github.com/ruby/stringio/commit/66360ee5f1 --- doc/stringio/getbyte.rdoc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/stringio/getbyte.rdoc b/doc/stringio/getbyte.rdoc index 48c334b5252a58..5e524941bca4ae 100644 --- a/doc/stringio/getbyte.rdoc +++ b/doc/stringio/getbyte.rdoc @@ -14,16 +14,18 @@ Returns +nil+ if at end-of-stream: Returns a byte, not a character: - s = 'тест' - s.bytes # => [209, 130, 208, 181, 209, 129, 209, 130] + s = 'Привет' + s.bytes + # => [208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130] strio = StringIO.new(s) - strio.getbyte # => 209 - strio.getbyte # => 130 + strio.getbyte # => 208 + strio.getbyte # => 159 s = 'こんにちは' - s.bytes # => [227, 129, 147, 227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175] + s.bytes + # => [227, 129, 147, 227, 130, 147, 227, 129, 171, 227, 129, 161, 227, 129, 175] strio = StringIO.new(s) strio.getbyte # => 227 strio.getbyte # => 129 -Related: StringIO.getc. +Related: #each_byte, #ungetbyte, #getc. From 5bc65db5550719a808858af361d5152931469c88 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 6 Dec 2025 17:20:13 -0600 Subject: [PATCH 343/367] [ruby/stringio] [DOC] Tweaks for StringIO#gets (https://github.com/ruby/stringio/pull/190) https://github.com/ruby/stringio/commit/77209fac20 --- doc/stringio/gets.rdoc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/stringio/gets.rdoc b/doc/stringio/gets.rdoc index 892c3feb53a9bf..bbefeb008ae245 100644 --- a/doc/stringio/gets.rdoc +++ b/doc/stringio/gets.rdoc @@ -19,10 +19,10 @@ With no arguments given, reads a line using the default record separator strio.eof? # => true strio.gets # => nil - strio = StringIO.new('тест') # Four 2-byte characters. + strio = StringIO.new('Привет') # Six 2-byte characters strio.pos # => 0 - strio.gets # => "тест" - strio.pos # => 8 + strio.gets # => "Привет" + strio.pos # => 12 Argument +sep+ @@ -67,11 +67,11 @@ but in other cases the position may be anywhere: The position need not be at a character boundary: - strio = StringIO.new('тест') # Four 2-byte characters. - strio.pos = 2 # At beginning of second character. - strio.gets # => "ест" - strio.pos = 3 # In middle of second character. - strio.gets # => "\xB5ст" + strio = StringIO.new('Привет') # Six 2-byte characters. + strio.pos = 2 # At beginning of second character. + strio.gets # => "ривет" + strio.pos = 3 # In middle of second character. + strio.gets # => "\x80ивет" Special Record Separators @@ -95,4 +95,5 @@ removes the trailing newline (if any) from the returned line: strio.gets # => "First line\n" strio.gets(chomp: true) # => "Second line" -Related: StringIO.each_line. +Related: #each_line, #readlines, +{Kernel#puts}[rdoc-ref:Kernel#puts]. From b4a1f170583eb5553814261c311b183cbb390ba2 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 15 Nov 2025 07:47:46 -0600 Subject: [PATCH 344/367] [ruby/stringio] [DOC] Tweaks for StringIO#each_line (https://github.com/ruby/stringio/pull/165) Adds to "Position": pos inside a character. Makes a couple of minor corrections. --------- https://github.com/ruby/stringio/commit/ff332abafa Co-authored-by: Sutou Kouhei --- doc/stringio/each_line.md | 189 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 doc/stringio/each_line.md diff --git a/doc/stringio/each_line.md b/doc/stringio/each_line.md new file mode 100644 index 00000000000000..e29640a12a9203 --- /dev/null +++ b/doc/stringio/each_line.md @@ -0,0 +1,189 @@ +With a block given calls the block with each remaining line (see "Position" below) in the stream; +returns `self`. + +Leaves stream position at end-of-stream. + +**No Arguments** + +With no arguments given, +reads lines using the default record separator +(global variable `$/`, whose initial value is `"\n"`). + +```ruby +strio = StringIO.new(TEXT) +strio.each_line {|line| p line } +strio.eof? # => true +``` + +Output: + +``` +"First line\n" +"Second line\n" +"\n" +"Fourth line\n" +"Fifth line\n" +``` + +**Argument `sep`** + +With only string argument `sep` given, +reads lines using that string as the record separator: + +```ruby +strio = StringIO.new(TEXT) +strio.each_line(' ') {|line| p line } +``` + +Output: + +``` +"First " +"line\nSecond " +"line\n\nFourth " +"line\nFifth " +"line\n" +``` + +**Argument `limit`** + +With only integer argument `limit` given, +reads lines using the default record separator; +also limits the size (in characters) of each line to the given limit: + +```ruby +strio = StringIO.new(TEXT) +strio.each_line(10) {|line| p line } +``` + +Output: + +``` +"First line" +"\n" +"Second lin" +"e\n" +"\n" +"Fourth lin" +"e\n" +"Fifth line" +"\n" +``` + +**Arguments `sep` and `limit`** + +With arguments `sep` and `limit` both given, +honors both: + +```ruby +strio = StringIO.new(TEXT) +strio.each_line(' ', 10) {|line| p line } +``` + +Output: + +``` +"First " +"line\nSecon" +"d " +"line\n\nFour" +"th " +"line\nFifth" +" " +"line\n" +``` + +**Position** + +As stated above, method `each` _remaining_ line in the stream. + +In the examples above each `strio` object starts with its position at beginning-of-stream; +but in other cases the position may be anywhere (see StringIO#pos): + +```ruby +strio = StringIO.new(TEXT) +strio.pos = 30 # Set stream position to character 30. +strio.each_line {|line| p line } +``` + +Output: + +``` +" line\n" +"Fifth line\n" +``` + +In all the examples above, the stream position is at the beginning of a character; +in other cases, that need not be so: + +```ruby +s = 'こんにちは' # Five 3-byte characters. +strio = StringIO.new(s) +strio.pos = 3 # At beginning of second character. +strio.each_line {|line| p line } +strio.pos = 4 # At second byte of second character. +strio.each_line {|line| p line } +strio.pos = 5 # At third byte of second character. +strio.each_line {|line| p line } +``` + +Output: + +``` +"んにちは" +"\x82\x93にちは" +"\x93にちは" +``` + +**Special Record Separators** + +Like some methods in class `IO`, StringIO.each honors two special record separators; +see {Special Line Separators}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Special+Line+Separator+Values]. + +```ruby +strio = StringIO.new(TEXT) +strio.each_line('') {|line| p line } # Read as paragraphs (separated by blank lines). +``` + +Output: + +``` +"First line\nSecond line\n\n" +"Fourth line\nFifth line\n" +``` + +```ruby +strio = StringIO.new(TEXT) +strio.each_line(nil) {|line| p line } # "Slurp"; read it all. +``` + +Output: + +``` +"First line\nSecond line\n\nFourth line\nFifth line\n" +``` + +**Keyword Argument `chomp`** + +With keyword argument `chomp` given as `true` (the default is `false`), +removes trailing newline (if any) from each line: + +```ruby +strio = StringIO.new(TEXT) +strio.each_line(chomp: true) {|line| p line } +``` + +Output: + +``` +"First line" +"Second line" +"" +"Fourth line" +"Fifth line" +``` + +With no block given, returns a new {Enumerator}[https://docs.ruby-lang.org/en/master/Enumerator.html]. + + +Related: StringIO.each_byte, StringIO.each_char, StringIO.each_codepoint. From 6ec5c5f1c8ac438752b53061d0cdd68a7e4ca8f9 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 15 Nov 2025 07:48:35 -0600 Subject: [PATCH 345/367] [ruby/stringio] [DOC] Doc for StringIO.size (https://github.com/ruby/stringio/pull/171) https://github.com/ruby/stringio/commit/95a111017a --- doc/stringio/size.rdoc | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/stringio/size.rdoc diff --git a/doc/stringio/size.rdoc b/doc/stringio/size.rdoc new file mode 100644 index 00000000000000..9323adf8c3783a --- /dev/null +++ b/doc/stringio/size.rdoc @@ -0,0 +1,5 @@ +Returns the number of bytes in the string in +self+: + + StringIO.new('hello').size # => 5 # Five 1-byte characters. + StringIO.new('тест').size # => 8 # Four 2-byte characters. + StringIO.new('こんにちは').size # => 15 # Five 3-byte characters. From 254653db8521618e08aaccaa63efdb42bd6ee84b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 15:38:52 +0900 Subject: [PATCH 346/367] [ruby/win32-registry] v0.1.2 https://github.com/ruby/win32-registry/commit/2a6ab00f67 --- ext/win32/win32-registry.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/win32/win32-registry.gemspec b/ext/win32/win32-registry.gemspec index 9b65af4a5b9976..9bd57bd7d1368f 100644 --- a/ext/win32/win32-registry.gemspec +++ b/ext/win32/win32-registry.gemspec @@ -1,7 +1,7 @@ # frozen_string_literal: true Gem::Specification.new do |spec| spec.name = "win32-registry" - spec.version = "0.1.1" + spec.version = "0.1.2" spec.authors = ["U.Nakamura"] spec.email = ["usa@garbagecollect.jp"] From a8b7fb7ed6990df00514240a247ed9dd97bbbf8b Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 06:40:40 +0000 Subject: [PATCH 347/367] Update default gems list at 254653db8521618e08aaccaa63efdb [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 8c1d15142f71b4..769a9c53d66062 100644 --- a/NEWS.md +++ b/NEWS.md @@ -234,7 +234,7 @@ releases. The following default gem is added. -* win32-registry 0.1.1 +* win32-registry 0.1.2 The following default gems are updated. From 8e87f201cf54b112642ed0421ddabd57336d672e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 15:42:31 +0900 Subject: [PATCH 348/367] [ruby/optparse] v0.8.1 https://github.com/ruby/optparse/commit/f2e31e81a5 --- lib/optparse.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/optparse.rb b/lib/optparse.rb index aae73c86b32787..97178e284bd53c 100644 --- a/lib/optparse.rb +++ b/lib/optparse.rb @@ -426,7 +426,7 @@ # class OptionParser # The version string - VERSION = "0.8.0" + VERSION = "0.8.1" # An alias for compatibility Version = VERSION From 492b1c73b35ab97d17d48ddd868e61cb76703dac Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 06:44:02 +0000 Subject: [PATCH 349/367] Update default gems list at 8e87f201cf54b112642ed0421ddabd [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 769a9c53d66062..6cf5c8a450a0e4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -254,7 +254,7 @@ The following default gems are updated. * json 2.17.1 * net-http 0.8.0 * openssl 4.0.0.pre -* optparse 0.8.0 +* optparse 0.8.1 * pp 0.6.3 * prism 1.6.0 * psych 5.3.0 From 81fbdff8fdf2ae7afb2fa19319ff7d40379521fe Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 24 Nov 2025 16:50:29 -0800 Subject: [PATCH 350/367] Use continuation bit in concurrent set This refactors the concurrent set to examine and reserve a slot via CAS with the hash, before then doing the same with the key. This allows us to use an extra bit from the hash as a "continuation bit" which marks whether we have ever probed past this key while inserting. When that bit isn't set on deletion we can clear the field instead of placing a tombstone. --- bootstraptest/test_ractor.rb | 3 + concurrent_set.c | 173 +++++++++++++++++++++++++---------- 2 files changed, 129 insertions(+), 47 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 81dc2d6b8d04be..13c4652d3760a8 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1494,6 +1494,9 @@ class C unless a[i].equal?(b[i]) raise [a[i], b[i]].inspect end + unless a[i] == i.to_s + raise [i, a[i], b[i]].inspect + end end :ok } diff --git a/concurrent_set.c b/concurrent_set.c index 3aa61507aaa7d2..eebf7df9cb6407 100644 --- a/concurrent_set.c +++ b/concurrent_set.c @@ -4,6 +4,9 @@ #include "ruby/atomic.h" #include "vm_sync.h" +#define CONCURRENT_SET_CONTINUATION_BIT ((VALUE)1 << (sizeof(VALUE) * CHAR_BIT - 1)) +#define CONCURRENT_SET_HASH_MASK (~CONCURRENT_SET_CONTINUATION_BIT) + enum concurrent_set_special_values { CONCURRENT_SET_EMPTY, CONCURRENT_SET_DELETED, @@ -24,6 +27,36 @@ struct concurrent_set { struct concurrent_set_entry *entries; }; +static void +concurrent_set_mark_continuation(struct concurrent_set_entry *entry, VALUE curr_hash_and_flags) +{ + if (curr_hash_and_flags & CONCURRENT_SET_CONTINUATION_BIT) return; + + RUBY_ASSERT((curr_hash_and_flags & CONCURRENT_SET_HASH_MASK) != 0); + + VALUE new_hash = curr_hash_and_flags | CONCURRENT_SET_CONTINUATION_BIT; + VALUE prev_hash = rbimpl_atomic_value_cas(&entry->hash, curr_hash_and_flags, new_hash, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); + + // At the moment we only expect to be racing concurrently against another + // thread also setting the continuation bit. + // In the future if deletion is concurrent this will need adjusting + RUBY_ASSERT(prev_hash == curr_hash_and_flags || prev_hash == new_hash); + (void)prev_hash; +} + +static VALUE +concurrent_set_hash(const struct concurrent_set *set, VALUE key) +{ + VALUE hash = set->funcs->hash(key); + hash &= CONCURRENT_SET_HASH_MASK; + if (hash == 0) { + hash ^= CONCURRENT_SET_HASH_MASK; + } + RUBY_ASSERT(hash != 0); + RUBY_ASSERT(!(hash & CONCURRENT_SET_CONTINUATION_BIT)); + return hash; +} + static void concurrent_set_free(void *ptr) { @@ -141,13 +174,9 @@ concurrent_set_try_resize_without_locking(VALUE old_set_obj, VALUE *set_obj_ptr) if (key < CONCURRENT_SET_SPECIAL_VALUE_COUNT) continue; if (!RB_SPECIAL_CONST_P(key) && rb_objspace_garbage_object_p(key)) continue; - VALUE hash = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_RELAXED); - if (hash == 0) { - // Either in-progress insert or extremely unlikely 0 hash. - // Re-calculate the hash. - hash = old_set->funcs->hash(key); - } - RUBY_ASSERT(hash == old_set->funcs->hash(key)); + VALUE hash = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_RELAXED) & CONCURRENT_SET_HASH_MASK; + RUBY_ASSERT(hash != 0); + RUBY_ASSERT(hash == concurrent_set_hash(old_set, key)); // Insert key into new_set. struct concurrent_set_probe probe; @@ -156,20 +185,19 @@ concurrent_set_try_resize_without_locking(VALUE old_set_obj, VALUE *set_obj_ptr) while (true) { struct concurrent_set_entry *entry = &new_set->entries[idx]; - if (entry->key == CONCURRENT_SET_EMPTY) { - new_set->size++; + if (entry->hash == CONCURRENT_SET_EMPTY) { + RUBY_ASSERT(entry->key == CONCURRENT_SET_EMPTY); + new_set->size++; RUBY_ASSERT(new_set->size <= new_set->capacity / 2); - RUBY_ASSERT(entry->hash == 0); entry->key = key; entry->hash = hash; break; } - else { - RUBY_ASSERT(entry->key >= CONCURRENT_SET_SPECIAL_VALUE_COUNT); - } + RUBY_ASSERT(entry->key >= CONCURRENT_SET_SPECIAL_VALUE_COUNT); + entry->hash |= CONCURRENT_SET_CONTINUATION_BIT; idx = concurrent_set_probe_next(&probe); } } @@ -203,20 +231,37 @@ rb_concurrent_set_find(VALUE *set_obj_ptr, VALUE key) if (hash == 0) { // We don't need to recompute the hash on every retry because it should // never change. - hash = set->funcs->hash(key); + hash = concurrent_set_hash(set, key); } - RUBY_ASSERT(hash == set->funcs->hash(key)); + RUBY_ASSERT(hash == concurrent_set_hash(set, key)); struct concurrent_set_probe probe; int idx = concurrent_set_probe_start(&probe, set, hash); while (true) { struct concurrent_set_entry *entry = &set->entries[idx]; + VALUE curr_hash_and_flags = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_ACQUIRE); + VALUE curr_hash = curr_hash_and_flags & CONCURRENT_SET_HASH_MASK; + bool continuation = curr_hash_and_flags & CONCURRENT_SET_CONTINUATION_BIT; + + if (curr_hash_and_flags == CONCURRENT_SET_EMPTY) { + return 0; + } + + if (curr_hash != hash) { + if (!continuation) { + return 0; + } + idx = concurrent_set_probe_next(&probe); + continue; + } + VALUE curr_key = rbimpl_atomic_value_load(&entry->key, RBIMPL_ATOMIC_ACQUIRE); switch (curr_key) { case CONCURRENT_SET_EMPTY: - return 0; + // In-progress insert: hash written but key not yet + break; case CONCURRENT_SET_DELETED: break; case CONCURRENT_SET_MOVED: @@ -225,13 +270,9 @@ rb_concurrent_set_find(VALUE *set_obj_ptr, VALUE key) goto retry; default: { - VALUE curr_hash = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_RELAXED); - if (curr_hash != 0 && curr_hash != hash) break; - if (UNLIKELY(!RB_SPECIAL_CONST_P(curr_key) && rb_objspace_garbage_object_p(curr_key))) { // This is a weakref set, so after marking but before sweeping is complete we may find a matching garbage object. - // Skip it and mark it as deleted. - rbimpl_atomic_value_cas(&entry->key, curr_key, CONCURRENT_SET_DELETED, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); + // Skip it and let the GC pass clean it up break; } @@ -241,6 +282,10 @@ rb_concurrent_set_find(VALUE *set_obj_ptr, VALUE key) return curr_key; } + if (!continuation) { + return 0; + } + break; } } @@ -266,23 +311,49 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) if (hash == 0) { // We don't need to recompute the hash on every retry because it should // never change. - hash = set->funcs->hash(key); + hash = concurrent_set_hash(set, key); } - RUBY_ASSERT(hash == set->funcs->hash(key)); + RUBY_ASSERT(hash == concurrent_set_hash(set, key)); struct concurrent_set_probe probe; int idx = concurrent_set_probe_start(&probe, set, hash); while (true) { struct concurrent_set_entry *entry = &set->entries[idx]; + VALUE curr_hash_and_flags = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_ACQUIRE); + VALUE curr_hash = curr_hash_and_flags & CONCURRENT_SET_HASH_MASK; + + if (curr_hash_and_flags == CONCURRENT_SET_EMPTY) { + if (!inserting) { + key = set->funcs->create(key, data); + RUBY_ASSERT(hash == concurrent_set_hash(set, key)); + inserting = true; + } + + // Reserve this slot for our hash value + curr_hash_and_flags = rbimpl_atomic_value_cas(&entry->hash, CONCURRENT_SET_EMPTY, hash, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); + if (curr_hash_and_flags != CONCURRENT_SET_EMPTY) { + // Lost race, retry same slot to check winner's hash + continue; + } + + // CAS succeeded, so these are the values stored + curr_hash_and_flags = hash; + curr_hash = hash; + // Fall through to try to claim key + } + + if (curr_hash != hash) { + goto probe_next; + } + VALUE curr_key = rbimpl_atomic_value_load(&entry->key, RBIMPL_ATOMIC_ACQUIRE); switch (curr_key) { - case CONCURRENT_SET_EMPTY: { - // Not in set + case CONCURRENT_SET_EMPTY: if (!inserting) { key = set->funcs->create(key, data); - RUBY_ASSERT(hash == set->funcs->hash(key)); + RUBY_ASSERT(hash == concurrent_set_hash(set, key)); inserting = true; } @@ -293,14 +364,11 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) if (UNLIKELY(load_factor_reached)) { concurrent_set_try_resize(set_obj, set_obj_ptr); - goto retry; } - curr_key = rbimpl_atomic_value_cas(&entry->key, CONCURRENT_SET_EMPTY, key, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); - if (curr_key == CONCURRENT_SET_EMPTY) { - rbimpl_atomic_value_store(&entry->hash, hash, RBIMPL_ATOMIC_RELAXED); - + VALUE prev_key = rbimpl_atomic_value_cas(&entry->key, CONCURRENT_SET_EMPTY, key, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); + if (prev_key == CONCURRENT_SET_EMPTY) { RB_GC_GUARD(set_obj); return key; } @@ -311,22 +379,16 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) // Another thread won the race, try again at the same location. continue; } - } case CONCURRENT_SET_DELETED: break; case CONCURRENT_SET_MOVED: // Wait RB_VM_LOCKING(); - goto retry; - default: { - VALUE curr_hash = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_RELAXED); - if (curr_hash != 0 && curr_hash != hash) break; - + default: if (UNLIKELY(!RB_SPECIAL_CONST_P(curr_key) && rb_objspace_garbage_object_p(curr_key))) { // This is a weakref set, so after marking but before sweeping is complete we may find a matching garbage object. - // Skip it and mark it as deleted. - rbimpl_atomic_value_cas(&entry->key, curr_key, CONCURRENT_SET_DELETED, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); + // Skip it and let the GC pass clean it up break; } @@ -343,15 +405,33 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) return curr_key; } - break; - } } + probe_next: + RUBY_ASSERT(curr_hash_and_flags != CONCURRENT_SET_EMPTY); + concurrent_set_mark_continuation(entry, curr_hash_and_flags); idx = concurrent_set_probe_next(&probe); } } +static void +concurrent_set_delete_entry_locked(struct concurrent_set *set, struct concurrent_set_entry *entry) +{ + ASSERT_vm_locking_with_barrier(); + + if (entry->hash & CONCURRENT_SET_CONTINUATION_BIT) { + entry->hash = CONCURRENT_SET_CONTINUATION_BIT; + entry->key = CONCURRENT_SET_DELETED; + set->deleted_entries++; + } + else { + entry->hash = CONCURRENT_SET_EMPTY; + entry->key = CONCURRENT_SET_EMPTY; + set->size--; + } +} + VALUE rb_concurrent_set_delete_by_identity(VALUE set_obj, VALUE key) { @@ -359,7 +439,7 @@ rb_concurrent_set_delete_by_identity(VALUE set_obj, VALUE key) struct concurrent_set *set = RTYPEDDATA_GET_DATA(set_obj); - VALUE hash = set->funcs->hash(key); + VALUE hash = concurrent_set_hash(set, key); struct concurrent_set_probe probe; int idx = concurrent_set_probe_start(&probe, set, hash); @@ -379,8 +459,8 @@ rb_concurrent_set_delete_by_identity(VALUE set_obj, VALUE key) break; default: if (key == curr_key) { - entry->key = CONCURRENT_SET_DELETED; - set->deleted_entries++; + RUBY_ASSERT((entry->hash & CONCURRENT_SET_HASH_MASK) == hash); + concurrent_set_delete_entry_locked(set, entry); return curr_key; } break; @@ -399,7 +479,7 @@ rb_concurrent_set_foreach_with_replace(VALUE set_obj, int (*callback)(VALUE *key for (unsigned int i = 0; i < set->capacity; i++) { struct concurrent_set_entry *entry = &set->entries[i]; - VALUE key = set->entries[i].key; + VALUE key = entry->key; switch (key) { case CONCURRENT_SET_EMPTY: @@ -414,8 +494,7 @@ rb_concurrent_set_foreach_with_replace(VALUE set_obj, int (*callback)(VALUE *key case ST_STOP: return; case ST_DELETE: - set->entries[i].key = CONCURRENT_SET_DELETED; - set->deleted_entries++; + concurrent_set_delete_entry_locked(set, entry); break; } break; From 462df17f8689e9ee87a45b88bb3283fd339e1247 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Tue, 9 Dec 2025 02:17:38 -0800 Subject: [PATCH 351/367] Attempt to reuse garbage slots in concurrent hash This removes all allocations from the find_or_insert loop, which requires us to start the search over after calling the provided create function. In exchange that allows us to assume that all concurrent threads insert will get the same view of the GC state, and so should all be attempting to clear and reuse a slot containing a garbage object. --- concurrent_set.c | 85 ++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/concurrent_set.c b/concurrent_set.c index eebf7df9cb6407..376b20d7d4e45c 100644 --- a/concurrent_set.c +++ b/concurrent_set.c @@ -222,11 +222,14 @@ rb_concurrent_set_find(VALUE *set_obj_ptr, VALUE key) VALUE set_obj; VALUE hash = 0; + struct concurrent_set *set; + struct concurrent_set_probe probe; + int idx; retry: set_obj = rbimpl_atomic_value_load(set_obj_ptr, RBIMPL_ATOMIC_ACQUIRE); RUBY_ASSERT(set_obj); - struct concurrent_set *set = RTYPEDDATA_GET_DATA(set_obj); + set = RTYPEDDATA_GET_DATA(set_obj); if (hash == 0) { // We don't need to recompute the hash on every retry because it should @@ -235,8 +238,7 @@ rb_concurrent_set_find(VALUE *set_obj_ptr, VALUE key) } RUBY_ASSERT(hash == concurrent_set_hash(set, key)); - struct concurrent_set_probe probe; - int idx = concurrent_set_probe_start(&probe, set, hash); + idx = concurrent_set_probe_start(&probe, set, hash); while (true) { struct concurrent_set_entry *entry = &set->entries[idx]; @@ -299,37 +301,43 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) { RUBY_ASSERT(key >= CONCURRENT_SET_SPECIAL_VALUE_COUNT); - bool inserting = false; - VALUE set_obj; - VALUE hash = 0; + // First attempt to find + { + VALUE result = rb_concurrent_set_find(set_obj_ptr, key); + if (result) return result; + } - retry: - set_obj = rbimpl_atomic_value_load(set_obj_ptr, RBIMPL_ATOMIC_ACQUIRE); + // First time we need to call create, and store the hash + VALUE set_obj = rbimpl_atomic_value_load(set_obj_ptr, RBIMPL_ATOMIC_ACQUIRE); RUBY_ASSERT(set_obj); + struct concurrent_set *set = RTYPEDDATA_GET_DATA(set_obj); + key = set->funcs->create(key, data); + VALUE hash = concurrent_set_hash(set, key); + + struct concurrent_set_probe probe; + int idx; + + goto start_search; + +retry: + // On retries we only need to load the hash object + set_obj = rbimpl_atomic_value_load(set_obj_ptr, RBIMPL_ATOMIC_ACQUIRE); + RUBY_ASSERT(set_obj); + set = RTYPEDDATA_GET_DATA(set_obj); - if (hash == 0) { - // We don't need to recompute the hash on every retry because it should - // never change. - hash = concurrent_set_hash(set, key); - } RUBY_ASSERT(hash == concurrent_set_hash(set, key)); - struct concurrent_set_probe probe; - int idx = concurrent_set_probe_start(&probe, set, hash); +start_search: + idx = concurrent_set_probe_start(&probe, set, hash); while (true) { struct concurrent_set_entry *entry = &set->entries[idx]; VALUE curr_hash_and_flags = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_ACQUIRE); VALUE curr_hash = curr_hash_and_flags & CONCURRENT_SET_HASH_MASK; + bool continuation = curr_hash_and_flags & CONCURRENT_SET_CONTINUATION_BIT; if (curr_hash_and_flags == CONCURRENT_SET_EMPTY) { - if (!inserting) { - key = set->funcs->create(key, data); - RUBY_ASSERT(hash == concurrent_set_hash(set, key)); - inserting = true; - } - // Reserve this slot for our hash value curr_hash_and_flags = rbimpl_atomic_value_cas(&entry->hash, CONCURRENT_SET_EMPTY, hash, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); if (curr_hash_and_flags != CONCURRENT_SET_EMPTY) { @@ -340,6 +348,7 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) // CAS succeeded, so these are the values stored curr_hash_and_flags = hash; curr_hash = hash; + // Fall through to try to claim key } @@ -350,13 +359,7 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) VALUE curr_key = rbimpl_atomic_value_load(&entry->key, RBIMPL_ATOMIC_ACQUIRE); switch (curr_key) { - case CONCURRENT_SET_EMPTY: - if (!inserting) { - key = set->funcs->create(key, data); - RUBY_ASSERT(hash == concurrent_set_hash(set, key)); - inserting = true; - } - + case CONCURRENT_SET_EMPTY: { rb_atomic_t prev_size = rbimpl_atomic_fetch_add(&set->size, 1, RBIMPL_ATOMIC_RELAXED); // Load_factor reached at 75% full. ex: prev_size: 32, capacity: 64, load_factor: 50%. @@ -369,6 +372,7 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) VALUE prev_key = rbimpl_atomic_value_cas(&entry->key, CONCURRENT_SET_EMPTY, key, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); if (prev_key == CONCURRENT_SET_EMPTY) { + RUBY_ASSERT(rb_concurrent_set_find(set_obj_ptr, key) == key); RB_GC_GUARD(set_obj); return key; } @@ -379,6 +383,7 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) // Another thread won the race, try again at the same location. continue; } + } case CONCURRENT_SET_DELETED: break; case CONCURRENT_SET_MOVED: @@ -386,22 +391,26 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data) RB_VM_LOCKING(); goto retry; default: + // We're never GC during our search + // If the continuation bit wasn't set at the start of our search, + // any concurrent find with the same hash value would also look at + // this location and try to swap curr_key if (UNLIKELY(!RB_SPECIAL_CONST_P(curr_key) && rb_objspace_garbage_object_p(curr_key))) { - // This is a weakref set, so after marking but before sweeping is complete we may find a matching garbage object. - // Skip it and let the GC pass clean it up - break; + if (continuation) { + goto probe_next; + } + rbimpl_atomic_value_cas(&entry->key, curr_key, CONCURRENT_SET_EMPTY, RBIMPL_ATOMIC_RELEASE, RBIMPL_ATOMIC_RELAXED); + continue; } if (set->funcs->cmp(key, curr_key)) { - // We've found a match. + // We've found a live match. RB_GC_GUARD(set_obj); - if (inserting) { - // We created key using set->funcs->create, but we didn't end - // up inserting it into the set. Free it here to prevent memory - // leaks. - if (set->funcs->free) set->funcs->free(key); - } + // We created key using set->funcs->create, but we didn't end + // up inserting it into the set. Free it here to prevent memory + // leaks. + if (set->funcs->free) set->funcs->free(key); return curr_key; } From 375025a3864fc944dc9f42909a6c5386c749d5fd Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Tue, 9 Dec 2025 15:42:04 -0800 Subject: [PATCH 352/367] Fix typo and shadowing --- concurrent_set.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/concurrent_set.c b/concurrent_set.c index 376b20d7d4e45c..234b6408b6b938 100644 --- a/concurrent_set.c +++ b/concurrent_set.c @@ -167,14 +167,14 @@ concurrent_set_try_resize_without_locking(VALUE old_set_obj, VALUE *set_obj_ptr) struct concurrent_set *new_set = RTYPEDDATA_GET_DATA(new_set_obj); for (int i = 0; i < old_capacity; i++) { - struct concurrent_set_entry *entry = &old_entries[i]; - VALUE key = rbimpl_atomic_value_exchange(&entry->key, CONCURRENT_SET_MOVED, RBIMPL_ATOMIC_ACQUIRE); + struct concurrent_set_entry *old_entry = &old_entries[i]; + VALUE key = rbimpl_atomic_value_exchange(&old_entry->key, CONCURRENT_SET_MOVED, RBIMPL_ATOMIC_ACQUIRE); RUBY_ASSERT(key != CONCURRENT_SET_MOVED); if (key < CONCURRENT_SET_SPECIAL_VALUE_COUNT) continue; if (!RB_SPECIAL_CONST_P(key) && rb_objspace_garbage_object_p(key)) continue; - VALUE hash = rbimpl_atomic_value_load(&entry->hash, RBIMPL_ATOMIC_RELAXED) & CONCURRENT_SET_HASH_MASK; + VALUE hash = rbimpl_atomic_value_load(&old_entry->hash, RBIMPL_ATOMIC_RELAXED) & CONCURRENT_SET_HASH_MASK; RUBY_ASSERT(hash != 0); RUBY_ASSERT(hash == concurrent_set_hash(old_set, key)); From 14ff851185bb8ff399e98b74cc107302a4e08e18 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 24 Nov 2025 08:48:19 +0100 Subject: [PATCH 353/367] [ruby/forwardable] Simpler and faster check for the delegation fastpath Fix: https://github.com/ruby/forwardable/issues/35 [Bug #21708] Trying to compile code to check if a method can use the delegation fastpath is a bit wasteful and cause `RUPYOPT=-d` to be full of misleading errors. It's simpler and faster to use a simple regexp to do the same check. https://github.com/ruby/forwardable/commit/de1fbd182e --- lib/forwardable.rb | 34 ++++++++++++----------------- lib/forwardable/forwardable.gemspec | 2 +- lib/forwardable/impl.rb | 17 --------------- 3 files changed, 15 insertions(+), 38 deletions(-) delete mode 100644 lib/forwardable/impl.rb diff --git a/lib/forwardable.rb b/lib/forwardable.rb index 76267c2cd18c57..040f467b231a0d 100644 --- a/lib/forwardable.rb +++ b/lib/forwardable.rb @@ -109,8 +109,6 @@ # +delegate.rb+. # module Forwardable - require 'forwardable/impl' - # Version of +forwardable.rb+ VERSION = "1.3.3" VERSION.freeze @@ -206,37 +204,33 @@ def self._delegator_method(obj, accessor, method, ali) if Module === obj ? obj.method_defined?(accessor) || obj.private_method_defined?(accessor) : obj.respond_to?(accessor, true) - accessor = "#{accessor}()" + accessor = "(#{accessor}())" end args = RUBY_VERSION >= '2.7' ? '...' : '*args, &block' method_call = ".__send__(:#{method}, #{args})" - if _valid_method?(method) + if method.match?(/\A[_a-zA-Z]\w*[?!]?\z/) loc, = caller_locations(2,1) pre = "_ =" mesg = "#{Module === obj ? obj : obj.class}\##{ali} at #{loc.path}:#{loc.lineno} forwarding to private method " - method_call = "#{<<-"begin;"}\n#{<<-"end;".chomp}" - begin; - unless defined? _.#{method} - ::Kernel.warn #{mesg.dump}"\#{_.class}"'##{method}', uplevel: 1 - _#{method_call} - else - _.#{method}(#{args}) - end - end; + method_call = <<~RUBY.chomp + if defined?(_.#{method}) + _.#{method}(#{args}) + else + ::Kernel.warn #{mesg.dump}"\#{_.class}"'##{method}', uplevel: 1 + _#{method_call} + end + RUBY end - _compile_method("#{<<-"begin;"}\n#{<<-"end;"}", __FILE__, __LINE__+1) - begin; + eval(<<~RUBY, nil, __FILE__, __LINE__ + 1) proc do def #{ali}(#{args}) - #{pre} - begin - #{accessor} - end#{method_call} + #{pre}#{accessor} + #{method_call} end end - end; + RUBY end end diff --git a/lib/forwardable/forwardable.gemspec b/lib/forwardable/forwardable.gemspec index 9ad59c5f8a8830..1b539bcfcb3daf 100644 --- a/lib/forwardable/forwardable.gemspec +++ b/lib/forwardable/forwardable.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.licenses = ["Ruby", "BSD-2-Clause"] spec.required_ruby_version = '>= 2.4.0' - spec.files = ["forwardable.gemspec", "lib/forwardable.rb", "lib/forwardable/impl.rb"] + spec.files = ["forwardable.gemspec", "lib/forwardable.rb"] spec.bindir = "exe" spec.executables = [] spec.require_paths = ["lib"] diff --git a/lib/forwardable/impl.rb b/lib/forwardable/impl.rb deleted file mode 100644 index 0322c136db4a64..00000000000000 --- a/lib/forwardable/impl.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Forwardable - # :stopdoc: - - def self._valid_method?(method) - catch {|tag| - eval("BEGIN{throw tag}; ().#{method}", binding, __FILE__, __LINE__) - } - rescue SyntaxError - false - else - true - end - - def self._compile_method(src, file, line) - eval(src, nil, file, line) - end -end From e8a55274f202df1cfddc25aa14da34ba5a0e538d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 10 Dec 2025 16:07:17 +0900 Subject: [PATCH 354/367] [ruby/forwardable] v1.4.0 https://github.com/ruby/forwardable/commit/0257b590c2 --- lib/forwardable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forwardable.rb b/lib/forwardable.rb index 040f467b231a0d..175d6d9c6b6858 100644 --- a/lib/forwardable.rb +++ b/lib/forwardable.rb @@ -110,7 +110,7 @@ # module Forwardable # Version of +forwardable.rb+ - VERSION = "1.3.3" + VERSION = "1.4.0" VERSION.freeze # Version for backward compatibility From ef4490d6b166fc1fd802d58faceeb038737afc7e Mon Sep 17 00:00:00 2001 From: git Date: Wed, 10 Dec 2025 07:09:05 +0000 Subject: [PATCH 355/367] Update default gems list at e8a55274f202df1cfddc25aa14da34 [ci skip] --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 6cf5c8a450a0e4..e7d1da919e631d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -247,6 +247,7 @@ The following default gems are updated. * etc 1.4.6 * fcntl 1.3.0 * fileutils 1.8.0 +* forwardable 1.4.0 * io-console 0.8.1 * io-nonblock 0.3.2 * io-wait 0.4.0.dev From c5608ab4d79f409aaa469b4f4a20962a0ba4a688 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 3 Dec 2025 15:35:03 +0100 Subject: [PATCH 356/367] Monitor: avoid repeated calls to `rb_fiber_current()` That call is surprisingly expensive, so trying doing it once in `#synchronize` and then passing the fiber to enter and exit saves quite a few cycles. --- ext/monitor/monitor.c | 103 +++++++++++++++++++++++++++++------------- thread_sync.c | 51 ++++++++++----------- 2 files changed, 95 insertions(+), 59 deletions(-) diff --git a/ext/monitor/monitor.c b/ext/monitor/monitor.c index 819c6e76996f61..3106c0302378f1 100644 --- a/ext/monitor/monitor.c +++ b/ext/monitor/monitor.c @@ -50,10 +50,10 @@ monitor_ptr(VALUE monitor) return mc; } -static int -mc_owner_p(struct rb_monitor *mc) +static bool +mc_owner_p(struct rb_monitor *mc, VALUE current_fiber) { - return mc->owner == rb_fiber_current(); + return mc->owner == current_fiber; } /* @@ -67,17 +67,44 @@ monitor_try_enter(VALUE monitor) { struct rb_monitor *mc = monitor_ptr(monitor); - if (!mc_owner_p(mc)) { + VALUE current_fiber = rb_fiber_current(); + if (!mc_owner_p(mc, current_fiber)) { if (!rb_mutex_trylock(mc->mutex)) { return Qfalse; } - RB_OBJ_WRITE(monitor, &mc->owner, rb_fiber_current()); + RB_OBJ_WRITE(monitor, &mc->owner, current_fiber); mc->count = 0; } mc->count += 1; return Qtrue; } + +struct monitor_args { + VALUE monitor; + struct rb_monitor *mc; + VALUE current_fiber; +}; + +static inline void +monitor_args_init(struct monitor_args *args, VALUE monitor) +{ + args->monitor = monitor; + args->mc = monitor_ptr(monitor); + args->current_fiber = rb_fiber_current(); +} + +static void +monitor_enter0(struct monitor_args *args) +{ + if (!mc_owner_p(args->mc, args->current_fiber)) { + rb_mutex_lock(args->mc->mutex); + RB_OBJ_WRITE(args->monitor, &args->mc->owner, args->current_fiber); + args->mc->count = 0; + } + args->mc->count++; +} + /* * call-seq: * enter -> nil @@ -87,27 +114,44 @@ monitor_try_enter(VALUE monitor) static VALUE monitor_enter(VALUE monitor) { - struct rb_monitor *mc = monitor_ptr(monitor); - if (!mc_owner_p(mc)) { - rb_mutex_lock(mc->mutex); - RB_OBJ_WRITE(monitor, &mc->owner, rb_fiber_current()); - mc->count = 0; - } - mc->count++; + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_enter0(&args); return Qnil; } +static inline void +monitor_check_owner0(struct monitor_args *args) +{ + if (!mc_owner_p(args->mc, args->current_fiber)) { + rb_raise(rb_eThreadError, "current fiber not owner"); + } +} + /* :nodoc: */ static VALUE monitor_check_owner(VALUE monitor) { - struct rb_monitor *mc = monitor_ptr(monitor); - if (!mc_owner_p(mc)) { - rb_raise(rb_eThreadError, "current fiber not owner"); - } + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_check_owner0(&args); return Qnil; } +static void +monitor_exit0(struct monitor_args *args) +{ + monitor_check_owner(args->monitor); + + if (args->mc->count <= 0) rb_bug("monitor_exit: count:%d", (int)args->mc->count); + args->mc->count--; + + if (args->mc->count == 0) { + RB_OBJ_WRITE(args->monitor, &args->mc->owner, Qnil); + rb_mutex_unlock(args->mc->mutex); + } +} + /* * call-seq: * exit -> nil @@ -117,17 +161,9 @@ monitor_check_owner(VALUE monitor) static VALUE monitor_exit(VALUE monitor) { - monitor_check_owner(monitor); - - struct rb_monitor *mc = monitor_ptr(monitor); - - if (mc->count <= 0) rb_bug("monitor_exit: count:%d", (int)mc->count); - mc->count--; - - if (mc->count == 0) { - RB_OBJ_WRITE(monitor, &mc->owner, Qnil); - rb_mutex_unlock(mc->mutex); - } + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_exit0(&args); return Qnil; } @@ -144,7 +180,7 @@ static VALUE monitor_owned_p(VALUE monitor) { struct rb_monitor *mc = monitor_ptr(monitor); - return (rb_mutex_locked_p(mc->mutex) && mc_owner_p(mc)) ? Qtrue : Qfalse; + return rb_mutex_locked_p(mc->mutex) && mc_owner_p(mc, rb_fiber_current()) ? Qtrue : Qfalse; } static VALUE @@ -210,9 +246,10 @@ monitor_sync_body(VALUE monitor) } static VALUE -monitor_sync_ensure(VALUE monitor) +monitor_sync_ensure(VALUE v_args) { - return monitor_exit(monitor); + monitor_exit0((struct monitor_args *)v_args); + return Qnil; } /* @@ -226,8 +263,10 @@ monitor_sync_ensure(VALUE monitor) static VALUE monitor_synchronize(VALUE monitor) { - monitor_enter(monitor); - return rb_ensure(monitor_sync_body, monitor, monitor_sync_ensure, monitor); + struct monitor_args args; + monitor_args_init(&args, monitor); + monitor_enter0(&args); + return rb_ensure(monitor_sync_body, (VALUE)&args, monitor_sync_ensure, (VALUE)&args); } void diff --git a/thread_sync.c b/thread_sync.c index 8967e24e341bad..9fb1639e9b7dd2 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -239,23 +239,34 @@ thread_mutex_remove(rb_thread_t *thread, rb_mutex_t *mutex) } static void -mutex_set_owner(VALUE self, rb_thread_t *th, rb_fiber_t *fiber) +mutex_set_owner(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber) { - rb_mutex_t *mutex = mutex_ptr(self); - mutex->thread = th->self; mutex->fiber_serial = rb_fiber_serial(fiber); } static void -mutex_locked(rb_thread_t *th, rb_fiber_t *fiber, VALUE self) +mutex_locked(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber) { - rb_mutex_t *mutex = mutex_ptr(self); - - mutex_set_owner(self, th, fiber); + mutex_set_owner(mutex, th, fiber); thread_mutex_insert(th, mutex); } +static inline bool +mutex_trylock(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber) +{ + if (mutex->fiber_serial == 0) { + RUBY_DEBUG_LOG("%p ok", mutex); + + mutex_locked(mutex, th, fiber); + return true; + } + else { + RUBY_DEBUG_LOG("%p ng", mutex); + return false; + } +} + /* * call-seq: * mutex.try_lock -> true or false @@ -266,21 +277,7 @@ mutex_locked(rb_thread_t *th, rb_fiber_t *fiber, VALUE self) VALUE rb_mutex_trylock(VALUE self) { - rb_mutex_t *mutex = mutex_ptr(self); - - if (mutex->fiber_serial == 0) { - RUBY_DEBUG_LOG("%p ok", mutex); - - rb_fiber_t *fiber = GET_EC()->fiber_ptr; - rb_thread_t *th = GET_THREAD(); - - mutex_locked(th, fiber, self); - return Qtrue; - } - else { - RUBY_DEBUG_LOG("%p ng", mutex); - return Qfalse; - } + return RBOOL(mutex_trylock(mutex_ptr(self), GET_THREAD(), GET_EC()->fiber_ptr)); } static VALUE @@ -321,7 +318,7 @@ do_mutex_lock(VALUE self, int interruptible_p) rb_raise(rb_eThreadError, "can't be called from trap context"); } - if (rb_mutex_trylock(self) == Qfalse) { + if (!mutex_trylock(mutex, th, fiber)) { if (mutex->fiber_serial == rb_fiber_serial(fiber)) { rb_raise(rb_eThreadError, "deadlock; recursive locking"); } @@ -342,7 +339,7 @@ do_mutex_lock(VALUE self, int interruptible_p) rb_ensure(call_rb_fiber_scheduler_block, self, delete_from_waitq, (VALUE)&sync_waiter); if (!mutex->fiber_serial) { - mutex_set_owner(self, th, fiber); + mutex_set_owner(mutex, th, fiber); } } else { @@ -383,7 +380,7 @@ do_mutex_lock(VALUE self, int interruptible_p) // unlocked by another thread while sleeping if (!mutex->fiber_serial) { - mutex_set_owner(self, th, fiber); + mutex_set_owner(mutex, th, fiber); } rb_ractor_sleeper_threads_dec(th->ractor); @@ -402,7 +399,7 @@ do_mutex_lock(VALUE self, int interruptible_p) } RUBY_VM_CHECK_INTS_BLOCKING(th->ec); /* may release mutex */ if (!mutex->fiber_serial) { - mutex_set_owner(self, th, fiber); + mutex_set_owner(mutex, th, fiber); } } else { @@ -421,7 +418,7 @@ do_mutex_lock(VALUE self, int interruptible_p) } if (saved_ints) th->ec->interrupt_flag = saved_ints; - if (mutex->fiber_serial == rb_fiber_serial(fiber)) mutex_locked(th, fiber, self); + if (mutex->fiber_serial == rb_fiber_serial(fiber)) mutex_locked(mutex, th, fiber); } RUBY_DEBUG_LOG("%p locked", mutex); From 6777d1012d24b4dd3585809030333ccfb1c46799 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 10 Dec 2025 10:35:37 +0100 Subject: [PATCH 357/367] Modernize Monitor TypedData Make it embedded and compaction aware. --- ext/monitor/monitor.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/ext/monitor/monitor.c b/ext/monitor/monitor.c index 3106c0302378f1..aeb96d7701242d 100644 --- a/ext/monitor/monitor.c +++ b/ext/monitor/monitor.c @@ -4,28 +4,35 @@ struct rb_monitor { long count; - const VALUE owner; - const VALUE mutex; + VALUE owner; + VALUE mutex; }; static void monitor_mark(void *ptr) { struct rb_monitor *mc = ptr; - rb_gc_mark(mc->owner); - rb_gc_mark(mc->mutex); + rb_gc_mark_movable(mc->owner); + rb_gc_mark_movable(mc->mutex); } -static size_t -monitor_memsize(const void *ptr) +static void +monitor_compact(void *ptr) { - return sizeof(struct rb_monitor); + struct rb_monitor *mc = ptr; + mc->owner = rb_gc_location(mc->owner); + mc->mutex = rb_gc_location(mc->mutex); } static const rb_data_type_t monitor_data_type = { - "monitor", - {monitor_mark, RUBY_TYPED_DEFAULT_FREE, monitor_memsize,}, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED + .wrap_struct_name = "monitor", + .function = { + .dmark = monitor_mark, + .dfree = RUBY_TYPED_DEFAULT_FREE, + .dsize = NULL, // Fully embeded + .dcompact = monitor_compact, + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE, }; static VALUE From 023c6d808a85bd8fb711ac3986972618037f12ad Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 10 Dec 2025 10:54:18 +0100 Subject: [PATCH 358/367] [ruby/json] Add a specific error for unescaped newlines It's the most likely control character so it's worth giving a better error message for it. https://github.com/ruby/json/commit/1da3fd9233 --- ext/json/parser/parser.c | 3 +++ test/json/json_parser_test.rb | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index c84c7ed660a06d..45de8d1ff62f1d 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -752,6 +752,9 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser break; default: if ((unsigned char)*pe < 0x20) { + if (*pe == '\n') { + raise_parse_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1); + } raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1); } raise_parse_error_at("invalid escape character in string: %s", state, pe - 1); diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 257e4f1736a2d0..3f0fb7522dc671 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -164,6 +164,14 @@ def test_parse_complex_objects end end + def test_parse_control_chars_in_string + 0.upto(31) do |ord| + assert_raise JSON::ParserError do + parse(%("#{ord.chr}")) + end + end + end + def test_parse_arrays assert_equal([1,2,3], parse('[1,2,3]')) assert_equal([1.2,2,3], parse('[1.2,2,3]')) From 2b66fc763a6657c9a25719c5f70ae7b66abc2232 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 10 Dec 2025 12:42:33 +0100 Subject: [PATCH 359/367] Fix typos in comment of rb_current_execution_context() --- vm_core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm_core.h b/vm_core.h index 0a1914f57a955b..4195ea5e59c9a4 100644 --- a/vm_core.h +++ b/vm_core.h @@ -2084,9 +2084,9 @@ rb_current_execution_context(bool expect_ec) * and the address of the `ruby_current_ec` can be stored on a function * frame. However, this address can be mis-used after native thread * migration of a coroutine. - * 1) Get `ptr =&ruby_current_ec` op NT1 and store it on the frame. + * 1) Get `ptr = &ruby_current_ec` on NT1 and store it on the frame. * 2) Context switch and resume it on the NT2. - * 3) `ptr` is used on NT2 but it accesses to the TLS on NT1. + * 3) `ptr` is used on NT2 but it accesses the TLS of NT1. * This assertion checks such misusage. * * To avoid accidents, `GET_EC()` should be called once on the frame. From ed18a212abe2d92feb441db2b6f084c0c77a65e4 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 10 Dec 2025 11:08:55 -0500 Subject: [PATCH 360/367] ZJIT: Check if shape is too complex before reading ivar by index (#15478) This fixes a crash when the new shape after a transition is too complex; we need to check that it's not complex before trying to read by index. --- zjit/src/hir.rs | 4 ++-- zjit/src/hir/opt_tests.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 4e597db936c345..1771f960c3f952 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3155,8 +3155,6 @@ impl Function { // We're only looking at T_OBJECT so ignore all of the imemo stuff. assert!(recv_type.flags().is_t_object()); next_shape_id = ShapeId(unsafe { rb_shape_transition_add_ivar_no_warnings(class, current_shape_id.0, id) }); - let ivar_result = unsafe { rb_shape_get_iv_index(next_shape_id.0, id, &mut ivar_index) }; - assert!(ivar_result, "New shape must have the ivar index"); // If the VM ran out of shapes, or this class generated too many leaf, // it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table). let new_shape_too_complex = unsafe { rb_jit_shape_too_complex_p(next_shape_id.0) }; @@ -3165,6 +3163,8 @@ impl Function { self.push_insn(block, Insn::IncrCounter(Counter::setivar_fallback_new_shape_too_complex)); self.push_insn_id(block, insn_id); continue; } + let ivar_result = unsafe { rb_shape_get_iv_index(next_shape_id.0, id, &mut ivar_index) }; + assert!(ivar_result, "New shape must have the ivar index"); let current_capacity = unsafe { rb_jit_shape_capacity(current_shape_id.0) }; let next_capacity = unsafe { rb_jit_shape_capacity(next_shape_id.0) }; // If the new shape has a different capacity, or is TOO_COMPLEX, we'll have to diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 3e2db528158090..2e20c8fe8a73e2 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -3932,6 +3932,38 @@ mod hir_opt_tests { "); } + #[test] + fn test_dont_specialize_setivar_when_next_shape_is_too_complex() { + eval(r#" + class AboutToBeTooComplex + def test = @abc = 5 + end + SHAPE_MAX_VARIATIONS = 8 # see shape.h + SHAPE_MAX_VARIATIONS.times do + AboutToBeTooComplex.new.instance_variable_set(:"@a#{_1}", 1) + end + AboutToBeTooComplex.new.test + TEST = AboutToBeTooComplex.instance_method(:test) + "#); + assert_snapshot!(hir_string_proc("TEST"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:Fixnum[5] = Const Value(5) + PatchPoint SingleRactorMode + IncrCounter setivar_fallback_new_shape_too_complex + SetIvar v6, :@abc, v10 + CheckInterrupts + Return v10 + "); + } + #[test] fn test_elide_freeze_with_frozen_hash() { eval(" From 1eb10ca3cb6cff98bb8c0946ed905921586c7d52 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 10 Dec 2025 09:45:09 -0800 Subject: [PATCH 361/367] ZJIT: Exclude failing ruby-bench benchmarks (#15479) --- .github/workflows/zjit-macos.yml | 2 +- .github/workflows/zjit-ubuntu.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index ed559d64e10d05..d6194825a445a7 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -158,7 +158,7 @@ jobs: include: # Test --call-threshold=2 with 2 iterations in total - ruby_opts: '--zjit-call-threshold=2' - bench_opts: '--warmup=1 --bench=1' + bench_opts: '--warmup=1 --bench=1 --excludes=fluentd,psych-load,railsbench' configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow runs-on: macos-14 diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 37a9000d704c5a..64bb0903f8ae7c 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -215,7 +215,7 @@ jobs: include: # Test --call-threshold=2 with 2 iterations in total - ruby_opts: '--zjit-call-threshold=2' - bench_opts: '--warmup=1 --bench=1' + bench_opts: '--warmup=1 --bench=1 --excludes=fluentd,psych-load,railsbench' configure: '--enable-zjit=dev_nodebug' # --enable-zjit=dev is too slow runs-on: ubuntu-24.04 From 41ee65899a7a6c6abca9701957bc84f598d2491a Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 8 Dec 2025 15:54:26 -0800 Subject: [PATCH 362/367] Always treat encoding as TYPEDDATA Encodings are RTypedData, not the deprecated RData. Although the structures are compatible we should use the correct API. --- encoding.c | 12 ++++++------ ext/-test-/string/fstring.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/encoding.c b/encoding.c index 3d5c1d777283fe..d14d371b04c319 100644 --- a/encoding.c +++ b/encoding.c @@ -222,7 +222,7 @@ enc_check_encoding(VALUE obj) if (!is_obj_encoding(obj)) { return -1; } - return check_encoding(RDATA(obj)->data); + return check_encoding(RTYPEDDATA_GET_DATA(obj)); } NORETURN(static void not_encoding(VALUE enc)); @@ -240,7 +240,7 @@ must_encoding(VALUE enc) if (index < 0) { not_encoding(enc); } - return DATA_PTR(enc); + return RTYPEDDATA_GET_DATA(enc); } static rb_encoding * @@ -328,7 +328,7 @@ str_to_encoding(VALUE enc) rb_encoding * rb_to_encoding(VALUE enc) { - if (enc_check_encoding(enc) >= 0) return RDATA(enc)->data; + if (enc_check_encoding(enc) >= 0) return RTYPEDDATA_GET_DATA(enc); return str_to_encoding(enc); } @@ -336,7 +336,7 @@ rb_encoding * rb_find_encoding(VALUE enc) { int idx; - if (enc_check_encoding(enc) >= 0) return RDATA(enc)->data; + if (enc_check_encoding(enc) >= 0) return RTYPEDDATA_GET_DATA(enc); idx = str_find_encindex(enc); if (idx < 0) return NULL; return rb_enc_from_index(idx); @@ -1345,7 +1345,7 @@ enc_inspect(VALUE self) if (!is_data_encoding(self)) { not_encoding(self); } - if (!(enc = DATA_PTR(self)) || rb_enc_from_index(rb_enc_to_index(enc)) != enc) { + if (!(enc = RTYPEDDATA_GET_DATA(self)) || rb_enc_from_index(rb_enc_to_index(enc)) != enc) { rb_raise(rb_eTypeError, "broken Encoding"); } @@ -1368,7 +1368,7 @@ enc_inspect(VALUE self) static VALUE enc_name(VALUE self) { - return rb_fstring_cstr(rb_enc_name((rb_encoding*)DATA_PTR(self))); + return rb_fstring_cstr(rb_enc_name((rb_encoding*)RTYPEDDATA_GET_DATA(self))); } static int diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c index 92a846a93c2e5c..0b5940f28c46ca 100644 --- a/ext/-test-/string/fstring.c +++ b/ext/-test-/string/fstring.c @@ -19,13 +19,13 @@ bug_s_fstring_fake_str(VALUE self) VALUE bug_s_rb_enc_interned_str(VALUE self, VALUE encoding) { - return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data); + return rb_enc_interned_str("foo", 3, NIL_P(encoding) ? NULL : RTYPEDDATA_GET_DATA(encoding)); } VALUE bug_s_rb_enc_str_new(VALUE self, VALUE encoding) { - return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RDATA(encoding)->data); + return rb_enc_str_new("foo", 3, NIL_P(encoding) ? NULL : RTYPEDDATA_GET_DATA(encoding)); } void From 330ddccfee1c986add758384b31b0677935bfa3a Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 10 Dec 2025 10:10:54 -0800 Subject: [PATCH 363/367] ubuntu.yml: Add a ruby-bench job without ZJIT (#15480) --- .github/workflows/ubuntu.yml | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 9f3ad412f7ba6c..46ad7c82128e23 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -202,6 +202,56 @@ jobs: steps: *make-steps + # Separated from `make` job to avoid making it a required status check + ruby-bench: + strategy: + matrix: + include: + # Using the same setup as ZJIT jobs + - bench_opts: '--warmup=1 --bench=1' + + runs-on: ubuntu-24.04 + + if: >- + ${{!(false + || contains(github.event.head_commit.message, '[DOC]') + || contains(github.event.pull_request.title, '[DOC]') + || contains(github.event.pull_request.labels.*.name, 'Documentation') + || (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]') + )}} + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - uses: ./.github/actions/setup/ubuntu + + - uses: ./.github/actions/setup/directories + with: + srcdir: src + builddir: build + makeup: true + + - name: Run configure + run: ../src/configure -C --disable-install-doc --prefix="$(pwd)/install" + + - run: make install + + - name: Checkout ruby-bench + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + repository: ruby/ruby-bench + path: ruby-bench + + - name: Run ruby-bench + run: ruby run_benchmarks.rb -e "ruby::../build/install/bin/ruby" ${{ matrix.bench_opts }} + working-directory: ruby-bench + + - uses: ./.github/actions/slack + with: + label: ruby-bench ${{ matrix.bench_opts }} ${{ matrix.ruby_opts }} + SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot + if: ${{ failure() }} + result: if: ${{ always() }} name: ${{ github.workflow }} result From 17395ca1daef43d63e1548ad4a9d29ee92d1b4a4 Mon Sep 17 00:00:00 2001 From: Tobi Lutke Date: Wed, 10 Dec 2025 14:46:22 -0500 Subject: [PATCH 364/367] ZJIT: Fold LoadField on frozen objects to constants When accessing instance variables from frozen objects via attr_reader/ attr_accessor, fold the LoadField instruction to a constant at compile time. This enables further optimizations like constant propagation. - Add fold_getinstancevariable_frozen optimization in Function::optimize - Check if receiver type has a known ruby_object() that is frozen - Read the field value at compile time and replace with Const instruction - Add 10 unit tests covering various value types (fixnum, string, symbol, nil, true/false) and negative cases (unfrozen, dynamic receiver) --- zjit/src/hir.rs | 14 ++ zjit/src/hir/opt_tests.rs | 404 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 418 insertions(+) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 1771f960c3f952..019ad85d12b571 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3682,6 +3682,20 @@ impl Function { // Don't bother re-inferring the type of val; we already know it. continue; } + + // LoadField is a common pattern for getting fields from Ruby objects, we want to fold away the pattern if the object is frozen. Cast recv to VALUE* and do an unsafe cast at compile time. + Insn::LoadField { recv, offset, return_type, .. } if return_type.is_subtype(types::BasicObject) => { + let recv_type = self.type_of(recv); + match recv_type.ruby_object() { + Some(recv_obj) if recv_obj.is_frozen() => { + let recv_ptr = recv_obj.as_ptr() as *const VALUE; + let val = unsafe { *recv_ptr.add(offset as usize / SIZEOF_VALUE) }; + self.new_insn(Insn::Const { val: Const::Value(val) }) + } + _ => insn_id, + } + } + Insn::AnyToString { str, .. } if self.is_a(str, types::String) => { self.make_equal_to(insn_id, str); // Don't bother re-inferring the type of str; we already know it. diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 2e20c8fe8a73e2..8e2b225b46c008 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -9501,4 +9501,408 @@ mod hir_opt_tests { Return v65 "); } + + // Tests for fold_getinstancevariable_frozen optimization + // This optimization folds LoadField on frozen objects to constants at compile time + + #[test] + fn test_fold_load_field_frozen_constant_object() { + // Basic case: frozen constant object with attr_accessor + eval(" + class TestFrozen + attr_accessor :a + def initialize + @a = 1 + end + end + + FROZEN_OBJ = TestFrozen.new.freeze + + def test = FROZEN_OBJ.a + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, FROZEN_OBJ) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestFrozen@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:Fixnum[1] = Const Value(1) + CheckInterrupts + Return v35 + "); + } + + #[test] + fn test_fold_load_field_frozen_multiple_ivars() { + // Frozen object with multiple instance variables + eval(" + class TestMultiIvars + attr_accessor :a, :b, :c + def initialize + @a = 10 + @b = 20 + @c = 30 + end + end + + MULTI_FROZEN = TestMultiIvars.new.freeze + + def test = MULTI_FROZEN.b + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:14: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, MULTI_FROZEN) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestMultiIvars@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:Fixnum[20] = Const Value(20) + CheckInterrupts + Return v35 + "); + } + + #[test] + fn test_fold_load_field_frozen_string_value() { + // Frozen object with a string ivar + eval(r#" + class TestFrozenStr + attr_accessor :name + def initialize + @name = "hello" + end + end + + FROZEN_STR = TestFrozenStr.new.freeze + + def test = FROZEN_STR.name + test + test + "#); + assert_snapshot!(hir_string("test"), @r#" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, FROZEN_STR) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestFrozenStr@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:String = Const Value(VALUE(0x1050)) + CheckInterrupts + Return v35 + "#); + } + + #[test] + fn test_fold_load_field_frozen_nil_value() { + // Frozen object with nil ivar + eval(" + class TestFrozenNil + attr_accessor :value + def initialize + @value = nil + end + end + + FROZEN_NIL = TestFrozenNil.new.freeze + + def test = FROZEN_NIL.value + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, FROZEN_NIL) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestFrozenNil@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:NilClass = Const Value(nil) + CheckInterrupts + Return v35 + "); + } + + #[test] + fn test_no_fold_load_field_unfrozen_object() { + // Non-frozen object should NOT be folded + eval(" + class TestUnfrozen + attr_accessor :a + def initialize + @a = 1 + end + end + + UNFROZEN_OBJ = TestUnfrozen.new + + def test = UNFROZEN_OBJ.a + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, UNFROZEN_OBJ) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestUnfrozen@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v26:BasicObject = LoadField v25, :@a@0x1049 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_fold_load_field_frozen_with_attr_reader() { + // Using attr_reader instead of attr_accessor + eval(" + class TestAttrReader + attr_reader :value + def initialize(v) + @value = v + end + end + + FROZEN_READER = TestAttrReader.new(42).freeze + + def test = FROZEN_READER.value + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, FROZEN_READER) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestAttrReader@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:Fixnum[42] = Const Value(42) + CheckInterrupts + Return v35 + "); + } + + #[test] + fn test_fold_load_field_frozen_symbol_value() { + // Frozen object with a symbol ivar + eval(" + class TestFrozenSym + attr_accessor :sym + def initialize + @sym = :hello + end + end + + FROZEN_SYM = TestFrozenSym.new.freeze + + def test = FROZEN_SYM.sym + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, FROZEN_SYM) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestFrozenSym@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:StaticSymbol[:hello] = Const Value(:hello) + CheckInterrupts + Return v35 + "); + } + + #[test] + fn test_fold_load_field_frozen_true_false() { + // Frozen object with boolean ivars + eval(" + class TestFrozenBool + attr_accessor :flag + def initialize + @flag = true + end + end + + FROZEN_TRUE = TestFrozenBool.new.freeze + + def test = FROZEN_TRUE.flag + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:12: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, FROZEN_TRUE) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestFrozenBool@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v35:TrueClass = Const Value(true) + CheckInterrupts + Return v35 + "); + } + + #[test] + fn test_no_fold_load_field_dynamic_receiver() { + // Dynamic receiver (not a constant) should NOT be folded even if object is frozen + eval(" + class TestDynamic + attr_accessor :val + def initialize + @val = 99 + end + end + + def test(obj) = obj.val + o = TestDynamic.new.freeze + test o + test o + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:9: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :obj, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(TestDynamic@0x1000, val@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(TestDynamic@0x1000) + v21:HeapObject[class_exact:TestDynamic] = GuardType v9, HeapObject[class_exact:TestDynamic] + v24:HeapObject[class_exact:TestDynamic] = GuardShape v21, 0x1038 + v25:BasicObject = LoadField v24, :@val@0x1039 + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_fold_load_field_frozen_nested_access() { + // Accessing multiple fields from frozen constant in sequence + eval(" + class TestNestedAccess + attr_accessor :x, :y + def initialize + @x = 100 + @y = 200 + end + end + + NESTED_FROZEN = TestNestedAccess.new.freeze + + def test = NESTED_FROZEN.x + NESTED_FROZEN.y + test + test + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:14: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 + v37:Fixnum[100] = Const Value(100) + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) + v31:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) + PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + v36:HeapObject[VALUE(0x1008)] = GuardShape v31, 0x1048 + v38:Fixnum[200] = Const Value(200) + PatchPoint MethodRedefined(Integer@0x1060, +@0x1068, cme:0x1070) + v39:Fixnum[300] = Const Value(300) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v39 + "); + } } From 3bc97b9814d380f2bafaf882b6abef4a3f382353 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 10 Dec 2025 15:21:55 -0500 Subject: [PATCH 365/367] Run zjit-test-update --- zjit/src/hir/opt_tests.rs | 72 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8e2b225b46c008..3a96436bdcdd86 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -9523,7 +9523,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:12: + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9538,9 +9538,9 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozen@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:Fixnum[1] = Const Value(1) + v27:Fixnum[1] = Const Value(1) CheckInterrupts - Return v35 + Return v27 "); } @@ -9564,7 +9564,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:14: + fn test@:13: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9579,9 +9579,9 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestMultiIvars@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:Fixnum[20] = Const Value(20) + v27:Fixnum[20] = Const Value(20) CheckInterrupts - Return v35 + Return v27 "); } @@ -9602,8 +9602,8 @@ mod hir_opt_tests { test test "#); - assert_snapshot!(hir_string("test"), @r#" - fn test@:12: + assert_snapshot!(hir_string("test"), @r" + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9618,10 +9618,10 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenStr@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:String = Const Value(VALUE(0x1050)) + v27:StringExact[VALUE(0x1050)] = Const Value(VALUE(0x1050)) CheckInterrupts - Return v35 - "#); + Return v27 + "); } #[test] @@ -9642,7 +9642,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:12: + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9657,9 +9657,9 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenNil@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:NilClass = Const Value(nil) + v27:NilClass = Const Value(nil) CheckInterrupts - Return v35 + Return v27 "); } @@ -9681,7 +9681,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:12: + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9720,7 +9720,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:12: + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9735,9 +9735,9 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestAttrReader@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:Fixnum[42] = Const Value(42) + v27:Fixnum[42] = Const Value(42) CheckInterrupts - Return v35 + Return v27 "); } @@ -9759,7 +9759,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:12: + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9774,9 +9774,9 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenSym@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:StaticSymbol[:hello] = Const Value(:hello) + v27:StaticSymbol[:hello] = Const Value(VALUE(0x1050)) CheckInterrupts - Return v35 + Return v27 "); } @@ -9798,7 +9798,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:12: + fn test@:11: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9813,9 +9813,9 @@ mod hir_opt_tests { PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenBool@0x1010) v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v35:TrueClass = Const Value(true) + v27:TrueClass = Const Value(true) CheckInterrupts - Return v35 + Return v27 "); } @@ -9875,7 +9875,7 @@ mod hir_opt_tests { test "); assert_snapshot!(hir_string("test"), @r" - fn test@:14: + fn test@:12: bb0(): EntryPoint interpreter v1:BasicObject = LoadSelf @@ -9886,23 +9886,23 @@ mod hir_opt_tests { bb2(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) - v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v28:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) - v25:HeapObject[VALUE(0x1008)] = GuardShape v20, 0x1048 - v37:Fixnum[100] = Const Value(100) + v39:HeapObject[VALUE(0x1008)] = GuardShape v28, 0x1048 + v50:Fixnum[100] = Const Value(100) PatchPoint SingleRactorMode - PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) - v31:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) + PatchPoint StableConstantNames(0x1050, NESTED_FROZEN) + v34:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1058, cme:0x1060) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) - v36:HeapObject[VALUE(0x1008)] = GuardShape v31, 0x1048 - v38:Fixnum[200] = Const Value(200) - PatchPoint MethodRedefined(Integer@0x1060, +@0x1068, cme:0x1070) - v39:Fixnum[300] = Const Value(300) + v42:HeapObject[VALUE(0x1008)] = GuardShape v34, 0x1048 + v51:Fixnum[200] = Const Value(200) + PatchPoint MethodRedefined(Integer@0x1088, +@0x1090, cme:0x1098) + v52:Fixnum[300] = Const Value(300) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v39 + Return v52 "); } } From 72ab0c3fc29a5a4f57bd8fda42c64665ef398db9 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 10 Dec 2025 15:25:18 -0500 Subject: [PATCH 366/367] Add a test that we don't fold non-BasicObject --- zjit/src/hir/opt_tests.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 3a96436bdcdd86..049bf18e88e20e 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -9905,4 +9905,34 @@ mod hir_opt_tests { Return v52 "); } + + #[test] + fn test_dont_fold_load_field_with_primitive_return_type() { + eval(r#" + S = "abc".freeze + def test = S.bytesize + test + "#); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, S) + v20:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) + PatchPoint NoSingletonClass(String@0x1010) + v24:CInt64 = LoadField v20, :len@0x1048 + v25:Fixnum = BoxFixnum v24 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } } From 54379ac8acd57db3fa2af3c9fe97e191da20b421 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 10 Dec 2025 15:26:30 -0500 Subject: [PATCH 367/367] Small cleanups --- zjit/src/hir.rs | 5 ++--- zjit/src/hir/opt_tests.rs | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 019ad85d12b571..82bd6579207d23 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3682,20 +3682,19 @@ impl Function { // Don't bother re-inferring the type of val; we already know it. continue; } - - // LoadField is a common pattern for getting fields from Ruby objects, we want to fold away the pattern if the object is frozen. Cast recv to VALUE* and do an unsafe cast at compile time. Insn::LoadField { recv, offset, return_type, .. } if return_type.is_subtype(types::BasicObject) => { let recv_type = self.type_of(recv); match recv_type.ruby_object() { Some(recv_obj) if recv_obj.is_frozen() => { let recv_ptr = recv_obj.as_ptr() as *const VALUE; + // Rust pointer .add() scales by size_of::() and offset is + // already scaled by SIZEOF_VALUE, so undo that. let val = unsafe { *recv_ptr.add(offset as usize / SIZEOF_VALUE) }; self.new_insn(Insn::Const { val: Const::Value(val) }) } _ => insn_id, } } - Insn::AnyToString { str, .. } if self.is_a(str, types::String) => { self.make_equal_to(insn_id, str); // Don't bother re-inferring the type of str; we already know it. diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 049bf18e88e20e..62ea7c11b0c672 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -9502,9 +9502,6 @@ mod hir_opt_tests { "); } - // Tests for fold_getinstancevariable_frozen optimization - // This optimization folds LoadField on frozen objects to constants at compile time - #[test] fn test_fold_load_field_frozen_constant_object() { // Basic case: frozen constant object with attr_accessor