You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
13 KiB
399 lines
13 KiB
/*
|
|
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package java.lang.invoke;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import static java.lang.invoke.LambdaForm.*;
|
|
import static java.lang.invoke.LambdaForm.BasicType.*;
|
|
|
|
/** Working storage for an LF that is being transformed.
|
|
* Similarly to a StringBuffer, the editing can take place in multiple steps.
|
|
*/
|
|
final class LambdaFormBuffer {
|
|
private int arity, length;
|
|
private Name[] names;
|
|
private Name[] originalNames; // snapshot of pre-transaction names
|
|
private byte flags;
|
|
private int firstChange;
|
|
private Name resultName;
|
|
private String debugName;
|
|
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];
|
|
debugName = lf.debugName;
|
|
assert(lf.nameRefsAreLegal());
|
|
}
|
|
|
|
private LambdaForm lambdaForm() {
|
|
assert(!inTrans()); // need endEdit call to tidy things up
|
|
return new LambdaForm(debugName, 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; // keep a record of where everything was to start with
|
|
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];
|
|
}
|
|
// check resultName also
|
|
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, NamedFunction[] fns) {
|
|
for (int i = 0; i < fns.length; i++) {
|
|
if (fns[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;
|
|
}
|
|
|
|
/** We have just overwritten the name at pos1 with the name at pos2.
|
|
* This means that there are two copies of the name, which we will have to fix later.
|
|
*/
|
|
private void noteDuplicate(int pos1, int pos2) {
|
|
Name n = names[pos1];
|
|
assert(n == names[pos2]);
|
|
assert(originalNames[pos1] != null); // something was replaced at pos1
|
|
assert(originalNames[pos2] == null || originalNames[pos2] == n);
|
|
if (dups == null) {
|
|
dups = new ArrayList<>();
|
|
}
|
|
dups.add(n);
|
|
}
|
|
|
|
/** Replace duplicate names by nulls, and remove all nulls. */
|
|
private void clearDuplicatesAndNulls() {
|
|
if (dups != null) {
|
|
// Remove duplicates.
|
|
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; // kill only one dup
|
|
}
|
|
}
|
|
}
|
|
dups.clear();
|
|
}
|
|
// Now that we are done with originalNames, remove "killed" names.
|
|
int oldLength = length;
|
|
for (int i = firstChange; i < length; i++) {
|
|
if (names[i] == null) {
|
|
System.arraycopy(names, i + 1, names, i, (--length - i));
|
|
--i; // restart loop at this position
|
|
}
|
|
}
|
|
if (length < oldLength) {
|
|
Arrays.fill(names, length, oldLength, null);
|
|
}
|
|
assert(!Arrays.asList(names).subList(0, length).contains(null));
|
|
}
|
|
|
|
/** Create a private, writable copy of names.
|
|
* Preserve the original copy, for reference.
|
|
*/
|
|
void startEdit() {
|
|
assert(verifyArity());
|
|
int oc = ownedCount();
|
|
assert(!inTrans()); // no nested transactions
|
|
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 {
|
|
// make a new buffer to hold the names
|
|
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());
|
|
}
|
|
|
|
private void changeName(int i, Name name) {
|
|
assert(inTrans());
|
|
assert(i < length);
|
|
Name oldName = names[i];
|
|
assert(oldName == originalNames[i]); // no multiple changes
|
|
assert(verifyFirstChange());
|
|
if (ownedCount() == 0)
|
|
growNames(0, 0);
|
|
names[i] = name;
|
|
if (firstChange > i) {
|
|
firstChange = i;
|
|
}
|
|
if (resultName != null && resultName == oldName) {
|
|
resultName = name;
|
|
}
|
|
}
|
|
|
|
/** Change the result name. Null means a void result. */
|
|
void setResult(Name name) {
|
|
assert(name == null || lastIndexOf(name) >= 0);
|
|
resultName = name;
|
|
}
|
|
|
|
/** Finish a transaction. */
|
|
LambdaForm endEdit() {
|
|
assert(verifyFirstChange());
|
|
// Assuming names have been changed pairwise from originalNames[i] to names[i],
|
|
// update arguments to ensure referential integrity.
|
|
for (int i = Math.max(firstChange, arity); i < length; i++) {
|
|
Name name = names[i];
|
|
if (name == null) continue; // space for removed duplicate
|
|
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
|
|
// to the right of all 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));
|
|
// copy the exprs just after the last remaining param
|
|
System.arraycopy(exprs, 0, names, argp, exprp);
|
|
// adjust arity
|
|
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;
|
|
}
|
|
|
|
/** Replace any Name whose function is in oldFns with a copy
|
|
* whose function is in the corresponding position in newFns.
|
|
* Only do this if the arguments are exactly equal to the given.
|
|
*/
|
|
LambdaFormBuffer replaceFunctions(NamedFunction[] oldFns, NamedFunction[] newFns,
|
|
Object... forArguments) {
|
|
assert(inTrans());
|
|
if (oldFns.length == 0) 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[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);
|
|
}
|
|
|
|
/** Replace a parameter by a fresh parameter. */
|
|
LambdaFormBuffer renameParameter(int pos, Name newParam) {
|
|
assert(newParam.isParam());
|
|
replaceName(pos, newParam);
|
|
return this;
|
|
}
|
|
|
|
/** Replace a parameter by a fresh expression. */
|
|
LambdaFormBuffer replaceParameterByNewExpression(int pos, Name binding) {
|
|
assert(!binding.isParam());
|
|
assert(lastIndexOf(binding) < 0); // else use replaceParameterByCopy
|
|
replaceName(pos, binding);
|
|
return this;
|
|
}
|
|
|
|
/** Replace a parameter by another parameter or expression already in the form. */
|
|
LambdaFormBuffer replaceParameterByCopy(int pos, int valuePos) {
|
|
assert(pos != valuePos);
|
|
replaceName(pos, names[valuePos]);
|
|
noteDuplicate(pos, valuePos); // temporarily, will occur twice in the names array
|
|
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);
|
|
}
|
|
|
|
/** Insert a fresh expression. */
|
|
LambdaFormBuffer insertExpression(int pos, Name expr) {
|
|
assert(!expr.isParam());
|
|
insertName(pos, expr, false);
|
|
return this;
|
|
}
|
|
|
|
/** Insert a fresh parameter. */
|
|
LambdaFormBuffer insertParameter(int pos, Name param) {
|
|
assert(param.isParam());
|
|
insertName(pos, param, true);
|
|
return this;
|
|
}
|
|
}
|