Skip to content

Fix ExifTool: eval STRING AST mutation, join LIST context, interpreter improvements#258

Merged
fglock merged 5 commits into
masterfrom
fix-exiftool-next
Mar 2, 2026
Merged

Fix ExifTool: eval STRING AST mutation, join LIST context, interpreter improvements#258
fglock merged 5 commits into
masterfrom
fix-exiftool-next

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 2, 2026

Summary

This PR fixes several issues discovered while running the Image::ExifTool test suite, building on the previous ExifTool crash fixes (#256, #257):

  • Fix eval STRING AST mutation corrupting my variables in loopsRuntimeCode.java now uses IdentityHashMap instead of mutating operatorAst.id, preventing AST corruption when eval is called repeatedly in loops (both JVM and interpreter paths)
  • Fix join LIST contextCompileBinaryOperator.java now sets LIST context for ListNode elements in compileJoinBinaryOp(), fixing cases where join didn't properly flatten lists
  • Enable interpreter for eval STRING by default with JPERL_EVAL_NO_INTERPRETER opt-out env var
  • Fix signal dispatch — select interrupt handling, kill-self routing, system alarm, stale flag cleanup
  • Add alarm bytecode opcode for interpreter mode
  • Fix can() for forward-declared subs — CODE type is always truthy in boolean context
  • Fix &foo code reference for forward-declared subs
  • Fix undef CODE ref semantics, add Compress::Zlib stub, fix binary data handling
  • Fix binary data handling — BYTE_STRING preservation in concat, read, and sysread
  • Fix AUTOLOAD not triggering for forward-declared subs
  • Fix Encode::is_utf8() inverted result
  • Rename MOVE opcode to ALIAS — fix my/our variable aliasing bug
  • Fix concat type propagation — STRING is sticky, guard BYTE_STRING path

Test plan

  • make passes (build + unit tests)
  • make test-all passes
  • ExifTool.t: 35/35 pass
  • Writer.t improvement from baseline

Known remaining issues (to be addressed in follow-up)

  • goto LABEL not supported in interpreter (affects WriteExif.pl's goto NoWrite pattern)
  • Protobuf.pm forward declaration with prototype not handled
  • XMP ternary lvalue assignment not supported

Generated with Devin

fglock and others added 4 commits March 2, 2026 11:35
…RPRETER opt-out

The bytecode interpreter for eval STRING is now enabled by default,
providing 46x faster compilation for workloads with many unique eval
strings. Set JPERL_EVAL_NO_INTERPRETER=1 to disable.

Update error_messages.t to accept interpreter-mode error variations.

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

Co-Authored-By: Devin <noreply@cognition.ai>
…in LIST context

Two fixes for ExifTool test failures:

1. eval STRING was mutating shared AST nodes (operatorAst.id), causing
   `my` variable declarations inside loops to stop reinitializing after
   the first eval. Use IdentityHashMap lookup instead of AST mutation.

2. join() with LIST argument was compiling elements in scalar context
   instead of list context, causing array arguments to return count
   instead of elements.

Also propagate disassemble flag to require/do for debugging.

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

Co-Authored-By: Devin <noreply@cognition.ai>
The bytecode compiler always treated goto LABEL as non-local control flow
(CREATE_GOTO + RETURN), causing Can't find label errors when goto was
used within the same function. This is the root cause of ExifTool
WriteExif failures -- WriteExif.pl uses goto NoWrite to jump within a
loop body across if/elsif branches.

Now the bytecode compiler tracks label PCs (gotoLabelPcs map) and emits
direct GOTO instructions for intra-function jumps, with forward reference
patching for labels not yet seen. This matches the JVM backend behavior.

ExifTool Writer.t: 33/61 -> 43/61 (10 more tests passing)

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

Co-Authored-By: Devin <noreply@cognition.ai>
Replace direct ast.id mutation with IdentityHashMap lookup in all four
locations that assign BEGIN variable IDs (SpecialBlockParser, SubroutineParser,
BytecodeCompiler, EmitVariable). This prevents eval STRING from corrupting
shared AST nodes, which caused my variables in loops to stop reinitializing.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the fix-exiftool-next branch from 3a91393 to 84589ad Compare March 2, 2026 13:10
UtimeOperator was passing filenames with embedded NUL directly to
native utimes(), causing flaky behavior when the NUL-truncated path
happened to exist. Use RuntimeIO.sanitizePathname() to strip trailing
NULs and reject embedded NULs with a warning, matching the behavior
already used by open, glob, and unlink.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the fix-exiftool-next branch from 5715091 to d0a59dd Compare March 2, 2026 13:37
@fglock fglock merged commit fbe865c into master Mar 2, 2026
2 checks passed
@fglock fglock deleted the fix-exiftool-next branch March 2, 2026 13:46
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