Skip to content

fix: App::perlbrew CPAN installation support#453

Merged
fglock merged 11 commits into
masterfrom
feature/app-perlbrew-cpan-install
Apr 7, 2026
Merged

fix: App::perlbrew CPAN installation support#453
fglock merged 11 commits into
masterfrom
feature/app-perlbrew-cpan-install

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 7, 2026

Summary

  • Add $Config{startperl} and $Config{sharpbang} to Config.pm — fixes fatal Use of uninitialized value error in ExtUtils::Helpers::Unix::make_executable() which blocked Module::Build::Tiny installation
  • Set $DynaLoader::VERSION = '1.54' in DynaLoader.java — satisfies CPAN dependency version checks
  • Create DynaLoader.pm stub — allows CPAN's on-disk inst_file lookup to find and parse the module version
  • Add dev/modules/app_perlbrew.md — detailed multi-phase plan for full App::perlbrew support

Results after this PR

Module Configure Build Test
Module::Build::Tiny OK OK 32/32 PASS
CPAN::Perl::Releases OK OK 104/105
Devel::PatchPerl OK OK 28/28 (3 programs skip)
File::Which OK OK 14/18
App::perlbrew OK OK Partial (blocked by remaining issues)

Remaining work (tracked in dev/modules/app_perlbrew.md)

  • Support - (stdin) argument in CLI parser (blocks local::lib)
  • Fix Test2::V0 import chain (blocks most perlbrew tests)
  • Add English.pm core module
  • Fix File::Spec::catpath() prototype
  • FindBin $0 handling improvements

Test plan

  • make passes (all unit tests)
  • $Config{startperl} returns correct shebang path
  • $DynaLoader::VERSION returns 1.54
  • Module::Build::Tiny Build.PL succeeds
  • Module::Build::Tiny tests pass (32/32)
  • ./jcpan -t App::perlbrew gets further than before (previously crashed at Module::Build::Tiny)

Generated with Devin

fglock and others added 6 commits April 7, 2026 14:05
- Add startperl and sharpbang to Config.pm so that
  ExtUtils::Helpers::Unix::make_executable() can rewrite shebang lines
  without fatal uninitialized value errors (FATAL warnings).

- Set DynaLoader VERSION = 1.54 in DynaLoader.java so CPAN
  dependency version checks pass.

- Create DynaLoader.pm stub so CPAN on-disk inst_file lookup can
  find and parse the module version. The actual bootstrap/boot_DynaLoader
  methods are still registered by DynaLoader.java at startup.

- Add dev/modules/app_perlbrew.md with detailed installation plan.

These fixes unblock Module::Build::Tiny installation (32/32 tests pass),
which is required by App::perlbrew.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Support '-' (dash) as stdin filename in ArgumentParser.java. When
  -M modules are present, use empty code to avoid blocking (modules
  typically exit during import). This unblocks local::lib installation.

- Add English.pm core module providing human-readable aliases for
  special variables (ERRNO, OSNAME, PID, etc.). Needed by
  App::perlbrew tests.

- Fix File::Spec prototype registrations in FileSpec.java:
  catpath: $$ -> $$$ (3 params: vol, dir, file)
  splitpath: $ -> $;$ (optional no_file param)
  abs2rel: $ -> $;$ (optional base param)
  rel2abs: $ -> $;$ (optional base param)

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
In Perl 5, return inside a map/grep block should exit the enclosing
subroutine, not just the block. This was broken because map/grep blocks
are compiled as separate methods and called through Java static methods
(ListOperators.map/grep), so return-value-based control flow markers
could not propagate through the JVM call stack.

The fix uses a two-layer approach:
1. Map/grep blocks create RETURN control flow markers (return-value based)
2. ListOperators detects these and throws PerlNonLocalReturnException
3. RuntimeCode.apply() catches the exception at subroutine boundaries

The exception propagates through map/grep blocks, eval blocks, and
nested calls, and is consumed at the first regular subroutine boundary.

This fixes Test2::Util::Importer::optimal_import() and many CPAN modules
that use return inside map/grep blocks.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
INIT/CHECK/END blocks in PerlOnJava were executed directly from Java
(SpecialBlock.runInitBlocks() → RuntimeCode.apply()) with no Perl
caller frame above them. This caused caller(1) to return empty,
breaking Test2::IPC which calls context() (using caller(1)) in an
INIT block.

Fix: wrap INIT/CHECK/END block execution in PerlLanguageProvider
with CallerStack.push("main",...)/pop(), matching Perl 5 behavior
where these blocks run from the main program scope.

This unblocks ~40 App::perlbrew tests that use Test2::Tools::Spec
→ Test2::AsyncSubtest → Test2::IPC.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
References taken to localized arrays (e.g., \@argv inside local @argv)
now retain their values after the local scope exits, matching Perl 5
behavior. This fixes the App::perlbrew new() pattern where $opt{args}
= \@argv was losing its values.

Root cause: local @array modified the RuntimeArray in-place, so
references pointed to the same object whose contents got restored.
Fix: swap the global map entry (like local %hash and local $scalar
already do) via new GlobalRuntimeArray class.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Mark Phase 5 (CallerStack, local @array, PerlIO, can(), myarchname)
as completed. Add Phase 6 (re-test) and Phase 7 (stretch goals).

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock force-pushed the feature/app-perlbrew-cpan-install branch from 44bc18b to 8d67bfc Compare April 7, 2026 12:09
fglock and others added 5 commits April 7, 2026 14:43
The RETURN opcode handler unconditionally wrapped return values in
RuntimeControlFlowList when isMapGrepBlock was set, causing even
implicit end-of-block returns (e.g. `map { 42 } @list`) to be
treated as non-local returns.  This threw PerlNonLocalReturnException
which escaped uncaught, crashing the program.

Add RETURN_NONLOCAL opcode for explicit `return` statements inside
map/grep blocks.  The regular RETURN opcode no longer wraps in
RuntimeControlFlowList, fixing the implicit return case while
preserving non-local return semantics for explicit `return`.

Also add a safety catch for PerlNonLocalReturnException in
PerlLanguageProvider.executeCode() so top-level non-local returns
are consumed gracefully instead of crashing.

Fixes op/sort.t interpreter regression (144→169 passing tests).

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The interpreter backend visitChop and visitChomp only processed the
first element of a ListNode, so chop with multiple args only operated
on the first one. Fix by compiling the operand in LIST context, matching
the JVM backend, so RuntimeList.chop()/chomp() iterates all elements
via polymorphic dispatch.

Also fix RuntimeHash.chomp() which incorrectly called chop() instead of
chomp() on hash values.

hashassign.t improved from 248/309 to 278/309.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…eturn values

- Fix hash assignment in scalar context: use LIST_TO_COUNT to count
  flattened RHS elements before assignment, instead of ARRAY_SIZE on
  the hash (which returned bucket info instead of element count)
- Fix RuntimeHash/RuntimeStash.countElements() to return size()*2
  (flattened element count) instead of size() (key count)
- Fix executeListToCount to use polymorphic countElements() for proper
  recursive flattening instead of directly accessing .elements.size()
- Fix executeHashSetFromList to use hash.setFromList() instead of
  RuntimeHash.createHash() for correct warning emission
- Fix list assignment return values: return resultReg (assigned values)
  instead of consumed rhsListReg (empty after setFromList addToArray)
- Fix scalar context count for list assignment to use ARRAY_SIZE on
  resultReg (which has scalarContextSize set) after SET_FROM_LIST

hashassign.t: 282/309 -> 307/309

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When a hash assignment receives exactly one element that is a hash or
array reference, Perl emits 'Reference found where even-sized list
expected' instead of 'Odd number of elements in hash assignment'.

Updated both createHashForAssignment() (JVM backend) and setFromList()
(interpreter backend) to check for this case.

hashassign.t: 307/309 -> 309/309

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… steps

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock merged commit 532aabe into master Apr 7, 2026
2 checks passed
@fglock fglock deleted the feature/app-perlbrew-cpan-install branch April 7, 2026 14:13
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