Skip to content

Module::Build support and Log::Log4perl fixes#328

Merged
fglock merged 20 commits into
masterfrom
feature/module-build-support
Mar 18, 2026
Merged

Module::Build support and Log::Log4perl fixes#328
fglock merged 20 commits into
masterfrom
feature/module-build-support

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 18, 2026

Summary

This PR adds Module::Build support and fixes several bugs discovered while testing Log::Log4perl.

Module::Build Support

  • Add fork-open emulation for Module::Build compatibility
  • Add core modules for Module::Build support (ExtUtils::Manifest, etc.)
  • Fix POSIX::access() and File::Spec::catdir() for ExtUtils::Install
  • Fix CPAN module loading issues for Module::Build

Parser/Compiler Fixes

  • Allow newlines between sigil and variable name (e.g., $\nfoo)
  • Fix parser filehandle detection
  • Fix symbolic array element access with no strict refs

Runtime Fixes

  • Fix sprintf %s treating "INFO" string as Infinity
  • Fix oct() crash on "0" input
  • Fix splitpath() returning filename in directory position
  • Fix system() and exec() to properly flatten array arguments
  • Fix file test on underscore after JAR path
  • Fix File::Path relative paths
  • Fix ${^LAST_FH} to return GLOB reference - was returning GLOB value instead of reference, causing Carp.pm to fail with "Can't use an undefined value as a GLOB reference" under strict refs

AUTOLOAD Fixes (for Log::Log4perl)

  • Fix $AUTOLOAD not being set on method call cache hits
  • Fix 'our' variable declaration being ignored when same variable declared in different packages within same lexical scope

Test Results

Log::Log4perl test improvements:

  • Before fixes: 10/73 test programs failed, 49/637 subtests failed
  • After fixes: 9/73 test programs failed, 26/670 subtests failed

Test plan

  • All unit tests pass (make)
  • Log::Log4perl test suite shows improvement
  • Carp::longmess() works correctly with filehandles

Generated with Devin

fglock and others added 20 commits March 18, 2026 08:09
Perl allows newlines between the sigil ($, @, %, etc.) and the variable
name. For example, this is valid Perl:

    join $
         Log::Log4perl::JOIN_MSG_ARRAY_CHAR, @Args;

This was causing Log::Log4perl to fail to load because Filter.pm has
this exact syntax (split across lines "because of CVS").

The fix uses Whitespace.skipWhitespace() instead of manually skipping
only WHITESPACE tokens, so that NEWLINE tokens are also properly
skipped before the identifier.

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

Co-Authored-By: Devin <noreply@cognition.ai>
The sprintf code was calling value.getDouble() before checking the
conversion type. For strings like "INFO", getDouble() returns Infinity
(because it starts with "INF"), which was then treated as a special
floating-point value even for %s format.

Fixed by handling non-numeric conversions (s, c, p, n) first before
checking for Inf/NaN. This matches Perl's behavior:
- printf "%s", "INFO" now prints "INFO" (was printing "Inf")
- printf "%.3s", "INFO" now prints "INF" (was printing "Inf")

This bug was causing Log::Log4perl's INFO log level to display as "Inf".

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

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

1. oct("0") crashed with "Index 1 out of bounds for length 1"
   - After stripping the leading "0", the code tried to access
     charAt(1) without checking if the string was exhausted
   - Now properly returns 0 for empty string or just "0"

2. splitpath("test.log") returned ("", "test.log", "") instead of
   ("", "", "test.log")
   - When there is no directory separator, the entire path should be
     the filename, not the directory
   - Also fixed: directory now includes trailing separator

These bugs were causing Log::Log4perl::Appender::File to fail.

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

Co-Authored-By: Devin <noreply@cognition.ai>
Two bugs were fixed:

1. Method call cache hit path: When an AUTOLOAD method was cached and
   reused, the $AUTOLOAD variable was not being set before calling the
   method. This caused $AUTOLOAD to be empty or stale on cache hits.

2. our variable declaration in different packages: When our $AUTOLOAD
   (or any our variable) was declared in multiple packages within the
   same lexical scope (e.g., the same file), the second declaration was
   silently ignored. This caused the variable to refer to the wrong
   package global. Fixed by creating a new symbol table entry when an
   our declaration uses a different package than an existing entry.

These fixes improve Log::Log4perl test results from 34/54 failing subtests
in t/024WarnDieCarp.t to 11/73 failing (more tests now pass and more tests
can be discovered).

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

Co-Authored-By: Devin <noreply@cognition.ai>
Add design document (dev/design/log4perl-compatibility.md) that tracks:
- Current test status: 9/73 programs fail, 26/670 subtests fail
- Completed fixes (AUTOLOAD, sprintf, oct, splitpath)
- Remaining issues with analysis and fix strategies
- Priority order for remaining work

Add partial fix for bareword filehandle method calls (IN->clearerr()):
- Added check in RuntimeCode.call() to detect filehandle strings
- Note: Fix is incomplete - isGlobalIODefined() needs to check the
  glob IO slot instead of glob.value to properly detect filehandles

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

Co-Authored-By: Devin <noreply@cognition.ai>
isGlobalIODefined() was checking glob.value for RuntimeIO, but the IO
slot is stored in glob.IO, not glob.value. This caused bareword
filehandle method calls like `IN->clearerr()` to fail with "Can't
locate object method via package 'IN'".

Fixes Log::Log4perl t/020Easy.t and t/051Extra.t tests.

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Initialize $( and $) in GlobalContext with getgid/getegid values
- Add ( and ) to special variable character lists in IdentifierParser
- Remove ( and ) from non-interpolating character list in strings

These variables now work both directly and in string interpolation.
Fixes Log::Log4perl t/033UsrCspec.t tests.

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Tests improved: 8/73 failed (was 9), 23/695 subtests failed (was 28)
- t/033UsrCspec.t now passes all 17 tests
- t/020Easy.t now runs 16/21 tests (was 1)
- Updated remaining issues and priority order

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

Co-Authored-By: Devin <noreply@cognition.ai>
- CompileAssignment.java: Handle local *$probe style dynamic glob names
- Opcodes.java: Add GETHOSTBYNAME opcode (389)
- CompileOperator.java: Add gethostbyname case
- MiscOpcodeHandler.java: Route GETHOSTBYNAME to ExtendedNativeUtils

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

Co-Authored-By: Devin <noreply@cognition.ai>
The NAME slot returns the glob's name without the package prefix.
Required by Carp.pm for error message formatting with filehandles.

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Added *{NAME} slot, local *$dynamic, gethostbyname fixes
- Documented Carp.pm/warnings.pm interaction issue
- Identified potential fix: add $VERSION to warnings.pm

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

Co-Authored-By: Devin <noreply@cognition.ai>
The special variable ${^LAST_FH} was returning a GLOB value instead of
a GLOB reference, which caused failures under "use strict" when using
*{${^LAST_FH}}{NAME} in Carp.pm.

Changes:
- ScalarSpecialVariable.LAST_FH: Return runtimeGlob.createReference()
  instead of the glob value directly, matching native Perl behavior
  where ref(${^LAST_FH}) returns "GLOB"
- ScalarSpecialVariable: Add globDeref() and globDerefNonStrict()
  overrides to delegate to getValueAsScalar(), following the existing
  pattern used by toString(), getInt(), etc.
- ReferenceOperators.ref(): Handle ScalarSpecialVariable by calling
  getValueAsScalar() first, since the proxy type field does not
  reflect the computed value type

This fixes the Carp.pm error "Can not use an undefined value as a GLOB
reference" that occurred when reading from a filehandle and then
calling Carp::longmess().

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

Co-Authored-By: Devin <noreply@cognition.ai>
Document cleaner approach: override scalar() instead of instanceof check
in ReferenceOperators.ref()

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

Co-Authored-By: Devin <noreply@cognition.ai>
Added debugging session notes documenting:
- Root cause: staged changes had removed ForkOpenCompleteException handling
- Impact: tests using subprocess spawning failed/hung
- Fix: restored files from committed version
- Next steps for PR maintenance

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

Co-Authored-By: Devin <noreply@cognition.ai>
In regex patterns, $) typically means end-of-string anchor followed by
closing paren (to close a regex group), NOT the EGID variable. Similarly,
$( is usually $ anchor + opening paren, not the real GID variable.

However, in double-quoted strings, $( and $) SHOULD interpolate to the
actual GID/EGID values.

The fix adds a check in shouldInterpolateVariable() to skip interpolation
of $( and $) specifically in regex context (isRegex flag), while still
allowing interpolation in double-quoted strings.

This fixes the ExifTool.t regression where a regex pattern containing
$)) was incorrectly interpolating $) as the EGID, causing 'Unmatched ('
errors.

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

Co-Authored-By: Devin <noreply@cognition.ai>
The previous fix for sprintf %s treating 'INFO' as Infinity incorrectly
moved %c handling before the Inf/NaN check. This caused sprintf '%c', Inf
to format the character instead of erroring with 'Cannot printf Inf with c'.

The fix now only skips the Inf/NaN check for %s (string) and %p (pointer),
while %c still goes through the Inf/NaN check so it properly errors.

This restores op/infnan.t from 1041/1088 back to 1071/1088 (same as master).

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

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

Added documentation for:
- Fix for $( and $) in regex patterns (ExifTool.t regression)
- Fix for sprintf %c with Inf/NaN regression (op/infnan.t)
- Remaining CI timeout issues (io/through.t, io/crlf_through.t, lib/croak.t)

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

Co-Authored-By: Devin <noreply@cognition.ai>
Commit a82bf0c introduced a 2x performance regression by calling
expensive JNA functions (getgid, getegid) during GlobalContext static
initialization. These calls happen at startup, impacting every
subprocess.

Solution: Move $( and $) to ScalarSpecialVariable with lazy evaluation
so the JNA calls only happen when the variables are actually accessed.

Performance restored: 942 tests/60s (was 468 tests/60s after regression)

Fixes timeout issue in PR #328 (io/through.t, io/crlf_through.t)

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

Co-Authored-By: Devin <noreply@cognition.ai>
Minor startup optimization: defer initialization of less commonly used
modules to be lazy-loaded via XSLoader::load() when they are actually
needed.

- UnicodeNormalize: Now loaded via XSLoader in its Perl file
- TimeHiRes: Now loaded via XSLoader in its Perl file
- JavaSystem: Only needed for java:: integration

Modules that lack XSLoader support in their Perl files are kept:
- UnicodeUCD, TermReadLine, TermReadKey, FileTemp, Encode

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

Co-Authored-By: Devin <noreply@cognition.ai>
Added dev/design/pr328-startup-performance.md with:
- Root cause analysis (JNA calls in static initialization)
- Performance measurements before/after fix
- Detailed fix descriptions
- Startup time breakdown
- Future optimization considerations

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock merged commit c58dc21 into master Mar 18, 2026
2 checks passed
@fglock fglock deleted the feature/module-build-support branch March 18, 2026 16:48
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