|
10 | 10 | import static org.perlonjava.runtime.runtimetypes.RuntimeScalarCache.scalarTrue; |
11 | 11 |
|
12 | 12 | public class ListOperators { |
| 13 | + /** |
| 14 | + * Perl evaluates the input list before entering a map/grep-style block. |
| 15 | + * Keep the scalar objects (and therefore their aliasing) but detach the |
| 16 | + * iteration order from a source array that the block may mutate. |
| 17 | + */ |
| 18 | + private static List<RuntimeScalar> snapshotElements(RuntimeList runtimeList) { |
| 19 | + List<RuntimeScalar> snapshot = new ArrayList<>(); |
| 20 | + for (RuntimeScalar element : runtimeList) { |
| 21 | + snapshot.add(element); |
| 22 | + } |
| 23 | + return snapshot; |
| 24 | + } |
| 25 | + |
13 | 26 | /** |
14 | 27 | * Eagerly release captured variable references from an ephemeral grep/map/all/any |
15 | 28 | * block closure. Like eval BLOCK closures, these blocks execute and are immediately |
@@ -55,8 +68,8 @@ public static RuntimeList map(RuntimeList runtimeList, RuntimeScalar perlMapClos |
55 | 68 | // This allows $_[0], $_[1], etc. to work inside map blocks |
56 | 69 | RuntimeArray mapArgs = outerArgs != null ? outerArgs : new RuntimeArray(); |
57 | 70 |
|
58 | | - // Iterate over each element in the current RuntimeArray |
59 | | - for (RuntimeScalar element : runtimeList) { |
| 71 | + // Iterate over the list value captured before the block starts. |
| 72 | + for (RuntimeScalar element : snapshotElements(runtimeList)) { |
60 | 73 | // Create $_ argument for the map subroutine |
61 | 74 | GlobalVariable.aliasTemporaryGlobalVariable("main::_", element); |
62 | 75 |
|
@@ -244,8 +257,8 @@ public static RuntimeList grep(RuntimeList runtimeList, RuntimeScalar perlFilter |
244 | 257 | // Use the outer @_ instead of an empty array |
245 | 258 | RuntimeArray filterArgs = outerArgs != null ? outerArgs : new RuntimeArray(); |
246 | 259 |
|
247 | | - // Iterate over each element in the current RuntimeArray |
248 | | - for (RuntimeScalar element : runtimeList) { |
| 260 | + // Iterate over the list value captured before the block starts. |
| 261 | + for (RuntimeScalar element : snapshotElements(runtimeList)) { |
249 | 262 | try { |
250 | 263 | // Create $_ argument for the filter subroutine |
251 | 264 | GlobalVariable.aliasTemporaryGlobalVariable("main::_", element); |
@@ -320,8 +333,8 @@ public static RuntimeList all(RuntimeList runtimeList, RuntimeScalar perlFilterC |
320 | 333 | try { |
321 | 334 | RuntimeArray filterArgs = outerArgs != null ? outerArgs : new RuntimeArray(); |
322 | 335 |
|
323 | | - // Iterate over each element in the current RuntimeArray |
324 | | - for (RuntimeScalar element : runtimeList) { |
| 336 | + // Iterate over the list value captured before the block starts. |
| 337 | + for (RuntimeScalar element : snapshotElements(runtimeList)) { |
325 | 338 | try { |
326 | 339 | // Create $_ argument for the filter subroutine |
327 | 340 | GlobalVariable.aliasTemporaryGlobalVariable("main::_", element); |
@@ -380,8 +393,8 @@ public static RuntimeList any(RuntimeList runtimeList, RuntimeScalar perlFilterC |
380 | 393 | try { |
381 | 394 | RuntimeArray filterArgs = outerArgs != null ? outerArgs : new RuntimeArray(); |
382 | 395 |
|
383 | | - // Iterate over each element in the current RuntimeArray |
384 | | - for (RuntimeScalar element : runtimeList) { |
| 396 | + // Iterate over the list value captured before the block starts. |
| 397 | + for (RuntimeScalar element : snapshotElements(runtimeList)) { |
385 | 398 | try { |
386 | 399 | // Create $_ argument for the filter subroutine |
387 | 400 | GlobalVariable.aliasTemporaryGlobalVariable("main::_", element); |
|
0 commit comments