提交 aad77330 编写于 作者: V vlivanov

8057042: LambdaFormEditor: derive new LFs from a base LF

Reviewed-by: vlivanov, psandoz
Contributed-by: john.r.rose@oracle.com
上级 a859b764
...@@ -54,6 +54,7 @@ import jdk.internal.org.objectweb.asm.Type; ...@@ -54,6 +54,7 @@ import jdk.internal.org.objectweb.asm.Type;
/*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) { /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
super(type, form); super(type, form);
assert(speciesData() == speciesData(form));
} }
// //
...@@ -81,6 +82,11 @@ import jdk.internal.org.objectweb.asm.Type; ...@@ -81,6 +82,11 @@ import jdk.internal.org.objectweb.asm.Type;
} }
} }
/*non-public*/
LambdaFormEditor editor() {
return form.editor();
}
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) { static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
return Species_L.make(type, form, x); return Species_L.make(type, form, x);
} }
...@@ -88,33 +94,23 @@ import jdk.internal.org.objectweb.asm.Type; ...@@ -88,33 +94,23 @@ import jdk.internal.org.objectweb.asm.Type;
@Override // there is a default binder in the super class, for 'L' types only @Override // there is a default binder in the super class, for 'L' types only
/*non-public*/ /*non-public*/
BoundMethodHandle bindArgumentL(int pos, Object value) { BoundMethodHandle bindArgumentL(int pos, Object value) {
MethodType type = type().dropParameterTypes(pos, pos+1); return editor().bindArgumentL(this, pos, value);
LambdaForm form = internalForm().bind(1+pos, speciesData());
return copyWithExtendL(type, form, value);
} }
/*non-public*/ /*non-public*/
BoundMethodHandle bindArgumentI(int pos, int value) { BoundMethodHandle bindArgumentI(int pos, int value) {
MethodType type = type().dropParameterTypes(pos, pos+1); return editor().bindArgumentI(this, pos, value);
LambdaForm form = internalForm().bind(1+pos, speciesData());
return copyWithExtendI(type, form, value);
} }
/*non-public*/ /*non-public*/
BoundMethodHandle bindArgumentJ(int pos, long value) { BoundMethodHandle bindArgumentJ(int pos, long value) {
MethodType type = type().dropParameterTypes(pos, pos+1); return editor().bindArgumentJ(this, pos, value);
LambdaForm form = internalForm().bind(1+pos, speciesData());
return copyWithExtendJ(type, form, value);
} }
/*non-public*/ /*non-public*/
BoundMethodHandle bindArgumentF(int pos, float value) { BoundMethodHandle bindArgumentF(int pos, float value) {
MethodType type = type().dropParameterTypes(pos, pos+1); return editor().bindArgumentF(this, pos, value);
LambdaForm form = internalForm().bind(1+pos, speciesData());
return copyWithExtendF(type, form, value);
} }
/*non-public*/ /*non-public*/
BoundMethodHandle bindArgumentD(int pos, double value) { BoundMethodHandle bindArgumentD(int pos, double value) {
MethodType type = type().dropParameterTypes(pos, pos + 1); return editor().bindArgumentD(this, pos, value);
LambdaForm form = internalForm().bind(1+pos, speciesData());
return copyWithExtendD(type, form, value);
} }
@Override @Override
...@@ -149,6 +145,14 @@ import jdk.internal.org.objectweb.asm.Type; ...@@ -149,6 +145,14 @@ import jdk.internal.org.objectweb.asm.Type;
*/ */
/*non-public*/ abstract SpeciesData speciesData(); /*non-public*/ abstract SpeciesData speciesData();
/*non-public*/ static SpeciesData speciesData(LambdaForm form) {
Object c = form.names[0].constraint;
if (c instanceof SpeciesData)
return (SpeciesData) c;
// if there is no BMH constraint, then use the null constraint
return SpeciesData.EMPTY;
}
/** /**
* Return the number of fields in this BMH. Equivalent to speciesData().fieldCount(). * Return the number of fields in this BMH. Equivalent to speciesData().fieldCount().
*/ */
......
...@@ -124,8 +124,7 @@ class LambdaForm { ...@@ -124,8 +124,7 @@ class LambdaForm {
MemberName vmentry; // low-level behavior, or null if not yet prepared MemberName vmentry; // low-level behavior, or null if not yet prepared
private boolean isCompiled; private boolean isCompiled;
// Caches for common structural transforms: Object transformCache; // managed by LambdaFormEditor
LambdaForm[] bindCache;
public static final int VOID_RESULT = -1, LAST_RESULT = -2; public static final int VOID_RESULT = -1, LAST_RESULT = -2;
...@@ -213,6 +212,13 @@ class LambdaForm { ...@@ -213,6 +212,13 @@ class LambdaForm {
} }
return btypes; return btypes;
} }
static byte[] basicTypesOrd(BasicType[] btypes) {
byte[] ords = new byte[btypes.length];
for (int i = 0; i < btypes.length; i++) {
ords[i] = (byte)btypes[i].ordinal();
}
return ords;
}
static boolean isBasicTypeChar(char c) { static boolean isBasicTypeChar(char c) {
return "LIJFDV".indexOf(c) >= 0; return "LIJFDV".indexOf(c) >= 0;
} }
...@@ -408,7 +414,7 @@ class LambdaForm { ...@@ -408,7 +414,7 @@ class LambdaForm {
* This allows Name references to be freely reused to construct * This allows Name references to be freely reused to construct
* fresh lambdas, without confusion. * fresh lambdas, without confusion.
*/ */
private boolean nameRefsAreLegal() { boolean nameRefsAreLegal() {
assert(arity >= 0 && arity <= names.length); assert(arity >= 0 && arity <= names.length);
assert(result >= -1 && result < names.length); assert(result >= -1 && result < names.length);
// Do all names possess an index consistent with their local definition order? // Do all names possess an index consistent with their local definition order?
...@@ -887,87 +893,8 @@ class LambdaForm { ...@@ -887,87 +893,8 @@ class LambdaForm {
public int hashCode() { public int hashCode() {
return result + 31 * Arrays.hashCode(names); return result + 31 * Arrays.hashCode(names);
} }
LambdaFormEditor editor() {
LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) { return LambdaFormEditor.lambdaFormEditor(this);
Name name = names[namePos];
BoundMethodHandle.SpeciesData newData = oldData.extendWith(name.type);
return bind(name, new Name(newData.getterFunction(oldData.fieldCount()), names[0]), oldData, newData);
}
LambdaForm bind(Name name, Name binding,
BoundMethodHandle.SpeciesData oldData,
BoundMethodHandle.SpeciesData newData) {
int pos = name.index;
assert(name.isParam());
assert(!binding.isParam());
assert(name.type == binding.type);
assert(0 <= pos && pos < arity && names[pos] == name);
assert(binding.function.memberDeclaringClassOrNull() == newData.fieldHolder());
assert(oldData.getterFunctions().length == newData.getterFunctions().length-1);
if (bindCache != null) {
LambdaForm form = bindCache[pos];
if (form != null) {
assert(form.contains(binding)) : "form << " + form + " >> does not contain binding << " + binding + " >>";
return form;
}
} else {
bindCache = new LambdaForm[arity];
}
assert(nameRefsAreLegal());
int arity2 = arity-1;
Name[] names2 = names.clone();
names2[pos] = binding; // we might move this in a moment
// The newly created LF will run with a different BMH.
// Switch over any pre-existing BMH field references to the new BMH class.
int firstOldRef = -1;
for (int i = 0; i < names2.length; i++) {
Name n = names[i];
if (n.function != null &&
n.function.memberDeclaringClassOrNull() == oldData.fieldHolder()) {
MethodHandle oldGetter = n.function.resolvedHandle;
MethodHandle newGetter = null;
for (int j = 0; j < oldData.getterHandles().length; j++) {
if (oldGetter == oldData.getterHandles()[j])
newGetter = newData.getterHandles()[j];
}
if (newGetter != null) {
if (firstOldRef < 0) firstOldRef = i;
Name n2 = new Name(newGetter, n.arguments);
names2[i] = n2;
}
}
}
// Walk over the new list of names once, in forward order.
// Replace references to 'name' with 'binding'.
// Replace data structure references to the old BMH species with the new.
// This might cause a ripple effect, but it will settle in one pass.
assert(firstOldRef < 0 || firstOldRef > pos);
for (int i = pos+1; i < names2.length; i++) {
if (i <= arity2) continue;
names2[i] = names2[i].replaceNames(names, names2, pos, i);
}
// (a0, a1, name=a2, a3, a4) => (a0, a1, a3, a4, binding)
int insPos = pos;
for (; insPos+1 < names2.length; insPos++) {
Name n = names2[insPos+1];
if (n.isParam()) {
names2[insPos] = n;
} else {
break;
}
}
names2[insPos] = binding;
// Since we moved some stuff, maybe update the result reference:
int result2 = result;
if (result2 == pos)
result2 = insPos;
else if (result2 > pos && result2 <= insPos)
result2 -= 1;
return bindCache[pos] = new LambdaForm(debugName, arity2, names2, result2);
} }
boolean contains(Name name) { boolean contains(Name name) {
......
/*
* Copyright (c) 2013, 2014, 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 String debugName;
private ArrayList<Name> dups;
private static final int F_TRANS = 0x10, F_OWNED = 0x03;
LambdaFormBuffer(LambdaForm lf) {
this(lf.arity, lf.names, lf.result);
debugName = lf.debugName;
assert(lf.nameRefsAreLegal());
}
private LambdaFormBuffer(int arity, Name[] names, int result) {
this.arity = arity;
setNames(names);
if (result == LAST_RESULT) result = length - 1;
if (result >= 0 && names[result].type != V_TYPE)
resultName = names[result];
}
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. */
void 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());
}
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;
}
}
/*
* Copyright (c) 2014, 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.Arrays;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import sun.invoke.util.Wrapper;
/** Transforms on LFs.
* A lambda-form editor can derive new LFs from its base LF.
* The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes.
* To support this caching, a LF has an optional pointer to its editor.
*/
class LambdaFormEditor {
final LambdaForm lambdaForm;
private LambdaFormEditor(LambdaForm lambdaForm) {
this.lambdaForm = lambdaForm;
}
// Factory method.
static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
// TO DO: Consider placing intern logic here, to cut down on duplication.
// lambdaForm = findPreexistingEquivalent(lambdaForm)
return new LambdaFormEditor(lambdaForm);
}
/** A description of a cached transform, possibly associated with the result of the transform.
* The logical content is a sequence of byte values, starting with a Kind.ordinal value.
* The sequence is unterminated, ending with an indefinite number of zero bytes.
* Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
*/
private static final class Transform {
final long packedBytes;
final byte[] fullBytes;
final LambdaForm result; // result of transform, or null, if there is none available
private enum Kind {
NO_KIND, // necessary because ordinal must be greater than zero
BIND_ARG, ADD_ARG, DUP_ARG,
SPREAD_ARGS,
FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
FOLD_ARGS, FOLD_ARGS_TO_VOID,
PERMUTE_ARGS
//maybe add more for guard with test, catch exception, pointwise type conversions
}
private static final boolean STRESS_TEST = false; // turn on to disable most packing
private static final int
PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4),
PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1,
PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE);
private static long packedBytes(byte[] bytes) {
if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0;
long pb = 0;
int bitset = 0;
for (int i = 0; i < bytes.length; i++) {
int b = bytes[i] & 0xFF;
bitset |= b;
pb |= (long)b << (i * PACKED_BYTE_SIZE);
}
if (!inRange(bitset))
return 0;
return pb;
}
private static long packedBytes(int b0, int b1) {
assert(inRange(b0 | b1));
return ( (b0 << 0*PACKED_BYTE_SIZE)
| (b1 << 1*PACKED_BYTE_SIZE));
}
private static long packedBytes(int b0, int b1, int b2) {
assert(inRange(b0 | b1 | b2));
return ( (b0 << 0*PACKED_BYTE_SIZE)
| (b1 << 1*PACKED_BYTE_SIZE)
| (b2 << 2*PACKED_BYTE_SIZE));
}
private static long packedBytes(int b0, int b1, int b2, int b3) {
assert(inRange(b0 | b1 | b2 | b3));
return ( (b0 << 0*PACKED_BYTE_SIZE)
| (b1 << 1*PACKED_BYTE_SIZE)
| (b2 << 2*PACKED_BYTE_SIZE)
| (b3 << 3*PACKED_BYTE_SIZE));
}
private static boolean inRange(int bitset) {
assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte
return ((bitset & ~PACKED_BYTE_MASK) == 0);
}
private static byte[] fullBytes(int... byteValues) {
byte[] bytes = new byte[byteValues.length];
int i = 0;
for (int bv : byteValues) {
bytes[i++] = bval(bv);
}
assert(packedBytes(bytes) == 0);
return bytes;
}
private byte byteAt(int i) {
long pb = packedBytes;
if (pb == 0) {
if (i >= fullBytes.length) return 0;
return fullBytes[i];
}
assert(fullBytes == null);
if (i > PACKED_BYTE_MAX_LENGTH) return 0;
int pos = (i * PACKED_BYTE_SIZE);
return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
}
Kind kind() { return Kind.values()[byteAt(0)]; }
private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
this.packedBytes = packedBytes;
this.fullBytes = fullBytes;
this.result = result;
}
private Transform(long packedBytes) {
this(packedBytes, null, null);
assert(packedBytes != 0);
}
private Transform(byte[] fullBytes) {
this(0, fullBytes, null);
}
private static byte bval(int b) {
assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
return (byte)b;
}
private static byte bval(Kind k) {
return bval(k.ordinal());
}
static Transform of(Kind k, int b1) {
byte b0 = bval(k);
if (inRange(b0 | b1))
return new Transform(packedBytes(b0, b1));
else
return new Transform(fullBytes(b0, b1));
}
static Transform of(Kind k, int b1, int b2) {
byte b0 = (byte) k.ordinal();
if (inRange(b0 | b1 | b2))
return new Transform(packedBytes(b0, b1, b2));
else
return new Transform(fullBytes(b0, b1, b2));
}
static Transform of(Kind k, int b1, int b2, int b3) {
byte b0 = (byte) k.ordinal();
if (inRange(b0 | b1 | b2 | b3))
return new Transform(packedBytes(b0, b1, b2, b3));
else
return new Transform(fullBytes(b0, b1, b2, b3));
}
private static final byte[] NO_BYTES = {};
static Transform of(Kind k, int... b123) {
return ofBothArrays(k, b123, NO_BYTES);
}
static Transform of(Kind k, int b1, byte[] b234) {
return ofBothArrays(k, new int[]{ b1 }, b234);
}
static Transform of(Kind k, int b1, int b2, byte[] b345) {
return ofBothArrays(k, new int[]{ b1, b2 }, b345);
}
private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) {
byte[] fullBytes = new byte[1 + b123.length + b456.length];
int i = 0;
fullBytes[i++] = bval(k);
for (int bv : b123) {
fullBytes[i++] = bval(bv);
}
for (byte bv : b456) {
fullBytes[i++] = bv;
}
long packedBytes = packedBytes(fullBytes);
if (packedBytes != 0)
return new Transform(packedBytes);
else
return new Transform(fullBytes);
}
Transform withResult(LambdaForm result) {
return new Transform(this.packedBytes, this.fullBytes, result);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Transform && equals((Transform)obj);
}
public boolean equals(Transform that) {
return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes);
}
@Override
public int hashCode() {
if (packedBytes != 0) {
assert(fullBytes == null);
return Long.hashCode(packedBytes);
}
return Arrays.hashCode(fullBytes);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
long bits = packedBytes;
if (bits != 0) {
buf.append("(");
while (bits != 0) {
buf.append(bits & PACKED_BYTE_MASK);
bits >>>= PACKED_BYTE_SIZE;
if (bits != 0) buf.append(",");
}
buf.append(")");
}
if (fullBytes != null) {
buf.append("unpacked");
buf.append(Arrays.toString(fullBytes));
}
if (result != null) {
buf.append(" result=");
buf.append(result);
}
return buf.toString();
}
}
/** Find a previously cached transform equivalent to the given one, and return its result. */
private LambdaForm getInCache(Transform key) {
assert(key.result == null);
// The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap.
Object c = lambdaForm.transformCache;
Transform k = null;
if (c instanceof ConcurrentHashMap) {
@SuppressWarnings("unchecked")
ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
k = m.get(key);
} else if (c == null) {
return null;
} else if (c instanceof Transform) {
// one-element cache avoids overhead of an array
Transform t = (Transform)c;
if (t.equals(key)) k = t;
} else {
Transform[] ta = (Transform[])c;
for (int i = 0; i < ta.length; i++) {
Transform t = ta[i];
if (t == null) break;
if (t.equals(key)) { k = t; break; }
}
}
assert(k == null || key.equals(k));
return k == null ? null : k.result;
}
/** Arbitrary but reasonable limits on Transform[] size for cache. */
private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
/** Cache a transform with its result, and return that result.
* But if an equivalent transform has already been cached, return its result instead.
*/
private LambdaForm putInCache(Transform key, LambdaForm form) {
key = key.withResult(form);
for (int pass = 0; ; pass++) {
Object c = lambdaForm.transformCache;
if (c instanceof ConcurrentHashMap) {
@SuppressWarnings("unchecked")
ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
Transform k = m.putIfAbsent(key, key);
return k != null ? k.result : form;
}
assert(pass == 0);
synchronized (lambdaForm) {
c = lambdaForm.transformCache;
if (c instanceof ConcurrentHashMap)
continue;
if (c == null) {
lambdaForm.transformCache = key;
return form;
}
Transform[] ta;
if (c instanceof Transform) {
Transform k = (Transform)c;
if (k.equals(key)) {
return k.result;
}
// expand one-element cache to small array
ta = new Transform[MIN_CACHE_ARRAY_SIZE];
ta[0] = k;
lambdaForm.transformCache = c = ta;
} else {
// it is already expanded
ta = (Transform[])c;
}
int len = ta.length;
int i;
for (i = 0; i < len; i++) {
Transform k = ta[i];
if (k == null) {
break;
}
if (k.equals(key)) {
return k.result;
}
}
if (i < len) {
// just fall through to cache update
} else if (len < MAX_CACHE_ARRAY_SIZE) {
len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE);
ta = Arrays.copyOf(ta, len);
lambdaForm.transformCache = ta;
} else {
ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2);
for (Transform k : ta) {
m.put(k, k);
}
lambdaForm.transformCache = m;
// The second iteration will update for this query, concurrently.
continue;
}
ta[i] = key;
return form;
}
}
}
private LambdaFormBuffer buffer() {
return new LambdaFormBuffer(lambdaForm);
}
/// Editing methods for method handles. These need to have fast paths.
private BoundMethodHandle.SpeciesData oldSpeciesData() {
return BoundMethodHandle.speciesData(lambdaForm);
}
private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
return oldSpeciesData().extendWith(type);
}
BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
assert(mh.speciesData() == oldSpeciesData());
BasicType bt = L_TYPE;
MethodType type2 = bindArgumentType(mh, pos, bt);
LambdaForm form2 = bindArgumentForm(1+pos);
return mh.copyWithExtendL(type2, form2, value);
}
BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) {
assert(mh.speciesData() == oldSpeciesData());
BasicType bt = I_TYPE;
MethodType type2 = bindArgumentType(mh, pos, bt);
LambdaForm form2 = bindArgumentForm(1+pos);
return mh.copyWithExtendI(type2, form2, value);
}
BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) {
assert(mh.speciesData() == oldSpeciesData());
BasicType bt = J_TYPE;
MethodType type2 = bindArgumentType(mh, pos, bt);
LambdaForm form2 = bindArgumentForm(1+pos);
return mh.copyWithExtendJ(type2, form2, value);
}
BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) {
assert(mh.speciesData() == oldSpeciesData());
BasicType bt = F_TYPE;
MethodType type2 = bindArgumentType(mh, pos, bt);
LambdaForm form2 = bindArgumentForm(1+pos);
return mh.copyWithExtendF(type2, form2, value);
}
BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) {
assert(mh.speciesData() == oldSpeciesData());
BasicType bt = D_TYPE;
MethodType type2 = bindArgumentType(mh, pos, bt);
LambdaForm form2 = bindArgumentForm(1+pos);
return mh.copyWithExtendD(type2, form2, value);
}
private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) {
assert(mh.form == lambdaForm);
assert(mh.form.names[1+pos].type == bt);
assert(BasicType.basicType(mh.type().parameterType(pos)) == bt);
return mh.type().dropParameterTypes(pos, pos+1);
}
/// Editing methods for lambda forms.
// Each editing method can (potentially) cache the edited LF so that it can be reused later.
LambdaForm bindArgumentForm(int pos) {
Transform key = Transform.of(Transform.Kind.BIND_ARG, pos);
LambdaForm form = getInCache(key);
if (form != null) {
assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
return form;
}
LambdaFormBuffer buf = buffer();
buf.startEdit();
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos));
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
Name newBaseAddress;
NamedFunction getter = newData.getterFunction(oldData.fieldCount());
if (pos != 0) {
// The newly created LF will run with a different BMH.
// Switch over any pre-existing BMH field references to the new BMH class.
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
newBaseAddress = oldBaseAddress.withConstraint(newData);
buf.renameParameter(0, newBaseAddress);
buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress));
} else {
// cannot bind the MH arg itself, unless oldData is empty
assert(oldData == BoundMethodHandle.SpeciesData.EMPTY);
newBaseAddress = new Name(L_TYPE).withConstraint(newData);
buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress));
buf.insertParameter(0, newBaseAddress);
}
buf.endEdit();
form = buf.lambdaForm();
return putInCache(key, form);
}
}
...@@ -530,7 +530,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -530,7 +530,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
* Pre-initialized NamedFunctions for bootstrapping purposes. * Pre-initialized NamedFunctions for bootstrapping purposes.
* Factored in an inner class to delay initialization until first usage. * Factored in an inner class to delay initialization until first usage.
*/ */
private static class Lazy { static class Lazy {
private static final Class<?> MHI = MethodHandleImpl.class; private static final Class<?> MHI = MethodHandleImpl.class;
static final NamedFunction NF_checkSpreadArgument; static final NamedFunction NF_checkSpreadArgument;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册