Skip to content

Comments

Fix interpreter eval STRING parity: stash.t, bop.t, benchmarks.t#224

Merged
fglock merged 2 commits intomasterfrom
fix-slowopcode-stubs
Feb 23, 2026
Merged

Fix interpreter eval STRING parity: stash.t, bop.t, benchmarks.t#224
fglock merged 2 commits intomasterfrom
fix-slowopcode-stubs

Conversation

@fglock
Copy link
Owner

@fglock fglock commented Feb 23, 2026

Summary

Fixes several interpreter mode (JPERL_EVAL_USE_INTERPRETER=1) regressions vs JVM compiler mode.

Changes

1. eval STRING package resolution (EvalStringHandler, InterpretedCode, BytecodeCompiler)

Store compile-time package in InterpretedCode.compilePackage; EvalStringHandler uses it instead of runtime InterpreterState.currentPackage, matching evalStringHelper behavior.

  • stash.t: 34/56 in both modes (was 16/56 interpreter)

2. Bitwise &/|/^ context-sensitive dispatch (OpcodeHandlerExtended)

BITWISE_AND/OR/XOR_BINARY opcodes called always-numeric bitwiseAndBinary/OrBinary/XorBinary instead of context-sensitive bitwiseAnd/Or/Xor (which falls back to string ops for non-numeric operands).

  • bop.t: 487/522 in both modes (was 480 interpreter)

3. LIST_SLICE_FROM startIndex encoding mismatch (SlowOpcodeHandler)

CompileAssignment emits startIndex via emitInt() (single int), but executeListSliceFrom() read it as 2 shorts. Caused out-of-bounds register access for list assignments with array slurp inside eval STRING.

4. visit(IdentifierNode) local scope shadowing (BytecodeCompiler)

capturedVarIndices was checked before local variableScopes, so my $x inside eval STRING could not shadow a captured outer $x.

  • perf/benchmarks.t: 1960/1960 in both modes (was 1950 interpreter)

Test Results

Test JVM Interpreter
op/stash.t 34/56 34/56
op/signatures.t 597/908 597/908 (no regression)
op/bop.t 487/522 487/522
perf/benchmarks.t 1960/1960 1960/1960

… JVM path

BITWISE_AND/OR/XOR_BINARY opcodes were calling bitwiseAndBinary/OrBinary/XorBinary
(always-numeric) instead of bitwiseAnd/Or/Xor (context-sensitive).

The JVM path maps & -> BitwiseOperators.bitwiseAnd() which checks if operands
look like numbers and falls back to string bitwise ops if not. The interpreter
was bypassing this check, causing strings with code points > 0xFF to silently
produce wrong results instead of throwing the expected error.

The *Binary variants are only correct for the explicit 'binary&', 'binary|',
'binary^' operators (use feature 'bitwise').

bop.t: 487/522 passing with and without JPERL_EVAL_USE_INTERPRETER=1 (was 480)
Two bugs fixed:

1. LIST_SLICE_FROM startIndex encoding mismatch:
   CompileAssignment emits startIndex via emitInt() (single int slot), but
   SlowOpcodeHandler.executeListSliceFrom() was reading it as 2 shorts
   (high+low). This caused wrong startIndex values and out-of-bounds register
   access when list assignment with array slurp ran inside eval STRING:
     eval 'my ($x, \@A) = 1..4; for (1..1) { ($x, \@A) = (1,2,$x) } 1'
   Fixed by reading startIndex as a single int to match emitInt().

2. visit(IdentifierNode) checked capturedVarIndices before local scope:
   When eval STRING captured outer $x at register 3, and the eval declared
   'my $x' (allocating a fresh register), subsequent references to $x inside
   a for loop still resolved to the captured register 3 instead of the new
   local register. Fixed by checking local variableScopes first, then falling
   through to capturedVarIndices only if not found locally.

perf/benchmarks.t: 1960/1960 with and without JPERL_EVAL_USE_INTERPRETER=1
@fglock fglock merged commit 2e7f177 into master Feb 23, 2026
2 checks passed
@fglock fglock deleted the fix-slowopcode-stubs branch February 23, 2026 19:35
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