Fix test regressions: tied handles, sparse arrays, autovivification#253
Merged
Fix test regressions: tied handles, sparse arrays, autovivification#253
Conversation
In Perl 5, @{undef} without strict refs converts undef to "" and
accesses @{""} (an empty package global). PerlOnJava was always throwing
"Can't use an undefined value as an ARRAY reference" regardless of
strict mode.
- Add strictAutovivify flag to RuntimeArray
- Set flag true in arrayDeref() (strict), false in arrayDerefNonStrict()
- In addToArray/scalar/lastElementIndex: throw only when strict, else
return empty/0/-1
- array.t: 119 → 128 tests passing
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <noreply@cognition.ai>
When assigning to a negative index on an empty array (e.g. $a[-1] = 0), the resolved key stays negative, causing IndexOutOfBoundsException. Now throws the correct Perl error message instead. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
- IOOperator: delegate sysread() on tied handles to TieHandle.tiedRead() - IOOperator: delegate syswrite() on tied handles to TieHandle.tiedWrite() - RuntimeArray.getList(): handle null elements (sparse arrays) to prevent NPE/wrong error when returning arrays with holes from subroutines Fixes: tiehandle.t 16→46 tests, array.t 128→173 tests Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
After local *glob restores a glob, the hash/array type field may still be TIED_HASH/TIED_ARRAY while elements are restored to the original StableHashMap/ArrayList. Add instanceof checks to prevent ClassCastException. Fixes: tiehandle.t 46→49 tests Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
Handle cases where a register contains a RuntimeList instead of RuntimeScalar by checking the type and calling .scalar() when needed, preventing ClassCastException in the bytecode interpreter. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
When a register contains a RuntimeScalarReadOnly (e.g. cached undef from an empty list scalar() call), replace it with a mutable RuntimeScalar before assigning. This prevents Modification of a read-only value attempted errors in the bytecode interpreter. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
The bytecode compiler only handled scalar variables in local lists like local ($x, *GLOB, @arr, %hash). Typeglob, array, and hash elements were silently skipped. This fix adds proper LOCAL_GLOB, LOCAL_ARRAY, and LOCAL_HASH opcode emission for all variable types in the list form of local. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
The eval STRING compiler path called createClassWithMethod directly without marking the AST block as blockIsSubroutine. This caused EmitBlock to emit redundant block-level regex state save/restore, which clobbered $1/$&/etc before the return value was collected. Also add getList() override to ScalarSpecialVariable to eagerly materialize regex capture values, preventing stale lazy references. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
Two bugs fixed:
1. `undef $scalar` was not working in the bytecode interpreter because
CompileOperator compiled it identically to bare `undef` — it never
modified the variable. Now emits UNDEFINE_SCALAR opcode on the
operand register before loading undef into the result register.
2. RuntimeScalarReadOnly values leaked into variable registers and hash
elements, causing "Modification of a read-only value attempted"
errors in large functions using the interpreter backend. Root cause:
MathOperators returns cached RuntimeScalarReadOnly from getScalarInt()
(e.g. unaryMinus(-1)), and MOVE copied the reference directly into
variable registers. Fixed by:
- MOVE: unwrap RuntimeScalarReadOnly into mutable RuntimeScalar
- HASH_SET: unwrap before storing in hash
- INC_REG/DEC_REG: unwrap results before storing back
- All compound assignment opcodes (+=, -=, *=, /=, %=, .=, etc.):
ensure target register is mutable before in-place modification
- Added ensureMutableScalar() helper for consistent unwrapping
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <noreply@cognition.ai>
Java's regex engine can hang indefinitely on certain patterns due to exponential backtracking inside Matcher.find(). This wraps regex input with a TimeoutCharSequence that checks elapsed time every 4096 charAt() calls and aborts after 10 seconds with a warning. Applied to all three regex entry points: match (m//), replace (s///), and split. Fixes #254. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
… interpreter The bytecode interpreter read-only checks only caught RuntimeScalarReadOnly but missed ScalarSpecialVariable (e.g. $1, $&), which also extends RuntimeBaseProxy and is effectively immutable. This caused read-only errors when registers containing special variables were used in mutating operations. - Add ScalarSpecialVariable handling to ensureMutableScalar() - Introduce isImmutableProxy() helper that checks both types - Replace all instanceof RuntimeScalarReadOnly guards with isImmutableProxy() across BytecodeInterpreter, OpcodeHandlerExtended, and SlowOpcodeHandler - Make ScalarSpecialVariable.getValueAsScalar() public for cross-package access Fixes ExifTool PLUS.t test 2 crash at STRING_CONCAT_ASSIGN opcode. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
- Fix parser to correctly handle `sort SubName @list` syntax by detecting bare identifier comparators before the try/catch path, constructing a code reference AST node directly instead of letting the sub be called with the list as arguments - Propagate current package to sub-compilers in BytecodeCompiler for both named and anonymous subroutines, fixing package resolution when bytecode interpreter fallback is triggered - Add string-to-coderef resolution in ListOperators.sort() for comparators passed as string subroutine names Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
- Fix invalidateCacheForClass to also remove the exact class entry from linearizedClassesCache (was only removing subclass entries matching "className::" prefix, missing the class itself) - Fix splice to handle null array elements (from $#a++) by replacing them with undef RuntimeScalar instead of propagating Java null These fix splice.t tests 30-32 (splice @isa cache invalidation, splice with nonexistent array elements). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
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[-1] = 0on empty array instead of IndexOutOfBoundsException.Test plan
Generated with Devin