Skip to content

Fix caller() returning wrong line numbers#326

Merged
fglock merged 14 commits into
masterfrom
fix/caller-line-numbers
Mar 17, 2026
Merged

Fix caller() returning wrong line numbers#326
fglock merged 14 commits into
masterfrom
fix/caller-line-numbers

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 17, 2026

Summary

Fixes caller() returning incorrect line numbers for stack frames, and fixes Moo croak-locations.t test 28.

Changes

  1. Preserve parse-time line numbers (earlier commit): The emit-time saveSourceLocation() call was overwriting correct line numbers. Fixed by preserving existing line numbers while updating package/subroutine context.

  2. Use CallerStack for interpreter frames (latest commit): ExceptionFormatter now uses CallerStack for interpreter frame call-site tracking instead of PC-based lookup. This fixes test 28 which was reporting line 18 instead of line 21.

Before

caller(0) returned wrong line numbers
croak-locations.t test 28: "LocationTestFile line 18" (WRONG)

After

caller(0) returns correct line numbers
croak-locations.t test 28: "LocationTestFile line 21" (CORRECT)
All 29 croak-locations.t tests now pass

Test Results

  • All unit tests pass
  • Moo croak-locations.t: 29/29 passing (all tests pass!)
  • No regressions

Test plan

  • make passes (build + unit tests)
  • Moo croak-locations.t all 29 tests pass
  • Manual verification with test28_exact.pl

Generated with Devin

@fglock fglock force-pushed the fix/caller-line-numbers branch 7 times, most recently from 84489b6 to e87e40a Compare March 17, 2026 13:33
The emit-time saveSourceLocation() call (added in Phase 28 for package context)
was overwriting correct line numbers with wrong values. ErrorMessageUtil.getLineNumber()
uses a forward-only cache that fails for out-of-order token access during emit.

The fix preserves existing line numbers when an entry already exists, while still
updating the package and subroutine context from emit time.

This combines: correct LINE from parse-time + correct PACKAGE from emit-time.

Before: caller(0) returned line 25 for all frames (wrong)
After: caller(0) returns correct line numbers matching Perl exactly

Carp stack traces now show correct "called at line N" information.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the fix/caller-line-numbers branch from e87e40a to d79aea8 Compare March 17, 2026 13:34
fglock and others added 13 commits March 17, 2026 14:41
Mo uses $^H |= 1538 in its import to enable strict. This was not working
because $^H was a regular variable that did not communicate with the compiler.

Changes:
- Add HINTS type to ScalarSpecialVariable enum
- Register $^H as a ScalarSpecialVariable in GlobalContext
- When $^H is written, update the symbol table strictOptionsStack
- When $^H is read, return the current strict options from symbol table
- Add setStrictOptions() and getStrictOptions() to ScopedSymbolTable

This fixes Mo t/strict.t test (was 27/28 passing, now 28/28).

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Create dev/design/strict_hints_refactor.md with analysis of using $^H
  as single source of truth (deferred implementation)
- Update moo_support.md: Phase 34 completed, Mo now 28/28 tests passing

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

Co-Authored-By: Devin <noreply@cognition.ai>
B::Deparse is used by Moo overloaded-coderefs.t test to verify that
Sub::Quote coercions get inlined into the constructor.

This implementation:
- First undefers Sub::Defer deferred subs
- Looks up source from Sub::Quote::quoted_from_sub()
- Strips only the first PRELUDE (non-greedy) to preserve inlined subs
- Falls back to { "DUMMY" } for non-Sub::Quote subs

Moo tests: 63/71 -> 64/71 passing (90%)
- overloaded-coderefs.t: 9/10 -> 10/10 passing

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

Co-Authored-By: Devin <noreply@cognition.ai>
Add Issue 5 to caller_package_context.md documenting:
- Problem: caller() returns wrong line (18 vs 21) and package (main vs Elsewhere)
- Root cause appears to be Sub::Quote generated code capturing stale context
- Basic caller() works correctly; issue specific to runtime-generated code
- Three hypotheses: stale tokenIndex, package not updated, deferred compilation timing
- Test script for reproduction comparing Perl vs PerlOnJava
- Proposed fix approaches including eager caller info capture

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Add dev/design/unified_caller_stack.md documenting the root cause
  of inconsistent caller() results between JVM and interpreter paths
- Add ByteCodeSourceMapper.getPackageAtLocation() method to enable
  interpreter path to look up package from tokenIndex
- Add DEBUG_CALLER env var for tracing source location lookups

The core issue: interpreter frames used compile-time package from
frame.packageName() instead of runtime package at call site. The fix
is to use ByteCodeSourceMapper (same as JVM path) for package lookup.

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

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

- Add ByteCodeSourceMapper.getPackageAtLocation() to look up package from tokenIndex
- Modify ExceptionFormatter to use this for interpreter frames instead of frame.packageName()
- This unifies package tracking between JVM and interpreter paths
- Package changes within subroutines (sub { pkg A; call(); pkg B; call(); }) now work correctly

The fix only affects package lookup; line number calculation is unchanged.
No regression in caller.t tests (41/112 passing, same as before).

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Add Phase 30: Unified caller() package tracking for interpreter path
- Update test results: 64/71 Moo (90%), 795/828 subtests (96%)
- Update croak-locations.t failure details (tests 15, 18)
- Mark Phase 34 (Interpreter caller parity) as completed
- Add unified_caller_stack.md to related documents
- Add latest commit reference (c35faad)

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

Co-Authored-By: Devin <noreply@cognition.ai>
The #line directive was being processed in Whitespace.parseLineDirective(),
but unquoted filenames were only handled in getSourceLocationAccurate() for
error messages, not during parsing. This caused caller() and __FILE__ to
return the original eval filename instead of the #line-adjusted filename.

Changes:
- Whitespace.java: Add handling for unquoted bareword filenames
- ByteCodeSourceMapper.java: Store #line-adjusted filename in LineInfo
- ErrorMessageUtil.java: Add DEBUG_CALLER debug output for setFileName()

This fixes Moo croak-locations.t tests 15 and 18.

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

Co-Authored-By: Devin <noreply@cognition.ai>
- croak-locations.t: tests 15, 18 now PASS (were failing)
- croak-locations.t: tests 19-26 now RUN (were previously skipped)
- Remaining failures: tests 27-28 are complex Sub::Quote stack walking issues
- Updated test results: 806/839 subtests (96%)
- Added Phase 37 documentation and analysis of remaining issues

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

Co-Authored-By: Devin <noreply@cognition.ai>
Add call site tracking for bytecode interpreter subroutine calls:
- Add getCallSiteInfo() helper to BytecodeInterpreter for PC-to-location mapping
- Wrap CALL_SUB with CallerStack push/pop around RuntimeCode.apply()
- Wrap CALL_METHOD with CallerStack push/pop around RuntimeCode.call()
- Add debug output to CallerStack.push() (enabled via DEBUG_CALLER env var)

This improves caller() accuracy for interpreted code by recording where
calls originate from, not just what function is executing.

Unit tests pass. Simple caller() tests work correctly.
Moo croak-locations.t test 28 still fails (line 18 vs expected line 21)
due to ExceptionFormatter not using CallerStack for interpreter frames.

See dev/design/caller_stack_fix_plan.md for detailed investigation notes.

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

Co-Authored-By: Devin <noreply@cognition.ai>
ExceptionFormatter now uses CallerStack for interpreter frame call-site
tracking instead of PC-based lookup. This fixes Moo croak-locations.t
test 28, which was reporting line 18 instead of the correct line 21.

The fix works by checking CallerStack[interpreterFrameIndex] first,
which contains the exact call-site location pushed by CALL_SUB/CALL_METHOD
opcodes. Falls back to PC-based lookup if CallerStack entry not available.

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

Co-Authored-By: Devin <noreply@cognition.ai>
Added null checks before calling equals() on sourceFileName in
ByteCodeSourceMapper. This fixes op/incfilter.t which was failing
with 0/153 tests due to NPE when processing @inc filters.

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

Co-Authored-By: Devin <noreply@cognition.ai>
When reading $^H, check the stored lvalue first to preserve custom
hint bits like 0x04000000. Only fall back to symbol table strict
options if no lvalue is stored.

This fixes comp/hints.t test regression (17, 21 now pass again).

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock merged commit e75dfc2 into master Mar 17, 2026
2 checks passed
@fglock fglock deleted the fix/caller-line-numbers branch March 17, 2026 21:13
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