From 56568bd69ca134aea2bfd63bcf1d8ac7b6d870c7 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Tue, 27 Jan 2026 23:31:14 +0100 Subject: [PATCH 1/3] Add missing getter methods to RuntimeArrayProxyEntry Fixes compilation errors in RuntimeSubroutineArgument where getParent() and getKey() methods were missing from RuntimeArrayProxyEntry. This resolves the build regressions without affecting test behavior. --- .../runtime/RuntimeArrayProxyEntry.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/org/perlonjava/runtime/RuntimeArrayProxyEntry.java b/src/main/java/org/perlonjava/runtime/RuntimeArrayProxyEntry.java index 403cd5513..651ec11dc 100644 --- a/src/main/java/org/perlonjava/runtime/RuntimeArrayProxyEntry.java +++ b/src/main/java/org/perlonjava/runtime/RuntimeArrayProxyEntry.java @@ -17,6 +17,24 @@ public class RuntimeArrayProxyEntry extends RuntimeBaseProxy { // Index associated with this proxy in the parent array private final int key; + /** + * Gets the parent RuntimeArray. + * + * @return the parent RuntimeArray + */ + public RuntimeArray getParent() { + return parent; + } + + /** + * Gets the key (index) for this proxy entry. + * + * @return the index in the parent array + */ + public int getKey() { + return key; + } + /** * Constructs a RuntimeArrayProxyEntry for a given index in the specified parent array. * From 43633ff4fab3ed4f166a0e0e877069ae02957142 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 28 Jan 2026 10:00:34 +0100 Subject: [PATCH 2/3] Fix array literal slices with ranges and 0 decrement operations - Fix array literal slices containing range operators (e.g., ('a','b','c')[0..2]) - Add pre/post decrement support for RuntimeArraySizeLvalue (0--) - Handle non-literal elements in ArrayLiteralNode by routing through slice method - Fixes failing tests 27, 28, 30 in op/array.t --- .../org/perlonjava/codegen/Dereference.java | 15 +++++++-- .../runtime/RuntimeArraySizeLvalue.java | 32 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/perlonjava/codegen/Dereference.java b/src/main/java/org/perlonjava/codegen/Dereference.java index 08e538a9c..e38453eb0 100644 --- a/src/main/java/org/perlonjava/codegen/Dereference.java +++ b/src/main/java/org/perlonjava/codegen/Dereference.java @@ -695,7 +695,18 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp emitterVisitor.ctx.mv.visitVarInsn(Opcodes.ASTORE, leftSlot); ArrayLiteralNode right = (ArrayLiteralNode) node.right; - if (right.elements.size() == 1) { + + // Check if all elements are literals (NumberNode, StringNode, etc.) + boolean allLiterals = true; + for (Node elem : right.elements) { + if (!(elem instanceof org.perlonjava.astnode.NumberNode) && + !(elem instanceof org.perlonjava.astnode.StringNode)) { + allLiterals = false; + break; + } + } + + if (allLiterals && right.elements.size() == 1) { // Single index: use get/delete/exists methods Node elem = right.elements.getFirst(); elem.accept(emitterVisitor.with(RuntimeContextType.SCALAR)); @@ -741,7 +752,7 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp emitterVisitor.ctx.javaClassInfo.releaseSpillSlot(); } } else { - // Multiple indices: use slice method (only for get operation) + // Multiple indices or non-literal elements: use slice method (only for get operation) if (!arrayOperation.equals("get")) { throw new PerlCompilerException(node.tokenIndex, "Array slice not supported for " + arrayOperation, emitterVisitor.ctx.errorUtil); } diff --git a/src/main/java/org/perlonjava/runtime/RuntimeArraySizeLvalue.java b/src/main/java/org/perlonjava/runtime/RuntimeArraySizeLvalue.java index 03d96dd7b..8b747e52a 100644 --- a/src/main/java/org/perlonjava/runtime/RuntimeArraySizeLvalue.java +++ b/src/main/java/org/perlonjava/runtime/RuntimeArraySizeLvalue.java @@ -35,4 +35,36 @@ public RuntimeScalar set(RuntimeScalar value) { parent.setLastElementIndex(value); return this; } + + /** + * Performs pre-decrement on the array size ($#array--). + * + * @return The updated array size after decrement. + */ + @Override + public RuntimeScalar preAutoDecrement() { + RuntimeArray parent = lvalue.arrayDeref(); + RuntimeScalar currentSize = new RuntimeScalar(parent.lastElementIndex()); + RuntimeScalar newSize = new RuntimeScalar(currentSize.getInt() - 1); + parent.setLastElementIndex(newSize); + this.value = newSize.value; + this.type = newSize.type; + return newSize; + } + + /** + * Performs post-decrement on the array size ($#array--). + * + * @return The original array size before decrement. + */ + @Override + public RuntimeScalar postAutoDecrement() { + RuntimeArray parent = lvalue.arrayDeref(); + RuntimeScalar originalSize = new RuntimeScalar(parent.lastElementIndex()); + RuntimeScalar newSize = new RuntimeScalar(originalSize.getInt() - 1); + parent.setLastElementIndex(newSize); + this.value = newSize.value; + this.type = newSize.type; + return originalSize; + } } From 11e25fee0cca057fd78439a08366ff32ba305fee Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 28 Jan 2026 10:04:00 +0100 Subject: [PATCH 3/3] Improve array literal slice fix to handle more node types - Refine the 'literal' check to be more precise about what needs LIST context - Only ranges (.. operator) and array slices (@) need special handling - Scalar operations (, , etc.) can use single-element optimization - This improves performance while maintaining correctness --- .../org/perlonjava/codegen/Dereference.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/perlonjava/codegen/Dereference.java b/src/main/java/org/perlonjava/codegen/Dereference.java index e38453eb0..90f3e66fc 100644 --- a/src/main/java/org/perlonjava/codegen/Dereference.java +++ b/src/main/java/org/perlonjava/codegen/Dereference.java @@ -696,17 +696,24 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp ArrayLiteralNode right = (ArrayLiteralNode) node.right; - // Check if all elements are literals (NumberNode, StringNode, etc.) - boolean allLiterals = true; + // Check if all elements are safe for single-element optimization + // (i.e., they can be evaluated in SCALAR context without needing LIST context expansion) + boolean allSafeForSingleElement = true; for (Node elem : right.elements) { - if (!(elem instanceof org.perlonjava.astnode.NumberNode) && - !(elem instanceof org.perlonjava.astnode.StringNode)) { - allLiterals = false; + // Ranges (.. operator) need LIST context to expand to multiple indices + if (elem instanceof BinaryOperatorNode binOp && "..".equals(binOp.operator)) { + allSafeForSingleElement = false; break; } + // Array slices need LIST context + if (elem instanceof OperatorNode opNode && opNode.operator.startsWith("@")) { + allSafeForSingleElement = false; + break; + } + // Most other nodes (NumberNode, StringNode, IdentifierNode, $var, $var[0], etc.) are safe } - if (allLiterals && right.elements.size() == 1) { + if (allSafeForSingleElement && right.elements.size() == 1) { // Single index: use get/delete/exists methods Node elem = right.elements.getFirst(); elem.accept(emitterVisitor.with(RuntimeContextType.SCALAR));