fix: correct caller() line numbers for method and function calls#657
Closed
fix: correct caller() line numbers for method and function calls#657
Conversation
1. FileSpec.splitdir scalar context: `scalar File::Spec->splitdir($dir)` was
returning the last component instead of the component count. Fixed by adding
a scalar-context branch that returns `dirs.length` as a RuntimeScalar.
Affected Test::Unit::Lite::AllTests::suite() path-depth calculation.
2. Symbol::qualify_to_ref returned GLOB instead of GLOBREFERENCE: the Java
implementation was using `new RuntimeScalar().set(new RuntimeGlob(...))` which
creates a GLOB-typed scalar, not a reference-to-glob. Changed to
`new RuntimeGlob(object.toString()).createReference()` which produces
GLOBREFERENCE (type=GLOBREFERENCE), matching how gensym() works.
Without this fix, `keys %{ *{Symbol::qualify_to_ref("Pkg::")} }` returned
empty, so list_tests() found 0 test methods.
3. IOOperator.select auto-vivification: when `select $var` is called with an
undefined scalar, Perl auto-vivifies it into an anonymous GLOB reference
(enabling the idiom `select select my $fh_null; tie *$fh_null, 'Class'`).
Added the same pattern used by open/socket/pipe: create a new RuntimeGlob,
wrap a fresh RuntimeIO in it, create a GLOBREFERENCE, and set() it back on
the lvalue via runtimeList.getFirst().
Also added unit tests for cases 1 and 2 in directory.t and typeglob.t.
After these fixes: Test::Unit::Lite goes from 0/39 passing to 37/39 passing.
Remaining 2 failures are a pre-existing caller() line-number off-by-one for
multi-line function calls (not introduced by this PR).
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
In handleArrowOperator (Dereference.java) and handleApplyOperator (EmitSubroutine.java), the JVM line number at the callCached/apply INVOKESTATIC was set to the token index of the closing ')' of the argument list, not to the token index of the call site itself. This caused caller() inside a called subroutine to report the wrong source line — specifically the last line of the argument list rather than the line where the call begins. Fix: emit setDebugInfoLineNumber with node.left.getIndex() (the token index of the receiver object / function name) immediately before the INVOKESTATIC instruction in both call paths. This anchors the JVM bytecode to the Perl line where the call starts, so caller() returns the correct line number. Verified by ./jcpan -t Test::Unit::Lite: all 39 subtests now pass (was 37/39; test_assert_deep_equals and test_fail_assert_not_equals had been failing because check_failures() verified caller() line numbers from assertion failures). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The caller() line number fix resolved the 2 remaining failures. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Same root cause as the JVM fix (af48192): CALL_SUB and CALL_METHOD instructions in the interpreter's bytecode had their pcToTokenIndex entries pointing to the token AFTER the closing ')' (inherited from the statement-level emit), instead of the token at the call-site. Fix all three interpreter call-emit sites: - CompileBinaryOperator.java, coderef->() path: use emitWithToken with node.left.getIndex() (the coderef token index). - CompileBinaryOperator.java, ->method() path: same, with the invocant's token index. - CompileBinaryOperatorHelper.java, "()" switch case: change from emit() to emitWithToken(), driven by the call-site tokenIndex now passed from CompileBinaryOperator (node.left.getIndex()). After fix: --interpreter and JVM both report the same line as system Perl for caller() inside a called subroutine with multi-line args. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Owner
Author
|
Superseded by PR #656 which now includes all commits from this branch. |
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
caller()inside a called subroutine was reporting the wrong source line — the line of the closing)of the argument list rather than the line where the call begins.handleArrowOperatorinDereference.java— method calls via$obj->method(args)/callCachedhandleApplyOperatorinEmitSubroutine.java— direct function calls viafoo(args)/applyByteCodeSourceMapper.setDebugInfoLineNumber(ctx, node.left.getIndex())immediately before each INVOKESTATIC instruction.node.left.getIndex()is the token index of the receiver object / function name, which corresponds to the line where the call begins — matching what__LINE__captures and what Perl'scaller()is supposed to return.The parse-time
saveSourceLocationinStatementResolveralready records this token index with the correct line number, so the existing debug-info infrastructure handles everything correctly once we anchor the call instruction to the right token.Test plan
make(all unit tests pass)./jcpan -t Test::Unit::Lite— all 39/39 subtests pass (was 37/39;test_assert_deep_equalsandtest_fail_assert_not_equalshad been failing becausecheck_failures()verifies the exact caller line number from assertion exceptions)dev/cpan-reports/cpan-compatibility.md:Test::Unit::LitePARTIAL → PASSGenerated with Devin