Skip to content

Add unpack operator to bytecode interpreter#222

Merged
fglock merged 9 commits into
masterfrom
fix-interpreter-pack-unpack
Feb 23, 2026
Merged

Add unpack operator to bytecode interpreter#222
fglock merged 9 commits into
masterfrom
fix-interpreter-pack-unpack

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Feb 23, 2026

Problem

unpack was missing from the bytecode interpreter, causing:

Unsupported operator: unpack at ExifTool.pm line 2257, near " = unpack('CC"

pack was already implemented but unpack was not.

Changes

  • Opcodes.java: add UNPACK = 305
  • CompileOperator.java: add unpack to the generic list-op handler (same path as pack)
  • MiscOpcodeHandler.java: add UNPACK -> Unpack.unpack(ctx, argsArray) — one line, reuses existing infrastructure
  • BytecodeInterpreter.java: add UNPACK to the MiscOpcodeHandler case group
  • InterpretedCode.java: add disassembly for all misc list operators (PACK, UNPACK, CRYPT, LOCALTIME, GMTIME, CHMOD, UNLINK, etc.) — previously they fell through to UNKNOWN

Verified

my $packed = pack('CC', 65, 66);
my @vals = unpack('CC', $packed);
say $vals[0];  # 65
say $vals[1];  # 66

The parser injects the default array (e.g. @main::ARGV at top level,
@_ inside a sub) directly as OperatorNode("@", IdentifierNode) on the
shift/pop node's operand. The bytecode compiler handlers were expecting
the operand to always be a ListNode wrapper, causing:

  shift requires array argument at -e line 2, near "say shift"

Fix: accept operand as either:
  - OperatorNode("@", ...) directly  (default @_/@argv case)
  - ListNode containing OperatorNode("@", ...)  (explicit array case)

Verified:
  ./jperl --interpreter -E 'say shift' 123        => 123
  ./jperl --interpreter -E 'say pop' 123          => 123
  sub foo { say shift } foo("hello")              => hello
  my @A=(1,2,3); say shift @A; say pop @A         => 1 / 3
pack was already implemented but unpack was missing, causing:
  Unsupported operator: unpack at ExifTool.pm line 2257

Changes:
- Opcodes.java: add UNPACK = 305
- CompileOperator.java: add 'unpack' to the generic list-op handler
- MiscOpcodeHandler.java: add UNPACK -> Unpack.unpack(ctx, argsArray)
- BytecodeInterpreter.java: add UNPACK to the MiscOpcodeHandler case group
- InterpretedCode.java: add disassembly for all misc list operators
  (PACK, UNPACK, CRYPT, LOCALTIME, GMTIME, CHMOD, UNLINK, etc.)

Verified:
  pack('CC', 65, 66) => 2-byte string
  unpack('CC', $packed) => (65, 66)
Three fixes:

1. push/unshift return value was lastResultReg=-1 (crash when result used)
   - ARRAY_PUSH/ARRAY_UNSHIFT now emit ARRAY_SIZE after the push so
     lastResultReg holds the new array size, matching Perl semantics
   - Fixes: my $n = push @A, 1  and  push @A, $x unless grep ...

2. push/unshift @{expr} with arbitrary expression (e.g. @{{key}})
   - Previously only handled @$scalar, not @{block_expr}
   - Now evaluates any non-IdentifierNode operand of @ and derefs it
   - Fixes: push @{{$baseType}}, $type  (ExifTool.pm)

3. Removed temporary debug code (JPERL_DEBUG printStackTrace)
Add SET_PACKAGE and PUSH_PACKAGE opcodes emitted by the bytecode compiler
when package declarations execute. The interpreter handles these by updating
InterpreterState.currentPackage (a ThreadLocal RuntimeScalar), also managed
via DynamicVariableManager for scoped package blocks (package Foo { }).

This fixes:
- caller() returning wrong package in interpreter mode (Exporter::export_ok_tags)
- eval STRING compiling in wrong package in interpreter mode
- ExceptionFormatter now uses runtime package for innermost interpreter frame
- ExceptionFormatter consumes InterpreterState frames in order instead of
  always using current() which always returned the same topmost frame
…ad/write/open, socket, bind, connect, listen, write, formline, printf, accept, sysseek, truncate, read
@fglock fglock merged commit c09fa2a into master Feb 23, 2026
2 checks passed
@fglock fglock deleted the fix-interpreter-pack-unpack branch February 23, 2026 14:50
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