fix(require): preserve $@/$! across on_scope_end callbacks#569
fix(require): preserve $@/$! across on_scope_end callbacks#569
Conversation
Mark Work items 1, 2, 3 complete (encoding.pm stub, $SIG handler parity), document the "real" cause of the URI::Find failure (it wasn't a regex-parity bug at all), and add Work item 5 covering the newly-surfaced Unicode::UCD-from-JAR loader bug that's the remaining blocker for `jcpan -t Text::WordCounter`. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Update — phases 2 and 3 of dev/modules/text_wordcounter.mdThis branch now carries three commits, each fixing a distinct 1.
|
When a `use Foo;` inside a module being loaded throws "Can't locate Foo.pm in @inc", doFile's catch block stores that message in $@. The finally block then fires on_scope_end callbacks registered for the file (e.g. by namespace::autoclean). Those callbacks internally use `eval { ... }` blocks, which Perl resets $@ to "" on success, clobbering the inner cause. By the time the outer `require` checks $@, only $! remains, so it falls into the "file not found" branch and reports a misleading "Can't locate <outer-file>.pm in @inc" instead of the real inner failure. Save and restore $@/$! around the callback execution in BHooksEndOfScope.endFileLoad so the inner failure message survives. Reproducible with `jcpan -t Text::WordCounter` (Text::WordCounter uses namespace::autoclean + Moose and pulls in Lingua::ZH::MMSEG; the latter fails because PerlOnJava lacks the `encoding` pragma, but the error was attributed to Text/WordCounter.pm itself). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds `src/main/perl/lib/encoding.pm` so older CPAN modules that still write `use encoding 'utf8';` can load. The `encoding` pragma was deprecated in Perl 5.18 and removed from core in 5.26+; PerlOnJava parses sources as UTF-8 unconditionally, so the source-encoding effects of the pragma are unnecessary. The stub: - accepts the historical import forms (`use encoding;`, `use encoding 'utf8';`, `use encoding 'utf8', STDIN => 'utf8'`); - applies binmode :encoding(LAYER) for filehandles named in the import list (matching the historical pragma's filehandle behaviour); - intentionally does NOT emulate chr/ord/length overrides; - exposes `encoding::name()` returning "utf8" so old probes work. Includes `src/test/resources/unit/encoding_pragma.t` covering all historical import forms and the real-world `use utf8 + use Encode + use encoding` combination from Lingua::ZH::MMSEG. Result: `jcpan -t Lingua::ZH::MMSEG` now passes (3/3 tests). Tracked in dev/modules/text_wordcounter.md (Work item 1). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Real Perl 5 treats two literal strings in %SIG as reserved:
'DEFAULT' - use the OS / default disposition (for __DIE__ /
__WARN__, equivalent to "no handler installed")
'IGNORE' - ignore the signal entirely (effective for __WARN__,
ineffective + warns for __DIE__)
PerlOnJava's WarnDie.catchEval, WarnDie.die and WarnDie.warn were
all unconditionally invoking RuntimeCode.apply() on whatever was in
$SIG{__DIE__} / $SIG{__WARN__}. With the literal string 'DEFAULT'
that became `&main::DEFAULT()`, which croaked with "Undefined
subroutine &main::DEFAULT".
Real-world repro: URI::Find::find() does
local $SIG{__DIE__} = 'DEFAULT';
then calls URI->new() which internally does `eval "require URI::git"`
to look up implementor classes for unknown schemes. URI::git doesn't
exist, so the eval-string failure dispatched through the bogus
'DEFAULT' "handler" and clobbered $@ inside _is_uri, making URIs
with non-standard schemes (git://, svn+ssh://) undetectable. This
caused two of URI-Find's t/Find.t subtests (355, 364) to fail.
Fix: introduce WarnDie.isReservedSigString() and gate the three
handler invocations on !isReservedSigString(sig). For __WARN__,
additionally honour 'IGNORE' by suppressing the STDERR write.
Reproducer:
$ jperl -e 'local $SIG{__DIE__} = "DEFAULT";
eval q{ require NoSuchModule };
print "[\$\@=$@]"'
# before: [$@=Undefined subroutine &main::DEFAULT called ...]
# after : [$@=Can't locate NoSuchModule.pm in @inc ...]
Result: URI::Find passes 578/578 subtests.
Tracked in dev/modules/text_wordcounter.md (Work item 3).
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Mark Work items 1, 2, 3 complete (encoding.pm stub, $SIG handler parity), document the "real" cause of the URI::Find failure (it wasn't a regex-parity bug at all), and add Work item 5 covering the newly-surfaced Unicode::UCD-from-JAR loader bug that's the remaining blocker for `jcpan -t Text::WordCounter`. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
7819bbe to
70495a3
Compare
Summary
When a
use Foo;inside a module being loaded throws "Can't locateFoo.pm in @inc",
ModuleOperators.doFile's catch block stores thatmessage in
$@. Thefinallyblock then callsBHooksEndOfScope.endFileLoad, which fires theon_scope_endcallbacks registered for the file. Some of those callbacks (notably
namespace::autoclean's cleanup routine) internally useeval { ... }blocks, which Perl resets
$@to""on success — clobbering theinner cause. By the time the outer
requirereads$@it is empty,only
$!(ENOENT) is left, and the outer require falls into the"file not found" branch and rebuilds an error message naming the
outer module.
This makes failures look very wrong. For example,
jcpan -t Text::WordCounterreportseven though
blib/lib/Text/WordCounter.pmexists; the real failureis
Can't locate Lingua/ZH/MMSEG.pm in @INCfrom auseinsideText/WordCounter.pm.Reproducer
Fix
BHooksEndOfScope.endFileLoadnow saves$@/$!before invoking theon_scope_end callback loop and restores them in a
finally, so theinner failure message survives.
Test plan
makepasses (all unit-test shards green)jcpan -t Text::WordCounternow reports the real underlyingcause (
Can't locate Lingua/ZH/MMSEG.pm in @INC) instead of amisleading "Text/WordCounter.pm" message
Configuration.javaregenerated byinjectGitInfois includedGenerated with Devin