Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,6 @@ Otherwise:
| `pp` | neither `Kernel#pp` nor `require "pp"` exists (`require "prettyprint"` does work) |
| `Process.fork` | does not exist — Go's runtime cannot be forked safely |
| `Kernel#fork` | **exists, and does not fork.** `fork { ... }` runs the block **in the same process**, so the “child” mutates the parent's globals, `ENV` and working directory, and returns a synthetic pid (`100001`, counting up) while `Process.pid` is unchanged. Under MRI the block runs in a real child and the parent is untouched. A `SystemExit` raised inside the block unwinds the whole program |
| `END { }` | **crashes the process** with a Go panic when the handler runs at exit — `panic: slice bounds out of range [:1] with capacity 0`, exit 2, where MRI runs the block. For an embedder it takes the host down rather than returning an error from `Run`. Tracked as #805. `BEGIN { }` works. Both used to be refused by the front end, which is how this row came to understate it: a gap turned into a crash and the note still described the gap |
| `Thread#backtrace` for another thread | raises `NotImplementedError`; rbgo keeps one frame stack per VM, not per thread |

## Platforms
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ require (
github.com/go-ruby-pagy/pagy v0.0.0-20260717071719-997d15eee011
github.com/go-ruby-paper-trail/paper-trail v0.0.0-20260717071745-42a249656e5a
github.com/go-ruby-parquet/parquet v0.0.0-20260923211642-bc75bcb9031e
github.com/go-ruby-parser/parser v0.13.0
github.com/go-ruby-parser/parser v0.13.1
github.com/go-ruby-pathname/pathname v0.0.0-20260916100446-0824484b665f
github.com/go-ruby-pg/pg v0.0.0-20260916100523-3831ec811a67
github.com/go-ruby-prawn/prawn v0.0.0-20260927145511-370d5985305c
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -533,8 +533,8 @@ github.com/go-ruby-paper-trail/paper-trail v0.0.0-20260717071745-42a249656e5a h1
github.com/go-ruby-paper-trail/paper-trail v0.0.0-20260717071745-42a249656e5a/go.mod h1:z370KNJcUH8n39yLdlT0Y1QvHukxjsq1XjGpIzvfqWE=
github.com/go-ruby-parquet/parquet v0.0.0-20260923211642-bc75bcb9031e h1:pxhP3nIRxTpj99e74JAVpGj7JQtRSNqLb+B5x2WT5N4=
github.com/go-ruby-parquet/parquet v0.0.0-20260923211642-bc75bcb9031e/go.mod h1:wC+J6qfQ8C0aKgi5qsbR03DYrLLuXXezUBHl967am/E=
github.com/go-ruby-parser/parser v0.13.0 h1:JspoqXWvdv1J1LSknUZmCKWwO5x1xT+1CbhFfRaEtNA=
github.com/go-ruby-parser/parser v0.13.0/go.mod h1:Se+ZvnD/4Vm0RRe1U5kZi0GmPSvSczJJXdZLiB0PlJY=
github.com/go-ruby-parser/parser v0.13.1 h1:gq7QVT7KdRp7nZt1UHsa73YDo77qrD8kGdQY/GVEA6g=
github.com/go-ruby-parser/parser v0.13.1/go.mod h1:Se+ZvnD/4Vm0RRe1U5kZi0GmPSvSczJJXdZLiB0PlJY=
github.com/go-ruby-pathname/pathname v0.0.0-20260916100446-0824484b665f h1:9pt89wlAp12vbCNGFYmbSqp/PJTHRwUx1yNDXW5hWRI=
github.com/go-ruby-pathname/pathname v0.0.0-20260916100446-0824484b665f/go.mod h1:IvW5q5Kyl5l3hrYwiHylOEmk5hpGSo7XqcmUXBHF5H0=
github.com/go-ruby-pg/pg v0.0.0-20260916100523-3831ec811a67 h1:X9MKhEAV60oYoL94tqb11lfLw3yW097mVqfjDdy5PK0=
Expand Down
46 changes: 46 additions & 0 deletions internal/vm/exe_block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) the go-embedded-ruby/ruby authors
//
// SPDX-License-Identifier: BSD-3-Clause

package vm_test

import "testing"

// TestEndBlockRunsInsteadOfCrashing (#805).
//
// `END { }` used to end the process with an uncontained Go panic --
// `slice bounds out of range [:1] with capacity 0`, exit 2 -- when the handler
// ran at exit. For a program that is a crash instead of an answer; for an
// embedder it is worse, because ruby.Run does not return an error, it takes the
// host process down.
//
// The cause was in the parser and is fixed by v0.13.1: `END { }` desugars to
// `at_exit { }`, and the Block it built left SplatIndex at Go's zero value --
// which is a valid index, where the field's "no splat" value is -1. So the
// block claimed a *splat parameter at position 0 with no parameters to hold it,
// and this compiler's rest-binding read past an empty environment.
//
// The test lives HERE as well as upstream because the defect was invisible in
// the AST: the parser's own suite was green throughout, and the whole cost
// landed on the consumer that lowers a Block. A downgrade of the dependency
// brings the crash back, and this is what notices.
func TestEndBlockRunsInsteadOfCrashing(t *testing.T) {
checkCases(t, []runCase{
{`END { puts "E" }`, "E\n"},
// MRI runs END handlers after the program body, in reverse order of
// registration -- the ordering is the part a "did it crash" test misses.
{`puts "main"
END { puts "E" }`, "main\nE\n"},
{`END { puts "first registered" }
END { puts "second registered" }`, "second registered\nfirst registered\n"},
// BEGIN runs before the body and was never broken; it is here so a
// regression in either one is attributable to the right half.
{`BEGIN { puts "B" }
puts "main"
END { puts "E" }`, "B\nmain\nE\n"},
// at_exit is the shape END desugars to, and it also never broke: if this
// one ever fails with the others, the fault is in at_exit, not in END.
{`at_exit { puts "A" }
puts "main"`, "main\nA\n"},
})
}
Loading