Skip to content

Fix exit() inside BEGIN blocks#331

Closed
fglock wants to merge 2 commits into
masterfrom
fix/exit-in-begin-block
Closed

Fix exit() inside BEGIN blocks#331
fglock wants to merge 2 commits into
masterfrom
fix/exit-in-begin-block

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 19, 2026

Summary

  • Fix exit() inside BEGIN blocks to exit the program instead of causing a compilation error
  • In Perl, calling exit() inside a BEGIN block terminates the program immediately
  • Previously, jperl would catch the PerlExitException and convert it to a "BEGIN failed--compilation aborted" error, then continue parsing and fail

Changes

  • Modified SpecialBlockParser.runSpecialBlock() to catch PerlExitException separately and re-throw it
  • This allows the exception to propagate to Main.main() which converts it to System.exit()

Test Plan

  • Verified exit 0 inside BEGIN block now exits cleanly with exit code 0
  • Verified plan skip_all in BEGIN blocks (used by Test::More) now works correctly
  • Log4perl tests t/056SyncApp2.t and t/066SQLite.t now skip cleanly without errors

Before/After

Before:

$ ./jperl -e 'BEGIN { exit 0; } print "should not print\n"'
exit 0
BEGIN failed--compilation aborted at -e line 1, near ""

After:

$ ./jperl -e 'BEGIN { exit 0; } print "should not print\n"'
(exits cleanly with code 0)

Generated with Devin

fglock and others added 2 commits March 19, 2026 08:43
…pilation error

In Perl, calling exit() inside a BEGIN block terminates the program immediately.
Previously, jperl would catch the PerlExitException and convert it to a
"BEGIN failed--compilation aborted" error, then continue parsing and fail.

This fix re-throws PerlExitException so it propagates to Main.main() which
converts it to System.exit().

Fixes tests that use `plan skip_all` in BEGIN blocks (e.g., Log4perl's
t/056SyncApp2.t and t/066SQLite.t) - they now skip cleanly without errors.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
…updates

- Document PR #331 fix for exit() inside BEGIN blocks
- Update test results (now 700 tests, 26 failures)
- Mark 'local' package variable bug as verified fixed
- Update t/020Easy.t issue category

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
fglock added a commit that referenced this pull request Mar 19, 2026
…updates

- Document PR #331 fix for exit() inside BEGIN blocks
- Update test results (now 700 tests, 26 failures)
- Mark 'local' package variable bug as verified fixed
- Update t/020Easy.t issue category

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock closed this Mar 19, 2026
fglock added a commit that referenced this pull request Mar 19, 2026
…updates

- Document PR #331 fix for exit() inside BEGIN blocks
- Update test results (now 700 tests, 26 failures)
- Mark 'local' package variable bug as verified fixed
- Update t/020Easy.t issue category

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
fglock added a commit that referenced this pull request Mar 19, 2026
…333)

* Document implementation attempt findings for local package variable bug

Added notes about an attempted approach (skipping our variables from
closure capture) that partially worked but broke Test::More due to
constructor signature mismatches. Documented that the fix needs to
happen at the variable access level, not closure capture level.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>

* Add test cases and document investigation for local package variable bug

- Added test cases in local.t (in __END__ section) for cross-package
  our/local scenarios that currently fail
- Updated design doc with detailed findings from implementation attempts:
  - Approach 1 (skip our from closures) broke constructor signatures
  - Approach 2 (non-lexical our) breaks Test::More/Test2 due to
    BEGIN block interactions
- The fix requires further investigation into how BEGIN blocks
  interact with lexical closure capture

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>

* Fix local package variable bug - our variables now see local changes

This fixes the bug where subroutines don't see 'local'ized values
of 'our' variables set from outside their package.

Root cause: 'our' variables were treated as lexical (stored in JVM
local variable slots), capturing values at subroutine definition time.
This meant 'local $Pkg::Var' changes weren't visible inside subroutines.

The fix:
1. EmitVariable.java: Distinguish between BEGIN-captured 'our' variables
   (in PerlOnJava::_BEGIN_* packages, which should remain lexical for
   compile-to-runtime persistence) and regular 'our' variables (which
   should look up from GlobalVariable at runtime).

2. EmitSubroutine.java and SubroutineParser.java: Use 4-argument
   addVariable() to preserve the original perlPackage when copying
   variables to subroutine scopes.

3. ScopedSymbolTable.java: Add 4-argument addVariable() overload to
   allow explicit package specification.

Test results:
- All existing tests pass
- New cross-package our/local tests pass (54 tests in local.t)
- Test::More/Test2 modules work correctly
- BEGIN block lexical persistence works correctly

See dev/design/local-package-variable-fix.md for detailed analysis.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>

* Fix exit() inside BEGIN blocks to exit program instead of causing compilation error

In Perl, calling exit() inside a BEGIN block terminates the program immediately.
Previously, jperl would catch the PerlExitException and convert it to a
"BEGIN failed--compilation aborted" error, then continue parsing and fail.

This fix re-throws PerlExitException so it propagates to Main.main() which
converts it to System.exit().

Fixes tests that use `plan skip_all` in BEGIN blocks (e.g., Log4perl's
t/056SyncApp2.t and t/066SQLite.t) - they now skip cleanly without errors.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>

* Update log4perl-compatibility.md with exit() in BEGIN fix and status updates

- Document PR #331 fix for exit() inside BEGIN blocks
- Update test results (now 700 tests, 26 failures)
- Mark 'local' package variable bug as verified fixed
- Update t/020Easy.t issue category

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>

---------

Co-authored-by: Devin <noreply@cognition.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant