fix: Geo::IP support — DynaLoader stubs, IPv6 Socket, nested eval STRING lexicals#511
Merged
fix: Geo::IP support — DynaLoader stubs, IPv6 Socket, nested eval STRING lexicals#511
Conversation
Two fixes to make `./jcpan -t Geo::IP` progress from configure-time
failure to 6/8 test files passing (20/21 subtests).
1. DynaLoader dl_* probe stubs
Geo::IP's Makefile.PL calls DynaLoader::dl_findfile('GeoIP') to
decide between XS and pure-Perl code paths. PerlOnJava only stubbed
bootstrap/boot_DynaLoader, so this died with "Undefined subroutine".
Register no-op stubs for dl_findfile, dl_load_file, dl_find_symbol,
dl_find_symbol_anywhere, dl_install_xsub, dl_undef_symbols, dl_error
in DynaLoader.java. All return empty/undef so modules fall through
to their pure-Perl implementations.
(DynaLoader.pm gets the same stubs behind `unless defined` guards,
but they are currently unreached because Java's initialize() presets
%INC{DynaLoader.pm}.)
2. Socket::pack_sockaddr_in6 / unpack_sockaddr_in6
Geo::IP's PP path does:
use Socket qw/ ... unpack_sockaddr_in6 /;
which failed because Socket.pm did not export it and Socket.java
did not implement it. Added both functions (28-byte sockaddr_in6
layout: family, port, flowinfo, 16-byte addr, scope_id) and listed
them in @export.
The two remaining Geo::IP test failures (country_v6.t, part of org.t)
are caused by a separate, unrelated limitation: PerlOnJava's inner
`eval STRING` does not inherit `my` lexicals from an outer
`eval STRING`, so the IPv6 subs that reference Geo::IP's `my @countries`
fail to compile. Not addressed here.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Standard Perl lets a string-eval's compile-time scope include every `my`
visible at the call site, including variables declared in an enclosing
eval STRING. PerlOnJava's interpreter backend broke this for named subs
defined inside a nested eval: strict-vars fired at parse time, or (once
parsing was fixed) the sub's closure captured nothing and `$y` returned
undef at runtime.
Minimal reproducer (fixed by this commit):
eval q{
my $y = 99;
eval q{ sub bar { return $y } 1 };
bar(); # now returns 99
};
Root cause
----------
`EvalStringHandler` (interpreter-backend eval STRING) created a fresh,
empty `ScopedSymbolTable` and only received the caller's lexical
environment as a `Map<name, register>` used purely to build runtime
capture arrays. The variable NAMES never made it into the symbol
table, so `Variable.checkStrictVarsAtParseTime` failed for named sub
bodies, and `SubroutineParser.handleNamedSub`'s JVM-path closure
capture had nothing to capture.
Fix
---
Seed the parser's symbol table with one `our` entry per caller lexical,
using the same "BEGIN package alias" trick that the JVM backend's
`RuntimeCode.evalStringHelper` already uses:
1. Allocate a unique `PerlOnJava::_BEGIN_<id>` namespace per eval
invocation.
2. Alias each captured `my`/`state` value into
`GlobalVariable.globalXxx` under that namespace.
3. Seed `ScopedSymbolTable.addVariable(name, "our", seedPkg, null)`
so `SubroutineParser.handleNamedSub` takes its `decl == "our"`
branch (line 1153) and looks the value up via the alias.
Direct references inside the eval body are unaffected: they go through
`BytecodeCompiler`'s own parentRegistry-populated symbol table (register
capture), not the parser's.
Move the capturedVars / adjustedRegistry computation up so it runs
before parse — the seeding step needs the final filtered set of
captured variables.
Impact
------
- `./jcpan -t Geo::IP` now passes 8/8 test files (was 6/8). The
remaining 2 failures on the parent branch were entirely due to this
bug: Geo::IP wraps its v6 subs in an inner `eval <<'__IPV6__'` that
references outer-eval `my @countries` / `@code3s` / `@names`.
- New test: `src/test/resources/unit/eval_nested_lexicals.t`
(8 subtests covering direct refs, anon subs, named subs, 3-deep
nesting, hash lookups). Matches standard Perl exactly.
- `make` (all unit tests): green.
- `make test-bundled-modules`: green.
- `perl5_t/t/op/eval.t`: 159/173 passing (91.9%).
Design doc: `dev/design/nested-eval-string-lexicals.md`.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
4d42ae7 to
393bae9
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
Two fixes that together take
./jcpan -t Geo::IPfrom "configure fails immediately" to 8 of 8 test files passing.Fix 1 — DynaLoader
dl_*probe stubs + Socket IPv6 (commit 1)Geo::IP's
Makefile.PLcallsDynaLoader::dl_findfile('GeoIP')to choose between XS and pure-Perl. We previously stubbed onlybootstrap/boot_DynaLoader. Added no-op stubs fordl_findfile,dl_load_file,dl_find_symbol,dl_find_symbol_anywhere,dl_install_xsub,dl_undef_symbols,dl_error.Also implemented
Socket::pack_sockaddr_in6/unpack_sockaddr_in6(28-byte sockaddr_in6 layout: family, port, flowinfo, 16-byte addr, scope_id) and added them to@EXPORT, unblocking the PP-path IPv6use Socket qw/ ... unpack_sockaddr_in6 /.Fix 2 — Nested
eval STRINGvisibility of outermylexicals in named subs (commit 2)Standard Perl lets a string-eval's compile-time scope include every
myvisible at the call site, including variables declared in an enclosingeval STRING. PerlOnJava's interpreter backend broke this for named subs defined inside a nested eval:Root cause:
EvalStringHandler(interpreter backend) created a fresh, emptyScopedSymbolTableand received the caller's lexical environment only as aMap<name, register>used to build runtime capture arrays. The variable names never made it into the symbol table, soVariable.checkStrictVarsAtParseTimefailed for named sub bodies, andSubroutineParser.handleNamedSub's JVM-path closure capture found nothing to capture.Fix: Seed the parser's symbol table with one
ourentry per caller lexical, using the same "BEGIN package alias" trick that the JVM backend'sRuntimeCode.evalStringHelperalready uses:PerlOnJava::_BEGIN_<id>namespace per eval invocation.my/statevalue intoGlobalVariable.globalXxxunder that namespace.addVariable(name, "our", seedPkg, null)soSubroutineParser.handleNamedSubtakes itsdecl == "our"branch and looks values up via the alias.Direct references inside the eval body are unaffected — they still go through
BytecodeCompiler's parentRegistry-populated symbol table (register capture).This mirrors exactly what the JVM backend has done correctly all along; the interpreter path was missing the alias step.
Geo::IP's remaining 2 test failures were entirely due to this: its v6 subs live in an inner
eval <<'__IPV6__'that references outer-evalmy @countries/@code3s/@names.Design doc:
dev/design/nested-eval-string-lexicals.md.Verification
make(all unit tests)make test-bundled-modulessrc/test/resources/unit/eval_nested_lexicals.t(8 subtests)./jcpan -t Geo::IPperl5_t/t/op/eval.tFiles
src/main/java/org/perlonjava/runtime/perlmodule/DynaLoader.javasrc/main/java/org/perlonjava/runtime/perlmodule/Socket.javapack_sockaddr_in6/unpack_sockaddr_in6.src/main/perl/lib/Socket.pmsrc/main/perl/lib/DynaLoader.pm.pm-level stubs (currently unreached —%INCis preset by Java — but useful if that changes).src/main/java/org/perlonjava/backend/bytecode/EvalStringHandler.javaourentries; move capture computation before parse so seeding sees the final filtered set.src/test/resources/unit/eval_nested_lexicals.tdev/design/nested-eval-string-lexicals.mdTest plan
makepassesmake test-bundled-modulespasses./jcpan -t Geo::IPpasses all 8 test fileseval_nested_lexicals.tmatches standard Perlperl5_t/t/op/eval.tGenerated with Devin