blob: bcf1733461b204cccdec6b7179cdc20de154341e [file] [log] [blame]
/*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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 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()); // need endEdit call to tidy things up
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; // 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());
}
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;
}
}