diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java
index 20461e7f7..4256e1c56 100644
--- a/src/main/java/org/perlonjava/core/Configuration.java
+++ b/src/main/java/org/perlonjava/core/Configuration.java
@@ -33,7 +33,7 @@ public final class Configuration {
* Automatically populated by Gradle/Maven during build.
* DO NOT EDIT MANUALLY - this value is replaced at build time.
*/
- public static final String gitCommitId = "ea174deff";
+ public static final String gitCommitId = "36a15d5a2";
/**
* Git commit date of the build (ISO format: YYYY-MM-DD).
diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java
index 8549cf816..21a7efdb7 100644
--- a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java
+++ b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java
@@ -2186,9 +2186,14 @@ public static String getCurrentPackage() {
}
/**
- * Replace lazy {@link ScalarSpecialVariable} references ($1, $&, etc.) in a return list
- * with concrete {@link RuntimeScalar} copies. Must be called BEFORE {@link RegexState#restore()}
- * so that the values reflect the subroutine's regex state, not the caller's.
+ * Replace lazy {@link ScalarSpecialVariable} references ($1, $&, etc.) and
+ * {@link RuntimeArray} references in a return list with concrete copies.
+ * Must be called BEFORE {@link RegexState#restore()} and local variable restoration
+ * so that the values reflect the subroutine's state, not the caller's.
+ *
+ *
This is critical for returning localized arrays (e.g., {@code local @ARGV}).
+ * Without this, the array reference in the return list would point to the restored
+ * (original) values after the local scope exits.
*/
public static void materializeSpecialVarsInResult(RuntimeList result) {
List elems = result.elements;
@@ -2200,6 +2205,21 @@ public static void materializeSpecialVarsInResult(RuntimeList result) {
concrete.type = resolved.type;
concrete.value = resolved.value;
elems.set(i, concrete);
+ } else if (elem instanceof RuntimeArray arr) {
+ // Copy array elements to ensure independence from local restoration.
+ // Replace the RuntimeArray reference with a new RuntimeArray containing copies.
+ RuntimeArray copy = new RuntimeArray();
+ for (RuntimeScalar arrElem : arr.elements) {
+ copy.elements.add(arrElem == null ? null : new RuntimeScalar(arrElem));
+ }
+ elems.set(i, copy);
+ } else if (elem instanceof RuntimeHash hash) {
+ // Copy hash elements for the same reason as arrays
+ RuntimeHash copy = new RuntimeHash();
+ for (var entry : hash.elements.entrySet()) {
+ copy.elements.put(entry.getKey(), new RuntimeScalar(entry.getValue()));
+ }
+ elems.set(i, copy);
}
}
}
diff --git a/src/test/resources/unit/local.t b/src/test/resources/unit/local.t
index b6e6557a3..cd8b7684b 100644
--- a/src/test/resources/unit/local.t
+++ b/src/test/resources/unit/local.t
@@ -223,6 +223,60 @@ ok(scalar(@global_array) < 10, 'local array element restored, array size ' . sca
is(NestedTest::outer(), 0, 'nested sub with our - after local');
}
+# Test for returning local array from subroutine
+# Regression test: the returned array should contain the localized values,
+# not the restored original values
+{
+ our @test_array = ("original1", "original2");
+
+ sub return_local_array {
+ local @test_array = ("local1", "local2", "local3");
+ return @test_array;
+ }
+
+ my @result = return_local_array();
+ is(scalar(@result), 3, 'returned local array has correct size');
+ is($result[0], "local1", 'returned local array element 0 is localized value');
+ is($result[1], "local2", 'returned local array element 1 is localized value');
+ is($result[2], "local3", 'returned local array element 2 is localized value');
+ is(scalar(@test_array), 2, 'original array restored after return');
+ is($test_array[0], "original1", 'original array element 0 restored');
+}
+
+# Test for returning modified local array from subroutine
+{
+ our @modify_array = ("a", "b", "c");
+
+ sub modify_and_return_local {
+ local @modify_array = @modify_array;
+ @modify_array = ("x"); # Modify the localized copy
+ return ("result", @modify_array);
+ }
+
+ my @result = modify_and_return_local();
+ is($result[0], "result", 'first return value is scalar');
+ is($result[1], "x", 'returned local array contains modified value');
+ is(scalar(@result), 2, 'return list has correct size');
+ is(join(",", @modify_array), "a,b,c", 'original array restored after return');
+}
+
+# Test for returning local hash from subroutine
+{
+ our %test_hash = (orig_key => "orig_value");
+
+ sub return_local_hash {
+ local %test_hash = (local_key => "local_value");
+ return %test_hash;
+ }
+
+ my %result = return_local_hash();
+ ok(exists $result{local_key}, 'returned local hash has local key');
+ is($result{local_key}, "local_value", 'returned local hash has local value');
+ ok(!exists $result{orig_key}, 'returned local hash does not have original key');
+ ok(exists $test_hash{orig_key}, 'original hash restored after return');
+ is($test_hash{orig_key}, "orig_value", 'original hash value restored');
+}
+
done_testing();
__END__