|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.lang.invoke; |
|
|
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.List; |
|
|
|
import static java.lang.invoke.LambdaForm.*; |
|
import static java.lang.invoke.LambdaForm.BasicType.*; |
|
|
|
|
|
|
|
*/ |
|
final class LambdaFormBuffer { |
|
private int arity, length; |
|
private Name[] names; |
|
private Name[] originalNames; |
|
private byte flags; |
|
private int firstChange; |
|
private Name resultName; |
|
private ArrayList<Name> dups; |
|
|
|
private static final int F_TRANS = 0x10, F_OWNED = 0x03; |
|
|
|
LambdaFormBuffer(LambdaForm lf) { |
|
this.arity = lf.arity; |
|
setNames(lf.names); |
|
int result = lf.result; |
|
if (result == LAST_RESULT) result = length - 1; |
|
if (result >= 0 && lf.names[result].type != V_TYPE) { |
|
resultName = lf.names[result]; |
|
} |
|
assert(lf.nameRefsAreLegal()); |
|
} |
|
|
|
private LambdaForm lambdaForm() { |
|
assert(!inTrans()); |
|
return new LambdaForm(arity, nameArray(), resultIndex()); |
|
} |
|
|
|
Name name(int i) { |
|
assert(i < length); |
|
return names[i]; |
|
} |
|
|
|
Name[] nameArray() { |
|
return Arrays.copyOf(names, length); |
|
} |
|
|
|
int resultIndex() { |
|
if (resultName == null) return VOID_RESULT; |
|
int index = indexOf(resultName, names); |
|
assert(index >= 0); |
|
return index; |
|
} |
|
|
|
void setNames(Name[] names2) { |
|
names = originalNames = names2; |
|
length = names2.length; |
|
flags = 0; |
|
} |
|
|
|
private boolean verifyArity() { |
|
for (int i = 0; i < arity && i < firstChange; i++) { |
|
assert(names[i].isParam()) : "#" + i + "=" + names[i]; |
|
} |
|
for (int i = arity; i < length; i++) { |
|
assert(!names[i].isParam()) : "#" + i + "=" + names[i]; |
|
} |
|
for (int i = length; i < names.length; i++) { |
|
assert(names[i] == null) : "#" + i + "=" + names[i]; |
|
} |
|
|
|
if (resultName != null) { |
|
int resultIndex = indexOf(resultName, names); |
|
assert(resultIndex >= 0) : "not found: " + resultName.exprString() + Arrays.asList(names); |
|
assert(names[resultIndex] == resultName); |
|
} |
|
return true; |
|
} |
|
|
|
private boolean verifyFirstChange() { |
|
assert(inTrans()); |
|
for (int i = 0; i < length; i++) { |
|
if (names[i] != originalNames[i]) { |
|
assert(firstChange == i) : Arrays.asList(firstChange, i, originalNames[i].exprString(), Arrays.asList(names)); |
|
return true; |
|
} |
|
} |
|
assert(firstChange == length) : Arrays.asList(firstChange, Arrays.asList(names)); |
|
return true; |
|
} |
|
|
|
private static int indexOf(NamedFunction fn, List<NamedFunction> fns) { |
|
for (int i = 0; i < fns.size(); i++) { |
|
if (fns.get(i) == fn) return i; |
|
} |
|
return -1; |
|
} |
|
|
|
private static int indexOf(Name n, Name[] ns) { |
|
for (int i = 0; i < ns.length; i++) { |
|
if (ns[i] == n) return i; |
|
} |
|
return -1; |
|
} |
|
|
|
boolean inTrans() { |
|
return (flags & F_TRANS) != 0; |
|
} |
|
|
|
int ownedCount() { |
|
return flags & F_OWNED; |
|
} |
|
|
|
void growNames(int insertPos, int growLength) { |
|
int oldLength = length; |
|
int newLength = oldLength + growLength; |
|
int oc = ownedCount(); |
|
if (oc == 0 || newLength > names.length) { |
|
names = Arrays.copyOf(names, (names.length + growLength) * 5 / 4); |
|
if (oc == 0) { |
|
flags++; |
|
oc++; |
|
assert(ownedCount() == oc); |
|
} |
|
} |
|
if (originalNames != null && originalNames.length < names.length) { |
|
originalNames = Arrays.copyOf(originalNames, names.length); |
|
if (oc == 1) { |
|
flags++; |
|
oc++; |
|
assert(ownedCount() == oc); |
|
} |
|
} |
|
if (growLength == 0) return; |
|
int insertEnd = insertPos + growLength; |
|
int tailLength = oldLength - insertPos; |
|
System.arraycopy(names, insertPos, names, insertEnd, tailLength); |
|
Arrays.fill(names, insertPos, insertEnd, null); |
|
if (originalNames != null) { |
|
System.arraycopy(originalNames, insertPos, originalNames, insertEnd, tailLength); |
|
Arrays.fill(originalNames, insertPos, insertEnd, null); |
|
} |
|
length = newLength; |
|
if (firstChange >= insertPos) { |
|
firstChange += growLength; |
|
} |
|
} |
|
|
|
int lastIndexOf(Name n) { |
|
int result = -1; |
|
for (int i = 0; i < length; i++) { |
|
if (names[i] == n) result = i; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void noteDuplicate(int pos1, int pos2) { |
|
Name n = names[pos1]; |
|
assert(n == names[pos2]); |
|
assert(originalNames[pos1] != null); |
|
assert(originalNames[pos2] == null || originalNames[pos2] == n); |
|
if (dups == null) { |
|
dups = new ArrayList<>(); |
|
} |
|
dups.add(n); |
|
} |
|
|
|
|
|
private void clearDuplicatesAndNulls() { |
|
if (dups != null) { |
|
|
|
assert(ownedCount() >= 1); |
|
for (Name dup : dups) { |
|
for (int i = firstChange; i < length; i++) { |
|
if (names[i] == dup && originalNames[i] != dup) { |
|
names[i] = null; |
|
assert(Arrays.asList(names).contains(dup)); |
|
break; |
|
} |
|
} |
|
} |
|
dups.clear(); |
|
} |
|
|
|
int oldLength = length; |
|
for (int i = firstChange; i < length; i++) { |
|
if (names[i] == null) { |
|
System.arraycopy(names, i + 1, names, i, (--length - i)); |
|
--i; |
|
} |
|
} |
|
if (length < oldLength) { |
|
Arrays.fill(names, length, oldLength, null); |
|
} |
|
assert(!Arrays.asList(names).subList(0, length).contains(null)); |
|
} |
|
|
|
|
|
|
|
*/ |
|
void startEdit() { |
|
assert(verifyArity()); |
|
int oc = ownedCount(); |
|
assert(!inTrans()); |
|
flags |= F_TRANS; |
|
Name[] oldNames = names; |
|
Name[] ownBuffer = (oc == 2 ? originalNames : null); |
|
assert(ownBuffer != oldNames); |
|
if (ownBuffer != null && ownBuffer.length >= length) { |
|
names = copyNamesInto(ownBuffer); |
|
} else { |
|
|
|
final int SLOP = 2; |
|
names = Arrays.copyOf(oldNames, Math.max(length + SLOP, oldNames.length)); |
|
if (oc < 2) ++flags; |
|
assert(ownedCount() == oc + 1); |
|
} |
|
originalNames = oldNames; |
|
assert(originalNames != names); |
|
firstChange = length; |
|
assert(inTrans()); |
|
} |
|
|
|
void changeName(int i, Name name) { |
|
assert(inTrans()); |
|
assert(i < length); |
|
Name oldName = names[i]; |
|
assert(oldName == originalNames[i]); |
|
assert(verifyFirstChange()); |
|
if (ownedCount() == 0) |
|
growNames(0, 0); |
|
names[i] = name; |
|
if (firstChange > i) { |
|
firstChange = i; |
|
} |
|
if (resultName != null && resultName == oldName) { |
|
resultName = name; |
|
} |
|
} |
|
|
|
|
|
void setResult(Name name) { |
|
assert(name == null || lastIndexOf(name) >= 0); |
|
resultName = name; |
|
} |
|
|
|
|
|
LambdaForm endEdit() { |
|
assert(verifyFirstChange()); |
|
// Assuming names have been changed pairwise from originalNames[i] to names[i], |
|
|
|
for (int i = Math.max(firstChange, arity); i < length; i++) { |
|
Name name = names[i]; |
|
if (name == null) continue; |
|
Name newName = name.replaceNames(originalNames, names, firstChange, i); |
|
if (newName != name) { |
|
names[i] = newName; |
|
if (resultName == name) { |
|
resultName = newName; |
|
} |
|
} |
|
} |
|
assert(inTrans()); |
|
flags &= ~F_TRANS; |
|
clearDuplicatesAndNulls(); |
|
originalNames = null; |
|
// If any parameters have been changed, then reorder them as needed. |
|
// This is a "sheep-and-goats" stable sort, pushing all non-parameters |
|
|
|
if (firstChange < arity) { |
|
Name[] exprs = new Name[arity - firstChange]; |
|
int argp = firstChange, exprp = 0; |
|
for (int i = firstChange; i < arity; i++) { |
|
Name name = names[i]; |
|
if (name.isParam()) { |
|
names[argp++] = name; |
|
} else { |
|
exprs[exprp++] = name; |
|
} |
|
} |
|
assert(exprp == (arity - argp)); |
|
|
|
System.arraycopy(exprs, 0, names, argp, exprp); |
|
|
|
arity -= exprp; |
|
} |
|
assert(verifyArity()); |
|
return lambdaForm(); |
|
} |
|
|
|
private Name[] copyNamesInto(Name[] buffer) { |
|
System.arraycopy(names, 0, buffer, 0, length); |
|
Arrays.fill(buffer, length, buffer.length, null); |
|
return buffer; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
LambdaFormBuffer replaceFunctions(List<NamedFunction> oldFns, List<NamedFunction> newFns, |
|
Object... forArguments) { |
|
assert(inTrans()); |
|
if (oldFns.isEmpty()) return this; |
|
for (int i = arity; i < length; i++) { |
|
Name n = names[i]; |
|
int nfi = indexOf(n.function, oldFns); |
|
if (nfi >= 0 && Arrays.equals(n.arguments, forArguments)) { |
|
changeName(i, new Name(newFns.get(nfi), n.arguments)); |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
private void replaceName(int pos, Name binding) { |
|
assert(inTrans()); |
|
assert(verifyArity()); |
|
assert(pos < arity); |
|
Name param = names[pos]; |
|
assert(param.isParam()); |
|
assert(param.type == binding.type); |
|
changeName(pos, binding); |
|
} |
|
|
|
|
|
LambdaFormBuffer renameParameter(int pos, Name newParam) { |
|
assert(newParam.isParam()); |
|
replaceName(pos, newParam); |
|
return this; |
|
} |
|
|
|
|
|
LambdaFormBuffer replaceParameterByNewExpression(int pos, Name binding) { |
|
assert(!binding.isParam()); |
|
assert(lastIndexOf(binding) < 0); |
|
replaceName(pos, binding); |
|
return this; |
|
} |
|
|
|
|
|
LambdaFormBuffer replaceParameterByCopy(int pos, int valuePos) { |
|
assert(pos != valuePos); |
|
replaceName(pos, names[valuePos]); |
|
noteDuplicate(pos, valuePos); |
|
return this; |
|
} |
|
|
|
private void insertName(int pos, Name expr, boolean isParameter) { |
|
assert(inTrans()); |
|
assert(verifyArity()); |
|
assert(isParameter ? pos <= arity : pos >= arity); |
|
growNames(pos, 1); |
|
if (isParameter) arity += 1; |
|
changeName(pos, expr); |
|
} |
|
|
|
|
|
LambdaFormBuffer insertExpression(int pos, Name expr) { |
|
assert(!expr.isParam()); |
|
insertName(pos, expr, false); |
|
return this; |
|
} |
|
|
|
|
|
LambdaFormBuffer insertParameter(int pos, Name param) { |
|
assert(param.isParam()); |
|
insertName(pos, param, true); |
|
return this; |
|
} |
|
} |