Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/main/java/org/perlonjava/runtime/RuntimeList.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.perlonjava.runtime;

import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -386,6 +387,32 @@ public RuntimeList createListReference() {
*/
public RuntimeArray setFromList(RuntimeList value) {

boolean hasUndefPlaceholderLhs = false;
for (RuntimeBase elem : elements) {
if (elem instanceof RuntimeScalarReadOnly runtimeScalarReadOnly
&& !runtimeScalarReadOnly.getDefinedBoolean()) {
hasUndefPlaceholderLhs = true;
break;
}
}

// Preserve RHS aliases before consuming `value`.
// Only needed when the LHS contains `undef` placeholders.
List<RuntimeScalar> rhsAliasElements = null;
IdentityHashMap<RuntimeScalar, Boolean> lhsScalars = null;
if (hasUndefPlaceholderLhs) {
RuntimeArray rhsAliases = new RuntimeArray();
value.setArrayOfAlias(rhsAliases);
rhsAliasElements = rhsAliases.elements;

lhsScalars = new IdentityHashMap<>();
for (RuntimeBase elem : elements) {
if (elem instanceof RuntimeScalar s && !(s instanceof RuntimeScalarReadOnly)) {
lhsScalars.put(s, Boolean.TRUE);
}
}
}

// Materialize the RHS once into a flat list.
// Avoids O(n^2) from repeated RuntimeArray.shift() which does removeFirst() on ArrayList.
RuntimeArray rhs = new RuntimeArray();
Expand All @@ -402,7 +429,19 @@ public RuntimeArray setFromList(RuntimeList value) {

for (RuntimeBase elem : elements) {
if (elem instanceof RuntimeScalarReadOnly runtimeScalarReadOnly && !runtimeScalarReadOnly.getDefinedBoolean()) {
// Discard one RHS value
// `undef` placeholder on LHS: consume one RHS value, but return something for this position.
// When safe, return the RHS element as an lvalue (alias). Otherwise return the pre-assignment value.
RuntimeScalar rhsValue = (rhsIndex < rhsSize) ? rhsElements.get(rhsIndex) : new RuntimeScalar();
RuntimeScalar rhsAlias = (rhsAliasElements != null && rhsIndex < rhsAliasElements.size())
? rhsAliasElements.get(rhsIndex)
: null;

// If the RHS element is also assigned on the LHS, it must not be returned as an alias
// (it would reflect the post-assignment value rather than the pre-assignment value).
boolean useAlias = rhsAlias != null && lhsScalars != null && !lhsScalars.containsKey(rhsAlias);

result.elements.add(useAlias ? rhsAlias : rhsValue);

if (rhsIndex < rhsSize) {
rhsIndex++;
}
Expand Down