Skip to content

Fix exiftool CLI and achieve 100% interpreter test parity#269

Open
fglock wants to merge 12 commits intomasterfrom
fix-exiftool-cli
Open

Fix exiftool CLI and achieve 100% interpreter test parity#269
fglock wants to merge 12 commits intomasterfrom
fix-exiftool-cli

Conversation

@fglock
Copy link
Owner

@fglock fglock commented Mar 4, 2026

Summary

  • Fix exiftool CLI: readdir list context, stat _ ctime, suppress JNA warnings
  • Prevent JVM emitter from permanently mutating the AST during code generation
  • Fix eval STRING pragma inheritance in interpreter mode
  • Fix interpreter x-operator context, length unicode, array exists/delete
  • Fix interpreter SCALAR_TO_LIST for PerlRange and reverse context
  • Fix eval STRING package context in INIT/END blocks — interpreter now saves/restores InterpreterState.currentPackage
  • Fix definedGlobalCodeRefAsScalar to recognize InterpretedCode via runtimeCode.defined()
  • Fix ARRAY_GET on RuntimeList to flatten aggregates before indexing — my ($a) = @_ in anonymous subs was returning array count instead of first element

Test results

  • Interpreter unit tests: 156/156 (100%) — up from 99/156 (63%)
  • Perl5 core tests: 93.2% pass rate (no regressions)
  • make: passes clean

Test plan

  • make passes
  • make test-interpreter — 156/156 (100%)
  • make test-all — no regressions

Generated with Devin

fglock and others added 11 commits March 4, 2026 20:59
- readdir in list context returned scalar count instead of file list,
  breaking recursive directory scanning (-r option)
- stat _ after file test ops returned mtime as ctime because native
  stat fields were not preserved; now statForFileTest calls nativeStat
- Remove AST split retry loop that caused infinite retries on large
  scripts like exiftool (8000+ lines)
- Restore CWD in glob.t to prevent cascading test failures

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

Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- REPEAT opcode now passes actual context (LIST vs SCALAR) instead of
  hardcoding SCALAR, fixing (list) x N producing string repetition
- length() uses codePointCount() instead of String.length() for proper
  Unicode character counting
- Add ARRAY_EXISTS and ARRAY_DELETE opcodes with compiler fast paths,
  fixing "exists $array[idx]" and "delete $array[idx]" which crashed
  with "slow path not yet implemented"

Test impact: re/pat.t 219->947 ok, op/pack.t 10773->14567 ok

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

Co-Authored-By: Devin <noreply@cognition.ai>
The interpreter eval STRING path used a single set of strict/feature
flags from the InterpretedCode object, reflecting state at end of
compilation. eval STRING inside no strict refs would still inherit
the outer use strict. Now snapshots per-eval-site pragma flags at
compile time and passes them to EvalStringHandler.

Test impact: re/pat.t 947->1043 ok (master: 1055, delta now -12)

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

Co-Authored-By: Devin <noreply@cognition.ai>
- EmitOperator.handleSystemBuiltin: wrap elements.addFirst(handle) in
  try/finally to restore on exit
- HashLiteralNode/ArrayLiteralNode.asListNode(): copy elements list so
  Dereference autoquoting doesn't mutate the original AST
- EmitOperatorDeleteExists: save/restore unary + unwrapping

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

Co-Authored-By: Devin <noreply@cognition.ai>
- SCALAR_TO_LIST now preserves aggregate types (PerlRange, RuntimeArray)
  instead of scalarizing them, matching JVM backend behavior where
  consumers like Pack.pack() iterate via RuntimeList iterator
- reverse operator now passes actual calling context instead of
  hardcoded LIST, enabling scalar-context string reversal

Test impact: pack.t interpreter now matches JVM backend exactly (0 delta)

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

Co-Authored-By: Devin <noreply@cognition.ai>
The AST splitting retry was accidentally removed in 44df585. Without it,
large eval blocks (like those in pack.t) hit MethodTooLargeException and
fall through incorrectly, causing thousands of test failures.

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

Co-Authored-By: Devin <noreply@cognition.ai>
Anonymous InterpretedCode (INIT/END blocks, closures) had null packageName,
causing eval STRING inside them to compile in 'main' instead of the
module's package. Three changes:

1. InterpretedCode constructor: set packageName from compilePackage
2. BytecodeCompiler: set packageName on anonymous sub InterpretedCode
3. BytecodeInterpreter: save/restore InterpreterState.currentPackage
   at subroutine boundaries so eval STRING inherits correct package

This fixes Test2::API's INIT { eval 'END { test2_set_is_end() }' }
which was failing with "Undefined subroutine &main::test2_set_is_end".

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

Co-Authored-By: Devin <noreply@cognition.ai>
definedGlobalCodeRefAsScalar checked only methodHandle/compilerSupplier/
isBuiltin, missing InterpretedCode subs (which override defined() to
return true). Use runtimeCode.defined() instead, matching the pattern
already used by existsGlobalCodeRefAsScalar.

This fixes "defined &Pkg::sub" returning false for interpreter-compiled
glob-assigned subs like *push = sub { ... } in Test2::API::Stack.

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

Co-Authored-By: Devin <noreply@cognition.ai>
ARRAY_GET on a RuntimeList containing aggregates (like RuntimeArray)
was returning the aggregate object itself instead of the scalar at
the given index. This caused `my ($a) = @_` in interpreter-compiled
anonymous subs to assign the array count instead of the first element.

Use flattenElements() to resolve aggregates into individual scalars
before indexing. This brings interpreter test pass rate to 100%.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock changed the title Fix exiftool CLI: readdir list context, stat _ ctime, remove AST retry Fix exiftool CLI and achieve 100% interpreter test parity Mar 5, 2026
… hash flattening

- Refactor all three list assignment cases in CompileAssignment to use
  new SET_FROM_LIST opcode (373) instead of per-element ARRAY_GET loops
- Fix BytecodeCompiler not inheriting pragma flags (strict, feature,
  warnings) from emitter context, which caused declared_refs feature
  to be lost in nested eval STRING (decl-refs.t 156→322)
- Fix RuntimeList.flattenElements() hash handling to emit key-value
  pairs instead of values only
- Revert SCALAR_TO_LIST back to wrapping (not flattening) to preserve
  aliasing semantics
- Add SET_FROM_LIST disassembly support in InterpretedCode

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

Co-Authored-By: Devin <noreply@cognition.ai>
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