Commit graph

637 commits

Author SHA1 Message Date
Aaron Patterson
89d89fa49d When reading from stdin, put a wrapper around the IO object
The purpose of this commit is to fix Bug #21188.  We need to detect when
stdin has run in to an EOF case.  Unfortunately we can't _call_ the eof
function on IO because it will block.

Here is a short script to demonstrate the issue:

```ruby
x = STDIN.gets
puts x
puts x.eof?
```

If you run the script, then type some characters (but _NOT_ a newline),
then hit Ctrl-D twice, it will print the input string.  Unfortunately,
calling `eof?` will try to read from STDIN again causing us to need a
3rd Ctrl-D to exit the program.

Before introducing the EOF callback to Prism, the input loop looked
kind of like this:

```ruby
loop do
  str = STDIN.gets
  process(str)

  if str.nil?
    p :DONE
  end
end
```

Which required 3 Ctrl-D to exit.  If we naively changed it to something
like this:

```ruby
loop do
  str = STDIN.gets
  process(str)

  if STDIN.eof?
    p :DONE
  end
end
```

It would still require 3 Ctrl-D because `eof?` would block.  In this
patch, we're wrapping the IO object, checking the buffer for a newline
and length, and then using that to simulate a non-blocking eof? method.

This commit wraps STDIN and emulates a non-blocking `eof` function.

[Bug #21188]
2025-08-04 12:34:33 -07:00
Aaron Patterson
a1403fb7cb Interpolated strings must not be frozen
Strings concatenated with backslash may end up being frozen when they
shouldn't be.  This commit fixes the issue.  It required a change
upstream in Prism, but also a change to the Prism compiler in CRuby.

  https://github.com/ruby/prism/pull/3606

[Bug #21187]
2025-07-22 08:10:55 -07:00
Yusuke Endoh
dc8ae24951 Revert "[Bug #21256] Fix it parameter when splatting and define_method is…"
This reverts commit 265059603c.
2025-07-18 13:28:46 +09:00
Aaron Patterson
86320a5300 Fix compilation for forwarding params in Prism
[Bug #21326]
2025-07-17 18:00:33 -04:00
Aaron Patterson
148db9c80f Fix flipflop line numbers
[ruby-core:121605]
2025-07-17 16:20:29 -04:00
Earlopain
265059603c [Bug #21256] Fix it parameter when splatting and define_method is used
It was failing to set the leads, like numblocks do, causing the result to be wrapped in an array
2025-07-17 16:18:17 -04:00
Aaron Patterson
dabdd81d17 Fix linked list iteration when displaying errors
When a script has problem with the magic comment encoding, we only
display that error.  However, if there are other syntax errors in the
file, the error linked list could contain multiple items.  This lead to
an inconsistency in the "size" field of the linked list, and the actual
items in the linked list.  In other words, the linked list had more than
one item, but the size field was one.

The error display routine would only allocate `size` items, but
iterating the linked list would overrun the array.  This commit changes
the iterator to compare the current node to the "finish" node in the
linked list, no longer assuming the linked list ends with NULL.

[Bug #21461]
2025-07-17 15:01:02 -04:00
Jeremy Evans
353fa6f0ba Avoid allocation for positional splat for literal array keyword argument
If all nodes in the array are safe, then it is safe to avoid
allocation for the positional splat:

```ruby
m(*a, kw: [:a])   # Safe
m(*a, kw: [meth]) # Unsafe
```

This avoids an unnecessary allocation in a Rails method call.
Details: https://github.com/rails/rails/pull/54949/files#r2052645431
2025-06-22 06:43:13 +09:00
Jeremy Evans
1d94a9e1a4 Fix handling of PM_CONSTANT_PATH_NODE node in keyword arguments with ARGS_SPLAT
This was handled correctly in parse.y (NODE_COLON2), but not in
prism. This wasn't caught earlier, because I only added tests for
the optimized case and not the unoptimized case. Add tests for
the unoptimized case.

In code terms:

```ruby
m(*a, kw: lvar::X)     # Does not require allocation for *a
m(*a, kw: method()::X) # Requires allocation for *a
```

This commit fixes the second case when prism is used.
2025-06-21 08:46:24 +09:00
John Hawthorn
db5724894f Fix a missing write barrier to mandatory_only_iseq
Found by wbcheck
2025-06-18 10:18:10 -07:00
Ufuk Kayserilioglu
5ec9a392cd
[Bug #21439] Fix PM_SPLAT_NODE compilation error in for loops (#13597)
[Bug #21439] Fix PM_SPLAT_NODE compilation error in for loops

This commit fixes a crash that occurred when using splat nodes (*) as
the index variable in for loops. The error "Unexpected node type for
index in for node: PM_SPLAT_NODE" was thrown because the compiler
didn't know how to handle splat nodes in this context.

The fix allows code like `for *x in [[1,2], [3,4]]` to compile and
execute correctly, where the splat collects each sub-array.
2025-06-12 11:33:10 -04:00
Peter Zhu
9f91f3617b Fix memory leak with invalid yield in prism
[Bug #21383]

The following script leaks memory:

    10.times do
      20_000.times do
        eval("class C; yield; end")
      rescue SyntaxError
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    16464
    25536
    29424
    35904
    39552
    44576
    46736
    51600
    56096
    59824

After:

    13488
    16160
    18240
    20528
    19760
    21808
    21680
    22272
    22064
    22336
2025-05-29 16:06:49 -04:00
Nick Dower
4921845b61 [Bug #21313] Handle it in rescue and ensure blocks.
The following is crashing for me:

```shell
ruby --yjit --yjit-call-threshold=1 -e '1.tap { raise rescue p it }'

ruby: YJIT has panicked. More info to follow...
thread '<unnamed>' panicked at ./yjit/src/codegen.rs:2402:14:
...
```

It seems `it` sometimes points to the wrong value:

```shell
ruby -e '1.tap { raise rescue p it }'
false

ruby -e '1.tap { begin; raise; ensure; p it; end } rescue nil'
false
```

But only when `$!` is set:

```shell
ruby -e '1.tap { begin; nil; ensure; p it; end }'
1

ruby -e '1.tap { begin; nil; rescue; ensure; p it; end }'
1

ruby -e '1.tap { begin; raise; rescue; ensure; p it; end }'
1
```
2025-05-17 00:17:21 +09:00
Aaron Patterson
203614080f opt_new needs to happen after safe navigation
If safe navigation instructions happen first, we get a stack
inconsistency error.
2025-04-29 13:33:23 -07:00
Max Bernstein
b42c8398ba Don't support blockarg in opt_new
We don't calculate the correct argc so the bookkeeping slot is something
else (unexpected) instead of Qnil (expected).
2025-04-29 09:13:25 -07:00
Aaron Patterson
8ac8225c50 Inline Class#new.
This commit inlines instructions for Class#new.  To make this work, we
added a new YARV instructions, `opt_new`.  `opt_new` checks whether or
not the `new` method is the default allocator method.  If it is, it
allocates the object, and pushes the instance on the stack.  If not, the
instruction jumps to the "slow path" method call instructions.

Old instructions:

```
> ruby --dump=insns -e'Object.new'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,10)>
0000 opt_getconstant_path                   <ic:0 Object>             (   1)[Li]
0002 opt_send_without_block                 <calldata!mid:new, argc:0, ARGS_SIMPLE>
0004 leave
```

New instructions:

```
> ./miniruby --dump=insns -e'Object.new'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,10)>
0000 opt_getconstant_path                   <ic:0 Object>             (   1)[Li]
0002 putnil
0003 swap
0004 opt_new                                <calldata!mid:new, argc:0, ARGS_SIMPLE>, 11
0007 opt_send_without_block                 <calldata!mid:initialize, argc:0, FCALL|ARGS_SIMPLE>
0009 jump                                   14
0011 opt_send_without_block                 <calldata!mid:new, argc:0, ARGS_SIMPLE>
0013 swap
0014 pop
0015 leave
```

This commit speeds up basic object allocation (`Foo.new`) by 60%, but
classes that take keyword parameters see an even bigger benefit because
no hash is allocated when instantiating the object (3x to 6x faster).

Here is an example that uses `Hash.new(capacity: 0)`:

```
> hyperfine "ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'" "./ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'"
Benchmark 1: ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'
  Time (mean ± σ):      1.082 s ±  0.004 s    [User: 1.074 s, System: 0.008 s]
  Range (min … max):    1.076 s …  1.088 s    10 runs

Benchmark 2: ./ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'
  Time (mean ± σ):     627.9 ms ±   3.5 ms    [User: 622.7 ms, System: 4.8 ms]
  Range (min … max):   622.7 ms … 633.2 ms    10 runs

Summary
  ./ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end' ran
    1.72 ± 0.01 times faster than ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'
```

This commit changes the backtrace for `initialize`:

```
aaron@tc ~/g/ruby (inline-new)> cat test.rb
class Foo
  def initialize
    puts caller
  end
end

def hello
  Foo.new
end

hello
aaron@tc ~/g/ruby (inline-new)> ruby -v test.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin24]
test.rb:8:in 'Class#new'
test.rb:8:in 'Object#hello'
test.rb:11:in '<main>'
aaron@tc ~/g/ruby (inline-new)> ./miniruby -v test.rb
ruby 3.5.0dev (2025-03-28T23:59:40Z inline-new c4157884e4) +PRISM [arm64-darwin24]
test.rb:8:in 'Object#hello'
test.rb:11:in '<main>'
```

It also increases memory usage for calls to `new` by 122 bytes:

```
aaron@tc ~/g/ruby (inline-new)> cat test.rb
require "objspace"

class Foo
  def initialize
    puts caller
  end
end

def hello
  Foo.new
end

puts ObjectSpace.memsize_of(RubyVM::InstructionSequence.of(method(:hello)))
aaron@tc ~/g/ruby (inline-new)> make runruby
RUBY_ON_BUG='gdb -x ./.gdbinit -p' ./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems  ./test.rb
656
aaron@tc ~/g/ruby (inline-new)> ruby -v test.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin24]
544
```

Thanks to @ko1 for coming up with this idea!

Co-Authored-By: John Hawthorn <john@hawthorn.email>
2025-04-25 13:46:05 -07:00
Yusuke Endoh
0d6263bd41 Fix coverage measurement for negative line numbers
Fixes [Bug #21220]

Co-Authored-By: Mike Bourgeous <mike@mikebourgeous.com>
Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>
2025-04-09 23:45:54 +09:00
Jean Boussier
432e5fa7e4 prism_compile.c: Avoid zero length allocation
The constant pool may be empty.
2025-04-07 12:00:09 +02:00
Kevin Newton
adaaa7878e Handle void expressions in defined?
[Bug #21029]
2025-03-18 14:44:28 -04:00
Kevin Newton
d492cfdaad
Align defined? implementations between parsers (#12584)
Fixes [Bug #21043]
2025-01-15 22:29:57 -05:00
Kevin Newton
cb419e3912 [PRISM] Handle forwarding inside eval
Fixes [Bug #21031]
2025-01-14 18:41:50 -05:00
Aaron Patterson
63723c8d59 Correctly set node_id on iseq location
The iseq location object has a slot for node ids.  parse.y was correctly
populating that field but Prism was not. This commit populates the field
with the ast node id for that iseq

[Bug #21014]
2025-01-07 17:08:43 -08:00
tomoya ishida
8fb17f86d7
[Bug #21006] Fix defined_expr compilation of method call with parenth… (#12518)
[Bug #21006] Fix defined_expr compilation of method call with parenthesized receiver
2025-01-07 05:06:02 +09:00
Kevin Newton
31905d9e23 Allow escaping from ensures through next
Fixes [Bug #21001]
2025-01-06 13:18:22 -05:00
Kevin Newton
2a1cff40f5 Do not warn unused block when using forwarding
Fixes [Bug #21003]
2025-01-05 19:56:21 -05:00
Kevin Newton
c859e15875 Handle defined? with call chains with blocks
Ensures we can handle expressions like `defined?(a {}.b)`.
2024-12-26 17:34:58 -05:00
Takashi Kokubun
667a0f9f92
Revert "[Bug #20965] Define it like an ordinary argument" (#12418)
Revert "[Bug #20965] Define `it` like an ordinary argument (#12398)"

Reverts ruby/ruby#12398 as per https://bugs.ruby-lang.org/issues/20970#note-6 and https://bugs.ruby-lang.org/issues/20965#note-7.
We need more time to design the intended behavior, and it's too late for Ruby 3.4.
2024-12-23 04:46:50 +00:00
Kevin Newton
391b6746cd Provide Ractor support for **
Fixes [Bug #20916]
2024-12-20 16:45:28 -05:00
Matt Valentine-House
e8d393c8ae [PRISM] Treat it as a local when compiling patterns
Fixes [Bug #20973]
2024-12-20 08:19:57 -05:00
Kazuki Yamaguchi
0397bfa2c8 [PRISM] Fix compiling popped opt_str_uminus and opt_str_freeze
Put a pop as needed. This example currently causes [BUG]:

	$ ruby --parser=prism -e'1.times{"".freeze;nil}'
	-e:1: [BUG] Stack consistency error (sp: 15, bp: 14)
	ruby 3.4.0dev (2024-12-20T00:48:01Z master 978df259ca) +PRISM [x86_64-linux]
2024-12-20 08:19:09 -05:00
Nobuyoshi Nakada
46fec0f62a
[Bug #20965] Define it like an ordinary argument (#12398)
Also fixes [Bug #20955]
2024-12-18 23:12:16 -08:00
Matt Valentine-House
86cf18e01e [PRISM] Recurse use_deconstructed_cache in Alternation Nodes
This fixes the behavioural difference between Prism and parse.y when
evaluating the following code

```ruby
1 in [1 | [1]]
```

Fixes [Bug #20956]
2024-12-17 15:13:53 +00:00
Nobuyoshi Nakada
5c372969ad [Bug #20940] [PRISM] Support NO_COLOR
Also use bold/faint SGR when possible.
2024-12-15 17:15:22 +09:00
Aaron Patterson
e11c86f43e Fix error messages so we don't output an extra line
Before this commit, when a file ended with a newline, the syntax error
message would show an extra line after the file.

For example, the error message would look like this:

```
[aaron@tc-lan-adapter ~/g/ruby (master)]$ echo "foo(" > test.rb
[aaron@tc-lan-adapter ~/g/ruby (master)]$ od -c test.rb
0000000    f   o   o   (  \n
0000005
[aaron@tc-lan-adapter ~/g/ruby (master)]$ wc -l test.rb
       1 test.rb
[aaron@tc-lan-adapter ~/g/ruby (master)]$ ./miniruby test.rb
test.rb: test.rb:1: syntax error found (SyntaxError)
> 1 | foo(
    |     ^ unexpected end-of-input; expected a `)` to close the arguments
  2 |
```

This commit fixes the "end of line" book keeping when printing an error
so that there is no extra line output at the end of the file:

```
[aaron@tc-lan-adapter ~/g/ruby (fix-last-line-error)]$ echo "foo(" | ./miniruby
-: -:1: syntax error found (SyntaxError)
> 1 | foo(
    |     ^ unexpected end-of-input; expected a `)` to close the arguments

[aaron@tc-lan-adapter ~/g/ruby (fix-last-line-error)]$ echo -n "foo(" | ./miniruby
-: -:1: syntax error found (SyntaxError)
> 1 | foo(
    |     ^ unexpected end-of-input; expected a `)` to close the arguments

```

Notice that in the above example, the error message only displays one
line regardless of whether or not the file ended with a newline.

[Bug #20918]
[ruby-core:120035]
2024-12-12 15:28:16 -08:00
John Hawthorn
54f8e6fbbc Use malloc for prism string source
Prism will later free this string via free rather than xfree, so we need
to use malloc rather than xmalloc.

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
Co-authored-by: Matthew Draper <matthew@trebex.net>
2024-12-11 16:30:57 -08:00
Nobuyoshi Nakada
de89bff122
INIT_ANCHOR no longer needed usually 2024-11-28 19:02:56 +09:00
Yusuke Endoh
c0e607cef1 Fix a possible leak of a file descriptor
The same issue as https://github.com/ruby/prism/pull/3246

Coverity Scan found this issue.
2024-11-28 13:26:26 +09:00
Matt Valentine-House
680e060026 [prism/compiler] end_cursor should never be NULL
This fixes a failed assertion reported to SimpleCov

https://github.com/simplecov-ruby/simplecov/issues/1113

This can be repro'd as follows:

1. Create a file `test.rb` containing the following code

```
@foo&.(@bar)
```

2. require it with branch coverage enabled

```
ruby -rcoverage -e "Coverage.start(branches: true); require_relative 'test.rb'"
```

The assertion is failing because the Prism compiler is incorrectly
detecting the start and end cursor position of the call site for the
implicit call .()

This patch replicates the parse.y behaviour of setting the default
end_cursor to be the final closing location of the call node.

This behaviour can be verified against `parse.y` by modifying the test
command as follows:

```
ruby --parser=parse.y -rcoverage -e "Coverage.start(branches: true); require_relative 'test.rb'"
```

[Bug #20866]
2024-11-21 13:51:59 +00:00
Jean byroot Boussier
6deeec5d45
Mark strings returned by Symbol#to_s as chilled (#12065)
* Use FL_USER0 for ELTS_SHARED

This makes space in RString for two bits for chilled strings.

* Mark strings returned by `Symbol#to_s` as chilled

[Feature #20350]

`STR_CHILLED` now spans on two user flags. If one bit is set it
marks a chilled string literal, if it's the other it marks a
`Symbol#to_s` chilled string.

Since it's not possible, and doesn't make much sense to include
debug info when `--debug-frozen-string-literal` is set, we can't
include allocation source, but we can safely include the symbol
name in the warning message, making it much easier to find the source
of the issue.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>

---------

Co-authored-by: Étienne Barrié <etienne.barrie@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2024-11-13 09:20:00 -05:00
Peter Zhu
51ffef2819 Fix memory leak in prism when syntax error in iseq compilation
If there's a syntax error during iseq compilation then prism would leak
memory because it would not free the pm_parse_result_t.

This commit changes pm_iseq_new_with_opt to have a rb_protect to catch
when an error is raised, and return NULL and set error_state to a value
that can be raised by calling rb_jump_tag after memory has been freed.

For example:

    10.times do
      10_000.times do
        eval("/[/=~s")
      rescue SyntaxError
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    39280
    68736
    99232
    128864
    158896
    188208
    217344
    246304
    275376
    304592

After:

    12192
    13200
    14256
    14848
    16000
    16000
    16000
    16064
    17232
    17952
2024-11-08 15:43:41 -05:00
Takashi Kokubun
478e0fc710
YJIT: Replace Array#each only when YJIT is enabled (#11955)
* YJIT: Replace Array#each only when YJIT is enabled

* Add comments about BUILTIN_ATTR_C_TRACE

* Make Ruby Array#each available with --yjit as well

* Fix all paths that expect a C location

* Use method_basic_definition_p to detect patches

* Copy a comment about C_TRACE flag to compilers

* Rephrase a comment about add_yjit_hook

* Give METHOD_ENTRY_BASIC flag to Array#each

* Add --yjit-c-builtin option

* Allow inconsistent source_location in test-spec

* Refactor a check of BUILTIN_ATTR_C_TRACE

* Set METHOD_ENTRY_BASIC without touching vm->running
2024-11-04 11:14:28 -05:00
Étienne Barrié
257f78fb67 Show where mutated chilled strings were allocated
[Feature #20205]

The warning now suggests running with --debug-frozen-string-literal:

```
test.rb:3: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
```

When using --debug-frozen-string-literal, the location where the string
was created is shown:

```
test.rb:3: warning: literal string will be frozen in the future
test.rb:1: info: the string was created here
```

When resurrecting strings and debug mode is not enabled, the overhead is a simple FL_TEST_RAW.
When mutating chilled strings and deprecation warnings are not enabled,
the overhead is a simple warning category enabled check.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-10-21 12:33:02 +02:00
Kevin Newton
e17243d325 Point keyword->table into iseq local table 2024-10-18 14:16:02 -04:00
Peter Zhu
90aa6aefc4 Fix memory leak in syntax error in prism
If there is a syntax error, there could be an ast_node in the result.
This could get leaked if there is a syntax error so parsing could not
complete (parsed is not set to true).

For example, the following script leaks memory:

    10.times do
      10_000.times do
        eval("def foo(...) super(...) {}; end")
      rescue SyntaxError
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    31328
    42768
    53856
    65120
    76208
    86768
    97856
    109120
    120208
    131296

After:

    20944
    20944
    20944
    20944
    20944
    20944
    20944
    20944
    20944
    20944
2024-10-16 14:52:46 -04:00
Nobuyoshi Nakada
9a90cd2284 Cast via uintptr_t function pointer between object pointer
- ISO C forbids conversion of function pointer to object pointer type
- ISO C forbids conversion of object pointer to function pointer type
2024-10-08 23:29:49 +09:00
Kevin Newton
30038656aa Fix intermediate array off-by-one error
Co-authored-by: Adam Hess <HParker@github.com>
2024-10-04 15:04:26 -04:00
Luke Gruber
d592ddd5e6 Fix compile issue with a short-circuited if/unless condition and defined?
This caused an issue when `defined?` was in the `if` condition. Its
instructions weren't appended to the instruction sequence even though it was compiled
if a compile-time known logical short-circuit happened before the `defined?`. The catch table
entry (`defined?` compilation produces a catch table entry) was still on the iseq even though the
instructions weren't there. This caused faulty exception handling in the method.
The solution is to no add the catch table entry for `defined?` after a compile-time known logical
short circuit.

This shouldn't touch much code, it's only for cases like the following,
which can occur during debugging:

    if false && defined?(Some::CONSTANT)
    "more code..."
    end

Fixes [Bug #20501]
2024-10-01 02:12:56 +09:00
Peter Zhu
6b8078cc03 Don't create empty string for interpolation
We don't need to create an empty string for interpolation unless it is
the only element.

For example:

    "#{hello} world"

Before:

    0000 putobject                              ""                        (   1)[Li]
    0002 putself
    0003 opt_send_without_block                 <calldata!mid:hello, argc:0, FCALL|VCALL|ARGS_SIMPLE>
    0005 dup
    0006 objtostring                            <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
    0008 anytostring
    0009 putobject                              " world"
    0011 concatstrings                          3
    0013 leave

After:

    0000 putself                                                          (   1)[Li]
    0001 opt_send_without_block                 <calldata!mid:hello, argc:0, FCALL|VCALL|ARGS_SIMPLE>
    0003 dup
    0004 objtostring                            <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
    0006 anytostring
    0007 putobject                              " world"
    0009 concatstrings                          2
    0011 leave
2024-09-30 09:09:09 -04:00
ydah
1b6c234fec s/reproducable/reproducible/ 2024-09-30 13:04:49 +09:00
Kevin Newton
addb5fea94 Fix up compiling popped ranges with non-optimizable bounds
Fixes [Bug #20763]
2024-09-27 13:43:37 -04:00