src/share/classes/java/lang/invoke/LambdaForm.java
Index
Unified diffs
Context diffs
Sdiffs
Patch
New
Old
Previous File
Next File
*** old/src/share/classes/java/lang/invoke/LambdaForm.java Fri Jul 11 20:08:48 2014
--- new/src/share/classes/java/lang/invoke/LambdaForm.java Fri Jul 11 20:08:47 2014
*** 241,251 ****
--- 241,256 ----
assert(namesOK(arity, names));
this.arity = arity;
this.result = fixResult(result, names);
this.names = names.clone();
this.debugName = fixDebugName(debugName);
! int maxOutArity = normalize();
+ if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
+ // Cannot use LF interpreter on very high arity expressions.
+ assert(maxOutArity <= MethodType.MAX_JVM_ARITY);
+ compileToBytecode();
+ }
}
LambdaForm(String debugName,
int arity, Name[] names) {
this(debugName,
*** 346,368 ****
--- 351,378 ----
assert(!n.isParam()) : n + " is param at " + i;
}
return true;
}
- /** Renumber and/or replace params so that they are interned and canonically numbered. */
private void normalize() {
+ * @return maximum argument list length among the names (since we have to pass over them anyway)
+ */
+ private int normalize() {
Name[] oldNames = null;
+ int maxOutArity = 0;
int changesStart = 0;
for (int i = 0; i < names.length; i++) {
Name n = names[i];
if (!n.initIndex(i)) {
if (oldNames == null) {
oldNames = names.clone();
changesStart = i;
}
names[i] = n.cloneWithIndex(i);
}
+ if (n.arguments != null && maxOutArity < n.arguments.length)
+ maxOutArity = n.arguments.length;
}
if (oldNames != null) {
int startFixing = arity;
if (startFixing <= changesStart)
startFixing = changesStart+1;
*** 385,394 ****
--- 395,405 ----
for (int i = arity; i < names.length; i++) {
names[i].internArguments();
}
assert(nameRefsAreLegal());
}
+ return maxOutArity;
}
/**
* Check that all embedded Name references are localizable to this lambda,
* and are properly ordered after their corresponding definitions.
*** 580,590 ****
--- 591,601 ----
if (TRACE_INTERPRETER)
traceInterpreter("compileToBytecode", this);
isCompiled = true;
return vmentry;
} catch (Error | Exception ex) {
! throw newInternalError("compileToBytecode: " + this, ex);
! throw newInternalError(this.toString(), ex);
}
}
private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
static {
*** 707,720 ****
--- 718,728 ----
}
/** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
private static final int COMPILE_THRESHOLD;
static {
if (MethodHandleStatics.COMPILE_THRESHOLD != null)
COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD;
else
COMPILE_THRESHOLD = 30; // default value
+ COMPILE_THRESHOLD = Math.max(-1, MethodHandleStatics.COMPILE_THRESHOLD);
}
private int invocationCounter = 0;
@Hidden
@DontInline
*** 726,736 ****
--- 734,746 ----
assert(arityCheck(argumentValues));
Object[] values = Arrays.copyOf(argumentValues, names.length);
for (int i = argumentValues.length; i < values.length; i++) {
values[i] = interpretName(names[i], values);
}
! return (result < 0) ? null : values[result];
! Object rv = (result < 0) ? null : values[result];
+ assert(resultCheck(argumentValues, rv));
+ return rv;
}
@Hidden
@DontInline
/** Evaluate a single Name within this form, applying its function to its arguments. */
*** 817,828 ****
--- 827,846 ----
}
private boolean arityCheck(Object[] argumentValues) {
assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length";
// also check that the leading (receiver) argument is somehow bound to this LF:
assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0];
! assert(((MethodHandle)argumentValues[0]).internalForm() == this);
! MethodHandle mh = (MethodHandle) argumentValues[0];
+ assert(mh.internalForm() == this);
// note: argument #0 could also be an interface wrapper, in the future
+ argumentTypesMatch(basicTypeSignature(), argumentValues);
+ return true;
+ }
+ private boolean resultCheck(Object[] argumentValues, Object result) {
+ MethodHandle mh = (MethodHandle) argumentValues[0];
+ MethodType mt = mh.type();
+ assert(valueMatches(returnType(), mt.returnType(), result));
return true;
}
private boolean isEmpty() {
if (result < 0)
*** 845,912 ****
--- 863,882 ----
continue;
}
buf.append("=").append(n.exprString());
buf.append(";");
}
+ if (arity == names.length) buf.append(")=>{");
buf.append(result < 0 ? "void" : names[result]).append("}");
if (TRACE_INTERPRETER) {
// Extra verbosity:
buf.append(":").append(basicTypeSignature());
buf.append("/").append(vmentry);
}
return buf.toString();
}
/**
* Apply immediate binding for a Name in this form indicated by its position relative to the form.
* The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not
* accepted as valid.
*/
LambdaForm bindImmediate(int pos, BasicType basicType, Object value) {
// must be an argument, and the types must match
assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value);
int arity2 = arity - 1;
Name[] names2 = new Name[names.length - 1];
for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2
Name n = names[r];
if (n.isParam()) {
if (n.index == pos) {
// do not copy over the argument that is to be replaced with a literal,
// but adjust the write index
--w;
} else {
names2[w] = new Name(w, n.type);
}
} else {
Object[] arguments2 = new Object[n.arguments.length];
for (int i = 0; i < n.arguments.length; ++i) {
Object arg = n.arguments[i];
if (arg instanceof Name) {
int ni = ((Name) arg).index;
if (ni == pos) {
arguments2[i] = value;
} else if (ni < pos) {
// replacement position not yet passed
arguments2[i] = names2[ni];
} else {
// replacement position passed
arguments2[i] = names2[ni - 1];
}
} else {
arguments2[i] = arg;
}
}
names2[w] = new Name(n.function, arguments2);
names2[w].initIndex(w);
}
}
int result2 = result == -1 ? -1 : result - 1;
return new LambdaForm(debugName, arity2, names2, result2);
}
LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) {
Name name = names[namePos];
BoundMethodHandle.SpeciesData newData = oldData.extendWith(name.type);
return bind(name, new Name(newData.getterFunction(oldData.fieldCount()), names[0]), oldData, newData);
}
*** 967,977 ****
--- 937,947 ----
// (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.isSiblingBindingBefore(binding)) {
! if (n.isParam()) {
names2[insPos] = n;
} else {
break;
}
}
*** 998,1017 ****
--- 968,987 ----
}
return false;
}
LambdaForm addArguments(int pos, BasicType... types) {
assert(pos <= arity);
+ // names array has MH in slot 0; skip it.
+ int argpos = pos + 1;
+ assert(argpos <= arity);
int length = names.length;
int inTypes = types.length;
Name[] names2 = Arrays.copyOf(names, length + inTypes);
int arity2 = arity + inTypes;
int result2 = result;
! if (result2 >= arity)
! if (result2 >= argpos)
result2 += inTypes;
// names array has MH in slot 0; skip it.
int argpos = pos + 1;
// Note: The LF constructor will rename names2[argpos...].
// Make space for new arguments (shift temporaries).
System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos);
for (int i = 0; i < inTypes; i++) {
names2[argpos + i] = new Name(types[i]);
*** 1100,1121 ****
--- 1070,1099 ----
NamedFunction(MethodHandle resolvedHandle) {
this(resolvedHandle.internalMemberName(), resolvedHandle);
}
NamedFunction(MemberName member, MethodHandle resolvedHandle) {
this.member = member;
//resolvedHandle = eraseSubwordTypes(resolvedHandle);
this.resolvedHandle = resolvedHandle;
+ // The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
+ //assert(!isInvokeBasic());
}
NamedFunction(MethodType basicInvokerType) {
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
if (basicInvokerType.parameterSlotCount() < MethodType.MAX_MH_INVOKER_ARITY) {
this.resolvedHandle = basicInvokerType.invokers().basicInvoker();
this.member = resolvedHandle.internalMemberName();
} else {
// necessary to pass BigArityTest
this.member = Invokers.invokeBasicMethod(basicInvokerType);
}
+ assert(isInvokeBasic());
+ }
+
+ private boolean isInvokeBasic() {
+ return member != null &&
+ member.isMethodHandleInvoke() &&
+ "invokeBasic".equals(member.getName());
}
// The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc.
// Any LambdaForm containing such a member is not interpretable.
// This is OK, since all such LFs are prepared with special primitive vmentry points.
*** 1177,1251 ****
--- 1155,1247 ----
// The following are predefined NamedFunction invokers. The system must build
// a separate invoker for each distinct signature.
/** void return type invokers. */
@Hidden
static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 0);
! assert(arityCheck(0, void.class, mh, a));
mh.invokeBasic();
return null;
}
@Hidden
static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 1);
! assert(arityCheck(1, void.class, mh, a));
mh.invokeBasic(a[0]);
return null;
}
@Hidden
static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 2);
! assert(arityCheck(2, void.class, mh, a));
mh.invokeBasic(a[0], a[1]);
return null;
}
@Hidden
static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 3);
! assert(arityCheck(3, void.class, mh, a));
mh.invokeBasic(a[0], a[1], a[2]);
return null;
}
@Hidden
static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 4);
! assert(arityCheck(4, void.class, mh, a));
mh.invokeBasic(a[0], a[1], a[2], a[3]);
return null;
}
@Hidden
static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 5);
! assert(arityCheck(5, void.class, mh, a));
mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
return null;
}
/** Object return type invokers. */
@Hidden
static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 0);
! assert(arityCheck(0, mh, a));
return mh.invokeBasic();
}
@Hidden
static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 1);
! assert(arityCheck(1, mh, a));
return mh.invokeBasic(a[0]);
}
@Hidden
static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 2);
! assert(arityCheck(2, mh, a));
return mh.invokeBasic(a[0], a[1]);
}
@Hidden
static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 3);
! assert(arityCheck(3, mh, a));
return mh.invokeBasic(a[0], a[1], a[2]);
}
@Hidden
static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 4);
! assert(arityCheck(4, mh, a));
return mh.invokeBasic(a[0], a[1], a[2], a[3]);
}
@Hidden
static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable {
! assert(a.length == 5);
! assert(arityCheck(5, mh, a));
return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
}
+ private static boolean arityCheck(int arity, MethodHandle mh, Object[] a) {
+ return arityCheck(arity, Object.class, mh, a);
+ }
+ private static boolean arityCheck(int arity, Class<?> rtype, MethodHandle mh, Object[] a) {
+ assert(a.length == arity)
+ : Arrays.asList(a.length, arity);
+ assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
+ : Arrays.asList(mh, rtype, arity);
+ MemberName member = mh.internalMemberName();
+ if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
+ assert(arity > 0);
+ assert(a[0] instanceof MethodHandle);
+ MethodHandle mh2 = (MethodHandle) a[0];
+ assert(mh2.type().basicType() == MethodType.genericMethodType(arity-1).changeReturnType(rtype))
+ : Arrays.asList(member, mh2, rtype, arity);
+ }
+ return true;
+ }
static final MethodType INVOKER_METHOD_TYPE =
MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
private static MethodHandle computeInvoker(MethodTypeForm typeForm) {
*** 1485,1495 ****
--- 1481,1495 ----
}
}
if (!replaced) return this;
return new Name(function, arguments);
}
+ /** In the arguments of this Name, replace oldNames[i] pairwise by newNames[i].
+ * Limit such replacements to {@code start<=i<end}. Return possibly changed self.
+ */
Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
+ if (start >= end) return this;
@SuppressWarnings("LocalVariableHidesMemberVariable")
Object[] arguments = this.arguments;
boolean replaced = false;
eachArg:
for (int j = 0; j < arguments.length; j++) {
*** 1570,1607 ****
--- 1570,1579 ----
}
assert(parameterType == L_TYPE);
return true;
}
/**
* Does this Name precede the given binding node in some canonical order?
* This predicate is used to order data bindings (via insertion sort)
* with some stability.
*/
boolean isSiblingBindingBefore(Name binding) {
assert(!binding.isParam());
if (isParam()) return true;
if (function.equals(binding.function) &&
arguments.length == binding.arguments.length) {
boolean sawInt = false;
for (int i = 0; i < arguments.length; i++) {
Object a1 = arguments[i];
Object a2 = binding.arguments[i];
if (!a1.equals(a2)) {
if (a1 instanceof Integer && a2 instanceof Integer) {
if (sawInt) continue;
sawInt = true;
if ((int)a1 < (int)a2) continue; // still might be true
}
return false;
}
}
return sawInt;
}
return false;
}
/** Return the index of the last occurrence of n in the argument array.
* Return -1 if the name is not used.
*/
int lastUseIndex(Name n) {
if (arguments == null) return -1;
*** 1856,1896 ****
--- 1828,1837 ----
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Hidden {
}
/*
// Smoke-test for the invokers used in this file.
static void testMethodHandleLinkers() throws Throwable {
MemberName.Factory lookup = MemberName.getFactory();
MemberName asList_MN = new MemberName(Arrays.class, "asList",
MethodType.methodType(List.class, Object[].class),
REF_invokeStatic);
//MethodHandleNatives.resolve(asList_MN, null);
asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class);
System.out.println("about to call "+asList_MN);
Object[] abc = { "a", "bc" };
List<?> lst = (List<?>) MethodHandle.linkToStatic(abc, asList_MN);
System.out.println("lst="+lst);
MemberName toString_MN = new MemberName(Object.class.getMethod("toString"));
String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN);
toString_MN = new MemberName(Object.class.getMethod("toString"), true);
String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN);
System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString()));
MemberName toArray_MN = new MemberName(List.class.getMethod("toArray"));
Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN);
System.out.println("toArray="+Arrays.toString(arr));
}
static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } }
// Requires these definitions in MethodHandle:
static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable;
static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable;
static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable;
static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable;
*/
private static final HashMap<String,Integer> DEBUG_NAME_COUNTERS;
static {
if (debugEnabled())
DEBUG_NAME_COUNTERS = new HashMap<>();
else
src/share/classes/java/lang/invoke/LambdaForm.java
Index
Unified diffs
Context diffs
Sdiffs
Patch
New
Old
Previous File
Next File