Conversation
2bb18eb to
32b08af
Compare
3 tasks
8cc6456 to
37992b0
Compare
fglock
added a commit
that referenced
this pull request
Apr 29, 2026
Append two new sections to dev/design/source_filters.md to record the work done in PR #597: * Phase 6 — Per-Compilation-Unit Scoping Documents the Spiffy filter leak, real Perl's per-PL_compiling semantics, the FilterUtilCall.saveAndReset()/restore() fix in PerlLanguageProvider.executePerlCode, and the deliberate skipSaveRestore carve-out for preprocessWithBeginFilters. * Phase 7 — `do CODEREF` $_ tie support and __FILE__ for filehandle/coderef do Documents Bug A (STORE called on tied $_, fixed via aliasGlobalVariable("main::_", fresh) — matching `local $_`) and Bug B (__FILE__ NPE on do FH / do CODE — fixed by setting actualFileName to the stringified GLOB/CODE). * Known Residual: op/incfilter.t 148/153 Explains the byte-stream framing gap that prevents reaching 153/153, why the gap exists (preprocessWithBeginFilters split point + EOF read framing), and why aligning to exactly 153 is not worth the invasive change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
fglock
added a commit
that referenced
this pull request
Apr 29, 2026
Append two new sections to dev/design/source_filters.md to record the work done in PR #597: * Phase 6 — Per-Compilation-Unit Scoping Documents the Spiffy filter leak, real Perl's per-PL_compiling semantics, the FilterUtilCall.saveAndReset()/restore() fix in PerlLanguageProvider.executePerlCode, and the deliberate skipSaveRestore carve-out for preprocessWithBeginFilters. * Phase 7 — `do CODEREF` $_ tie support and __FILE__ for filehandle/coderef do Documents Bug A (STORE called on tied $_, fixed via aliasGlobalVariable("main::_", fresh) — matching `local $_`) and Bug B (__FILE__ NPE on do FH / do CODE — fixed by setting actualFileName to the stringified GLOB/CODE). * Known Residual: op/incfilter.t 148/153 Explains the byte-stream framing gap that prevents reaching 153/153, why the gap exists (preprocessWithBeginFilters split point + EOF read framing), and why aligning to exactly 153 is not worth the invasive change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
deada09 to
280b294
Compare
fglock
added a commit
that referenced
this pull request
Apr 29, 2026
Append two new sections to dev/design/source_filters.md to record the work done in PR #597: * Phase 6 — Per-Compilation-Unit Scoping Documents the Spiffy filter leak, real Perl's per-PL_compiling semantics, the FilterUtilCall.saveAndReset()/restore() fix in PerlLanguageProvider.executePerlCode, and the deliberate skipSaveRestore carve-out for preprocessWithBeginFilters. * Phase 7 — `do CODEREF` $_ tie support and __FILE__ for filehandle/coderef do Documents Bug A (STORE called on tied $_, fixed via aliasGlobalVariable("main::_", fresh) — matching `local $_`) and Bug B (__FILE__ NPE on do FH / do CODE — fixed by setting actualFileName to the stringified GLOB/CODE). * Known Residual: op/incfilter.t 148/153 Explains the byte-stream framing gap that prevents reaching 153/153, why the gap exists (preprocessWithBeginFilters split point + EOF read framing), and why aligning to exactly 153 is not worth the invasive change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
280b294 to
dbed553
Compare
2 tasks
fglock
added a commit
that referenced
this pull request
Apr 29, 2026
Append two new sections to dev/design/source_filters.md to record the work done in PR #597: * Phase 6 — Per-Compilation-Unit Scoping Documents the Spiffy filter leak, real Perl's per-PL_compiling semantics, the FilterUtilCall.saveAndReset()/restore() fix in PerlLanguageProvider.executePerlCode, and the deliberate skipSaveRestore carve-out for preprocessWithBeginFilters. * Phase 7 — `do CODEREF` $_ tie support and __FILE__ for filehandle/coderef do Documents Bug A (STORE called on tied $_, fixed via aliasGlobalVariable("main::_", fresh) — matching `local $_`) and Bug B (__FILE__ NPE on do FH / do CODE — fixed by setting actualFileName to the stringified GLOB/CODE). * Known Residual: op/incfilter.t 148/153 Explains the byte-stream framing gap that prevents reaching 153/153, why the gap exists (preprocessWithBeginFilters split point + EOF read framing), and why aligning to exactly 153 is not worth the invasive change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
dbed553 to
500dcf3
Compare
fglock
added a commit
that referenced
this pull request
Apr 29, 2026
Append two new sections to dev/design/source_filters.md to record the work done in PR #597: * Phase 6 — Per-Compilation-Unit Scoping Documents the Spiffy filter leak, real Perl's per-PL_compiling semantics, the FilterUtilCall.saveAndReset()/restore() fix in PerlLanguageProvider.executePerlCode, and the deliberate skipSaveRestore carve-out for preprocessWithBeginFilters. * Phase 7 — `do CODEREF` $_ tie support and __FILE__ for filehandle/coderef do Documents Bug A (STORE called on tied $_, fixed via aliasGlobalVariable("main::_", fresh) — matching `local $_`) and Bug B (__FILE__ NPE on do FH / do CODE — fixed by setting actualFileName to the stringified GLOB/CODE). * Known Residual: op/incfilter.t 148/153 Explains the byte-stream framing gap that prevents reaching 153/153, why the gap exists (preprocessWithBeginFilters split point + EOF read framing), and why aligning to exactly 153 is not worth the invasive change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
500dcf3 to
956524c
Compare
Previously src/main/perl/lib/YAML.pm was a 12-line wrapper around
YAML::PP that pinned $VERSION = '1.31' but produced YAML 1.2 output
(`null` instead of `~`, JSON-style booleans, snakeyaml error
messages). This caused 5/12 failures in the YAML-1.31 distribution's
own t/2-scalars.t, and would never satisfy YAML.pm-1.x-specific
assertions in the rest of that test suite.
Vendor the real pure-Perl YAML.pm 1.31 distribution into
src/main/perl/lib/:
- YAML.pm (replaced)
- YAML/{Any,Dumper,Error,Loader,Marshall,Mo,Node,Tag,Types}.pm
- YAML/Loader/Base.pm
- YAML/Dumper/Base.pm
YAML::PP (Java-backed via YAMLPP.java + snakeyaml-engine) is kept
unchanged for users who specifically `use YAML::PP;` and for the
yaml_pp.t unit test. Storable.java already uses snakeyaml-engine
directly and is unaffected.
Verified on the YAML-1.31 distribution itself:
- t/000-compile-modules.t: 12/12 pass (all YAML modules load)
- t/2-scalars.t: 12/12 pass (was 7/12)
Documented in dev/modules/yaml_any_fixes.md.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Drop seven tests from the upstream YAML-1.31 distribution into src/test/resources/module/YAML/t/ so `make test-bundled-modules` exercises the freshly-vendored real YAML.pm against its own test suite: - 2-scalars.t — full scalar round-trip + error matching - dump-synopsis.t — dump example - issue-149.t — load without trailing newline - issue-69.t — inline collection trailing whitespace - numify.t — B::svref_2object numification - roundtrip.t — Test::Deep round-trip on `=` key - rt-90593.t — RT bug regression Verified via `./gradlew testModule --rerun-tasks`: all 7 YAML tests pass. The two pre-existing failures in the suite (Net-SSLeay/t/local/33_x509_create_cert.t and Text-CSV/t/55_combi.t) are unrelated to YAML and present on master. The remaining tests in the upstream YAML distribution (basic-tests.t, bugs-*, dump-*, load-*, marshall.t, etc.) all use `Test::Base` and are blocked on the Spiffy source-filter leak documented as "Bug 1" in dev/modules/yaml_any_fixes.md. They will land alongside that fix. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Real Perl scopes source filters per compilation unit
(PL_compiling / PL_rsfp_filters): each require / do FILE /
string-eval starts with its own initially-empty filter chain, and
the outer chain is restored when the nested compilation finishes.
PerlOnJava kept the active filter stack and the one-shot
"filterInstalledDuringUse" flag in plain ThreadLocals — global per
thread. A filter installed by an outer `use Foo` (whose import()
runs while the parent file is still being parsed) leaked into
whatever module Foo::import() happened to require next. The most
visible victim was Spiffy:
use Spiffy -Base;
field _filters => [qw(norm trim)];
Spiffy::import installs a Filter::Util::Call source filter that
rewrites every "sub …{" to inject "my $self = shift;", then calls
Exporter::export which require'd Exporter::Heavy. The filter
leaked into Exporter::Heavy.pm (visible as the warning "my variable
$self masks earlier declaration in same scope at Exporter/Heavy.pm
line 237") and the parent's filter-installed flag was consumed by
the nested file's own use statements. The result: the parent file
(Test::Base.pm and any other Spiffy -Base user) was parsed
unfiltered, and `field FOO => [qw(...)]` died with `syntax error
near "=> [qw"`.
Fix: introduce FilterUtilCall.saveAndReset() / restore() that
capture the filter stack and flag on entry to a nested compile and
restore them on exit. Wire them into
PerlLanguageProvider.executePerlCode via an outer try/finally so
both normal and exceptional exits restore the parent's state.
One case deliberately opts out: preprocessWithBeginFilters runs a
`BEGIN { filter_add(...) }` prefix through executePerlCode
specifically so the filter install *leaks* into the caller, where
applyFilters() will then apply it to the parent file's remaining
source. That path sets a thread-local `skipSaveRestore` flag
which saveAndReset()/restore() honour as a no-op. Without this
carve-out, op/incfilter.t regressed from 143/153 to 14/153
(file-handle / coderef source filters from @inc stopped working).
Effect on the bundled YAML-1.31 distribution: 27 of the 35
previously blocked t/*.t files now pass cleanly. Added a unit
regression test (src/test/resources/unit/source_filter_scope.t)
plus 27 new module tests under src/test/resources/module/YAML/t/.
Verified:
- make: green (no PerlOnJava unit-test regressions)
- make test-bundled-modules: 317 tests, only 2 pre-existing
unrelated failures (Net-SSLeay/t/local/33_x509_create_cert.t,
Text-CSV/t/55_combi.t — present on master, no diff in those
module dirs). All 34 YAML tests pass.
- perl5_t/t/op/incfilter.t: back to 143/153 baseline.
Documented in dev/modules/yaml_any_fixes.md.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ref do
Two pre-existing bugs in ModuleOperators.do_file's source-generator
paths. Both surfaced cleanly while bringing perl5_t/t/op/incfilter.t
back to baseline; fixing them also lifts the baseline.
1) `do CODEREF`: the generator loop did
GlobalVariable.getGlobalVariable("main::_").set("");
on every iteration to clear $_ before calling the coderef. When
the previous iteration's coderef tied $_ (e.g. a class with only
TIESCALAR/FETCH and no STORE — which is the exact pattern in
incfilter.t lines 261-268) the next iteration's `set("")` invoked
STORE on the tied scalar and died with `Can't locate object
method "STORE" via package "main"`.
Fix: install a fresh untied RuntimeScalar in the $_ slot for each
iteration via aliasGlobalVariable, then aliasGlobalVariable back
to the caller's original $_ at the end. This matches Perl's
`local $_` semantics in pp_require and never invokes STORE on the
user's tied scalar.
2) `do FILEHANDLE` and `do CODEREF`: parsedArgs.fileName was left
null because actualFileName was only set in the scalar-ref branch.
Any reference to __FILE__ inside the do'd source then NPE'd in
StringNode.
Fix: set actualFileName to the stringified filehandle / code ref
("GLOB(0x...)" / "CODE(0x...)") so __FILE__ resolves to a
non-null value matching real Perl's behaviour. Verified against
the `qr/(?:GLOB|CODE)\(0x[0-9a-f]+\)/` assertion in incfilter.t.
Effect on perl5_t/t/op/incfilter.t: 143 → 148 passing
(tied-$_ tests + __FILE__ tests + the refcount-inside-filter test).
The remaining 5 failures are unrelated pre-existing edge cases.
make: green.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Append two new sections to dev/design/source_filters.md to record the work done in PR #597: * Phase 6 — Per-Compilation-Unit Scoping Documents the Spiffy filter leak, real Perl's per-PL_compiling semantics, the FilterUtilCall.saveAndReset()/restore() fix in PerlLanguageProvider.executePerlCode, and the deliberate skipSaveRestore carve-out for preprocessWithBeginFilters. * Phase 7 — `do CODEREF` $_ tie support and __FILE__ for filehandle/coderef do Documents Bug A (STORE called on tied $_, fixed via aliasGlobalVariable("main::_", fresh) — matching `local $_`) and Bug B (__FILE__ NPE on do FH / do CODE — fixed by setting actualFileName to the stringified GLOB/CODE). * Known Residual: op/incfilter.t 148/153 Explains the byte-stream framing gap that prevents reaching 153/153, why the gap exists (preprocessWithBeginFilters split point + EOF read framing), and why aligning to exactly 153 is not worth the invasive change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… ref
When `%$undef_var` or `@$undef_var` is dereferenced, PerlOnJava
returns an autoviv proxy hash/array that holds a back-pointer to
the originating scalar. The proxy is converted into a real
hash/array reference and stored back into the scalar by
`AutovivificationHash.vivify` / `AutovivificationArray.vivify`
the *first* time the proxy is mutated.
`tie` was an exception: it overwrote the proxy's `elements` with a
TieHash/TieArray *without* calling vivify first. Result:
my $h;
tie %$h, 'My::Class';
print ref($h); # was: '' — should be 'HASH'
The tie attached to an orphaned proxy hash; the user's `$h` stayed
undef. This silently broke `YAML::Node::yaml_mapping::new` /
`yaml_sequence::new`, whose pattern is
my $new;
tie %$new, $class, $self;
return $new;
— used by `YAML::Type::blessed::yaml_dump` to wrap blessed hash
refs. Returning undef there cascaded into
`YAML::Dumper::_prewalk` reaching its "Unknown type" fallback and
emitting a spurious "Use of uninitialized value in join or string"
warning during e.g. `jcpan -t URI::git`'s CPAN
`store_persistent_state` YAML dump.
Fix: in `TieOperators.tie`, vivify the autoviv proxy before
replacing its `elements` with the tie backing object. This binds
the user's scalar to a real hash/array ref *first*, so the tie
attaches to a hash/array that is reachable via the user's variable.
Regression test: `src/test/resources/unit/tie_autovivification.t`
covers both hash and array forms plus the YAML::Node-style
"return the tied variable" pattern.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
956524c to
555f2d8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A single PR with two related fixes that together unblock the bulk of the upstream
YAML-1.31test suite (and, more broadly, every CPAN module that uses Spiffy /Test::Base/Filter::Simple/ anyFilter::Util::Call-based source filter).Fix 1 — vendor real
YAML.pm1.31 instead of the YAML::PP shimsrc/main/perl/lib/YAML.pmwas a 12-line wrapper aroundYAML::PPthat pinned$YAML::VERSION = '1.31'but produced YAML 1.2 output (nullinstead of~, JSON-style booleans, snakeyaml error messages). This caused 5/12 sub-test failures in the YAML-1.31 distribution's ownt/2-scalars.tand made YAML.pm-1.x-specific assertions impossible to satisfy.Vendor the actual pure-Perl
YAML.pm1.31 intosrc/main/perl/lib/:YAML.pm(replaced) +YAML/{Any,Dumper,Error,Loader,Marshall,Mo,Node,Tag,Types}.pm+YAML/{Loader,Dumper}/Base.pm.YAML::PP(Java-backed viaYAMLPP.java+snakeyaml-engine) is kept unchanged —use YAML::PP;is its own bundled module with its own unit test (yaml_pp.t), docs, and CPAN consumers.Storable.javaalready usessnakeyaml-enginedirectly and is unaffected.Fix 2 — scope
Filter::Util::Callstate per compilation unitReal Perl scopes source filters per compilation unit (
PL_compiling/PL_rsfp_filters): eachrequire/do FILEstarts with its own initially-empty filter chain, and the outer chain is restored when the nested compilation finishes.PerlOnJava kept the active filter stack and the one-shot
filterInstalledDuringUseflag in plainThreadLocals — global per thread. A filter installed by an outeruse Foo(whoseimport()runs while the parent file is still being parsed) leaked into whatever moduleFoo::import()happened torequirenext.Most visible victim — Spiffy:
Before the fix,
Spiffy::importinstalled its filter, then calledExporter::exportwhichrequiredExporter::Heavy. The filter leaked intoExporter::Heavy.pm(visible as"my" variable $self masks earlier declaration … at Exporter/Heavy.pm line 237), and the parent'sfilterInstalledDuringUseflag was consumed by the nested file's ownusestatements. The parent was then parsed unfiltered, andfield FOO => [qw(...)]died withsyntax error … near "=> [qw". This brickedTest::Base.pmand ~35 of the 54 YAML-1.31 test files at compile time.Fix: introduce
FilterUtilCall.saveAndResetFilterState()/restoreFilterState(FilterStateSnapshot)and wrap the require/do compile inModuleOperators.do_filewithtry { … } finally { restoreFilterState(snapshot); }. Mirrors real Perl's per-compilation-unit scoping for the require/do path, and naturally leavespreprocessWithBeginFilters' recursiveexecutePerlCodecall alone (soop/incfilter.tdoes not regress).Fix 3 —
do CODEREF$_localization +__FILE__for filehandle/coderef doTwo related issues uncovered while bringing
perl5_t/t/op/incfilter.tpast the Spiffy regression (it gained +5 tests as a side effect):do CODEREFand tied$_: the generator loop didgetGlobalVariable("main::_").set("")on each iteration to clear$_. When a previous iteration's coderef tied$_to a class with onlyTIESCALAR/FETCH(the pattern inincfilter.tlines 261-268), the next iteration'sset("")invoked the missingSTOREand died. Fixed by replacing the$_slot with a fresh untied scalar viaaliasGlobalVariable(matches Perl'slocal $_semantics inpp_require).__FILE__NPE insidedo FILEHANDLE/do CODEREF:actualFileNamewas only set in the scalar-ref branch.__FILE__produced aStringNodewith nullvalueand crashed downstream. Fixed by settingactualFileName = fileName(the stringifiedGLOB(0x…)/CODE(0x…)). Matchesincfilter.t's assertionqr/(?:GLOB|CODE)\(0x[0-9a-f]+\)/.What changed
Java
runtime/perlmodule/FilterUtilCall.java— newFilterStateSnapshot,saveAndResetFilterState(),restoreFilterState().runtime/operators/ModuleOperators.java— wrap require/do compile in try/finally; localize$_indo CODEREFgenerator loop; setactualFileNamefor filehandle / coderef do.Perl (vendored)
src/main/perl/lib/YAML.pm(replaced) +YAML/{Any,Dumper,Error,Loader,Marshall,Mo,Node,Tag,Types}.pm+YAML/{Loader,Dumper}/Base.pm.Tests
src/test/resources/unit/source_filter_scope.t— Spiffy-style minimal repro (without depending on Spiffy itself: an inlineInlineFilterpackage whoseimport()doesfilter_addthenrequiresCwd). Confirmed to catch the bug — fails 1/4 with the fix neutralised, passes 4/4 with the fix.src/test/resources/module/YAML/t/— 34 upstream YAML-1.31 tests added to the bundled-module suite.Docs
dev/design/source_filters.md— full investigation, design rationale, results table, and explanation of whydo_fileis the right wire-up location.What did not change
src/main/java/.../YAMLPP.java— kept (separate bundled module).src/main/perl/lib/YAML/PP.pm— kept.src/main/perl/lib/Storable.pm— unaffected.src/main/java/.../PerlLanguageProvider.java— no longer touched. Earlier revisions of this PR wrappedexecutePerlCodeand needed askipSaveRestorecarve-out; the unified design does neither.Test plan
make— green../gradlew test— green (the same task CI runs viamake ci).make test-bundled-modules— 317 tests, only the 2 pre-existing unrelated failures (Net-SSLeay/t/local/33_x509_create_cert.t,Text-CSV/t/55_combi.t— present on master, no diff in those module dirs). All 34 YAML tests green../jperl -e 'use Test::Base; print "ok\n"'works (was:syntax error at .../Test/Base.pm line 53).t/2-scalars.tagainst the upstream YAML-1.31 distribution: 12/12 (was 7/12).perl5_t/t/op/incfilter.t: 148/153 (was 143/153 on master, +5 from thedo CODEREF/__FILE__fixes).saveAndResetFilterState/restoreFilterStateneutralised to no-ops, test 2 fails withgot: 'REPLACEME', expected: 'ok_marker'.Commits
Generated with Devin