| /* |
| * 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; |
| } |
| } |