From 046a96525b8e3d64cf1a4ac4a0c0c20fed1025d8 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 13:51:15 +0100 Subject: [PATCH] Fix interpreter shift/pop with default @_/@ARGV argument 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 --- .../backend/bytecode/CompileOperator.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index 4f2a84a88..29cf81ebc 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -894,19 +894,24 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode bytecodeCompiler.lastResultReg = rd; } else if (op.equals("pop")) { // Array pop: $x = pop @array or $x = pop @$ref - // operand: ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) - if (node.operand == null || !(node.operand instanceof ListNode)) { + // operand: OperatorNode("@", ...) directly (default @_/@ARGV injected by parser) + // or ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) + if (node.operand == null) { bytecodeCompiler.throwCompilerException("pop requires array argument"); } - ListNode list = (ListNode) node.operand; - if (list.elements.isEmpty() || !(list.elements.get(0) instanceof OperatorNode)) { - bytecodeCompiler.throwCompilerException("pop requires array variable"); - } - - OperatorNode arrayOp = (OperatorNode) list.elements.get(0); - if (!arrayOp.operator.equals("@")) { + OperatorNode arrayOp; + if (node.operand instanceof OperatorNode directOp && directOp.operator.equals("@")) { + // Direct OperatorNode("@", ...) - default array case + arrayOp = directOp; + } else if (node.operand instanceof ListNode list && !list.elements.isEmpty() + && list.elements.get(0) instanceof OperatorNode listOp + && listOp.operator.equals("@")) { + // ListNode-wrapped OperatorNode("@", ...) - explicit array case + arrayOp = listOp; + } else { bytecodeCompiler.throwCompilerException("pop requires array variable: pop @array"); + return; } int arrayReg = -1; // Will be assigned in if/else blocks @@ -953,19 +958,24 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode bytecodeCompiler.lastResultReg = rd; } else if (op.equals("shift")) { // Array shift: $x = shift @array or $x = shift @$ref - // operand: ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) - if (node.operand == null || !(node.operand instanceof ListNode)) { + // operand: OperatorNode("@", ...) directly (default @_/@ARGV injected by parser) + // or ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) + if (node.operand == null) { bytecodeCompiler.throwCompilerException("shift requires array argument"); } - ListNode list = (ListNode) node.operand; - if (list.elements.isEmpty() || !(list.elements.get(0) instanceof OperatorNode)) { - bytecodeCompiler.throwCompilerException("shift requires array variable"); - } - - OperatorNode arrayOp = (OperatorNode) list.elements.get(0); - if (!arrayOp.operator.equals("@")) { + OperatorNode arrayOp; + if (node.operand instanceof OperatorNode directOp && directOp.operator.equals("@")) { + // Direct OperatorNode("@", ...) - default array case + arrayOp = directOp; + } else if (node.operand instanceof ListNode list && !list.elements.isEmpty() + && list.elements.get(0) instanceof OperatorNode listOp + && listOp.operator.equals("@")) { + // ListNode-wrapped OperatorNode("@", ...) - explicit array case + arrayOp = listOp; + } else { bytecodeCompiler.throwCompilerException("shift requires array variable: shift @array"); + return; } int arrayReg = -1; // Will be assigned in if/else blocks