--- a/jdk/src/share/classes/java/lang/ClassValue.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/ClassValue.java Fri Aug 10 16:03:28 2012 -0700
@@ -489,9 +489,18 @@
/** Remove an entry. */
synchronized
void removeEntry(ClassValue<?> classValue) {
- // make all cache elements for this guy go stale:
- if (remove(classValue.identity) != null) {
+ Entry<?> e = remove(classValue.identity);
+ if (e == null) {
+ // Uninitialized, and no pending calls to computeValue. No change.
+ } else if (e.isPromise()) {
+ // State is uninitialized, with a pending call to finishEntry.
+ // Since remove is a no-op in such a state, keep the promise
+ // by putting it back into the map.
+ put(classValue.identity, e);
+ } else {
+ // In an initialized state. Bump forward, and de-initialize.
classValue.bumpVersion();
+ // Make all cache elements for this guy go stale.
removeStaleEntries(classValue);
}
}
--- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Fri Aug 10 10:15:52 2012 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1204 +0,0 @@
-/*
- * Copyright (c) 2008, 2011, 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 sun.invoke.util.VerifyType;
-import sun.invoke.util.Wrapper;
-import sun.invoke.util.ValueConversions;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Collections;
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-
-/**
- * This method handle performs simple conversion or checking of a single argument.
- * @author jrose
- */
-class AdapterMethodHandle extends BoundMethodHandle {
-
- //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH
- //Object argument; // parameter to the conversion if needed
- //int vmargslot; // which argument slot is affected
- private final int conversion; // the type of conversion: RETYPE_ONLY, etc.
-
- // Constructors in this class *must* be package scoped or private.
- private AdapterMethodHandle(MethodHandle target, MethodType newType,
- long conv, Object convArg) {
- super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv)));
- this.conversion = convCode(conv);
- // JVM might update VM-specific bits of conversion (ignore)
- MethodHandleNatives.init(this, target, convArgPos(conv));
- }
- AdapterMethodHandle(MethodHandle target, MethodType newType,
- long conv) {
- this(target, newType, conv, null);
- }
-
- int getConversion() { return conversion; }
-
- // TO DO: When adapting another MH with a null conversion, clone
- // the target and change its type, instead of adding another layer.
-
- /** Can a JVM-level adapter directly implement the proposed
- * argument conversions, as if by fixed-arity MethodHandle.asType?
- */
- static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
- // same number of args, of course
- int len = newType.parameterCount();
- if (len != oldType.parameterCount())
- return false;
-
- // Check return type.
- Class<?> exp = newType.returnType();
- Class<?> ret = oldType.returnType();
- if (!VerifyType.isNullConversion(ret, exp)) {
- if (!convOpSupported(OP_COLLECT_ARGS))
- return false;
- if (!canConvertArgument(ret, exp, level))
- return false;
- }
-
- // Check args pairwise.
- for (int i = 0; i < len; i++) {
- Class<?> src = newType.parameterType(i); // source type
- Class<?> dst = oldType.parameterType(i); // destination type
- if (!canConvertArgument(src, dst, level))
- return false;
- }
-
- return true;
- }
-
- /** Can a JVM-level adapter directly implement the proposed
- * argument conversion, as if by fixed-arity MethodHandle.asType?
- */
- static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
- // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
- // so we don't need to repeat so much decision making.
- if (VerifyType.isNullConversion(src, dst)) {
- return true;
- } else if (convOpSupported(OP_COLLECT_ARGS)) {
- // If we can build filters, we can convert anything to anything.
- return true;
- } else if (src.isPrimitive()) {
- if (dst.isPrimitive())
- return canPrimCast(src, dst);
- else
- return canBoxArgument(src, dst);
- } else {
- if (dst.isPrimitive())
- return canUnboxArgument(src, dst, level);
- else
- return true; // any two refs can be interconverted
- }
- }
-
- /**
- * Create a JVM-level adapter method handle to conform the given method
- * handle to the similar newType, using only pairwise argument conversions.
- * For each argument, convert incoming argument to the exact type needed.
- * The argument conversions allowed are casting, boxing and unboxing,
- * integral widening or narrowing, and floating point widening or narrowing.
- * @param newType required call type
- * @param target original method handle
- * @param level which strength of conversion is allowed
- * @return an adapter to the original handle with the desired new type,
- * or the original target if the types are already identical
- * or null if the adaptation cannot be made
- */
- static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) {
- MethodType oldType = target.type();
- if (newType == oldType) return target;
-
- if (!canPairwiseConvert(newType, oldType, level))
- return null;
- // (after this point, it is an assertion error to fail to convert)
-
- // Find last non-trivial conversion (if any).
- int lastConv = newType.parameterCount()-1;
- while (lastConv >= 0) {
- Class<?> src = newType.parameterType(lastConv); // source type
- Class<?> dst = oldType.parameterType(lastConv); // destination type
- if (isTrivialConversion(src, dst, level)) {
- --lastConv;
- } else {
- break;
- }
- }
-
- Class<?> needReturn = newType.returnType();
- Class<?> haveReturn = oldType.returnType();
- boolean retConv = !isTrivialConversion(haveReturn, needReturn, level);
-
- // Now build a chain of one or more adapters.
- MethodHandle adapter = target, adapter2;
- MethodType midType = oldType;
- for (int i = 0; i <= lastConv; i++) {
- Class<?> src = newType.parameterType(i); // source type
- Class<?> dst = midType.parameterType(i); // destination type
- if (isTrivialConversion(src, dst, level)) {
- // do nothing: difference is trivial
- continue;
- }
- // Work the current type backward toward the desired caller type:
- midType = midType.changeParameterType(i, src);
- if (i == lastConv) {
- // When doing the last (or only) real conversion,
- // force all remaining null conversions to happen also.
- MethodType lastMidType = newType;
- if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn);
- assert(VerifyType.isNullConversion(lastMidType, midType));
- midType = lastMidType;
- }
-
- // Tricky case analysis follows.
- // It parallels canConvertArgument() above.
- if (src.isPrimitive()) {
- if (dst.isPrimitive()) {
- adapter2 = makePrimCast(midType, adapter, i, dst);
- } else {
- adapter2 = makeBoxArgument(midType, adapter, i, src);
- }
- } else {
- if (dst.isPrimitive()) {
- // Caller has boxed a primitive. Unbox it for the target.
- // The box type must correspond exactly to the primitive type.
- // This is simpler than the powerful set of widening
- // conversions supported by reflect.Method.invoke.
- // Those conversions require a big nest of if/then/else logic,
- // which we prefer to make a user responsibility.
- adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
- } else {
- // Simple reference conversion.
- // Note: Do not check for a class hierarchy relation
- // between src and dst. In all cases a 'null' argument
- // will pass the cast conversion.
- adapter2 = makeCheckCast(midType, adapter, i, dst);
- }
- }
- assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
- assert(adapter2.type() == midType);
- adapter = adapter2;
- }
- if (retConv) {
- adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
- assert(adapter2 != null);
- adapter = adapter2;
- }
- if (adapter.type() != newType) {
- // Only trivial conversions remain.
- adapter2 = makeRetypeOnly(newType, adapter);
- assert(adapter2 != null);
- adapter = adapter2;
- // Actually, that's because there were no non-trivial ones:
- assert(lastConv == -1 || retConv);
- }
- assert(adapter.type() == newType);
- return adapter;
- }
-
- private static boolean isTrivialConversion(Class<?> src, Class<?> dst, int level) {
- if (src == dst || dst == void.class) return true;
- if (!VerifyType.isNullConversion(src, dst)) return false;
- if (level > 1) return true; // explicitCastArguments
- boolean sp = src.isPrimitive();
- boolean dp = dst.isPrimitive();
- if (sp != dp) return false;
- if (sp) {
- // in addition to being a null conversion, forbid boolean->int etc.
- return Wrapper.forPrimitiveType(dst)
- .isConvertibleFrom(Wrapper.forPrimitiveType(src));
- } else {
- return dst.isAssignableFrom(src);
- }
- }
-
- private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
- MethodHandle adjustReturn;
- if (haveReturn == void.class) {
- // synthesize a zero value for the given void
- Object zero = Wrapper.forBasicType(needReturn).zero();
- adjustReturn = MethodHandles.constant(needReturn, zero);
- } else {
- MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
- adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
- }
- return makeCollectArguments(adjustReturn, target, 0, false);
- }
-
- /**
- * Create a JVM-level adapter method handle to permute the arguments
- * of the given method.
- * @param newType required call type
- * @param target original method handle
- * @param argumentMap for each target argument, position of its source in newType
- * @return an adapter to the original handle with the desired new type,
- * or the original target if the types are already identical
- * and the permutation is null
- * @throws IllegalArgumentException if the adaptation cannot be made
- * directly by a JVM-level adapter, without help from Java code
- */
- static MethodHandle makePermutation(MethodType newType, MethodHandle target,
- int[] argumentMap) {
- MethodType oldType = target.type();
- boolean nullPermutation = true;
- for (int i = 0; i < argumentMap.length; i++) {
- int pos = argumentMap[i];
- if (pos != i)
- nullPermutation = false;
- if (pos < 0 || pos >= newType.parameterCount()) {
- argumentMap = new int[0]; break;
- }
- }
- if (argumentMap.length != oldType.parameterCount())
- throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
- if (nullPermutation) {
- MethodHandle res = makePairwiseConvert(newType, target, 0);
- // well, that was easy
- if (res == null)
- throw newIllegalArgumentException("cannot convert pairwise: "+newType);
- return res;
- }
-
- // Check return type. (Not much can be done with it.)
- Class<?> exp = newType.returnType();
- Class<?> ret = oldType.returnType();
- if (!VerifyType.isNullConversion(ret, exp))
- throw newIllegalArgumentException("bad return conversion for "+newType);
-
- // See if the argument types match up.
- for (int i = 0; i < argumentMap.length; i++) {
- int j = argumentMap[i];
- Class<?> src = newType.parameterType(j);
- Class<?> dst = oldType.parameterType(i);
- if (!VerifyType.isNullConversion(src, dst))
- throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType);
- }
-
- // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters.
- // A workable greedy algorithm is as follows:
- // Drop unused outgoing arguments (right to left: shallowest first).
- // Duplicate doubly-used outgoing arguments (left to right: deepest first).
- // Then the remaining problem is a true argument permutation.
- // Marshal the outgoing arguments as required from left to right.
- // That is, find the deepest outgoing stack position that does not yet
- // have the correct argument value, and correct at least that position
- // by swapping or rotating in the misplaced value (from a shallower place).
- // If the misplaced value is followed by one or more consecutive values
- // (also misplaced) issue a rotation which brings as many as possible
- // into position. Otherwise make progress with either a swap or a
- // rotation. Prefer the swap as cheaper, but do not use it if it
- // breaks a slot pair. Prefer the rotation over the swap if it would
- // preserve more consecutive values shallower than the target position.
- // When more than one rotation will work (because the required value
- // is already adjacent to the target position), then use a rotation
- // which moves the old value in the target position adjacent to
- // one of its consecutive values. Also, prefer shorter rotation
- // spans, since they use fewer memory cycles for shuffling.
-
- throw new UnsupportedOperationException("NYI");
- }
-
- private static byte basicType(Class<?> type) {
- if (type == null) return T_VOID;
- switch (Wrapper.forBasicType(type)) {
- case BOOLEAN: return T_BOOLEAN;
- case CHAR: return T_CHAR;
- case FLOAT: return T_FLOAT;
- case DOUBLE: return T_DOUBLE;
- case BYTE: return T_BYTE;
- case SHORT: return T_SHORT;
- case INT: return T_INT;
- case LONG: return T_LONG;
- case OBJECT: return T_OBJECT;
- case VOID: return T_VOID;
- }
- return 99; // T_ILLEGAL or some such
- }
-
- /** Number of stack slots for the given type.
- * Two for T_DOUBLE and T_FLOAT, one for the rest.
- */
- private static int type2size(int type) {
- assert(type >= T_BOOLEAN && type <= T_OBJECT);
- return (type == T_LONG || type == T_DOUBLE) ? 2 : 1;
- }
- private static int type2size(Class<?> type) {
- return type2size(basicType(type));
- }
-
- /** The given stackMove is the number of slots pushed.
- * It might be negative. Scale it (multiply) by the
- * VM's notion of how an address changes with a push,
- * to get the raw SP change for stackMove.
- * Then shift and mask it into the correct field.
- */
- private static long insertStackMove(int stackMove) {
- // following variable must be long to avoid sign extension after '<<'
- long spChange = stackMove * MethodHandleNatives.JVM_STACK_MOVE_UNIT;
- return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
- }
-
- static int extractStackMove(int convOp) {
- int spChange = convOp >> CONV_STACK_MOVE_SHIFT;
- return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT;
- }
-
- static int extractStackMove(MethodHandle target) {
- if (target instanceof AdapterMethodHandle) {
- AdapterMethodHandle amh = (AdapterMethodHandle) target;
- return extractStackMove(amh.getConversion());
- } else {
- return 0;
- }
- }
-
- /** Construct an adapter conversion descriptor for a single-argument conversion. */
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- private static long makeConv(int convOp, int argnum, int src, int dest) {
- assert(src == (src & CONV_TYPE_MASK));
- assert(dest == (dest & CONV_TYPE_MASK));
- assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
- int stackMove = type2size(dest) - type2size(src);
- return ((long) argnum << 32 |
- (long) convOp << CONV_OP_SHIFT |
- (int) src << CONV_SRC_TYPE_SHIFT |
- (int) dest << CONV_DEST_TYPE_SHIFT |
- insertStackMove(stackMove)
- );
- }
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- private static long makeDupConv(int convOp, int argnum, int stackMove) {
- // simple argument motion, requiring one slot to specify
- assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
- byte src = 0, dest = 0;
- return ((long) argnum << 32 |
- (long) convOp << CONV_OP_SHIFT |
- (int) src << CONV_SRC_TYPE_SHIFT |
- (int) dest << CONV_DEST_TYPE_SHIFT |
- insertStackMove(stackMove)
- );
- }
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- private static long makeSwapConv(int convOp, int srcArg, byte srcType, int destSlot, byte destType) {
- // more complex argument motion, requiring two slots to specify
- assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS);
- return ((long) srcArg << 32 |
- (long) convOp << CONV_OP_SHIFT |
- (int) srcType << CONV_SRC_TYPE_SHIFT |
- (int) destType << CONV_DEST_TYPE_SHIFT |
- (int) destSlot << CONV_VMINFO_SHIFT
- );
- }
- @SuppressWarnings("cast") // some (int) casts below provide clarity but trigger warnings
- private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) {
- // spreading or collecting, at a particular slot location
- assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS);
- // src = spread ? T_OBJECT (for array) : common type of collected args (else void)
- // dest = spread ? element type of array : result type of collector (can be void)
- return ((long) argnum << 32 |
- (long) convOp << CONV_OP_SHIFT |
- (int) src << CONV_SRC_TYPE_SHIFT |
- (int) dest << CONV_DEST_TYPE_SHIFT |
- insertStackMove(stackMove)
- );
- }
- static long makeConv(int convOp) {
- assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
- return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
- }
- private static int convCode(long conv) {
- return (int)conv;
- }
- private static int convArgPos(long conv) {
- return (int)(conv >>> 32);
- }
- private static boolean convOpSupported(int convOp) {
- assert(convOp >= 0 && convOp <= CONV_OP_LIMIT);
- return ((1<<convOp) & MethodHandleNatives.CONV_OP_IMPLEMENTED_MASK) != 0;
- }
-
- /** One of OP_RETYPE_ONLY, etc. */
- int conversionOp() { return (conversion & CONV_OP_MASK) >> CONV_OP_SHIFT; }
-
- /* Return one plus the position of the first non-trivial difference
- * between the given types. This is not a symmetric operation;
- * we are considering adapting the targetType to adapterType.
- * Trivial differences are those which could be ignored by the JVM
- * without subverting the verifier. Otherwise, adaptable differences
- * are ones for which we could create an adapter to make the type change.
- * Return zero if there are no differences (other than trivial ones).
- * Return 1+N if N is the only adaptable argument difference.
- * Return the -2-N where N is the first of several adaptable
- * argument differences.
- * Return -1 if there there are differences which are not adaptable.
- */
- private static int diffTypes(MethodType adapterType,
- MethodType targetType,
- boolean raw) {
- int diff;
- diff = diffReturnTypes(adapterType, targetType, raw);
- if (diff != 0) return diff;
- int nargs = adapterType.parameterCount();
- if (nargs != targetType.parameterCount())
- return -1;
- diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
- //System.out.println("diff "+adapterType);
- //System.out.println(" "+diff+" "+targetType);
- return diff;
- }
- private static int diffReturnTypes(MethodType adapterType,
- MethodType targetType,
- boolean raw) {
- Class<?> src = targetType.returnType();
- Class<?> dst = adapterType.returnType();
- if ((!raw
- ? VerifyType.canPassUnchecked(src, dst)
- : VerifyType.canPassRaw(src, dst)
- ) > 0)
- return 0; // no significant difference
- if (raw && !src.isPrimitive() && !dst.isPrimitive())
- return 0; // can force a reference return (very carefully!)
- //if (false) return 1; // never adaptable!
- return -1; // some significant difference
- }
- private static int diffParamTypes(MethodType adapterType, int astart,
- MethodType targetType, int tstart,
- int nargs, boolean raw) {
- assert(nargs >= 0);
- int res = 0;
- for (int i = 0; i < nargs; i++) {
- Class<?> src = adapterType.parameterType(astart+i);
- Class<?> dest = targetType.parameterType(tstart+i);
- if ((!raw
- ? VerifyType.canPassUnchecked(src, dest)
- : VerifyType.canPassRaw(src, dest)
- ) <= 0) {
- // found a difference; is it the only one so far?
- if (res != 0)
- return -1-res; // return -2-i for prev. i
- res = 1+i;
- }
- }
- return res;
- }
-
- /** Can a retyping adapter (alone) validly convert the target to newType? */
- static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
- return canRetype(newType, targetType, false);
- }
- /** Can a retyping adapter (alone) convert the target to newType?
- * It is allowed to widen subword types and void to int, to make bitwise
- * conversions between float/int and double/long, and to perform unchecked
- * reference conversions on return. This last feature requires that the
- * caller be trusted, and perform explicit cast conversions on return values.
- */
- static boolean canRetypeRaw(MethodType newType, MethodType targetType) {
- return canRetype(newType, targetType, true);
- }
- static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) {
- if (!convOpSupported(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)) return false;
- int diff = diffTypes(newType, targetType, raw);
- // %%% This assert is too strong. Factor diff into VerifyType and reconcile.
- assert(raw || (diff == 0) == VerifyType.isNullConversion(newType, targetType));
- return diff == 0;
- }
-
- /** Factory method: Performs no conversions; simply retypes the adapter.
- * Allows unchecked argument conversions pairwise, if they are safe.
- * Returns null if not possible.
- */
- static MethodHandle makeRetypeOnly(MethodType newType, MethodHandle target) {
- return makeRetype(newType, target, false);
- }
- static MethodHandle makeRetypeRaw(MethodType newType, MethodHandle target) {
- return makeRetype(newType, target, true);
- }
- static MethodHandle makeRetype(MethodType newType, MethodHandle target, boolean raw) {
- MethodType oldType = target.type();
- if (oldType == newType) return target;
- if (!canRetype(newType, oldType, raw))
- return null;
- // TO DO: clone the target guy, whatever he is, with new type.
- return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
- }
-
- static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
- MethodType type = target.type();
- int last = type.parameterCount() - 1;
- if (type.parameterType(last) != arrayType)
- target = target.asType(type.changeParameterType(last, arrayType));
- target = target.asFixedArity(); // make sure this attribute is turned off
- return new AsVarargsCollector(target, arrayType);
- }
-
- static class AsVarargsCollector extends AdapterMethodHandle {
- final MethodHandle target;
- final Class<?> arrayType;
- MethodHandle cache;
-
- AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
- super(target, target.type(), makeConv(OP_RETYPE_ONLY));
- this.target = target;
- this.arrayType = arrayType;
- this.cache = target.asCollector(arrayType, 0);
- }
-
- @Override
- public boolean isVarargsCollector() {
- return true;
- }
-
- @Override
- public MethodHandle asFixedArity() {
- return target;
- }
-
- @Override
- public MethodHandle asType(MethodType newType) {
- MethodType type = this.type();
- int collectArg = type.parameterCount() - 1;
- int newArity = newType.parameterCount();
- if (newArity == collectArg+1 &&
- type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
- // if arity and trailing parameter are compatible, do normal thing
- return super.asType(newType);
- }
- // check cache
- if (cache.type().parameterCount() == newArity)
- return cache.asType(newType);
- // build and cache a collector
- int arrayLength = newArity - collectArg;
- MethodHandle collector;
- try {
- collector = target.asCollector(arrayType, arrayLength);
- } catch (IllegalArgumentException ex) {
- throw new WrongMethodTypeException("cannot build collector");
- }
- cache = collector;
- return collector.asType(newType);
- }
- }
-
- /** Can a checkcast adapter validly convert the target to newType?
- * The JVM supports all kind of reference casts, even silly ones.
- */
- static boolean canCheckCast(MethodType newType, MethodType targetType,
- int arg, Class<?> castType) {
- if (!convOpSupported(OP_CHECK_CAST)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- if (!canCheckCast(src, castType)
- || !VerifyType.isNullConversion(castType, dst))
- return false;
- int diff = diffTypes(newType, targetType, false);
- return (diff == arg+1) || (diff == 0); // arg is sole non-trivial diff
- }
- /** Can an primitive conversion adapter validly convert src to dst? */
- static boolean canCheckCast(Class<?> src, Class<?> dst) {
- return (!src.isPrimitive() && !dst.isPrimitive());
- }
-
- /** Factory method: Forces a cast at the given argument.
- * The castType is the target of the cast, and can be any type
- * with a null conversion to the corresponding target parameter.
- * Return null if this cannot be done.
- */
- static MethodHandle makeCheckCast(MethodType newType, MethodHandle target,
- int arg, Class<?> castType) {
- if (!canCheckCast(newType, target.type(), arg, castType))
- return null;
- long conv = makeConv(OP_CHECK_CAST, arg, T_OBJECT, T_OBJECT);
- return new AdapterMethodHandle(target, newType, conv, castType);
- }
-
- /** Can an primitive conversion adapter validly convert the target to newType?
- * The JVM currently supports all conversions except those between
- * floating and integral types.
- */
- static boolean canPrimCast(MethodType newType, MethodType targetType,
- int arg, Class<?> convType) {
- if (!convOpSupported(OP_PRIM_TO_PRIM)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- if (!canPrimCast(src, convType)
- || !VerifyType.isNullConversion(convType, dst))
- return false;
- int diff = diffTypes(newType, targetType, false);
- return (diff == arg+1); // arg is sole non-trivial diff
- }
- /** Can an primitive conversion adapter validly convert src to dst? */
- static boolean canPrimCast(Class<?> src, Class<?> dst) {
- if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
- return false;
- } else {
- boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
- boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
- return !(sflt | dflt); // no float support at present
- }
- }
-
- /** Factory method: Truncate the given argument with zero or sign extension,
- * and/or convert between single and doubleword versions of integer or float.
- * The convType is the target of the conversion, and can be any type
- * with a null conversion to the corresponding target parameter.
- * Return null if this cannot be done.
- */
- static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
- int arg, Class<?> convType) {
- Class<?> src = newType.parameterType(arg);
- if (canPrimCast(src, convType))
- return makePrimCastOnly(newType, target, arg, convType);
- Class<?> dst = convType;
- boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
- boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
- if (sflt | dflt) {
- MethodHandle convMethod;
- if (sflt)
- convMethod = ((src == double.class)
- ? ValueConversions.convertFromDouble(dst)
- : ValueConversions.convertFromFloat(dst));
- else
- convMethod = ((dst == double.class)
- ? ValueConversions.convertToDouble(src)
- : ValueConversions.convertToFloat(src));
- long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
- return new AdapterMethodHandle(target, newType, conv, convMethod);
- }
- throw new InternalError("makePrimCast");
- }
- static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target,
- int arg, Class<?> convType) {
- MethodType oldType = target.type();
- if (!canPrimCast(newType, oldType, arg, convType))
- return null;
- Class<?> src = newType.parameterType(arg);
- long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType));
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an unboxing conversion validly convert src to dst?
- * The JVM currently supports all kinds of casting and unboxing.
- * The convType is the unboxed type; it can be either a primitive or wrapper.
- */
- static boolean canUnboxArgument(MethodType newType, MethodType targetType,
- int arg, Class<?> convType, int level) {
- if (!convOpSupported(OP_REF_TO_PRIM)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- Class<?> boxType = Wrapper.asWrapperType(convType);
- convType = Wrapper.asPrimitiveType(convType);
- if (!canCheckCast(src, boxType)
- || boxType == convType
- || !VerifyType.isNullConversion(convType, dst))
- return false;
- int diff = diffTypes(newType, targetType, false);
- return (diff == arg+1); // arg is sole non-trivial diff
- }
- /** Can an primitive unboxing adapter validly convert src to dst? */
- static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
- assert(dst.isPrimitive());
- // if we have JVM support for boxing, we can also do complex unboxing
- if (convOpSupported(OP_PRIM_TO_REF)) return true;
- Wrapper dw = Wrapper.forPrimitiveType(dst);
- // Level 0 means cast and unbox. This works on any reference.
- if (level == 0) return !src.isPrimitive();
- assert(level >= 0 && level <= 2);
- // Levels 1 and 2 allow widening and/or narrowing conversions.
- // These are not supported directly by the JVM.
- // But if the input reference is monomorphic, we can do it.
- return dw.wrapperType() == src;
- }
-
- /** Factory method: Unbox the given argument.
- * Return null if this cannot be done.
- */
- static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
- int arg, Class<?> convType, int level) {
- MethodType oldType = target.type();
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = oldType.parameterType(arg);
- Class<?> boxType = Wrapper.asWrapperType(convType);
- Class<?> primType = Wrapper.asPrimitiveType(convType);
- if (!canUnboxArgument(newType, oldType, arg, convType, level))
- return null;
- MethodType castDone = newType;
- if (!VerifyType.isNullConversion(src, boxType)) {
- // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int
- if (level != 0) {
- // must include additional conversions
- if (src == Object.class || !Wrapper.isWrapperType(src)) {
- // src must be examined at runtime, to detect Byte, Character, etc.
- MethodHandle unboxMethod = (level == 1
- ? ValueConversions.unbox(dst)
- : ValueConversions.unboxCast(dst));
- long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
- return new AdapterMethodHandle(target, newType, conv, unboxMethod);
- }
- // Example: Byte->int
- // Do this by reformulating the problem to Byte->byte.
- Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
- MethodType midType = newType.changeParameterType(arg, srcPrim);
- MethodHandle fixPrim; // makePairwiseConvert(midType, target, 0);
- if (canPrimCast(midType, oldType, arg, dst))
- fixPrim = makePrimCast(midType, target, arg, dst);
- else
- fixPrim = target;
- return makeUnboxArgument(newType, fixPrim, arg, srcPrim, 0);
- }
- castDone = newType.changeParameterType(arg, boxType);
- }
- long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
- MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
- if (castDone == newType)
- return adapter;
- return makeCheckCast(newType, adapter, arg, boxType);
- }
-
- /** Can a boxing conversion validly convert src to dst? */
- static boolean canBoxArgument(MethodType newType, MethodType targetType,
- int arg, Class<?> convType) {
- if (!convOpSupported(OP_PRIM_TO_REF)) return false;
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = targetType.parameterType(arg);
- Class<?> boxType = Wrapper.asWrapperType(convType);
- convType = Wrapper.asPrimitiveType(convType);
- if (!canCheckCast(boxType, dst)
- || boxType == convType
- || !VerifyType.isNullConversion(src, convType))
- return false;
- int diff = diffTypes(newType, targetType, false);
- return (diff == arg+1); // arg is sole non-trivial diff
- }
-
- /** Can an primitive boxing adapter validly convert src to dst? */
- static boolean canBoxArgument(Class<?> src, Class<?> dst) {
- if (!convOpSupported(OP_PRIM_TO_REF)) return false;
- return (src.isPrimitive() && !dst.isPrimitive());
- }
-
- /** Factory method: Box the given argument.
- * Return null if this cannot be done.
- */
- static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
- int arg, Class<?> convType) {
- MethodType oldType = target.type();
- Class<?> src = newType.parameterType(arg);
- Class<?> dst = oldType.parameterType(arg);
- Class<?> boxType = Wrapper.asWrapperType(convType);
- Class<?> primType = Wrapper.asPrimitiveType(convType);
- if (!canBoxArgument(newType, oldType, arg, convType)) {
- return null;
- }
- if (!VerifyType.isNullConversion(boxType, dst))
- target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst);
- MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType));
- long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT);
- return new AdapterMethodHandle(target, newType, conv, boxerMethod);
- }
-
- /** Can an adapter simply drop arguments to convert the target to newType? */
- static boolean canDropArguments(MethodType newType, MethodType targetType,
- int dropArgPos, int dropArgCount) {
- if (dropArgCount == 0)
- return canRetypeOnly(newType, targetType);
- if (!convOpSupported(OP_DROP_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- // parameter types must be the same up to the drop point
- if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0)
- return false;
- int afterPos = dropArgPos + dropArgCount;
- int afterCount = nptypes - afterPos;
- if (dropArgPos < 0 || dropArgPos >= nptypes ||
- dropArgCount < 1 || afterPos > nptypes ||
- targetType.parameterCount() != nptypes - dropArgCount)
- return false;
- // parameter types after the drop point must also be the same
- if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Drop selected arguments.
- * Allow unchecked retyping of remaining arguments, pairwise.
- * Return null if this is not possible.
- */
- static MethodHandle makeDropArguments(MethodType newType, MethodHandle target,
- int dropArgPos, int dropArgCount) {
- if (dropArgCount == 0)
- return makeRetypeOnly(newType, target);
- if (!canDropArguments(newType, target.type(), dropArgPos, dropArgCount))
- return null;
- // in arglist: [0: ...keep1 | dpos: drop... | dpos+dcount: keep2... ]
- // out arglist: [0: ...keep1 | dpos: keep2... ]
- int keep2InPos = dropArgPos + dropArgCount;
- int dropSlot = newType.parameterSlotDepth(keep2InPos);
- int keep1InSlot = newType.parameterSlotDepth(dropArgPos);
- int slotCount = keep1InSlot - dropSlot;
- assert(slotCount >= dropArgCount);
- assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
- long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an adapter duplicate an argument to convert the target to newType? */
- static boolean canDupArguments(MethodType newType, MethodType targetType,
- int dupArgPos, int dupArgCount) {
- if (!convOpSupported(OP_DUP_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- if (dupArgCount < 0 || dupArgPos + dupArgCount > nptypes)
- return false;
- if (targetType.parameterCount() != nptypes + dupArgCount)
- return false;
- // parameter types must be the same up to the duplicated arguments
- if (diffParamTypes(newType, 0, targetType, 0, nptypes, false) != 0)
- return false;
- // duplicated types must be, well, duplicates
- if (diffParamTypes(newType, dupArgPos, targetType, nptypes, dupArgCount, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Duplicate the selected argument.
- * Return null if this is not possible.
- */
- static MethodHandle makeDupArguments(MethodType newType, MethodHandle target,
- int dupArgPos, int dupArgCount) {
- if (!canDupArguments(newType, target.type(), dupArgPos, dupArgCount))
- return null;
- if (dupArgCount == 0)
- return target;
- // in arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... ]
- // out arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... | dup... ]
- int keep2InPos = dupArgPos + dupArgCount;
- int dupSlot = newType.parameterSlotDepth(keep2InPos);
- int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
- int slotCount = keep1InSlot - dupSlot;
- assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
- long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an adapter swap two arguments to convert the target to newType? */
- static boolean canSwapArguments(MethodType newType, MethodType targetType,
- int swapArg1, int swapArg2) {
- if (!convOpSupported(OP_SWAP_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- if (swapArg1 >= swapArg2) return false; // caller resp
- int nptypes = newType.parameterCount();
- if (targetType.parameterCount() != nptypes)
- return false;
- if (swapArg1 < 0 || swapArg2 >= nptypes)
- return false;
- if (diffParamTypes(newType, 0, targetType, 0, swapArg1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg1, targetType, swapArg2, 1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg1+1, targetType, swapArg1+1, swapArg2-swapArg1-1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg2, targetType, swapArg1, 1, false) != 0)
- return false;
- if (diffParamTypes(newType, swapArg2+1, targetType, swapArg2+1, nptypes-swapArg2-1, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Swap the selected arguments.
- * Return null if this is not possible.
- */
- static MethodHandle makeSwapArguments(MethodType newType, MethodHandle target,
- int swapArg1, int swapArg2) {
- if (swapArg1 == swapArg2)
- return target;
- if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; }
- if (type2size(newType.parameterType(swapArg1)) !=
- type2size(newType.parameterType(swapArg2))) {
- // turn a swap into a pair of rotates:
- // [x a b c y] rot2(-1,argc=5) => [a b c y x] rot1(+1,argc=4) => target[y a b c x]
- int argc = swapArg2 - swapArg1 + 1;
- final int ROT = 1;
- ArrayList<Class<?>> rot1Params = new ArrayList<Class<?>>(target.type().parameterList());
- Collections.rotate(rot1Params.subList(swapArg1, swapArg1 + argc), -ROT);
- MethodType rot1Type = MethodType.methodType(target.type().returnType(), rot1Params);
- MethodHandle rot1 = makeRotateArguments(rot1Type, target, swapArg1, argc, +ROT);
- assert(rot1 != null);
- if (argc == 2) return rot1;
- MethodHandle rot2 = makeRotateArguments(newType, rot1, swapArg1, argc-1, -ROT);
- assert(rot2 != null);
- return rot2;
- }
- if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2))
- return null;
- Class<?> type1 = newType.parameterType(swapArg1);
- Class<?> type2 = newType.parameterType(swapArg2);
- // in arglist: [0: ...keep1 | pos1: a1 | pos1+1: keep2... | pos2: a2 | pos2+1: keep3... ]
- // out arglist: [0: ...keep1 | pos1: a2 | pos1+1: keep2... | pos2: a1 | pos2+1: keep3... ]
- int swapSlot2 = newType.parameterSlotDepth(swapArg2 + 1);
- long conv = makeSwapConv(OP_SWAP_ARGS, swapArg1, basicType(type1), swapSlot2, basicType(type2));
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- static int positiveRotation(int argCount, int rotateBy) {
- assert(argCount > 0);
- if (rotateBy >= 0) {
- if (rotateBy < argCount)
- return rotateBy;
- return rotateBy % argCount;
- } else if (rotateBy >= -argCount) {
- return rotateBy + argCount;
- } else {
- return (-1-((-1-rotateBy) % argCount)) + argCount;
- }
- }
-
- final static int MAX_ARG_ROTATION = 1;
-
- /** Can an adapter rotate arguments to convert the target to newType? */
- static boolean canRotateArguments(MethodType newType, MethodType targetType,
- int firstArg, int argCount, int rotateBy) {
- if (!convOpSupported(OP_ROT_ARGS)) return false;
- rotateBy = positiveRotation(argCount, rotateBy);
- if (rotateBy == 0) return false; // no rotation
- if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION)
- return false; // too many argument positions
- // Rotate incoming args right N to the out args, N in 1..(argCouunt-1).
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- if (targetType.parameterCount() != nptypes)
- return false;
- if (firstArg < 0 || firstArg >= nptypes) return false;
- int argLimit = firstArg + argCount;
- if (argLimit > nptypes) return false;
- if (diffParamTypes(newType, 0, targetType, 0, firstArg, false) != 0)
- return false;
- int newChunk1 = argCount - rotateBy, newChunk2 = rotateBy;
- // swap new chunk1 with target chunk2
- if (diffParamTypes(newType, firstArg, targetType, argLimit-newChunk1, newChunk1, false) != 0)
- return false;
- // swap new chunk2 with target chunk1
- if (diffParamTypes(newType, firstArg+newChunk1, targetType, firstArg, newChunk2, false) != 0)
- return false;
- return true;
- }
-
- /** Factory method: Rotate the selected argument range.
- * Return null if this is not possible.
- */
- static MethodHandle makeRotateArguments(MethodType newType, MethodHandle target,
- int firstArg, int argCount, int rotateBy) {
- rotateBy = positiveRotation(argCount, rotateBy);
- if (!canRotateArguments(newType, target.type(), firstArg, argCount, rotateBy))
- return null;
- // Decide whether it should be done as a right or left rotation,
- // on the JVM stack. Return the number of stack slots to rotate by,
- // positive if right, negative if left.
- int limit = firstArg + argCount;
- int depth0 = newType.parameterSlotDepth(firstArg);
- int depth1 = newType.parameterSlotDepth(limit-rotateBy);
- int depth2 = newType.parameterSlotDepth(limit);
- int chunk1Slots = depth0 - depth1; assert(chunk1Slots > 0);
- int chunk2Slots = depth1 - depth2; assert(chunk2Slots > 0);
- // From here on out, it assumes a single-argument shift.
- assert(MAX_ARG_ROTATION == 1);
- int srcArg, dstArg;
- int dstSlot;
- int moveChunk;
- if (rotateBy == 1) {
- // Rotate right/down N (rotateBy = +N, N small, c2 small):
- // in arglist: [0: ...keep1 | arg1: c1... | limit-N: c2 | limit: keep2... ]
- // out arglist: [0: ...keep1 | arg1: c2 | arg1+N: c1... | limit: keep2... ]
- srcArg = limit-1;
- dstArg = firstArg;
- //dstSlot = depth0 - chunk2Slots; //chunk2Slots is not relevant
- dstSlot = depth0 + MethodHandleNatives.OP_ROT_ARGS_DOWN_LIMIT_BIAS;
- moveChunk = chunk2Slots;
- } else {
- // Rotate left/up N (rotateBy = -N, N small, c1 small):
- // in arglist: [0: ...keep1 | arg1: c1 | arg1+N: c2... | limit: keep2... ]
- // out arglist: [0: ...keep1 | arg1: c2 ... | limit-N: c1 | limit: keep2... ]
- srcArg = firstArg;
- dstArg = limit-1;
- dstSlot = depth2;
- moveChunk = chunk1Slots;
- }
- byte srcType = basicType(newType.parameterType(srcArg));
- byte dstType = basicType(newType.parameterType(dstArg));
- assert(moveChunk == type2size(srcType));
- long conv = makeSwapConv(OP_ROT_ARGS, srcArg, srcType, dstSlot, dstType);
- return new AdapterMethodHandle(target, newType, conv);
- }
-
- /** Can an adapter spread an argument to convert the target to newType? */
- static boolean canSpreadArguments(MethodType newType, MethodType targetType,
- Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
- if (!convOpSupported(OP_SPREAD_ARGS)) return false;
- if (diffReturnTypes(newType, targetType, false) != 0)
- return false;
- int nptypes = newType.parameterCount();
- // parameter types must be the same up to the spread point
- if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0)
- return false;
- int afterPos = spreadArgPos + spreadArgCount;
- int afterCount = nptypes - (spreadArgPos + 1);
- if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
- spreadArgCount < 0 ||
- targetType.parameterCount() != afterPos + afterCount)
- return false;
- // parameter types after the spread point must also be the same
- if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0)
- return false;
- // match the array element type to the spread arg types
- Class<?> rawSpreadArgType = newType.parameterType(spreadArgPos);
- if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType))
- return false;
- for (int i = 0; i < spreadArgCount; i++) {
- Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
- Class<?> dst = targetType.parameterType(spreadArgPos + i);
- if (src == null || !canConvertArgument(src, dst, 1))
- return false;
- }
- return true;
- }
-
-
- /** Factory method: Spread selected argument. */
- static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
- Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
- // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
- MethodType targetType = target.type();
- assert(canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
- : "[newType, targetType, spreadArgType, spreadArgPos, spreadArgCount] = "
- + Arrays.asList(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount);
- // dest is not significant; remove?
- int dest = T_VOID;
- for (int i = 0; i < spreadArgCount; i++) {
- Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
- if (arg == null) arg = Object.class;
- int dest2 = basicType(arg);
- if (dest == T_VOID) dest = dest2;
- else if (dest != dest2) dest = T_VOID;
- if (dest == T_VOID) break;
- targetType = targetType.changeParameterType(spreadArgPos + i, arg);
- }
- target = target.asType(targetType);
- int arrayArgSize = 1; // always a reference
- // in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ]
- // out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
- int keep2OutPos = spreadArgPos + spreadArgCount;
- int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...|
- int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...|
- assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
- int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...|
- assert(slotCount >= spreadArgCount);
- int stackMove = - arrayArgSize + slotCount; // pop array, push N slots
- long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove);
- MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
- assert(res.type().parameterType(spreadArgPos) == spreadArgType);
- return res;
- }
-
- /** Can an adapter collect a series of arguments, replacing them by zero or one results? */
- static boolean canCollectArguments(MethodType targetType,
- MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
- if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS)) return false;
- int collectArgCount = collectorType.parameterCount();
- Class<?> rtype = collectorType.returnType();
- assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
- // [(Object)Object[], (Object[])Object[], 0, 1]
- : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
- ;
- return true;
- }
-
- /** Factory method: Collect or filter selected argument(s). */
- static MethodHandle makeCollectArguments(MethodHandle target,
- MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
- assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
- MethodType targetType = target.type();
- MethodType collectorType = collector.type();
- int collectArgCount = collectorType.parameterCount();
- Class<?> collectValType = collectorType.returnType();
- int collectValCount = (collectValType == void.class ? 0 : 1);
- int collectValSlots = collectorType.returnSlotCount();
- MethodType newType = targetType
- .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
- if (!retainOriginalArgs) {
- newType = newType
- .insertParameterTypes(collectArgPos, collectorType.parameterList());
- } else {
- // parameter types at the fold point must be the same
- assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
- : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
- }
- // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
- // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
- // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
- int keep2InPos = collectArgPos + collectArgCount;
- int keep1InSlot = newType.parameterSlotDepth(collectArgPos); // leading edge of |collect...|
- int collectSlot = newType.parameterSlotDepth(keep2InPos); // trailing edge of |collect...|
- int slotCount = keep1InSlot - collectSlot; // slots in |collect...|
- assert(slotCount >= collectArgCount);
- assert(collectSlot == targetType.parameterSlotDepth(
- collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) ));
- int dest = basicType(collectValType);
- int src = T_VOID;
- // src is not significant; remove?
- for (int i = 0; i < collectArgCount; i++) {
- int src2 = basicType(collectorType.parameterType(i));
- if (src == T_VOID) src = src2;
- else if (src != src2) src = T_VOID;
- if (src == T_VOID) break;
- }
- int stackMove = collectValSlots; // push 0..2 results
- if (!retainOriginalArgs) stackMove -= slotCount; // pop N arguments
- int lastCollectArg = keep2InPos-1;
- long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS,
- lastCollectArg, src, dest, stackMove);
- MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector);
- assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount)
- .equals(collector.type().parameterList()));
- return res;
- }
-
- @Override
- String debugString() {
- return getNameString(nonAdapter((MethodHandle)vmtarget), this);
- }
-
- private static MethodHandle nonAdapter(MethodHandle mh) {
- while (mh instanceof AdapterMethodHandle) {
- mh = (MethodHandle) mh.vmtarget;
- }
- return mh;
- }
-}
--- a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java Fri Aug 10 16:03:28 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -25,164 +25,828 @@
package java.lang.invoke;
-import sun.invoke.util.VerifyType;
+import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*;
+import static java.lang.invoke.LambdaForm.basicTypes;
+import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+import java.lang.invoke.LambdaForm.Name;
+import java.lang.invoke.LambdaForm.NamedFunction;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
-import static java.lang.invoke.MethodHandleStatics.*;
+
+import com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter;
+import com.sun.xml.internal.ws.org.objectweb.asm.MethodVisitor;
+import com.sun.xml.internal.ws.org.objectweb.asm.Type;
/**
* The flavor of method handle which emulates an invoke instruction
* on a predetermined argument. The JVM dispatches to the correct method
* when the handle is created, not when it is invoked.
- * @author jrose
+ *
+ * All bound arguments are encapsulated in dedicated species.
*/
-class BoundMethodHandle extends MethodHandle {
- //MethodHandle vmtarget; // next BMH or final DMH or methodOop
- private final Object argument; // argument to insert
- private final int vmargslot; // position at which it is inserted
-
- // Constructors in this class *must* be package scoped or private.
+/* non-public */ abstract class BoundMethodHandle extends MethodHandle {
- /** Bind a direct MH to its receiver (or first ref. argument).
- * The JVM will pre-dispatch the MH if it is not already static.
- */
- /*non-public*/ BoundMethodHandle(DirectMethodHandle mh, Object argument) {
- super(mh.type().dropParameterTypes(0, 1));
- // check the type now, once for all:
- this.argument = checkReferenceArgument(argument, mh, 0);
- this.vmargslot = this.type().parameterSlotCount();
- initTarget(mh, 0);
+ /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
+ super(type, form);
}
- /** Insert an argument into an arbitrary method handle.
- * If argnum is zero, inserts the first argument, etc.
- * The argument type must be a reference.
- */
- /*non-public*/ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
- this(mh.type().dropParameterTypes(argnum, argnum+1),
- mh, argument, argnum);
- }
+ //
+ // BMH API and internals
+ //
- /** Insert an argument into an arbitrary method handle.
- * If argnum is zero, inserts the first argument, etc.
- */
- /*non-public*/ BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
- super(type);
- if (mh.type().parameterType(argnum).isPrimitive())
- this.argument = bindPrimitiveArgument(argument, mh, argnum);
- else {
- this.argument = checkReferenceArgument(argument, mh, argnum);
+ static MethodHandle bindSingle(MethodType type, LambdaForm form, char xtype, Object x) {
+ // for some type signatures, there exist pre-defined concrete BMH classes
+ try {
+ switch (xtype) {
+ case 'L':
+ if (true) return bindSingle(type, form, x); // Use known fast path.
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('L').constructor[0].invokeBasic(type, form, x);
+ case 'I':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
+ case 'J':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
+ case 'F':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
+ case 'D':
+ return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
+ default : throw new InternalError("unexpected xtype: " + xtype);
+ }
+ } catch (Throwable t) {
+ throw new InternalError(t);
}
- this.vmargslot = type.parameterSlotDepth(argnum);
- initTarget(mh, argnum);
- }
-
- private void initTarget(MethodHandle mh, int argnum) {
- //this.vmtarget = mh; // maybe updated by JVM
- MethodHandleNatives.init(this, mh, argnum);
- }
-
- /** For the AdapterMethodHandle subclass.
- */
- /*non-public*/ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
- super(type);
- this.argument = argument;
- this.vmargslot = vmargslot;
- assert(this instanceof AdapterMethodHandle);
}
- /** Initialize the current object as a self-bound method handle, binding it
- * as the first argument of the method handle {@code entryPoint}.
- * The invocation type of the resulting method handle will be the
- * same as {@code entryPoint}, except that the first argument
- * type will be dropped.
- */
- /*non-public*/ BoundMethodHandle(MethodHandle entryPoint) {
- super(entryPoint.type().dropParameterTypes(0, 1));
- this.argument = this; // kludge; get rid of
- this.vmargslot = this.type().parameterSlotDepth(0);
- initTarget(entryPoint, 0);
+ static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
+ return new Species_L(type, form, x);
}
- /** Make sure the given {@code argument} can be used as {@code argnum}-th
- * parameter of the given method handle {@code mh}, which must be a reference.
- * <p>
- * If this fails, throw a suitable {@code WrongMethodTypeException},
- * which will prevent the creation of an illegally typed bound
- * method handle.
- */
- final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) {
- Class<?> ptype = mh.type().parameterType(argnum);
- if (ptype.isPrimitive()) {
- // fail
- } else if (argument == null) {
- return null;
- } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
- return argument;
+ MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
+ try {
+ switch (xtype) {
+ case 'L': return cloneExtendL(type, form, x);
+ case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
+ case 'J': return cloneExtendJ(type, form, (long) x);
+ case 'F': return cloneExtendF(type, form, (float) x);
+ case 'D': return cloneExtendD(type, form, (double) x);
+ }
+ } catch (Throwable t) {
+ throw new InternalError(t);
}
- throw badBoundArgumentException(argument, mh, argnum);
+ throw new InternalError("unexpected type: " + xtype);
}
- /** Make sure the given {@code argument} can be used as {@code argnum}-th
- * parameter of the given method handle {@code mh}, which must be a primitive.
- * <p>
- * If this fails, throw a suitable {@code WrongMethodTypeException},
- * which will prevent the creation of an illegally typed bound
- * method handle.
- */
- final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) {
- Class<?> ptype = mh.type().parameterType(argnum);
- Wrapper wrap = Wrapper.forPrimitiveType(ptype);
- Object zero = wrap.zero();
- if (zero == null) {
- // fail
- } else if (argument == null) {
- if (ptype != int.class && wrap.isSubwordOrInt())
- return Integer.valueOf(0);
- else
- return zero;
- } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) {
- if (ptype != int.class && wrap.isSubwordOrInt())
- return Wrapper.INT.wrap(argument);
- else
- return argument;
- }
- throw badBoundArgumentException(argument, mh, argnum);
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ MethodType type = type().dropParameterTypes(pos, pos+1);
+ LambdaForm form = internalForm().bind(1+pos, speciesData());
+ return cloneExtend(type, form, basicType, value);
}
- final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
- String atype = (argument == null) ? "null" : argument.getClass().toString();
- return new ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
+ @Override
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
+ try {
+ return clone(srcType, form);
+ } catch (Throwable t) {
+ throw new InternalError(t);
+ }
}
@Override
- String debugString() {
- return addTypeString(baseName(), this);
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ try {
+ return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
+ } catch (Throwable t) {
+ throw new InternalError(t);
+ }
+ }
+
+ static final String EXTENSION_TYPES = "LIJFD";
+ static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
+ static byte extensionIndex(char type) {
+ int i = EXTENSION_TYPES.indexOf(type);
+ if (i < 0) throw new InternalError();
+ return (byte) i;
+ }
+
+ /**
+ * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
+ * static field containing this value, and they must accordingly implement this method.
+ */
+ protected abstract SpeciesData speciesData();
+
+ @Override
+ final Object internalValues() {
+ Object[] boundValues = new Object[speciesData().fieldCount()];
+ for (int i = 0; i < boundValues.length; ++i) {
+ boundValues[i] = arg(i);
+ }
+ return Arrays.asList(boundValues);
+ }
+
+ public final Object arg(int i) {
+ try {
+ switch (speciesData().fieldType(i)) {
+ case 'L': return argL(i);
+ case 'I': return argI(i);
+ case 'F': return argF(i);
+ case 'D': return argD(i);
+ case 'J': return argJ(i);
+ }
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ throw new InternalError("unexpected type: " + speciesData().types+"."+i);
+ }
+ public final Object argL(int i) throws Throwable { return speciesData().getters[i].invokeBasic(this); }
+ public final int argI(int i) throws Throwable { return (int) speciesData().getters[i].invokeBasic(this); }
+ public final float argF(int i) throws Throwable { return (float) speciesData().getters[i].invokeBasic(this); }
+ public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
+ public final long argJ(int i) throws Throwable { return (long) speciesData().getters[i].invokeBasic(this); }
+
+ //
+ // cloning API
+ //
+
+ public abstract BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable;
+ public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
+
+ // The following is a grossly irregular hack:
+ @Override MethodHandle reinvokerTarget() {
+ try {
+ return (MethodHandle) argL(0);
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ //
+ // concrete BMH classes required to close bootstrap loops
+ //
+
+ private // make it private to force users to access the enclosing class first
+ static final class Species_L extends BoundMethodHandle {
+ final Object argL0;
+ public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
+ super(mt, lf);
+ this.argL0 = argL0;
+ }
+ // The following is a grossly irregular hack:
+ @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
+ @Override
+ public SpeciesData speciesData() {
+ return SPECIES_DATA;
+ }
+ public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
+ @Override
+ public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+ return new Species_L(mt, lf, argL0);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg);
+ }
+ }
+
+/*
+ static final class Species_LL extends BoundMethodHandle {
+ final Object argL0;
+ final Object argL1;
+ public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
+ super(mt, lf);
+ this.argL0 = argL0;
+ this.argL1 = argL1;
+ }
+ @Override
+ public SpeciesData speciesData() {
+ return SPECIES_DATA;
+ }
+ public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LL", Species_LL.class);
+ @Override
+ public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+ return new Species_LL(mt, lf, argL0, argL1);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
+ }
+ }
+
+ static final class Species_JL extends BoundMethodHandle {
+ final long argJ0;
+ final Object argL1;
+ public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
+ super(mt, lf);
+ this.argJ0 = argJ0;
+ this.argL1 = argL1;
+ }
+ @Override
+ public SpeciesData speciesData() {
+ return SPECIES_DATA;
+ }
+ public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("JL", Species_JL.class);
+ @Override public final long argJ0() { return argJ0; }
+ @Override public final Object argL1() { return argL1; }
+ @Override
+ public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
+ return new Species_JL(mt, lf, argJ0, argL1);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ @Override
+ public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
+ return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
+ }
+ }
+*/
+
+ //
+ // BMH species meta-data
+ //
+
+ /**
+ * Meta-data wrapper for concrete BMH classes.
+ */
+ static class SpeciesData {
+ final String types;
+ final Class<? extends BoundMethodHandle> clazz;
+ // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
+ // Therefore, we need a non-final link in the chain. Use array elements.
+ final MethodHandle[] constructor;
+ final MethodHandle[] getters;
+ final SpeciesData[] extensions;
+
+ public int fieldCount() {
+ return types.length();
+ }
+ public char fieldType(int i) {
+ return types.charAt(i);
+ }
+
+ public String toString() {
+ return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
+ }
+
+ /**
+ * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
+ * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
+ * getter.
+ */
+ Name getterName(Name mhName, int i) {
+ MethodHandle mh = getters[i];
+ assert(mh != null) : this+"."+i;
+ return new Name(mh, mhName);
+ }
+
+ static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
+
+ private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
+ this.types = types;
+ this.clazz = clazz;
+ if (!INIT_DONE) {
+ this.constructor = new MethodHandle[1];
+ this.getters = new MethodHandle[types.length()];
+ } else {
+ this.constructor = Factory.makeCtors(clazz, types, null);
+ this.getters = Factory.makeGetters(clazz, types, null);
+ }
+ this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
+ }
+
+ private void initForBootstrap() {
+ assert(!INIT_DONE);
+ if (constructor[0] == null) {
+ Factory.makeCtors(clazz, types, this.constructor);
+ Factory.makeGetters(clazz, types, this.getters);
+ }
+ }
+
+ private SpeciesData(String types) {
+ // Placeholder only.
+ this.types = types;
+ this.clazz = null;
+ this.constructor = null;
+ this.getters = null;
+ this.extensions = null;
+ }
+ private boolean isPlaceholder() { return clazz == null; }
+
+ private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
+ private static final boolean INIT_DONE; // set after <clinit> finishes...
+
+ SpeciesData extendWithType(char type) {
+ int i = extensionIndex(type);
+ SpeciesData d = extensions[i];
+ if (d != null) return d;
+ extensions[i] = d = get(types+type);
+ return d;
+ }
+
+ SpeciesData extendWithIndex(byte index) {
+ SpeciesData d = extensions[index];
+ if (d != null) return d;
+ extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
+ return d;
+ }
+
+ private static SpeciesData get(String types) {
+ // Acquire cache lock for query.
+ SpeciesData d = lookupCache(types);
+ if (!d.isPlaceholder())
+ return d;
+ synchronized (d) {
+ // Use synch. on the placeholder to prevent multiple instantiation of one species.
+ // Creating this class forces a recursive call to getForClass.
+ if (lookupCache(types).isPlaceholder())
+ Factory.generateConcreteBMHClass(types);
+ }
+ // Reacquire cache lock.
+ d = lookupCache(types);
+ // Class loading must have upgraded the cache.
+ assert(d != null && !d.isPlaceholder());
+ return d;
+ }
+ static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
+ // clazz is a new class which is initializing its SPECIES_DATA field
+ return updateCache(types, new SpeciesData(types, clazz));
+ }
+ private static synchronized SpeciesData lookupCache(String types) {
+ SpeciesData d = CACHE.get(types);
+ if (d != null) return d;
+ d = new SpeciesData(types);
+ assert(d.isPlaceholder());
+ CACHE.put(types, d);
+ return d;
+ }
+ private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
+ SpeciesData d2;
+ assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
+ assert(!d.isPlaceholder());
+ CACHE.put(types, d);
+ return d;
+ }
+
+ static {
+ // pre-fill the BMH speciesdata cache with BMH's inner classes
+ final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
+ SpeciesData d0 = BoundMethodHandle.SPECIES_DATA; // trigger class init
+ assert(d0 == null || d0 == lookupCache("")) : d0;
+ try {
+ for (Class<?> c : rootCls.getDeclaredClasses()) {
+ if (rootCls.isAssignableFrom(c)) {
+ final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
+ SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
+ assert(d != null) : cbmh.getName();
+ assert(d.clazz == cbmh);
+ assert(d == lookupCache(d.types));
+ }
+ }
+ } catch (Throwable e) {
+ throw new InternalError(e);
+ }
+
+ for (SpeciesData d : CACHE.values()) {
+ d.initForBootstrap();
+ }
+ // Note: Do not simplify this, because INIT_DONE must not be
+ // a compile-time constant during bootstrapping.
+ INIT_DONE = Boolean.TRUE;
+ }
+ }
+
+ static SpeciesData getSpeciesData(String types) {
+ return SpeciesData.get(types);
}
- /** Component of toString() before the type string. */
- protected String baseName() {
- MethodHandle mh = this;
- while (mh instanceof BoundMethodHandle) {
- Object info = MethodHandleNatives.getTargetInfo(mh);
- if (info instanceof MethodHandle) {
- mh = (MethodHandle) info;
- } else {
- String name = null;
- if (info instanceof MemberName)
- name = ((MemberName)info).getName();
- if (name != null)
- return name;
- else
- return noParens(super.toString()); // "invoke", probably
+ /**
+ * Generation of concrete BMH classes.
+ *
+ * A concrete BMH species is fit for binding a number of values adhering to a
+ * given type pattern. Reference types are erased.
+ *
+ * BMH species are cached by type pattern.
+ *
+ * A BMH species has a number of fields with the concrete (possibly erased) types of
+ * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
+ * which can be included as names in lambda forms.
+ */
+ static class Factory {
+
+ static final String JLO_SIG = "Ljava/lang/Object;";
+ static final String JLS_SIG = "Ljava/lang/String;";
+ static final String JLC_SIG = "Ljava/lang/Class;";
+ static final String MH = "java/lang/invoke/MethodHandle";
+ static final String MH_SIG = "L"+MH+";";
+ static final String BMH = "java/lang/invoke/BoundMethodHandle";
+ static final String BMH_SIG = "L"+BMH+";";
+ static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData";
+ static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
+
+ static final String SPECIES_PREFIX_NAME = "Species_";
+ static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
+
+ static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
+ static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
+ static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
+ static final String VOID_SIG = "()V";
+
+ static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
+
+ static final Class<?>[] TYPES = new Class<?>[] { Object.class, int.class, long.class, float.class, double.class };
+
+ static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
+
+ /**
+ * Generate a concrete subclass of BMH for a given combination of bound types.
+ *
+ * A concrete BMH species adheres to the following schema:
+ *
+ * <pre>
+ * class Species_<<types>> extends BoundMethodHandle {
+ * <<fields>>
+ * final SpeciesData speciesData() { return SpeciesData.get("<<types>>"); }
+ * }
+ * </pre>
+ *
+ * The {@code <<types>>} signature is precisely the string that is passed to this
+ * method.
+ *
+ * The {@code <<fields>>} section consists of one field definition per character in
+ * the type signature, adhering to the naming schema described in the definition of
+ * {@link #makeFieldName()}.
+ *
+ * For example, a concrete BMH species for two reference and one integral bound values
+ * would have the following shape:
+ *
+ * <pre>
+ * class BoundMethodHandle { ... private static
+ * final class Species_LLI extends BoundMethodHandle {
+ * final Object argL0;
+ * final Object argL1;
+ * final int argI2;
+ * public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
+ * super(mt, lf);
+ * this.argL0 = argL0;
+ * this.argL1 = argL1;
+ * this.argI2 = argI2;
+ * }
+ * public final SpeciesData speciesData() { return SPECIES_DATA; }
+ * public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
+ * public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
+ * return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
+ * }
+ * public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
+ * return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
+ * }
+ * }
+ * </pre>
+ *
+ * @param types the type signature, wherein reference types are erased to 'L'
+ * @return the generated concrete BMH class
+ */
+ static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
+ final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+
+ final String className = SPECIES_PREFIX_PATH + types;
+ final String sourceFile = SPECIES_PREFIX_NAME + types;
+ cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
+ cw.visitSource(sourceFile, null);
+
+ // emit static types and SPECIES_DATA fields
+ cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
+
+ // emit bound argument fields
+ for (int i = 0; i < types.length(); ++i) {
+ final char t = types.charAt(i);
+ final String fieldName = makeFieldName(types, i);
+ final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
+ cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
+ }
+
+ MethodVisitor mv;
+
+ // emit constructor
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", makeSignature(types, true), null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+
+ mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true));
+
+ for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
+ // i counts the arguments, j counts corresponding argument slots
+ char t = types.charAt(i);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
+ mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
+ if (t == 'J' || t == 'D') {
+ ++j; // adjust argument register access
+ }
}
- assert(mh != this);
+
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // emit implementation of reinvokerTarget()
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
+ mv.visitTypeInsn(CHECKCAST, MH);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // emit implementation of speciesData()
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
+ mv.visitCode();
+ mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // emit clone()
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
+ mv.visitCode();
+ // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...)
+ // obtain constructor
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+ mv.visitInsn(ICONST_0);
+ mv.visitInsn(AALOAD);
+ // load mt, lf
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ // put fields on the stack
+ emitPushFields(types, className, mv);
+ // finally, invoke the constructor and return
+ mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types, false));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // for each type, emit cloneExtendT()
+ for (Class<?> c : TYPES) {
+ char t = Wrapper.basicTypeChar(c);
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
+ mv.visitCode();
+ // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
+ // obtain constructor
+ mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ int iconstInsn = ICONST_0 + extensionIndex(t);
+ assert(iconstInsn <= ICONST_5);
+ mv.visitInsn(iconstInsn);
+ mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
+ mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
+ mv.visitInsn(ICONST_0);
+ mv.visitInsn(AALOAD);
+ // load mt, lf
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ // put fields on the stack
+ emitPushFields(types, className, mv);
+ // put narg on stack
+ mv.visitVarInsn(typeLoadOp(t), 3);
+ // finally, invoke the constructor and return
+ mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ // emit class initializer
+ mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
+ mv.visitCode();
+ mv.visitLdcInsn(types);
+ mv.visitLdcInsn(Type.getObjectType(className));
+ mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG);
+ mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ cw.visitEnd();
+
+ // load class
+ final byte[] classFile = cw.toByteArray();
+ InvokerBytecodeGenerator.maybeDump(className, classFile);
+ Class<? extends BoundMethodHandle> bmhClass =
+ //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
+ UNSAFE.defineClass(className, classFile, 0, classFile.length).asSubclass(BoundMethodHandle.class);
+ UNSAFE.ensureClassInitialized(bmhClass);
+
+ return bmhClass;
+ }
+
+ private static int typeLoadOp(char t) {
+ switch (t) {
+ case 'L': return ALOAD;
+ case 'I': return ILOAD;
+ case 'J': return LLOAD;
+ case 'F': return FLOAD;
+ case 'D': return DLOAD;
+ default : throw new InternalError("unrecognized type " + t);
+ }
}
- return noParens(mh.toString());
+
+ private static void emitPushFields(String types, String className, MethodVisitor mv) {
+ for (int i = 0; i < types.length(); ++i) {
+ char tc = types.charAt(i);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
+ }
+ }
+
+ static String typeSig(char t) {
+ return t == 'L' ? JLO_SIG : String.valueOf(t);
+ }
+
+ //
+ // Getter MH generation.
+ //
+
+ private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
+ String fieldName = makeFieldName(types, index);
+ Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
+ try {
+ return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
+ if (mhs == null) mhs = new MethodHandle[types.length()];
+ for (int i = 0; i < mhs.length; ++i) {
+ mhs[i] = makeGetter(cbmhClass, types, i);
+ assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
+ }
+ return mhs;
+ }
+
+ static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
+ if (mhs == null) mhs = new MethodHandle[1];
+ mhs[0] = makeCbmhCtor(cbmh, types);
+ return mhs;
+ }
+
+ //
+ // Auxiliary methods.
+ //
+
+ static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
+ try {
+ Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
+ return (SpeciesData) F_SPECIES_DATA.get(null);
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ /**
+ * Field names in concrete BMHs adhere to this pattern:
+ * arg + type + index
+ * where type is a single character (L, I, J, F, D).
+ */
+ private static String makeFieldName(String types, int index) {
+ assert index >= 0 && index < types.length();
+ return "arg" + types.charAt(index) + index;
+ }
+
+ private static String makeSignature(String types, boolean ctor) {
+ StringBuilder buf = new StringBuilder(SIG_INCIPIT);
+ for (char c : types.toCharArray()) {
+ buf.append(typeSig(c));
+ }
+ return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
+ }
+
+ static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
+ try {
+ return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null)));
+ } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
+ throw new InternalError(e);
+ }
+ }
+
+ /**
+ * Wrap a constructor call in a {@link LambdaForm}.
+ *
+ * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs
+ * are turned into bytecode, because the call to the allocator is routed through an MH, and the
+ * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to
+ * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through
+ * {@link MethodHandle#linkToSpecial}.
+ *
+ * The last {@link LambdaForm#Name Name} in the argument's form is expected to be the {@code void}
+ * result of the {@code <init>} invocation. This entry is replaced.
+ */
+ private static MethodHandle linkConstructor(MethodHandle cmh) {
+ final LambdaForm lf = cmh.form;
+ final int initNameIndex = lf.names.length - 1;
+ final Name initName = lf.names[initNameIndex];
+ final MemberName ctorMN = initName.function.member;
+ final MethodType ctorMT = ctorMN.getInvocationType();
+
+ // obtain function member (call target)
+ // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!)
+ final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class);
+ MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic);
+ try {
+ linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class);
+ assert(linkerMN.isStatic());
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ // extend arguments array
+ Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1);
+ newArgs[newArgs.length - 1] = ctorMN;
+ // replace function
+ final NamedFunction nf = new NamedFunction(linkerMN);
+ final Name linkedCtor = new Name(nf, newArgs);
+ linkedCtor.initIndex(initNameIndex);
+ lf.names[initNameIndex] = linkedCtor;
+ return cmh;
+ }
+
}
- private static String noParens(String str) {
- int paren = str.indexOf('(');
- if (paren >= 0) str = str.substring(0, paren);
- return str;
- }
+ private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
+
+ /**
+ * All subclasses must provide such a value describing their type signature.
+ */
+ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
}
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java Fri Aug 10 16:03:28 2012 -0700
@@ -26,7 +26,7 @@
package java.lang.invoke;
import sun.invoke.empty.Empty;
-import sun.misc.Unsafe;
+import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
@@ -86,13 +86,9 @@
public class CallSite {
static { MethodHandleImpl.initStatics(); }
- // Fields used only by the JVM. Do not use or change.
- private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
- private int vmindex; // supplied by the JVM (BCI within calling method)
-
// The actual payload of this call site:
/*package-private*/
- MethodHandle target;
+ MethodHandle target; // Note: This field is known to the JVM. Do not change.
/**
* Make a blank call site object with the given method type.
@@ -151,24 +147,6 @@
return target.type();
}
- /** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite.
- * The parameters are JVM-specific.
- */
- void initializeFromJVM(String name,
- MethodType type,
- MemberName callerMethod,
- int callerBCI) {
- if (this.vmmethod != null) {
- // FIXME
- throw new BootstrapMethodError("call site has already been linked to an invokedynamic instruction");
- }
- if (!this.type().equals(type)) {
- throw wrongTargetType(target, type);
- }
- this.vmindex = callerBCI;
- this.vmmethod = callerMethod;
- }
-
/**
* Returns the target method of the call site, according to the
* behavior defined by this call site's specific class.
@@ -233,7 +211,7 @@
public abstract MethodHandle dynamicInvoker();
/*non-public*/ MethodHandle makeDynamicInvoker() {
- MethodHandle getTarget = MethodHandleImpl.bindReceiver(GET_TARGET, this);
+ MethodHandle getTarget = GET_TARGET.bindReceiver(this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
}
@@ -255,12 +233,10 @@
}
// unsafe stuff:
- private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long TARGET_OFFSET;
-
static {
try {
- TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+ TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
} catch (Exception ex) { throw new Error(ex); }
}
@@ -270,7 +246,7 @@
}
/*package-private*/
MethodHandle getTargetVolatile() {
- return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
+ return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
}
/*package-private*/
void setTargetVolatile(MethodHandle newTarget) {
@@ -284,8 +260,7 @@
// Extra arguments for BSM, if any:
Object info,
// Caller information:
- MemberName callerMethod, int callerBCI) {
- Class<?> callerClass = callerMethod.getDeclaringClass();
+ Class<?> callerClass) {
Object caller = IMPL_LOOKUP.in(callerClass);
CallSite site;
try {
--- a/jdk/src/share/classes/java/lang/invoke/CountingMethodHandle.java Fri Aug 10 10:15:52 2012 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2011, 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 static java.lang.invoke.MethodHandleNatives.Constants.*;
-
-/**
- * This method handle is used to optionally provide a count of how
- * many times it was invoked.
- *
- * @author never
- */
-class CountingMethodHandle extends AdapterMethodHandle {
- private int vmcount;
-
- private CountingMethodHandle(MethodHandle target) {
- super(target, target.type(), AdapterMethodHandle.makeConv(OP_RETYPE_ONLY));
- }
-
- /** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
- static MethodHandle wrap(MethodHandle mh) {
- if (MethodHandleNatives.COUNT_GWT) {
- return new CountingMethodHandle(mh);
- }
- return mh;
- }
-}
--- a/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java Fri Aug 10 16:03:28 2012 -0700
@@ -25,29 +25,635 @@
package java.lang.invoke;
+import sun.misc.Unsafe;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import sun.invoke.util.VerifyAccess;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.MethodTypeForm.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+import sun.invoke.util.Wrapper;
/**
- * The flavor of method handle which emulates invokespecial or invokestatic.
+ * The flavor of method handle which implements a constant reference
+ * to a class member.
* @author jrose
*/
class DirectMethodHandle extends MethodHandle {
- //inherited oop vmtarget; // methodOop or virtual class/interface oop
- private final int vmindex; // method index within class or interface
- { vmindex = VM_INDEX_UNINITIALIZED; } // JVM may change this
+ final MemberName member;
+
+ // Constructors and factory methods in this class *must* be package scoped or private.
+ private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
+ super(mtype, form);
+ if (!member.isResolved()) throw new InternalError();
+ this.member = member;
+ }
+
+ // Factory methods:
+
+ static DirectMethodHandle make(Class<?> receiver, MemberName member) {
+ MethodType mtype = member.getMethodOrFieldType();
+ if (!member.isStatic()) {
+ if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
+ throw new InternalError(member.toString());
+ mtype = mtype.insertParameterTypes(0, receiver);
+ }
+ if (!member.isField()) {
+ LambdaForm lform = preparedLambdaForm(member);
+ return new DirectMethodHandle(mtype, lform, member);
+ } else {
+ LambdaForm lform = preparedFieldLambdaForm(member);
+ if (member.isStatic()) {
+ long offset = MethodHandleNatives.staticFieldOffset(member);
+ Object base = MethodHandleNatives.staticFieldBase(member);
+ return new StaticAccessor(mtype, lform, member, base, offset);
+ } else {
+ long offset = MethodHandleNatives.objectFieldOffset(member);
+ assert(offset == (int)offset);
+ return new Accessor(mtype, lform, member, (int)offset);
+ }
+ }
+ }
+ static DirectMethodHandle make(MemberName member) {
+ if (member.isConstructor())
+ return makeAllocator(member);
+ return make(member.getDeclaringClass(), member);
+ }
+ static DirectMethodHandle make(Method method) {
+ return make(method.getDeclaringClass(), new MemberName(method));
+ }
+ static DirectMethodHandle make(Field field) {
+ return make(field.getDeclaringClass(), new MemberName(field));
+ }
+ private static DirectMethodHandle makeAllocator(MemberName ctor) {
+ assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
+ Class<?> instanceClass = ctor.getDeclaringClass();
+ ctor = ctor.asConstructor();
+ assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
+ MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
+ LambdaForm lform = preparedLambdaForm(ctor);
+ MemberName init = ctor.asSpecial();
+ assert(init.getMethodType().returnType() == void.class);
+ return new Constructor(mtype, lform, ctor, init, instanceClass);
+ }
+
+ @Override
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ return new DirectMethodHandle(mt, lf, member);
+ }
+
+ @Override
+ String debugString() {
+ return "DMH["+member.toString()+"]="+super.debugString();
+ }
+
+ //// Implementation methods.
+ @Override
+ @ForceInline
+ MemberName internalMemberName() {
+ return member;
+ }
+
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ // If the member needs dispatching, do so.
+ if (pos == 0 && basicType == 'L') {
+ DirectMethodHandle concrete = maybeRebind(value);
+ if (concrete != null)
+ return concrete.bindReceiver(value);
+ }
+ return super.bindArgument(pos, basicType, value);
+ }
+
+ @Override
+ MethodHandle bindReceiver(Object receiver) {
+ // If the member needs dispatching, do so.
+ DirectMethodHandle concrete = maybeRebind(receiver);
+ if (concrete != null)
+ return concrete.bindReceiver(receiver);
+ return super.bindReceiver(receiver);
+ }
+
+ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+
+ private DirectMethodHandle maybeRebind(Object receiver) {
+ if (receiver != null) {
+ switch (member.getReferenceKind()) {
+ case REF_invokeInterface:
+ case REF_invokeVirtual:
+ // Pre-dispatch the member.
+ Class<?> concreteClass = receiver.getClass();
+ MemberName concrete = new MemberName(concreteClass, member.getName(), member.getMethodType(), REF_invokeSpecial);
+ concrete = IMPL_NAMES.resolveOrNull(REF_invokeSpecial, concrete, concreteClass);
+ if (concrete != null)
+ return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
+ break;
+ }
+ }
+ return null;
+ }
- // Constructors in this class *must* be package scoped or private.
- DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class<?> lookupClass) {
- super(mtype);
+ /**
+ * Create a LF which can invoke the given method.
+ * Cache and share this structure among all methods with
+ * the same basicType and refKind.
+ */
+ private static LambdaForm preparedLambdaForm(MemberName m) {
+ assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
+ MethodType mtype = m.getInvocationType().basicType();
+ assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
+ int which;
+ switch (m.getReferenceKind()) {
+ case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
+ case REF_invokeStatic: which = LF_INVSTATIC; break;
+ case REF_invokeSpecial: which = LF_INVSPECIAL; break;
+ case REF_invokeInterface: which = LF_INVINTERFACE; break;
+ case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break;
+ default: throw new InternalError(m.toString());
+ }
+ if (which == LF_INVSTATIC && shouldBeInitialized(m)) {
+ // precompute the barrier-free version:
+ preparedLambdaForm(mtype, which);
+ which = LF_INVSTATIC_INIT;
+ }
+ LambdaForm lform = preparedLambdaForm(mtype, which);
+ maybeCompile(lform, m);
+ assert(lform.methodType().dropParameterTypes(0, 1)
+ .equals(m.getInvocationType().basicType()))
+ : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
+ return lform;
+ }
+
+ private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
+ LambdaForm lform = mtype.form().cachedLambdaForm(which);
+ if (lform != null) return lform;
+ lform = makePreparedLambdaForm(mtype, which);
+ return mtype.form().setCachedLambdaForm(which, lform);
+ }
- assert(m.isMethod() || !doDispatch && m.isConstructor());
- if (!m.isResolved())
- throw new InternalError();
+ private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
+ boolean needsInit = (which == LF_INVSTATIC_INIT);
+ boolean doesAlloc = (which == LF_NEWINVSPECIAL);
+ String linkerName, lambdaName;
+ switch (which) {
+ case LF_INVVIRTUAL: linkerName = "linkToVirtual"; lambdaName = "DMH.invokeVirtual"; break;
+ case LF_INVSTATIC: linkerName = "linkToStatic"; lambdaName = "DMH.invokeStatic"; break;
+ case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; lambdaName = "DMH.invokeStaticInit"; break;
+ case LF_INVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.invokeSpecial"; break;
+ case LF_INVINTERFACE: linkerName = "linkToInterface"; lambdaName = "DMH.invokeInterface"; break;
+ case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.newInvokeSpecial"; break;
+ default: throw new InternalError("which="+which);
+ }
+ MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
+ if (doesAlloc)
+ mtypeWithArg = mtypeWithArg
+ .insertParameterTypes(0, Object.class) // insert newly allocated obj
+ .changeReturnType(void.class); // <init> returns void
+ MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
+ try {
+ linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ final int DMH_THIS = 0;
+ final int ARG_BASE = 1;
+ final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ int nameCursor = ARG_LIMIT;
+ final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
+ final int GET_MEMBER = nameCursor++;
+ final int LINKER_CALL = nameCursor++;
+ Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+ assert(names.length == nameCursor);
+ if (doesAlloc) {
+ // names = { argx,y,z,... new C, init method }
+ names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
+ names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
+ } else if (needsInit) {
+ names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
+ } else {
+ names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
+ }
+ Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
+ assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args!
+ int result = LambdaForm.LAST_RESULT;
+ if (doesAlloc) {
+ assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one
+ System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
+ outArgs[0] = names[NEW_OBJ];
+ result = NEW_OBJ;
+ }
+ names[LINKER_CALL] = new Name(linker, outArgs);
+ lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
+ LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
+ // This is a tricky bit of code. Don't send it through the LF interpreter.
+ lform.compileToBytecode();
+ return lform;
+ }
- MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
+ private static void maybeCompile(LambdaForm lform, MemberName m) {
+ if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
+ // Help along bootstrapping...
+ lform.compileToBytecode();
+ }
+
+ /** Static wrapper for DirectMethodHandle.internalMemberName. */
+ @ForceInline
+ /*non-public*/ static Object internalMemberName(Object mh) {
+ return ((DirectMethodHandle)mh).member;
+ }
+
+ /** Static wrapper for DirectMethodHandle.internalMemberName.
+ * This one also forces initialization.
+ */
+ /*non-public*/ static Object internalMemberNameEnsureInit(Object mh) {
+ DirectMethodHandle dmh = (DirectMethodHandle)mh;
+ dmh.ensureInitialized();
+ return dmh.member;
+ }
+
+ /*non-public*/ static
+ boolean shouldBeInitialized(MemberName member) {
+ switch (member.getReferenceKind()) {
+ case REF_invokeStatic:
+ case REF_getStatic:
+ case REF_putStatic:
+ case REF_newInvokeSpecial:
+ break;
+ default:
+ // No need to initialize the class on this kind of member.
+ return false;
+ }
+ Class<?> cls = member.getDeclaringClass();
+ if (cls == ValueConversions.class ||
+ cls == MethodHandleImpl.class ||
+ cls == Invokers.class) {
+ // These guys have lots of <clinit> DMH creation but we know
+ // the MHs will not be used until the system is booted.
+ return false;
+ }
+ if (VerifyAccess.isSamePackage(MethodHandle.class, cls) ||
+ VerifyAccess.isSamePackage(ValueConversions.class, cls)) {
+ // It is a system class. It is probably in the process of
+ // being initialized, but we will help it along just to be safe.
+ if (UNSAFE.shouldBeInitialized(cls)) {
+ UNSAFE.ensureClassInitialized(cls);
+ }
+ return false;
+ }
+ return UNSAFE.shouldBeInitialized(cls);
+ }
+
+ private static class EnsureInitialized extends ClassValue<WeakReference<Thread>> {
+ @Override
+ protected WeakReference<Thread> computeValue(Class<?> type) {
+ UNSAFE.ensureClassInitialized(type);
+ if (UNSAFE.shouldBeInitialized(type))
+ // If the previous call didn't block, this can happen.
+ // We are executing inside <clinit>.
+ return new WeakReference<>(Thread.currentThread());
+ return null;
+ }
+ static final EnsureInitialized INSTANCE = new EnsureInitialized();
}
- boolean isValid() {
- return (vmindex != VM_INDEX_UNINITIALIZED);
+ private void ensureInitialized() {
+ if (checkInitialized(member)) {
+ // The coast is clear. Delete the <clinit> barrier.
+ if (member.isField())
+ updateForm(preparedFieldLambdaForm(member));
+ else
+ updateForm(preparedLambdaForm(member));
+ }
+ }
+ private static boolean checkInitialized(MemberName member) {
+ Class<?> defc = member.getDeclaringClass();
+ WeakReference<Thread> ref = EnsureInitialized.INSTANCE.get(defc);
+ if (ref == null) {
+ return true; // the final state
+ }
+ Thread clinitThread = ref.get();
+ // Somebody may still be running defc.<clinit>.
+ if (clinitThread == Thread.currentThread()) {
+ // If anybody is running defc.<clinit>, it is this thread.
+ if (UNSAFE.shouldBeInitialized(defc))
+ // Yes, we are running it; keep the barrier for now.
+ return false;
+ } else {
+ // We are in a random thread. Block.
+ UNSAFE.ensureClassInitialized(defc);
+ }
+ assert(!UNSAFE.shouldBeInitialized(defc));
+ // put it into the final state
+ EnsureInitialized.INSTANCE.remove(defc);
+ return true;
+ }
+
+ /*non-public*/ static void ensureInitialized(Object mh) {
+ ((DirectMethodHandle)mh).ensureInitialized();
+ }
+
+ /** This subclass handles constructor references. */
+ static class Constructor extends DirectMethodHandle {
+ final MemberName initMethod;
+ final Class<?> instanceClass;
+
+ private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
+ MemberName initMethod, Class<?> instanceClass) {
+ super(mtype, form, constructor);
+ this.initMethod = initMethod;
+ this.instanceClass = instanceClass;
+ assert(initMethod.isResolved());
+ }
+ }
+
+ /*non-public*/ static Object constructorMethod(Object mh) {
+ Constructor dmh = (Constructor)mh;
+ return dmh.initMethod;
+ }
+
+ /*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException {
+ Constructor dmh = (Constructor)mh;
+ return UNSAFE.allocateInstance(dmh.instanceClass);
+ }
+
+ /** This subclass handles non-static field references. */
+ static class Accessor extends DirectMethodHandle {
+ final Class<?> fieldType;
+ final int fieldOffset;
+ private Accessor(MethodType mtype, LambdaForm form, MemberName member,
+ int fieldOffset) {
+ super(mtype, form, member);
+ this.fieldType = member.getFieldType();
+ this.fieldOffset = fieldOffset;
+ }
+
+ @Override Object checkCast(Object obj) {
+ return fieldType.cast(obj);
+ }
+ }
+
+ @ForceInline
+ /*non-public*/ static long fieldOffset(Object accessorObj) {
+ // Note: We return a long because that is what Unsafe.getObject likes.
+ // We store a plain int because it is more compact.
+ return ((Accessor)accessorObj).fieldOffset;
+ }
+
+ @ForceInline
+ /*non-public*/ static Object checkBase(Object obj) {
+ // Note that the object's class has already been verified,
+ // since the parameter type of the Accessor method handle
+ // is either member.getDeclaringClass or a subclass.
+ // This was verified in DirectMethodHandle.make.
+ // Therefore, the only remaining check is for null.
+ // Since this check is *not* guaranteed by Unsafe.getInt
+ // and its siblings, we need to make an explicit one here.
+ obj.getClass(); // maybe throw NPE
+ return obj;
+ }
+
+ /** This subclass handles static field references. */
+ static class StaticAccessor extends DirectMethodHandle {
+ final private Class<?> fieldType;
+ final private Object staticBase;
+ final private long staticOffset;
+
+ private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
+ Object staticBase, long staticOffset) {
+ super(mtype, form, member);
+ this.fieldType = member.getFieldType();
+ this.staticBase = staticBase;
+ this.staticOffset = staticOffset;
+ }
+
+ @Override Object checkCast(Object obj) {
+ return fieldType.cast(obj);
+ }
+ }
+
+ @ForceInline
+ /*non-public*/ static Object nullCheck(Object obj) {
+ obj.getClass();
+ return obj;
+ }
+
+ @ForceInline
+ /*non-public*/ static Object staticBase(Object accessorObj) {
+ return ((StaticAccessor)accessorObj).staticBase;
+ }
+
+ @ForceInline
+ /*non-public*/ static long staticOffset(Object accessorObj) {
+ return ((StaticAccessor)accessorObj).staticOffset;
+ }
+
+ @ForceInline
+ /*non-public*/ static Object checkCast(Object mh, Object obj) {
+ return ((DirectMethodHandle) mh).checkCast(obj);
+ }
+
+ Object checkCast(Object obj) {
+ return member.getReturnType().cast(obj);
+ }
+
+ // Caching machinery for field accessors:
+ private static byte
+ AF_GETFIELD = 0,
+ AF_PUTFIELD = 1,
+ AF_GETSTATIC = 2,
+ AF_PUTSTATIC = 3,
+ AF_GETSTATIC_INIT = 4,
+ AF_PUTSTATIC_INIT = 5,
+ AF_LIMIT = 6;
+ // Enumerate the different field kinds using Wrapper,
+ // with an extra case added for checked references.
+ private static int
+ FT_LAST_WRAPPER = Wrapper.values().length-1,
+ FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(),
+ FT_CHECKED_REF = FT_LAST_WRAPPER+1,
+ FT_LIMIT = FT_LAST_WRAPPER+2;
+ private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
+ return ((formOp * FT_LIMIT * 2)
+ + (isVolatile ? FT_LIMIT : 0)
+ + ftypeKind);
+ }
+ private static final LambdaForm[] ACCESSOR_FORMS
+ = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
+ private static int ftypeKind(Class<?> ftype) {
+ if (ftype.isPrimitive())
+ return Wrapper.forPrimitiveType(ftype).ordinal();
+ else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
+ return FT_UNCHECKED_REF;
+ else
+ return FT_CHECKED_REF;
+ }
+
+ /**
+ * Create a LF which can access the given field.
+ * Cache and share this structure among all fields with
+ * the same basicType and refKind.
+ */
+ private static LambdaForm preparedFieldLambdaForm(MemberName m) {
+ Class<?> ftype = m.getFieldType();
+ boolean isVolatile = m.isVolatile();
+ byte formOp;
+ switch (m.getReferenceKind()) {
+ case REF_getField: formOp = AF_GETFIELD; break;
+ case REF_putField: formOp = AF_PUTFIELD; break;
+ case REF_getStatic: formOp = AF_GETSTATIC; break;
+ case REF_putStatic: formOp = AF_PUTSTATIC; break;
+ default: throw new InternalError(m.toString());
+ }
+ if (shouldBeInitialized(m)) {
+ // precompute the barrier-free version:
+ preparedFieldLambdaForm(formOp, isVolatile, ftype);
+ assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
+ (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
+ formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
+ }
+ LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
+ maybeCompile(lform, m);
+ assert(lform.methodType().dropParameterTypes(0, 1)
+ .equals(m.getInvocationType().basicType()))
+ : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
+ return lform;
+ }
+ private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
+ int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype));
+ LambdaForm lform = ACCESSOR_FORMS[afIndex];
+ if (lform != null) return lform;
+ lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype));
+ ACCESSOR_FORMS[afIndex] = lform; // don't bother with a CAS
+ return lform;
+ }
+
+ private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
+ boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
+ boolean isStatic = (formOp >= AF_GETSTATIC);
+ boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
+ boolean needsCast = (ftypeKind == FT_CHECKED_REF);
+ Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
+ Class<?> ft = fw.primitiveType();
+ assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
+ String tname = fw.primitiveSimpleName();
+ String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
+ if (isVolatile) ctname += "Volatile";
+ String getOrPut = (isGetter ? "get" : "put");
+ String linkerName = (getOrPut + ctname); // getObject, putIntVolatile, etc.
+ MethodType linkerType;
+ if (isGetter)
+ linkerType = MethodType.methodType(ft, Object.class, long.class);
+ else
+ linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
+ MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
+ try {
+ linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+
+ // What is the external type of the lambda form?
+ MethodType mtype;
+ if (isGetter)
+ mtype = MethodType.methodType(ft);
+ else
+ mtype = MethodType.methodType(void.class, ft);
+ mtype = mtype.basicType(); // erase short to int, etc.
+ if (!isStatic)
+ mtype = mtype.insertParameterTypes(0, Object.class);
+ final int DMH_THIS = 0;
+ final int ARG_BASE = 1;
+ final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ // if this is for non-static access, the base pointer is stored at this index:
+ final int OBJ_BASE = isStatic ? -1 : ARG_BASE;
+ // if this is for write access, the value to be written is stored at this index:
+ final int SET_VALUE = isGetter ? -1 : ARG_LIMIT - 1;
+ int nameCursor = ARG_LIMIT;
+ final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any
+ final int F_OFFSET = nameCursor++; // Either static offset or field offset.
+ final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
+ final int INIT_BAR = (needsInit ? nameCursor++ : -1);
+ final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1);
+ final int LINKER_CALL = nameCursor++;
+ final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
+ final int RESULT = nameCursor-1; // either the call or the cast
+ Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+ if (needsInit)
+ names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
+ if (needsCast && !isGetter)
+ names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
+ Object[] outArgs = new Object[1 + linkerType.parameterCount()];
+ assert(outArgs.length == (isGetter ? 3 : 4));
+ outArgs[0] = UNSAFE;
+ if (isStatic) {
+ outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]);
+ outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]);
+ } else {
+ outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
+ outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]);
+ }
+ if (!isGetter) {
+ outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
+ }
+ for (Object a : outArgs) assert(a != null);
+ names[LINKER_CALL] = new Name(linker, outArgs);
+ if (needsCast && isGetter)
+ names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
+ for (Name n : names) assert(n != null);
+ String fieldOrStatic = (isStatic ? "Static" : "Field");
+ String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging
+ if (needsCast) lambdaName += "Cast";
+ if (needsInit) lambdaName += "Init";
+ return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
+ }
+
+ private static final NamedFunction
+ NF_internalMemberName,
+ NF_internalMemberNameEnsureInit,
+ NF_ensureInitialized,
+ NF_fieldOffset,
+ NF_checkBase,
+ NF_staticBase,
+ NF_staticOffset,
+ NF_checkCast,
+ NF_allocateInstance,
+ NF_constructorMethod;
+ static {
+ try {
+ NamedFunction nfs[] = {
+ NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("internalMemberName", Object.class)),
+ NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
+ NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("ensureInitialized", Object.class)),
+ NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("fieldOffset", Object.class)),
+ NF_checkBase = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("checkBase", Object.class)),
+ NF_staticBase = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("staticBase", Object.class)),
+ NF_staticOffset = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("staticOffset", Object.class)),
+ NF_checkCast = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("checkCast", Object.class, Object.class)),
+ NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("allocateInstance", Object.class)),
+ NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
+ .getDeclaredMethod("constructorMethod", Object.class))
+ };
+ for (NamedFunction nf : nfs) {
+ // Each nf must be statically invocable or we get tied up in our bootstraps.
+ assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
+ nf.resolve();
+ }
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/DontInline.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 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.lang.annotation.*;
+
+/**
+ * Internal marker for some methods in the JSR 292 implementation.
+ */
+/*non-public*/
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+@interface DontInline {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/ForceInline.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 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.lang.annotation.*;
+
+/**
+ * Internal marker for some methods in the JSR 292 implementation.
+ */
+/*non-public*/
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+@interface ForceInline {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (c) 2012, 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 sun.invoke.util.VerifyAccess;
+import java.lang.invoke.LambdaForm.Name;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import sun.invoke.util.Wrapper;
+
+import java.io.*;
+import java.util.*;
+
+import com.sun.xml.internal.ws.org.objectweb.asm.*;
+
+import java.lang.reflect.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+
+/**
+ * Code generation backend for LambdaForm.
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+class InvokerBytecodeGenerator {
+ /** Define class names for convenience. */
+ private static final String MH = "java/lang/invoke/MethodHandle";
+ private static final String BMH = "java/lang/invoke/BoundMethodHandle";
+ private static final String LF = "java/lang/invoke/LambdaForm";
+ private static final String LFN = "java/lang/invoke/LambdaForm$Name";
+ private static final String CLS = "java/lang/Class";
+ private static final String OBJ = "java/lang/Object";
+ private static final String OBJARY = "[Ljava/lang/Object;";
+
+ private static final String LF_SIG = "L" + LF + ";";
+ private static final String LFN_SIG = "L" + LFN + ";";
+ private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
+
+ /** Name of its super class*/
+ private static final String superName = LF;
+
+ /** Name of new class */
+ private final String className;
+
+ /** Name of the source file (for stack trace printing). */
+ private final String sourceFile;
+
+ private final LambdaForm lambdaForm;
+ private final String invokerName;
+ private final MethodType invokerType;
+ private final int[] localsMap;
+
+ /** ASM bytecode generation. */
+ private ClassWriter cw;
+ private MethodVisitor mv;
+
+ private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
+ private static final Class<?> HOST_CLASS = LambdaForm.class;
+
+ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
+ String className, String invokerName, MethodType invokerType) {
+ if (invokerName.contains(".")) {
+ int p = invokerName.indexOf(".");
+ className = invokerName.substring(0, p);
+ invokerName = invokerName.substring(p+1);
+ }
+ if (DUMP_CLASS_FILES) {
+ className = makeDumpableClassName(className);
+ }
+ this.className = superName + "$" + className;
+ this.sourceFile = "LambdaForm$" + className;
+ this.lambdaForm = lambdaForm;
+ this.invokerName = invokerName;
+ this.invokerType = invokerType;
+ this.localsMap = new int[localsMapSize];
+ }
+
+ private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
+ this(null, invokerType.parameterCount(),
+ className, invokerName, invokerType);
+ // Create an array to map name indexes to locals indexes.
+ for (int i = 0; i < localsMap.length; i++) {
+ localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
+ }
+ }
+
+ private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
+ this(form, form.names.length,
+ className, form.debugName, invokerType);
+ // Create an array to map name indexes to locals indexes.
+ Name[] names = form.names;
+ for (int i = 0, index = 0; i < localsMap.length; i++) {
+ localsMap[i] = index;
+ index += Wrapper.forBasicType(names[i].type).stackSlots();
+ }
+ }
+
+
+ /** instance counters for dumped classes */
+ private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
+ /** debugging flag for saving generated class files */
+ private final static File DUMP_CLASS_FILES_DIR;
+
+ static {
+ if (DUMP_CLASS_FILES) {
+ DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
+ try {
+ File dumpDir = new File("DUMP_CLASS_FILES");
+ if (!dumpDir.exists()) {
+ dumpDir.mkdirs();
+ }
+ DUMP_CLASS_FILES_DIR = dumpDir;
+ System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
+ } catch (Exception e) {
+ throw new InternalError(e);
+ }
+ } else {
+ DUMP_CLASS_FILES_COUNTERS = null;
+ DUMP_CLASS_FILES_DIR = null;
+ }
+ }
+
+ static void maybeDump(final String className, final byte[] classFile) {
+ if (DUMP_CLASS_FILES) {
+ System.out.println("dump: " + className);
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ try {
+ String dumpName = className;
+ //dumpName = dumpName.replace('/', '-');
+ File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
+ dumpFile.getParentFile().mkdirs();
+ FileOutputStream file = new FileOutputStream(dumpFile);
+ file.write(classFile);
+ file.close();
+ return null;
+ } catch (IOException ex) {
+ throw new InternalError(ex);
+ }
+ }
+ });
+ }
+
+ }
+
+ private static String makeDumpableClassName(String className) {
+ Integer ctr;
+ synchronized (DUMP_CLASS_FILES_COUNTERS) {
+ ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
+ if (ctr == null) ctr = 0;
+ DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
+ }
+ String sfx = ctr.toString();
+ while (sfx.length() < 3)
+ sfx = "0"+sfx;
+ className += sfx;
+ return className;
+ }
+
+ class CpPatch {
+ int index;
+ Object value;
+ CpPatch(int index, Object value) {
+ this.index = index;
+ this.value = value;
+ }
+ }
+
+ Map<Object, CpPatch> cpPatches = new HashMap<>();
+
+ int cph = 0; // for counting constant placeholders
+
+ String constantPlaceholder(Object arg) {
+ String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
+ if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid
+ if (cpPatches.containsKey(cpPlaceholder)) {
+ throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
+ }
+ // insert placeholder in CP and remember the patch
+ int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool
+ cpPatches.put(cpPlaceholder, new CpPatch(index, arg));
+ return cpPlaceholder;
+ }
+
+ Object[] cpPatches(byte[] classFile) {
+ int size = getConstantPoolSize(classFile);
+ Object[] res = new Object[size];
+ for (CpPatch p : cpPatches.values()) {
+ res[p.index] = p.value;
+ }
+ return res;
+ }
+
+ /**
+ * Extract the number of constant pool entries from a given class file.
+ *
+ * @param classFile the bytes of the class file in question.
+ * @return the number of entries in the constant pool.
+ */
+ private static int getConstantPoolSize(byte[] classFile) {
+ // The first few bytes:
+ // u4 magic;
+ // u2 minor_version;
+ // u2 major_version;
+ // u2 constant_pool_count;
+ return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
+ }
+
+ /**
+ * Extract the MemberName of a newly-defined method.
+ *
+ * @param classFile
+ * @return
+ */
+ private MemberName loadMethod(byte[] classFile) {
+ Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
+ return resolveInvokerMember(invokerClass, invokerName, invokerType);
+ }
+
+ /**
+ * Define a given class as anonymous class in the runtime system.
+ *
+ * @param classBytes
+ * @param patches
+ * @return
+ */
+ private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
+ Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
+ UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
+ return invokerClass;
+ }
+
+ /**
+ * TODO
+ *
+ * @param invokerClass
+ * @param name
+ * @param type
+ * @return
+ */
+ private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
+ MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
+ //System.out.println("resolveInvokerMember => "+member);
+ //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m);
+ try {
+ member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ //System.out.println("resolveInvokerMember => "+member);
+ return member;
+ }
+
+ /**
+ * Set up class file generation.
+ */
+ private void classFilePrologue() {
+ cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+ cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
+ cw.visitSource(sourceFile, null);
+
+ String invokerDesc = invokerType.toMethodDescriptorString();
+ mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
+
+ // Force inlining of this invoker method.
+ mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+ }
+
+ /**
+ * Tear down class file generation.
+ */
+ private void classFileEpilogue() {
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /*
+ * Low-level emit helpers.
+ */
+ private void emitConst(Object con) {
+ if (con == null) {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ return;
+ }
+ if (con instanceof Integer) {
+ emitIconstInsn((int) con);
+ return;
+ }
+ if (con instanceof Long) {
+ long x = (long) con;
+ if (x == (short) x) {
+ emitIconstInsn((int) x);
+ mv.visitInsn(Opcodes.I2L);
+ return;
+ }
+ }
+ if (con instanceof Float) {
+ float x = (float) con;
+ if (x == (short) x) {
+ emitIconstInsn((int) x);
+ mv.visitInsn(Opcodes.I2F);
+ return;
+ }
+ }
+ if (con instanceof Double) {
+ double x = (double) con;
+ if (x == (short) x) {
+ emitIconstInsn((int) x);
+ mv.visitInsn(Opcodes.I2D);
+ return;
+ }
+ }
+ if (con instanceof Boolean) {
+ emitIconstInsn((boolean) con ? 1 : 0);
+ return;
+ }
+ // fall through:
+ mv.visitLdcInsn(con);
+ }
+
+ private void emitIconstInsn(int i) {
+ int opcode;
+ switch (i) {
+ case 0: opcode = Opcodes.ICONST_0; break;
+ case 1: opcode = Opcodes.ICONST_1; break;
+ case 2: opcode = Opcodes.ICONST_2; break;
+ case 3: opcode = Opcodes.ICONST_3; break;
+ case 4: opcode = Opcodes.ICONST_4; break;
+ case 5: opcode = Opcodes.ICONST_5; break;
+ default:
+ if (i == (byte) i) {
+ mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF);
+ } else if (i == (short) i) {
+ mv.visitIntInsn(Opcodes.SIPUSH, (char) i);
+ } else {
+ mv.visitLdcInsn(i);
+ }
+ return;
+ }
+ mv.visitInsn(opcode);
+ }
+
+ /*
+ * NOTE: These load/store methods use the localsMap to find the correct index!
+ */
+ private void emitLoadInsn(char type, int index) {
+ int opcode;
+ switch (type) {
+ case 'I': opcode = Opcodes.ILOAD; break;
+ case 'J': opcode = Opcodes.LLOAD; break;
+ case 'F': opcode = Opcodes.FLOAD; break;
+ case 'D': opcode = Opcodes.DLOAD; break;
+ case 'L': opcode = Opcodes.ALOAD; break;
+ default:
+ throw new InternalError("unknown type: " + type);
+ }
+ mv.visitVarInsn(opcode, localsMap[index]);
+ }
+ private void emitAloadInsn(int index) {
+ emitLoadInsn('L', index);
+ }
+
+ private void emitStoreInsn(char type, int index) {
+ int opcode;
+ switch (type) {
+ case 'I': opcode = Opcodes.ISTORE; break;
+ case 'J': opcode = Opcodes.LSTORE; break;
+ case 'F': opcode = Opcodes.FSTORE; break;
+ case 'D': opcode = Opcodes.DSTORE; break;
+ case 'L': opcode = Opcodes.ASTORE; break;
+ default:
+ throw new InternalError("unknown type: " + type);
+ }
+ mv.visitVarInsn(opcode, localsMap[index]);
+ }
+ private void emitAstoreInsn(int index) {
+ emitStoreInsn('L', index);
+ }
+
+ /**
+ * Emit a boxing call.
+ *
+ * @param type primitive type class to box.
+ */
+ private void emitBoxing(Class<?> type) {
+ Wrapper wrapper = Wrapper.forPrimitiveType(type);
+ String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+ String name = "valueOf";
+ String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
+ }
+
+ /**
+ * Emit an unboxing call (plus preceding checkcast).
+ *
+ * @param type wrapper type class to unbox.
+ */
+ private void emitUnboxing(Class<?> type) {
+ Wrapper wrapper = Wrapper.forWrapperType(type);
+ String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+ String name = wrapper.primitiveSimpleName() + "Value";
+ String desc = "()" + wrapper.basicTypeChar();
+ mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
+ }
+
+ /**
+ * Emit an implicit conversion.
+ *
+ * @param ptype type of value present on stack
+ * @param pclass type of value required on stack
+ */
+ private void emitImplicitConversion(char ptype, Class<?> pclass) {
+ switch (ptype) {
+ case 'L':
+ if (VerifyType.isNullConversion(Object.class, pclass))
+ return;
+ if (isStaticallyNameable(pclass)) {
+ mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
+ } else {
+ mv.visitLdcInsn(constantPlaceholder(pclass));
+ mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
+ mv.visitInsn(Opcodes.SWAP);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
+ if (pclass.isArray())
+ mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
+ }
+ return;
+ case 'I':
+ if (!VerifyType.isNullConversion(int.class, pclass))
+ emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
+ return;
+ case 'J':
+ assert(pclass == long.class);
+ return;
+ case 'F':
+ assert(pclass == float.class);
+ return;
+ case 'D':
+ assert(pclass == double.class);
+ return;
+ }
+ throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
+ }
+
+ /**
+ * Emits an actual return instruction conforming to the given return type.
+ */
+ private void emitReturnInsn(Class<?> type) {
+ int opcode;
+ switch (Wrapper.basicTypeChar(type)) {
+ case 'I': opcode = Opcodes.IRETURN; break;
+ case 'J': opcode = Opcodes.LRETURN; break;
+ case 'F': opcode = Opcodes.FRETURN; break;
+ case 'D': opcode = Opcodes.DRETURN; break;
+ case 'L': opcode = Opcodes.ARETURN; break;
+ case 'V': opcode = Opcodes.RETURN; break;
+ default:
+ throw new InternalError("unknown return type: " + type);
+ }
+ mv.visitInsn(opcode);
+ }
+
+ private static String getInternalName(Class<?> c) {
+ assert(VerifyAccess.isTypeVisible(c, Object.class));
+ return c.getName().replace('.', '/');
+ }
+
+ /**
+ * Generate customized bytecode for a given LambdaForm.
+ *
+ * @param form
+ * @param invokerType
+ * @return
+ */
+ static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
+ InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
+ return g.loadMethod(g.generateCustomizedCodeBytes());
+ }
+
+ /**
+ * Generate an invoker method for the passed {@link LambdaForm}.
+ */
+ private byte[] generateCustomizedCodeBytes() {
+ classFilePrologue();
+
+ // Suppress this method in backtraces displayed to the user.
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+ // Mark this method as a compiled LambdaForm
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
+
+ // iterate over the form's names, generating bytecode instructions for each
+ // start iterating at the first name following the arguments
+ for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
+ Name name = lambdaForm.names[i];
+ MemberName member = name.function.member();
+
+ if (isSelectAlternative(member)) {
+ // selectAlternative idiom
+ // FIXME: make sure this idiom is really present!
+ emitSelectAlternative(name, lambdaForm.names[i + 1]);
+ i++; // skip MH.invokeBasic of the selectAlternative result
+ } else if (isStaticallyInvocable(member)) {
+ emitStaticInvoke(member, name);
+ } else {
+ emitInvoke(name);
+ }
+
+ // store the result from evaluating to the target name in a local if required
+ // (if this is the last value, i.e., the one that is going to be returned,
+ // avoid store/load/return and just return)
+ if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
+ // return value - do nothing
+ } else if (name.type != 'V') {
+ // non-void: actually assign
+ emitStoreInsn(name.type, name.index());
+ }
+ }
+
+ // return statement
+ emitReturn();
+
+ classFileEpilogue();
+ bogusMethod(lambdaForm);
+
+ final byte[] classFile = cw.toByteArray();
+ maybeDump(className, classFile);
+ return classFile;
+ }
+
+ /**
+ * Emit an invoke for the given name.
+ *
+ * @param name
+ */
+ void emitInvoke(Name name) {
+ if (true) {
+ // push receiver
+ MethodHandle target = name.function.resolvedHandle;
+ assert(target != null) : name.exprString();
+ mv.visitLdcInsn(constantPlaceholder(target));
+ mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+ } else {
+ // load receiver
+ emitAloadInsn(0);
+ mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+ mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
+ mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
+ // TODO more to come
+ }
+
+ // push arguments
+ for (int i = 0; i < name.arguments.length; i++) {
+ emitPushArgument(name, i);
+ }
+
+ // invocation
+ MethodType type = name.function.methodType();
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
+ }
+
+ static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
+ // Sample classes from each package we are willing to bind to statically:
+ java.lang.Object.class,
+ java.util.Arrays.class,
+ sun.misc.Unsafe.class
+ //MethodHandle.class already covered
+ };
+
+ static boolean isStaticallyInvocable(MemberName member) {
+ if (member == null) return false;
+ if (member.isConstructor()) return false;
+ Class<?> cls = member.getDeclaringClass();
+ if (cls.isArray() || cls.isPrimitive())
+ return false; // FIXME
+ if (cls.isAnonymousClass() || cls.isLocalClass())
+ return false; // inner class of some sort
+ if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
+ return false; // not on BCP
+ if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
+ return true; // in java.lang.invoke package
+ if (member.isPublic() && isStaticallyNameable(cls))
+ return true;
+ return false;
+ }
+
+ static boolean isStaticallyNameable(Class<?> cls) {
+ while (cls.isArray())
+ cls = cls.getComponentType();
+ if (cls.isPrimitive())
+ return true; // int[].class, for example
+ if (cls.getClassLoader() != Object.class.getClassLoader())
+ return false;
+ if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
+ return true;
+ if (!Modifier.isPublic(cls.getModifiers()))
+ return false;
+ for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
+ if (VerifyAccess.isSamePackage(pkgcls, cls))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Emit an invoke for the given name, using the MemberName directly.
+ *
+ * @param name
+ */
+ void emitStaticInvoke(MemberName member, Name name) {
+ assert(member.equals(name.function.member()));
+ String cname = getInternalName(member.getDeclaringClass());
+ String mname = member.getName();
+ String mtype;
+ byte refKind = member.getReferenceKind();
+ if (refKind == REF_invokeSpecial) {
+ // in order to pass the verifier, we need to convert this to invokevirtual in all cases
+ assert(member.canBeStaticallyBound()) : member;
+ refKind = REF_invokeVirtual;
+ }
+
+ // push arguments
+ for (int i = 0; i < name.arguments.length; i++) {
+ emitPushArgument(name, i);
+ }
+
+ // invocation
+ if (member.isMethod()) {
+ mtype = member.getMethodType().toMethodDescriptorString();
+ mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype);
+ } else {
+ mtype = MethodType.toFieldDescriptorString(member.getFieldType());
+ mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
+ }
+ }
+ int refKindOpcode(byte refKind) {
+ switch (refKind) {
+ case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
+ case REF_invokeStatic: return Opcodes.INVOKESTATIC;
+ case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
+ case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
+ case REF_getField: return Opcodes.GETFIELD;
+ case REF_putField: return Opcodes.PUTFIELD;
+ case REF_getStatic: return Opcodes.GETSTATIC;
+ case REF_putStatic: return Opcodes.PUTSTATIC;
+ }
+ throw new InternalError("refKind="+refKind);
+ }
+
+ /**
+ * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
+ *
+ * @param member
+ * @return true if member is a call to MethodHandleImpl.selectAlternative
+ */
+ private boolean isSelectAlternative(MemberName member) {
+ return member != null &&
+ member.getDeclaringClass() == MethodHandleImpl.class &&
+ member.getName().equals("selectAlternative");
+ }
+
+ /**
+ * Emit bytecode for the selectAlternative idiom.
+ *
+ * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
+ *
+ * Lambda(a0:L,a1:I)=>{
+ * t2:I=foo.test(a1:I);
+ * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
+ * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
+ *
+ * @param selectAlternativeName
+ * @param invokeBasicName
+ */
+ private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+ MethodType type = selectAlternativeName.function.methodType();
+
+ Name receiver = (Name) invokeBasicName.arguments[0];
+
+ Label L_fallback = new Label();
+ Label L_done = new Label();
+
+ // load test result
+ emitPushArgument(selectAlternativeName, 0);
+ mv.visitInsn(Opcodes.ICONST_1);
+
+ // if_icmpne L_fallback
+ mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
+
+ // invoke selectAlternativeName.arguments[1]
+ MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
+ emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
+ emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
+ emitInvoke(invokeBasicName);
+
+ // goto L_done
+ mv.visitJumpInsn(Opcodes.GOTO, L_done);
+
+ // L_fallback:
+ mv.visitLabel(L_fallback);
+
+ // invoke selectAlternativeName.arguments[2]
+ MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
+ emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
+ emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
+ emitInvoke(invokeBasicName);
+
+ // L_done:
+ mv.visitLabel(L_done);
+ }
+
+ /**
+ *
+ * @param name
+ * @param paramIndex
+ */
+ private void emitPushArgument(Name name, int paramIndex) {
+ Object arg = name.arguments[paramIndex];
+ char ptype = name.function.parameterType(paramIndex);
+ MethodType mtype = name.function.methodType();
+ if (arg instanceof Name) {
+ Name n = (Name) arg;
+ emitLoadInsn(n.type, n.index());
+ emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
+ } else if ((arg == null || arg instanceof String) && ptype == 'L') {
+ emitConst(arg);
+ } else {
+ if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
+ emitConst(arg);
+ } else {
+ mv.visitLdcInsn(constantPlaceholder(arg));
+ emitImplicitConversion('L', mtype.parameterType(paramIndex));
+ }
+ }
+ }
+
+ /**
+ * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
+ */
+ private void emitReturn() {
+ // return statement
+ if (lambdaForm.result == -1) {
+ // void
+ mv.visitInsn(Opcodes.RETURN);
+ } else {
+ LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
+ char rtype = Wrapper.basicTypeChar(invokerType.returnType());
+
+ // put return value on the stack if it is not already there
+ if (lambdaForm.result != lambdaForm.names.length - 1) {
+ emitLoadInsn(rn.type, lambdaForm.result);
+ }
+
+ // potentially generate cast
+ // rtype is the return type of the invoker - generated code must conform to this
+ // rn.type is the type of the result Name in the LF
+ if (rtype != rn.type) {
+ // need cast
+ if (rtype == 'L') {
+ // possibly cast the primitive to the correct type for boxing
+ char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
+ if (boxedType != rn.type) {
+ emitPrimCast(rn.type, boxedType);
+ }
+ // cast primitive to reference ("boxing")
+ emitBoxing(invokerType.returnType());
+ } else {
+ // to-primitive cast
+ if (rn.type != 'L') {
+ // prim-to-prim cast
+ emitPrimCast(rn.type, rtype);
+ } else {
+ // ref-to-prim cast ("unboxing")
+ throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
+ }
+ }
+ }
+
+ // generate actual return statement
+ emitReturnInsn(invokerType.returnType());
+ }
+ }
+
+ /**
+ * Emit a type conversion bytecode casting from "from" to "to".
+ */
+ private void emitPrimCast(char from, char to) {
+ // Here's how.
+ // - indicates forbidden
+ // <-> indicates implicit
+ // to ----> boolean byte short char int long float double
+ // from boolean <-> - - - - - - -
+ // byte - <-> i2s i2c <-> i2l i2f i2d
+ // short - i2b <-> i2c <-> i2l i2f i2d
+ // char - i2b i2s <-> <-> i2l i2f i2d
+ // int - i2b i2s i2c <-> i2l i2f i2d
+ // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d
+ // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d
+ // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <->
+ if (from == to) {
+ // no cast required, should be dead code anyway
+ return;
+ }
+ Wrapper wfrom = Wrapper.forBasicType(from);
+ Wrapper wto = Wrapper.forBasicType(to);
+ if (wfrom.isSubwordOrInt()) {
+ // cast from {byte,short,char,int} to anything
+ emitI2X(to);
+ } else {
+ // cast from {long,float,double} to anything
+ if (wto.isSubwordOrInt()) {
+ // cast to {byte,short,char,int}
+ emitX2I(from);
+ if (wto.bitWidth() < 32) {
+ // targets other than int require another conversion
+ emitI2X(to);
+ }
+ } else {
+ // cast to {long,float,double} - this is verbose
+ boolean error = false;
+ switch (from) {
+ case 'J':
+ if (to == 'F') { mv.visitInsn(Opcodes.L2F); }
+ else if (to == 'D') { mv.visitInsn(Opcodes.L2D); }
+ else error = true;
+ break;
+ case 'F':
+ if (to == 'J') { mv.visitInsn(Opcodes.F2L); }
+ else if (to == 'D') { mv.visitInsn(Opcodes.F2D); }
+ else error = true;
+ break;
+ case 'D':
+ if (to == 'J') { mv.visitInsn(Opcodes.D2L); }
+ else if (to == 'F') { mv.visitInsn(Opcodes.D2F); }
+ else error = true;
+ break;
+ default:
+ error = true;
+ break;
+ }
+ if (error) {
+ throw new IllegalStateException("unhandled prim cast: " + from + "2" + to);
+ }
+ }
+ }
+ }
+
+ private void emitI2X(char type) {
+ switch (type) {
+ case 'B': mv.visitInsn(Opcodes.I2B); break;
+ case 'S': mv.visitInsn(Opcodes.I2S); break;
+ case 'C': mv.visitInsn(Opcodes.I2C); break;
+ case 'I': /* naught */ break;
+ case 'J': mv.visitInsn(Opcodes.I2L); break;
+ case 'F': mv.visitInsn(Opcodes.I2F); break;
+ case 'D': mv.visitInsn(Opcodes.I2D); break;
+ case 'Z':
+ // For compatibility with ValueConversions and explicitCastArguments:
+ mv.visitInsn(Opcodes.ICONST_1);
+ mv.visitInsn(Opcodes.IAND);
+ break;
+ default: throw new InternalError("unknown type: " + type);
+ }
+ }
+
+ private void emitX2I(char type) {
+ switch (type) {
+ case 'J': mv.visitInsn(Opcodes.L2I); break;
+ case 'F': mv.visitInsn(Opcodes.F2I); break;
+ case 'D': mv.visitInsn(Opcodes.D2I); break;
+ default: throw new InternalError("unknown type: " + type);
+ }
+ }
+
+ private static String basicTypeCharSignature(String prefix, MethodType type) {
+ StringBuilder buf = new StringBuilder(prefix);
+ for (Class<?> ptype : type.parameterList())
+ buf.append(Wrapper.forBasicType(ptype).basicTypeChar());
+ buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar());
+ return buf.toString();
+ }
+
+ /**
+ * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
+ *
+ * @param sig
+ * @return
+ */
+ static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
+ assert(LambdaForm.isValidSignature(sig));
+ //System.out.println("generateExactInvoker "+sig);
+ // compute method type
+ // first parameter and return type
+ char tret = LambdaForm.signatureReturn(sig);
+ MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class);
+ // other parameter types
+ int arity = LambdaForm.signatureArity(sig);
+ for (int i = 1; i < arity; i++) {
+ type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
+ }
+ InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
+ return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
+ }
+
+ private byte[] generateLambdaFormInterpreterEntryPointBytes() {
+ classFilePrologue();
+
+ // Suppress this method in backtraces displayed to the user.
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+ // create parameter array
+ emitIconstInsn(invokerType.parameterCount());
+ mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
+
+ // fill parameter array
+ for (int i = 0; i < invokerType.parameterCount(); i++) {
+ Class<?> ptype = invokerType.parameterType(i);
+ mv.visitInsn(Opcodes.DUP);
+ emitIconstInsn(i);
+ emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
+ // box if primitive type
+ if (ptype.isPrimitive()) {
+ emitBoxing(ptype);
+ }
+ mv.visitInsn(Opcodes.AASTORE);
+ }
+ // invoke
+ emitAloadInsn(0);
+ mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
+ mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
+
+ // maybe unbox
+ Class<?> rtype = invokerType.returnType();
+ if (rtype.isPrimitive() && rtype != void.class) {
+ emitUnboxing(Wrapper.asWrapperType(rtype));
+ }
+
+ // return statement
+ emitReturnInsn(rtype);
+
+ classFileEpilogue();
+ bogusMethod(invokerType);
+
+ final byte[] classFile = cw.toByteArray();
+ maybeDump(className, classFile);
+ return classFile;
+ }
+
+ /**
+ * Generate bytecode for a NamedFunction invoker.
+ *
+ * @param srcType
+ * @param dstType
+ * @return
+ */
+ static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
+ MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
+ String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType());
+ InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
+ return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
+ }
+
+ static int nfi = 0;
+
+ private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
+ MethodType dstType = typeForm.erasedType();
+ classFilePrologue();
+
+ // Suppress this method in backtraces displayed to the user.
+ mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+
+ // Load receiver
+ emitAloadInsn(0);
+
+ // Load arguments from array
+ for (int i = 0; i < dstType.parameterCount(); i++) {
+ emitAloadInsn(1);
+ emitIconstInsn(i);
+ mv.visitInsn(Opcodes.AALOAD);
+
+ // Maybe unbox
+ Class<?> dptype = dstType.parameterType(i);
+ if (dptype.isPrimitive()) {
+ Class<?> sptype = dstType.basicType().wrap().parameterType(i);
+ Wrapper dstWrapper = Wrapper.forBasicType(dptype);
+ Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int
+ emitUnboxing(srcWrapper.wrapperType());
+ emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+ }
+ }
+
+ // Invoke
+ String targetDesc = dstType.basicType().toMethodDescriptorString();
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc);
+
+ // Box primitive types
+ Class<?> rtype = dstType.returnType();
+ if (rtype != void.class && rtype.isPrimitive()) {
+ Wrapper srcWrapper = Wrapper.forBasicType(rtype);
+ Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
+ // boolean casts not allowed
+ emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+ emitBoxing(dstWrapper.primitiveType());
+ }
+
+ // If the return type is void we return a null reference.
+ if (rtype == void.class) {
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ }
+ emitReturnInsn(Object.class); // NOTE: NamedFunction invokers always return a reference value.
+
+ classFileEpilogue();
+ bogusMethod(dstType);
+
+ final byte[] classFile = cw.toByteArray();
+ maybeDump(className, classFile);
+ return classFile;
+ }
+
+ /**
+ * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
+ * for debugging purposes.
+ */
+ private void bogusMethod(Object... os) {
+ if (DUMP_CLASS_FILES) {
+ mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
+ for (Object o : os) {
+ mv.visitLdcInsn(o.toString());
+ mv.visitInsn(Opcodes.POP);
+ }
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ }
+}
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java Fri Aug 10 16:03:28 2012 -0700
@@ -25,8 +25,11 @@
package java.lang.invoke;
+import java.util.Arrays;
import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.lang.invoke.LambdaForm.*;
/**
* Construction and caching of often-used invokers.
@@ -36,11 +39,15 @@
// exact type (sans leading taget MH) for the outgoing call
private final MethodType targetType;
+ // FIXME: Get rid of the invokers that are not useful.
+
// exact invoker for the outgoing call
private /*lazy*/ MethodHandle exactInvoker;
// erased (partially untyped but with primitives) invoker for the outgoing call
+ // FIXME: get rid of
private /*lazy*/ MethodHandle erasedInvoker;
+ // FIXME: get rid of
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
// general invoker for the outgoing call
@@ -63,14 +70,13 @@
this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
}
- /*non-public*/ static MethodType invokerType(MethodType targetType) {
- return targetType.insertParameterTypes(0, MethodHandle.class);
- }
-
/*non-public*/ MethodHandle exactInvoker() {
MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker;
- invoker = lookupInvoker("invokeExact");
+ MethodType mtype = targetType;
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
+ invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+ assert(checkInvoker(invoker));
exactInvoker = invoker;
return invoker;
}
@@ -78,29 +84,50 @@
/*non-public*/ MethodHandle generalInvoker() {
MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
- invoker = lookupInvoker("invoke");
+ MethodType mtype = targetType;
+ prepareForGenericCall(mtype);
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
+ invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+ assert(checkInvoker(invoker));
generalInvoker = invoker;
return invoker;
}
- private MethodHandle lookupInvoker(String name) {
- MethodHandle invoker;
- try {
- invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, name, targetType);
- } catch (ReflectiveOperationException ex) {
- throw new InternalError("JVM cannot find invoker for "+targetType, ex);
- }
- assert(invokerType(targetType) == invoker.type());
- assert(!invoker.isVarargsCollector());
+ /*non-public*/ MethodHandle makeBasicInvoker() {
+ MethodHandle invoker = DirectMethodHandle.make(invokeBasicMethod(targetType));
+ assert(targetType == targetType.basicType());
+ // Note: This is not cached here. It is cached by the calling MethodTypeForm.
+ assert(checkInvoker(invoker));
return invoker;
}
+ static MemberName invokeBasicMethod(MethodType type) {
+ String name = "invokeBasic";
+ try {
+ //Lookup.findVirtual(MethodHandle.class, name, type);
+ return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, name, type);
+
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError("JVM cannot find invoker for "+type, ex);
+ }
+ }
+
+ private boolean checkInvoker(MethodHandle invoker) {
+ assert(targetType.invokerType().equals(invoker.type()))
+ : java.util.Arrays.asList(targetType, targetType.invokerType(), invoker);
+ assert(invoker.internalMemberName() == null ||
+ invoker.internalMemberName().getMethodType().equals(targetType));
+ assert(!invoker.isVarargsCollector());
+ return true;
+ }
+
+ // FIXME: get rid of
/*non-public*/ MethodHandle erasedInvoker() {
MethodHandle xinvoker = exactInvoker();
MethodHandle invoker = erasedInvoker;
if (invoker != null) return invoker;
MethodType erasedType = targetType.erase();
- invoker = xinvoker.asType(invokerType(erasedType));
+ invoker = xinvoker.asType(erasedType.invokerType());
erasedInvoker = invoker;
return invoker;
}
@@ -118,7 +145,7 @@
/*non-public*/ MethodHandle varargsInvoker() {
MethodHandle vaInvoker = varargsInvoker;
if (vaInvoker != null) return vaInvoker;
- vaInvoker = spreadInvoker(0).asType(invokerType(MethodType.genericMethodType(0, true)));
+ vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType());
varargsInvoker = vaInvoker;
return vaInvoker;
}
@@ -137,16 +164,18 @@
uninitializedCallSite = invoker;
return invoker;
}
- if (THROW_UCS == null) {
+ invoker = THROW_UCS;
+ if (invoker == null) {
try {
- THROW_UCS = IMPL_LOOKUP
+ THROW_UCS = invoker = IMPL_LOOKUP
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
- invoker = AdapterMethodHandle.makeRetypeRaw(targetType, THROW_UCS);
+ invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
+ invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount());
assert(invoker.type().equals(targetType));
uninitializedCallSite = invoker;
return invoker;
@@ -155,4 +184,208 @@
public String toString() {
return "Invokers"+targetType;
}
+
+ private static MethodType fixMethodType(Class<?> callerClass, Object type) {
+ if (type instanceof MethodType)
+ return (MethodType) type;
+ else
+ return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
+ }
+
+ static MemberName exactInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
+ MethodType mtype = fixMethodType(callerClass, type);
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_LINKER);
+ appendixResult[0] = mtype;
+ return lform.vmentry;
+ }
+
+ static MemberName genericInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
+ MethodType mtype = fixMethodType(callerClass, type);
+ LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_LINKER);
+ prepareForGenericCall(mtype);
+ appendixResult[0] = mtype;
+ return lform.vmentry;
+ }
+
+ private static LambdaForm invokeForm(MethodType mtype, int which) {
+ mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
+ boolean isLinker, isGeneric;
+ String debugName;
+ switch (which) {
+ case MethodTypeForm.LF_EX_LINKER: isLinker = true; isGeneric = false; debugName = "invokeExact_MT"; break;
+ case MethodTypeForm.LF_EX_INVOKER: isLinker = false; isGeneric = false; debugName = "exactInvoker"; break;
+ case MethodTypeForm.LF_GEN_LINKER: isLinker = true; isGeneric = true; debugName = "invoke_MT"; break;
+ case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break;
+ default: throw new InternalError();
+ }
+ LambdaForm lform = mtype.form().cachedLambdaForm(which);
+ if (lform != null) return lform;
+ // exactInvokerForm (Object,Object)Object
+ // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
+ final int THIS_MH = 0;
+ final int CALL_MH = THIS_MH + (isLinker ? 0 : 1);
+ final int ARG_BASE = CALL_MH + 1;
+ final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ final int INARG_LIMIT = OUTARG_LIMIT + (isLinker ? 1 : 0);
+ int nameCursor = OUTARG_LIMIT;
+ final int MTYPE_ARG = nameCursor++; // might be last in-argument
+ final int CHECK_TYPE = nameCursor++;
+ final int LINKER_CALL = nameCursor++;
+ MethodType invokerFormType = mtype.invokerType();
+ if (isLinker) {
+ invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
+ } else {
+ invokerFormType = invokerFormType.invokerType();
+ }
+ Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
+ assert(names.length == nameCursor);
+ if (MTYPE_ARG >= INARG_LIMIT) {
+ assert(names[MTYPE_ARG] == null);
+ names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
+ // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
+ }
+
+ // Make the final call. If isGeneric, then prepend the result of type checking.
+ MethodType outCallType;
+ Object[] outArgs;
+ if (!isGeneric) {
+ names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], names[MTYPE_ARG]);
+ // mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
+ outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
+ outCallType = mtype;
+ } else {
+ names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], names[MTYPE_ARG]);
+ // mh.invokeGeneric(a*):R =>
+ // let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
+ // gamh.invokeBasic(mt, mh, a*)
+ final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2;
+ outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
+ // prepend arguments:
+ System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
+ outArgs[PREPEND_GAMH] = names[CHECK_TYPE];
+ outArgs[PREPEND_MT] = names[MTYPE_ARG];
+ outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class);
+ }
+ names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
+ lform = new LambdaForm(debugName, INARG_LIMIT, names);
+ if (isLinker)
+ lform.compileToBytecode(); // JVM needs a real methodOop
+ lform = mtype.form().setCachedLambdaForm(which, lform);
+ return lform;
+ }
+
+ /*non-public*/ static
+ WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) {
+ // FIXME: merge with JVM logic for throwing WMTE
+ return new WrongMethodTypeException("expected "+expected+" but found "+actual);
+ }
+
+ /** Static definition of MethodHandle.invokeExact checking code. */
+ /*non-public*/ static
+ @ForceInline
+ void checkExactType(Object mhObj, Object expectedObj) {
+ MethodHandle mh = (MethodHandle) mhObj;
+ MethodType expected = (MethodType) expectedObj;
+ MethodType actual = mh.type();
+ if (actual != expected)
+ throw newWrongMethodTypeException(expected, actual);
+ }
+
+ /** Static definition of MethodHandle.invokeGeneric checking code. */
+ /*non-public*/ static
+ @ForceInline
+ Object checkGenericType(Object mhObj, Object expectedObj) {
+ MethodHandle mh = (MethodHandle) mhObj;
+ MethodType expected = (MethodType) expectedObj;
+ //MethodType actual = mh.type();
+ MethodHandle gamh = expected.form().genericInvoker;
+ if (gamh != null) return gamh;
+ return prepareForGenericCall(expected);
+ }
+
+ /**
+ * Returns an adapter GA for invoking a MH with type adjustments.
+ * The MethodType of the generic invocation site is prepended to MH
+ * and its arguments as follows:
+ * {@code (R)MH.invoke(A*) => GA.invokeBasic(TYPEOF<A*,R>, MH, A*)}
+ */
+ /*non-public*/ static MethodHandle prepareForGenericCall(MethodType mtype) {
+ // force any needed adapters to be preconstructed
+ MethodTypeForm form = mtype.form();
+ MethodHandle gamh = form.genericInvoker;
+ if (gamh != null) return gamh;
+ try {
+ // Trigger adapter creation.
+ gamh = InvokeGeneric.generalInvokerOf(form.erasedType);
+ form.genericInvoker = gamh;
+ return gamh;
+ } catch (Exception ex) {
+ throw new InternalError("Exception while resolving inexact invoke", ex);
+ }
+ }
+
+ static MemberName linkToCallSiteMethod(MethodType mtype) {
+ LambdaForm lform = callSiteForm(mtype);
+ return lform.vmentry;
+ }
+
+ private static LambdaForm callSiteForm(MethodType mtype) {
+ mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
+ LambdaForm lform = mtype.form().cachedLambdaForm(MethodTypeForm.LF_CS_LINKER);
+ if (lform != null) return lform;
+ // exactInvokerForm (Object,Object)Object
+ // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
+ final int ARG_BASE = 0;
+ final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ final int INARG_LIMIT = OUTARG_LIMIT + 1;
+ int nameCursor = OUTARG_LIMIT;
+ final int CSITE_ARG = nameCursor++; // the last in-argument
+ final int CALL_MH = nameCursor++; // result of getTarget
+ final int LINKER_CALL = nameCursor++;
+ MethodType invokerFormType = mtype.appendParameterTypes(CallSite.class);
+ Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
+ assert(names.length == nameCursor);
+ assert(names[CSITE_ARG] != null);
+ names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]);
+ // (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*)
+ final int PREPEND_MH = 0, PREPEND_COUNT = 1;
+ Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
+ // prepend MH argument:
+ System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
+ outArgs[PREPEND_MH] = names[CALL_MH];
+ names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs);
+ lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names);
+ lform.compileToBytecode(); // JVM needs a real methodOop
+ lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform);
+ return lform;
+ }
+
+ /** Static definition of MethodHandle.invokeGeneric checking code. */
+ /*non-public*/ static
+ @ForceInline
+ Object getCallSiteTarget(Object site) {
+ return ((CallSite)site).getTarget();
+ }
+
+ // Local constant functions:
+ private static final NamedFunction NF_checkExactType;
+ private static final NamedFunction NF_checkGenericType;
+ private static final NamedFunction NF_getCallSiteTarget;
+ static {
+ try {
+ NF_checkExactType = new NamedFunction(Invokers.class
+ .getDeclaredMethod("checkExactType", Object.class, Object.class));
+ NF_checkGenericType = new NamedFunction(Invokers.class
+ .getDeclaredMethod("checkGenericType", Object.class, Object.class));
+ NF_getCallSiteTarget = new NamedFunction(Invokers.class
+ .getDeclaredMethod("getCallSiteTarget", Object.class));
+ NF_checkExactType.resolve();
+ NF_checkGenericType.resolve();
+ NF_getCallSiteTarget.resolve();
+ // bound
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,1620 @@
+/*
+ * Copyright (c) 2011, 2012, 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.lang.annotation.*;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import sun.invoke.util.Wrapper;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+/**
+ * The symbolic, non-executable form of a method handle's invocation semantics.
+ * It consists of a series of names.
+ * The first N (N=arity) names are parameters,
+ * while any remaining names are temporary values.
+ * Each temporary specifies the application of a function to some arguments.
+ * The functions are method handles, while the arguments are mixes of
+ * constant values and local names.
+ * The result of the lambda is defined as one of the names, often the last one.
+ * <p>
+ * Here is an approximate grammar:
+ * <pre>
+ * LambdaForm = "(" ArgName* ")=>{" TempName* Result "}"
+ * ArgName = "a" N ":" T
+ * TempName = "t" N ":" T "=" Function "(" Argument* ");"
+ * Function = ConstantValue
+ * Argument = NameRef | ConstantValue
+ * Result = NameRef | "void"
+ * NameRef = "a" N | "t" N
+ * N = (any whole number)
+ * T = "L" | "I" | "J" | "F" | "D" | "V"
+ * </pre>
+ * Names are numbered consecutively from left to right starting at zero.
+ * (The letters are merely a taste of syntax sugar.)
+ * Thus, the first temporary (if any) is always numbered N (where N=arity).
+ * Every occurrence of a name reference in an argument list must refer to
+ * a name previously defined within the same lambda.
+ * A lambda has a void result if and only if its result index is -1.
+ * If a temporary has the type "V", it cannot be the subject of a NameRef,
+ * even though possesses a number.
+ * Note that all reference types are erased to "L", which stands for {@code Object).
+ * All subword types (boolean, byte, short, char) are erased to "I" which is {@code int}.
+ * The other types stand for the usual primitive types.
+ * <p>
+ * Function invocation closely follows the static rules of the Java verifier.
+ * Arguments and return values must exactly match when their "Name" types are
+ * considered.
+ * Conversions are allowed only if they do not change the erased type.
+ * <ul>
+ * <li>L = Object: casts are used freely to convert into and out of reference types
+ * <li>I = int: subword types are forcibly narrowed when passed as arguments (see {@code explicitCastArguments})
+ * <li>J = long: no implicit conversions
+ * <li>F = float: no implicit conversions
+ * <li>D = double: no implicit conversions
+ * <li>V = void: a function result may be void if and only if its Name is of type "V"
+ * </ul>
+ * Although implicit conversions are not allowed, explicit ones can easily be
+ * encoded by using temporary expressions which call type-transformed identity functions.
+ * <p>
+ * Examples:
+ * <pre>
+ * (a0:J)=>{ a0 }
+ * == identity(long)
+ * (a0:I)=>{ t1:V = System.out#println(a0); void }
+ * == System.out#println(int)
+ * (a0:L)=>{ t1:V = System.out#println(a0); a0 }
+ * == identity, with printing side-effect
+ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0);
+ * t3:L = BoundMethodHandle#target(a0);
+ * t4:L = MethodHandle#invoke(t3, t2, a1); t4 }
+ * == general invoker for unary insertArgument combination
+ * (a0:L, a1:L)=>{ t2:L = FilterMethodHandle#filter(a0);
+ * t3:L = MethodHandle#invoke(t2, a1);
+ * t4:L = FilterMethodHandle#target(a0);
+ * t5:L = MethodHandle#invoke(t4, t3); t5 }
+ * == general invoker for unary filterArgument combination
+ * (a0:L, a1:L)=>{ ...(same as previous example)...
+ * t5:L = MethodHandle#invoke(t4, t3, a1); t5 }
+ * == general invoker for unary/unary foldArgument combination
+ * (a0:L, a1:I)=>{ t2:I = identity(long).asType((int)->long)(a1); t2 }
+ * == invoker for identity method handle which performs i2l
+ * (a0:L, a1:L)=>{ t2:L = BoundMethodHandle#argument(a0);
+ * t3:L = Class#cast(t2,a1); t3 }
+ * == invoker for identity method handle which performs cast
+ * </pre>
+ * <p>
+ * @author John Rose, JSR 292 EG
+ */
+class LambdaForm {
+ final int arity;
+ final int result;
+ final Name[] names;
+ final String debugName;
+ MemberName vmentry; // low-level behavior, or null if not yet prepared
+ private boolean isCompiled;
+
+ // Caches for common structural transforms:
+ LambdaForm[] bindCache;
+
+ public static final int VOID_RESULT = -1, LAST_RESULT = -2;
+
+ LambdaForm(String debugName,
+ int arity, Name[] names, int result) {
+ assert(namesOK(arity, names));
+ this.arity = arity;
+ this.result = fixResult(result, names);
+ this.names = names.clone();
+ this.debugName = debugName;
+ normalize();
+ }
+
+ LambdaForm(String debugName,
+ int arity, Name[] names) {
+ this(debugName,
+ arity, names, LAST_RESULT);
+ }
+
+ LambdaForm(String debugName,
+ Name[] formals, Name[] temps, Name result) {
+ this(debugName,
+ formals.length, buildNames(formals, temps, result), LAST_RESULT);
+ }
+
+ private static Name[] buildNames(Name[] formals, Name[] temps, Name result) {
+ int arity = formals.length;
+ int length = arity + temps.length + (result == null ? 0 : 1);
+ Name[] names = Arrays.copyOf(formals, length);
+ System.arraycopy(temps, 0, names, arity, temps.length);
+ if (result != null)
+ names[length - 1] = result;
+ return names;
+ }
+
+ private LambdaForm(String sig) {
+ // Make a blank lambda form, which returns a constant zero or null.
+ // It is used as a template for managing the invocation of similar forms that are non-empty.
+ // Called only from getPreparedForm.
+ assert(isValidSignature(sig));
+ this.arity = signatureArity(sig);
+ this.result = (signatureReturn(sig) == 'V' ? -1 : arity);
+ this.names = buildEmptyNames(arity, sig);
+ this.debugName = "LF.zero";
+ assert(nameRefsAreLegal());
+ assert(isEmpty());
+ assert(sig.equals(basicTypeSignature()));
+ }
+
+ private static Name[] buildEmptyNames(int arity, String basicTypeSignature) {
+ assert(isValidSignature(basicTypeSignature));
+ int resultPos = arity + 1; // skip '_'
+ if (arity < 0 || basicTypeSignature.length() != resultPos+1)
+ throw new IllegalArgumentException("bad arity for "+basicTypeSignature);
+ int numRes = (basicTypeSignature.charAt(resultPos) == 'V' ? 0 : 1);
+ Name[] names = arguments(numRes, basicTypeSignature.substring(0, arity));
+ for (int i = 0; i < numRes; i++) {
+ names[arity + i] = constantZero(arity + i, basicTypeSignature.charAt(resultPos + i));
+ }
+ return names;
+ }
+
+ private static int fixResult(int result, Name[] names) {
+ if (result >= 0) {
+ if (names[result].type == 'V')
+ return -1;
+ } else if (result == LAST_RESULT) {
+ return names.length - 1;
+ }
+ return result;
+ }
+
+ private static boolean namesOK(int arity, Name[] names) {
+ for (int i = 0; i < names.length; i++) {
+ Name n = names[i];
+ assert(n != null) : "n is null";
+ if (i < arity)
+ assert( n.isParam()) : n + " is not param at " + i;
+ else
+ 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() {
+ Name[] oldNames = null;
+ 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 (oldNames != null) {
+ int startFixing = arity;
+ if (startFixing <= changesStart)
+ startFixing = changesStart+1;
+ for (int i = startFixing; i < names.length; i++) {
+ Name fixed = names[i].replaceNames(oldNames, names, changesStart, i);
+ names[i] = fixed.newIndex(i);
+ }
+ }
+ assert(nameRefsAreLegal());
+ int maxInterned = Math.min(arity, INTERNED_ARGUMENT_LIMIT);
+ boolean needIntern = false;
+ for (int i = 0; i < maxInterned; i++) {
+ Name n = names[i], n2 = internArgument(n);
+ if (n != n2) {
+ names[i] = n2;
+ needIntern = true;
+ }
+ }
+ if (needIntern) {
+ for (int i = arity; i < names.length; i++) {
+ names[i].internArguments();
+ }
+ assert(nameRefsAreLegal());
+ }
+ }
+
+ /**
+ * Check that all embedded Name references are localizable to this lambda,
+ * and are properly ordered after their corresponding definitions.
+ * <p>
+ * Note that a Name can be local to multiple lambdas, as long as
+ * it possesses the same index in each use site.
+ * This allows Name references to be freely reused to construct
+ * fresh lambdas, without confusion.
+ */
+ private boolean nameRefsAreLegal() {
+ assert(arity >= 0 && arity <= names.length);
+ assert(result >= -1 && result < names.length);
+ // Do all names possess an index consistent with their local definition order?
+ for (int i = 0; i < arity; i++) {
+ Name n = names[i];
+ assert(n.index() == i) : Arrays.asList(n.index(), i);
+ assert(n.isParam());
+ }
+ // Also, do all local name references
+ for (int i = arity; i < names.length; i++) {
+ Name n = names[i];
+ assert(n.index() == i);
+ for (Object arg : n.arguments) {
+ if (arg instanceof Name) {
+ Name n2 = (Name) arg;
+ int i2 = n2.index;
+ assert(0 <= i2 && i2 < names.length) : n.debugString() + ": 0 <= i2 && i2 < names.length: 0 <= " + i2 + " < " + names.length;
+ assert(names[i2] == n2) : Arrays.asList("-1-", i, "-2-", n.debugString(), "-3-", i2, "-4-", n2.debugString(), "-5-", names[i2].debugString(), "-6-", this);
+ assert(i2 < i); // ref must come after def!
+ }
+ }
+ }
+ return true;
+ }
+
+ /** Invoke this form on the given arguments. */
+ // final Object invoke(Object... args) throws Throwable {
+ // // NYI: fit this into the fast path?
+ // return interpretWithArguments(args);
+ // }
+
+ /** Report the return type. */
+ char returnType() {
+ if (result < 0) return 'V';
+ Name n = names[result];
+ return n.type;
+ }
+
+ /** Report the N-th argument type. */
+ char parameterType(int n) {
+ assert(n < arity);
+ return names[n].type;
+ }
+
+ /** Report the arity. */
+ int arity() {
+ return arity;
+ }
+
+ /** Return the method type corresponding to my basic type signature. */
+ MethodType methodType() {
+ return signatureType(basicTypeSignature());
+ }
+ /** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
+ final String basicTypeSignature() {
+ StringBuilder buf = new StringBuilder(arity() + 3);
+ for (int i = 0, a = arity(); i < a; i++)
+ buf.append(parameterType(i));
+ return buf.append('_').append(returnType()).toString();
+ }
+ static int signatureArity(String sig) {
+ assert(isValidSignature(sig));
+ return sig.indexOf('_');
+ }
+ static char signatureReturn(String sig) {
+ return sig.charAt(signatureArity(sig)+1);
+ }
+ static boolean isValidSignature(String sig) {
+ int arity = sig.indexOf('_');
+ if (arity < 0) return false; // must be of the form *_*
+ int siglen = sig.length();
+ if (siglen != arity + 2) return false; // *_X
+ for (int i = 0; i < siglen; i++) {
+ if (i == arity) continue; // skip '_'
+ char c = sig.charAt(i);
+ if (c == 'V')
+ return (i == siglen - 1 && arity == siglen - 2);
+ if (ALL_TYPES.indexOf(c) < 0) return false; // must be [LIJFD]
+ }
+ return true; // [LIJFD]*_[LIJFDV]
+ }
+ static Class<?> typeClass(char t) {
+ switch (t) {
+ case 'I': return int.class;
+ case 'J': return long.class;
+ case 'F': return float.class;
+ case 'D': return double.class;
+ case 'L': return Object.class;
+ case 'V': return void.class;
+ default: assert false;
+ }
+ return null;
+ }
+ static MethodType signatureType(String sig) {
+ Class<?>[] ptypes = new Class<?>[signatureArity(sig)];
+ for (int i = 0; i < ptypes.length; i++)
+ ptypes[i] = typeClass(sig.charAt(i));
+ Class<?> rtype = typeClass(signatureReturn(sig));
+ return MethodType.methodType(rtype, ptypes);
+ }
+
+ /*
+ * Code generation issues:
+ *
+ * Compiled LFs should be reusable in general.
+ * The biggest issue is how to decide when to pull a name into
+ * the bytecode, versus loading a reified form from the MH data.
+ *
+ * For example, an asType wrapper may require execution of a cast
+ * after a call to a MH. The target type of the cast can be placed
+ * as a constant in the LF itself. This will force the cast type
+ * to be compiled into the bytecodes and native code for the MH.
+ * Or, the target type of the cast can be erased in the LF, and
+ * loaded from the MH data. (Later on, if the MH as a whole is
+ * inlined, the data will flow into the inlined instance of the LF,
+ * as a constant, and the end result will be an optimal cast.)
+ *
+ * This erasure of cast types can be done with any use of
+ * reference types. It can also be done with whole method
+ * handles. Erasing a method handle might leave behind
+ * LF code that executes correctly for any MH of a given
+ * type, and load the required MH from the enclosing MH's data.
+ * Or, the erasure might even erase the expected MT.
+ *
+ * Also, for direct MHs, the MemberName of the target
+ * could be erased, and loaded from the containing direct MH.
+ * As a simple case, a LF for all int-valued non-static
+ * field getters would perform a cast on its input argument
+ * (to non-constant base type derived from the MemberName)
+ * and load an integer value from the input object
+ * (at a non-constant offset also derived from the MemberName).
+ * Such MN-erased LFs would be inlinable back to optimized
+ * code, whenever a constant enclosing DMH is available
+ * to supply a constant MN from its data.
+ *
+ * The main problem here is to keep LFs reasonably generic,
+ * while ensuring that hot spots will inline good instances.
+ * "Reasonably generic" means that we don't end up with
+ * repeated versions of bytecode or machine code that do
+ * not differ in their optimized form. Repeated versions
+ * of machine would have the undesirable overheads of
+ * (a) redundant compilation work and (b) extra I$ pressure.
+ * To control repeated versions, we need to be ready to
+ * erase details from LFs and move them into MH data,
+ * whevener those details are not relevant to significant
+ * optimization. "Significant" means optimization of
+ * code that is actually hot.
+ *
+ * Achieving this may require dynamic splitting of MHs, by replacing
+ * a generic LF with a more specialized one, on the same MH,
+ * if (a) the MH is frequently executed and (b) the MH cannot
+ * be inlined into a containing caller, such as an invokedynamic.
+ *
+ * Compiled LFs that are no longer used should be GC-able.
+ * If they contain non-BCP references, they should be properly
+ * interlinked with the class loader(s) that their embedded types
+ * depend on. This probably means that reusable compiled LFs
+ * will be tabulated (indexed) on relevant class loaders,
+ * or else that the tables that cache them will have weak links.
+ */
+
+ /**
+ * Make this LF directly executable, as part of a MethodHandle.
+ * Invariant: Every MH which is invoked must prepare its LF
+ * before invocation.
+ * (In principle, the JVM could do this very lazily,
+ * as a sort of pre-invocation linkage step.)
+ */
+ public void prepare() {
+ if (COMPILE_THRESHOLD == 0) {
+ compileToBytecode();
+ }
+ if (this.vmentry != null) {
+ // already prepared (e.g., a primitive DMH invoker form)
+ return;
+ }
+ LambdaForm prep = getPreparedForm(basicTypeSignature());
+ this.vmentry = prep.vmentry;
+ // TO DO: Maybe add invokeGeneric, invokeWithArguments
+ }
+
+ /** Generate optimizable bytecode for this form. */
+ MemberName compileToBytecode() {
+ MethodType invokerType = methodType();
+ assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
+ if (vmentry != null && isCompiled) {
+ return vmentry; // already compiled somehow
+ }
+ try {
+ vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
+ if (TRACE_INTERPRETER)
+ traceInterpreter("compileToBytecode", this);
+ isCompiled = true;
+ return vmentry;
+ } catch (Error | Exception ex) {
+ throw new InternalError(this.toString(), ex);
+ }
+ }
+
+ private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
+ static {
+ int capacity = 512; // expect many distinct signatures over time
+ float loadFactor = 0.75f; // normal default
+ int writers = 1;
+ PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
+ }
+
+ private static Map<String,LambdaForm> computeInitialPreparedForms() {
+ // Find all predefined invokers and associate them with canonical empty lambda forms.
+ HashMap<String,LambdaForm> forms = new HashMap<>();
+ for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
+ if (!m.isStatic() || !m.isPackage()) continue;
+ MethodType mt = m.getMethodType();
+ if (mt.parameterCount() > 0 &&
+ mt.parameterType(0) == MethodHandle.class &&
+ m.getName().startsWith("interpret_")) {
+ String sig = basicTypeSignature(mt);
+ assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
+ LambdaForm form = new LambdaForm(sig);
+ form.vmentry = m;
+ mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
+ // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
+ forms.put(sig, form);
+ }
+ }
+ //System.out.println("computeInitialPreparedForms => "+forms);
+ return forms;
+ }
+
+ // Set this false to disable use of the interpret_L methods defined in this file.
+ private static final boolean USE_PREDEFINED_INTERPRET_METHODS = true;
+
+ // The following are predefined exact invokers. The system must build
+ // a separate invoker for each distinct signature.
+ static Object interpret_L(MethodHandle mh) throws Throwable {
+ Object[] av = {mh};
+ String sig = null;
+ assert(argumentTypesMatch(sig = "L_L", av));
+ Object res = mh.form.interpretWithArguments(av);
+ assert(returnTypesMatch(sig, av, res));
+ return res;
+ }
+ static Object interpret_L(MethodHandle mh, Object x1) throws Throwable {
+ Object[] av = {mh, x1};
+ String sig = null;
+ assert(argumentTypesMatch(sig = "LL_L", av));
+ Object res = mh.form.interpretWithArguments(av);
+ assert(returnTypesMatch(sig, av, res));
+ return res;
+ }
+ static Object interpret_L(MethodHandle mh, Object x1, Object x2) throws Throwable {
+ Object[] av = {mh, x1, x2};
+ String sig = null;
+ assert(argumentTypesMatch(sig = "LLL_L", av));
+ Object res = mh.form.interpretWithArguments(av);
+ assert(returnTypesMatch(sig, av, res));
+ return res;
+ }
+ private static LambdaForm getPreparedForm(String sig) {
+ MethodType mtype = signatureType(sig);
+ //LambdaForm prep = PREPARED_FORMS.get(sig);
+ LambdaForm prep = mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
+ if (prep != null) return prep;
+ assert(isValidSignature(sig));
+ prep = new LambdaForm(sig);
+ prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
+ //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
+ return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
+ }
+
+ // The next few routines are called only from assert expressions
+ // They verify that the built-in invokers process the correct raw data types.
+ private static boolean argumentTypesMatch(String sig, Object[] av) {
+ int arity = signatureArity(sig);
+ assert(av.length == arity) : "av.length == arity: av.length=" + av.length + ", arity=" + arity;
+ assert(av[0] instanceof MethodHandle) : "av[0] not instace of MethodHandle: " + av[0];
+ MethodHandle mh = (MethodHandle) av[0];
+ MethodType mt = mh.type();
+ assert(mt.parameterCount() == arity-1);
+ for (int i = 0; i < av.length; i++) {
+ Class<?> pt = (i == 0 ? MethodHandle.class : mt.parameterType(i-1));
+ assert(valueMatches(sig.charAt(i), pt, av[i]));
+ }
+ return true;
+ }
+ private static boolean valueMatches(char tc, Class<?> type, Object x) {
+ // The following line is needed because (...)void method handles can use non-void invokers
+ if (type == void.class) tc = 'V'; // can drop any kind of value
+ assert tc == basicType(type) : tc + " == basicType(" + type + ")=" + basicType(type);
+ switch (tc) {
+ case 'I': assert checkInt(type, x) : "checkInt(" + type + "," + x +")"; break;
+ case 'J': assert x instanceof Long : "instanceof Long: " + x; break;
+ case 'F': assert x instanceof Float : "instanceof Float: " + x; break;
+ case 'D': assert x instanceof Double : "instanceof Double: " + x; break;
+ case 'L': assert checkRef(type, x) : "checkRef(" + type + "," + x + ")"; break;
+ case 'V': break; // allow anything here; will be dropped
+ default: assert(false);
+ }
+ return true;
+ }
+ private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
+ MethodHandle mh = (MethodHandle) av[0];
+ return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
+ }
+ private static boolean checkInt(Class<?> type, Object x) {
+ assert(x instanceof Integer);
+ if (type == int.class) return true;
+ Wrapper w = Wrapper.forBasicType(type);
+ assert(w.isSubwordOrInt());
+ Object x1 = Wrapper.INT.wrap(w.wrap(x));
+ return x.equals(x1);
+ }
+ private static boolean checkRef(Class<?> type, Object x) {
+ assert(!type.isPrimitive());
+ if (x == null) return true;
+ if (type.isInterface()) return true;
+ return type.isInstance(x);
+ }
+
+ /** 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
+ }
+ private int invocationCounter = 0;
+
+ @Hidden
+ /** Interpretively invoke this form on the given arguments. */
+ Object interpretWithArguments(Object... argumentValues) throws Throwable {
+ if (TRACE_INTERPRETER)
+ return interpretWithArgumentsTracing(argumentValues);
+ if (COMPILE_THRESHOLD != 0 &&
+ invocationCounter < COMPILE_THRESHOLD) {
+ invocationCounter++; // benign race
+ if (invocationCounter >= COMPILE_THRESHOLD) {
+ // Replace vmentry with a bytecode version of this LF.
+ compileToBytecode();
+ }
+ }
+ 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];
+ }
+
+ @Hidden
+ /** Evaluate a single Name within this form, applying its function to its arguments. */
+ Object interpretName(Name name, Object[] values) throws Throwable {
+ if (TRACE_INTERPRETER)
+ traceInterpreter("| interpretName", name.debugString(), (Object[]) null);
+ Object[] arguments = Arrays.copyOf(name.arguments, name.arguments.length, Object[].class);
+ for (int i = 0; i < arguments.length; i++) {
+ Object a = arguments[i];
+ if (a instanceof Name) {
+ int i2 = ((Name)a).index();
+ assert(names[i2] == a);
+ a = values[i2];
+ arguments[i] = a;
+ }
+ }
+ return name.function.invokeWithArguments(arguments);
+ }
+
+ Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
+ traceInterpreter("[ interpretWithArguments", this, argumentValues);
+ if (invocationCounter < COMPILE_THRESHOLD) {
+ int ctr = invocationCounter++; // benign race
+ traceInterpreter("| invocationCounter", ctr);
+ if (invocationCounter >= COMPILE_THRESHOLD) {
+ compileToBytecode();
+ }
+ }
+ Object rval;
+ try {
+ 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);
+ }
+ rval = (result < 0) ? null : values[result];
+ } catch (Throwable ex) {
+ traceInterpreter("] throw =>", ex);
+ throw ex;
+ }
+ traceInterpreter("] return =>", rval);
+ return rval;
+ }
+
+ //** This transform is applied (statically) to every name.function. */
+ /*
+ private static MethodHandle eraseSubwordTypes(MethodHandle mh) {
+ MethodType mt = mh.type();
+ if (mt.hasPrimitives()) {
+ mt = mt.changeReturnType(eraseSubwordType(mt.returnType()));
+ for (int i = 0; i < mt.parameterCount(); i++) {
+ mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i)));
+ }
+ mh = MethodHandles.explicitCastArguments(mh, mt);
+ }
+ return mh;
+ }
+ private static Class<?> eraseSubwordType(Class<?> type) {
+ if (!type.isPrimitive()) return type;
+ if (type == int.class) return type;
+ Wrapper w = Wrapper.forPrimitiveType(type);
+ if (w.isSubwordOrInt()) return int.class;
+ return type;
+ }
+ */
+
+ static void traceInterpreter(String event, Object obj, Object... args) {
+ if (!TRACE_INTERPRETER) return;
+ System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : ""));
+ }
+ static void traceInterpreter(String event, Object obj) {
+ traceInterpreter(event, obj, (Object[])null);
+ }
+ 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);
+ // note: argument #0 could also be an interface wrapper, in the future
+ return true;
+ }
+
+ private boolean isEmpty() {
+ if (result < 0)
+ return (names.length == arity);
+ else if (result == arity && names.length == arity + 1)
+ return names[arity].isConstantZero();
+ else
+ return false;
+ }
+
+ public String toString() {
+ StringBuilder buf = new StringBuilder("Lambda(");
+ for (int i = 0; i < names.length; i++) {
+ if (i == arity) buf.append(")=>{");
+ Name n = names[i];
+ if (i >= arity) buf.append("\n ");
+ buf.append(n);
+ if (i < arity) {
+ if (i+1 < arity) buf.append(",");
+ continue;
+ }
+ buf.append("=").append(n.exprString());
+ 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, char 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.extendWithType(name.type);
+ return bind(name, newData.getterName(names[0], oldData.fieldCount()), 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.clazz);
+ assert(oldData.getters.length == newData.getters.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.clazz) {
+ MethodHandle oldGetter = n.function.resolvedHandle;
+ MethodHandle newGetter = null;
+ for (int j = 0; j < oldData.getters.length; j++) {
+ if (oldGetter == oldData.getters[j])
+ newGetter = newData.getters[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.isSiblingBindingBefore(binding)) {
+ 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) {
+ int pos = name.index();
+ if (pos >= 0) {
+ return pos < names.length && name.equals(names[pos]);
+ }
+ for (int i = arity; i < names.length; i++) {
+ if (name.equals(names[i]))
+ return true;
+ }
+ return false;
+ }
+
+ LambdaForm addArguments(int pos, char... types) {
+ assert(pos <= 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)
+ 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]);
+ }
+ return new LambdaForm(debugName, arity2, names2, result2);
+ }
+
+ LambdaForm addArguments(int pos, List<Class<?>> types) {
+ char[] basicTypes = new char[types.size()];
+ for (int i = 0; i < basicTypes.length; i++)
+ basicTypes[i] = basicType(types.get(i));
+ return addArguments(pos, basicTypes);
+ }
+
+ LambdaForm permuteArguments(int skip, int[] reorder, char[] types) {
+ // Note: When inArg = reorder[outArg], outArg is fed by a copy of inArg.
+ // The types are the types of the new (incoming) arguments.
+ int length = names.length;
+ int inTypes = types.length;
+ int outArgs = reorder.length;
+ assert(skip+outArgs == arity);
+ assert(permutedTypesMatch(reorder, types, names, skip));
+ int pos = 0;
+ // skip trivial first part of reordering:
+ while (pos < outArgs && reorder[pos] == pos) pos += 1;
+ Name[] names2 = new Name[length - outArgs + inTypes];
+ System.arraycopy(names, 0, names2, 0, skip+pos);
+ // copy the body:
+ int bodyLength = length - arity;
+ System.arraycopy(names, skip+outArgs, names2, skip+inTypes, bodyLength);
+ int arity2 = names2.length - bodyLength;
+ int result2 = result;
+ if (result2 >= 0) {
+ if (result2 < skip+outArgs) {
+ // return the corresponding inArg
+ result2 = reorder[result2-skip];
+ } else {
+ result2 = result2 - outArgs + inTypes;
+ }
+ }
+ // rework names in the body:
+ for (int j = pos; j < outArgs; j++) {
+ Name n = names[skip+j];
+ int i = reorder[j];
+ // replace names[skip+j] by names2[skip+i]
+ Name n2 = names2[skip+i];
+ if (n2 == null)
+ names2[skip+i] = n2 = new Name(types[i]);
+ else
+ assert(n2.type == types[i]);
+ for (int k = arity2; k < names2.length; k++) {
+ names2[k] = names2[k].replaceName(n, n2);
+ }
+ }
+ // some names are unused, but must be filled in
+ for (int i = skip+pos; i < arity2; i++) {
+ if (names2[i] == null)
+ names2[i] = argument(i, types[i - skip]);
+ }
+ for (int j = arity; j < names.length; j++) {
+ int i = j - arity + arity2;
+ // replace names2[i] by names[j]
+ Name n = names[j];
+ Name n2 = names2[i];
+ if (n != n2) {
+ for (int k = i+1; k < names2.length; k++) {
+ names2[k] = names2[k].replaceName(n, n2);
+ }
+ }
+ }
+ return new LambdaForm(debugName, arity2, names2, result2);
+ }
+
+ static boolean permutedTypesMatch(int[] reorder, char[] types, Name[] names, int skip) {
+ int inTypes = types.length;
+ int outArgs = reorder.length;
+ for (int i = 0; i < outArgs; i++) {
+ assert(names[skip+i].isParam());
+ assert(names[skip+i].type == types[reorder[i]]);
+ }
+ return true;
+ }
+
+ static class NamedFunction {
+ final MemberName member;
+ MethodHandle resolvedHandle;
+ MethodHandle invoker;
+
+ NamedFunction(MethodHandle resolvedHandle) {
+ this(resolvedHandle.internalMemberName(), resolvedHandle);
+ }
+ NamedFunction(MemberName member, MethodHandle resolvedHandle) {
+ this.member = member;
+ //resolvedHandle = eraseSubwordTypes(resolvedHandle);
+ this.resolvedHandle = resolvedHandle;
+ }
+
+ // 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.
+ // And even without the resolvedHandle, the name can still be compiled and optimized.
+ NamedFunction(Method method) {
+ this(new MemberName(method));
+ }
+ NamedFunction(Field field) {
+ this(new MemberName(field));
+ }
+ NamedFunction(MemberName member) {
+ this.member = member;
+ this.resolvedHandle = null;
+ }
+
+ MethodHandle resolvedHandle() {
+ if (resolvedHandle == null) resolve();
+ return resolvedHandle;
+ }
+
+ void resolve() {
+ resolvedHandle = DirectMethodHandle.make(member);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (other == null) return false;
+ if (!(other instanceof NamedFunction)) return false;
+ NamedFunction that = (NamedFunction) other;
+ return this.member != null && this.member.equals(that.member);
+ }
+
+ @Override
+ public int hashCode() {
+ if (member != null)
+ return member.hashCode();
+ return super.hashCode();
+ }
+
+ // Put the predefined NamedFunction invokers into the table.
+ static void initializeInvokers() {
+ for (MemberName m : MemberName.getFactory().getMethods(NamedFunction.class, false, null, null, null)) {
+ if (!m.isStatic() || !m.isPackage()) continue;
+ MethodType type = m.getMethodType();
+ if (type.equals(INVOKER_METHOD_TYPE) &&
+ m.getName().startsWith("invoke_")) {
+ String sig = m.getName().substring("invoke_".length());
+ int arity = LambdaForm.signatureArity(sig);
+ MethodType srcType = MethodType.genericMethodType(arity);
+ if (LambdaForm.signatureReturn(sig) == 'V')
+ srcType = srcType.changeReturnType(void.class);
+ MethodTypeForm typeForm = srcType.form();
+ typeForm.namedFunctionInvoker = DirectMethodHandle.make(m);
+ }
+ }
+ }
+
+ // 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);
+ mh.invokeBasic();
+ return null;
+ }
+ @Hidden
+ static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 1);
+ mh.invokeBasic(a[0]);
+ return null;
+ }
+ @Hidden
+ static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 2);
+ mh.invokeBasic(a[0], a[1]);
+ return null;
+ }
+ @Hidden
+ static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 3);
+ 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);
+ 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);
+ 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);
+ return mh.invokeBasic();
+ }
+ @Hidden
+ static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 1);
+ return mh.invokeBasic(a[0]);
+ }
+ @Hidden
+ static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 2);
+ return mh.invokeBasic(a[0], a[1]);
+ }
+ @Hidden
+ static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable {
+ assert(a.length == 3);
+ 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);
+ 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);
+ return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]);
+ }
+
+ static final MethodType INVOKER_METHOD_TYPE =
+ MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+
+ private static MethodHandle computeInvoker(MethodTypeForm typeForm) {
+ MethodHandle mh = typeForm.namedFunctionInvoker;
+ if (mh != null) return mh;
+ MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while
+ mh = DirectMethodHandle.make(invoker);
+ MethodHandle mh2 = typeForm.namedFunctionInvoker;
+ if (mh2 != null) return mh2; // benign race
+ if (!mh.type().equals(INVOKER_METHOD_TYPE))
+ throw new InternalError(mh.debugString());
+ return typeForm.namedFunctionInvoker = mh;
+ }
+
+ @Hidden
+ Object invokeWithArguments(Object... arguments) throws Throwable {
+ // If we have a cached invoker, call it right away.
+ // NOTE: The invoker always returns a reference value.
+ if (TRACE_INTERPRETER) return invokeWithArgumentsTracing(arguments);
+ assert(checkArgumentTypes(arguments, methodType()));
+ return invoker().invokeBasic(resolvedHandle(), arguments);
+ }
+
+ @Hidden
+ Object invokeWithArgumentsTracing(Object[] arguments) throws Throwable {
+ Object rval;
+ try {
+ traceInterpreter("[ call", this, arguments);
+ if (invoker == null) {
+ traceInterpreter("| getInvoker", this);
+ invoker();
+ }
+ if (resolvedHandle == null) {
+ traceInterpreter("| resolve", this);
+ resolvedHandle();
+ }
+ assert(checkArgumentTypes(arguments, methodType()));
+ rval = invoker().invokeBasic(resolvedHandle(), arguments);
+ } catch (Throwable ex) {
+ traceInterpreter("] throw =>", ex);
+ throw ex;
+ }
+ traceInterpreter("] return =>", rval);
+ return rval;
+ }
+
+ private MethodHandle invoker() {
+ if (invoker != null) return invoker;
+ // Get an invoker and cache it.
+ return invoker = computeInvoker(methodType().form());
+ }
+
+ private static boolean checkArgumentTypes(Object[] arguments, MethodType methodType) {
+ if (true) return true; // FIXME
+ MethodType dstType = methodType.form().erasedType();
+ MethodType srcType = dstType.basicType().wrap();
+ Class<?>[] ptypes = new Class<?>[arguments.length];
+ for (int i = 0; i < arguments.length; i++) {
+ Object arg = arguments[i];
+ Class<?> ptype = arg == null ? Object.class : arg.getClass();
+ // If the dest. type is a primitive we keep the
+ // argument type.
+ ptypes[i] = dstType.parameterType(i).isPrimitive() ? ptype : Object.class;
+ }
+ MethodType argType = MethodType.methodType(srcType.returnType(), ptypes).wrap();
+ assert(argType.isConvertibleTo(srcType)) : "wrong argument types: cannot convert " + argType + " to " + srcType;
+ return true;
+ }
+
+ String basicTypeSignature() {
+ //return LambdaForm.basicTypeSignature(resolvedHandle.type());
+ return LambdaForm.basicTypeSignature(methodType());
+ }
+
+ MethodType methodType() {
+ if (resolvedHandle != null)
+ return resolvedHandle.type();
+ else
+ // only for certain internal LFs during bootstrapping
+ return member.getInvocationType();
+ }
+
+ MemberName member() {
+ assert(assertMemberIsConsistent());
+ return member;
+ }
+
+ // Called only from assert.
+ private boolean assertMemberIsConsistent() {
+ if (resolvedHandle instanceof DirectMethodHandle) {
+ MemberName m = resolvedHandle.internalMemberName();
+ assert(m.equals(member));
+ }
+ return true;
+ }
+
+ Class<?> memberDeclaringClassOrNull() {
+ return (member == null) ? null : member.getDeclaringClass();
+ }
+
+ char returnType() {
+ return basicType(methodType().returnType());
+ }
+
+ char parameterType(int n) {
+ return basicType(methodType().parameterType(n));
+ }
+
+ int arity() {
+ //int siglen = member.getMethodType().parameterCount();
+ //if (!member.isStatic()) siglen += 1;
+ //return siglen;
+ return methodType().parameterCount();
+ }
+
+ public String toString() {
+ if (member == null) return resolvedHandle.toString();
+ return member.getDeclaringClass().getSimpleName()+"."+member.getName();
+ }
+ }
+
+ void resolve() {
+ for (Name n : names) n.resolve();
+ }
+
+ public static char basicType(Class<?> type) {
+ char c = Wrapper.basicTypeChar(type);
+ if ("ZBSC".indexOf(c) >= 0) c = 'I';
+ assert("LIJFDV".indexOf(c) >= 0);
+ return c;
+ }
+ public static char[] basicTypes(List<Class<?>> types) {
+ char[] btypes = new char[types.size()];
+ for (int i = 0; i < btypes.length; i++) {
+ btypes[i] = basicType(types.get(i));
+ }
+ return btypes;
+ }
+ public static String basicTypeSignature(MethodType type) {
+ char[] sig = new char[type.parameterCount() + 2];
+ int sigp = 0;
+ for (Class<?> pt : type.parameterList()) {
+ sig[sigp++] = basicType(pt);
+ }
+ sig[sigp++] = '_';
+ sig[sigp++] = basicType(type.returnType());
+ assert(sigp == sig.length);
+ return String.valueOf(sig);
+ }
+
+ static final class Name {
+ final char type;
+ private short index;
+ final NamedFunction function;
+ final Object[] arguments;
+
+ private Name(int index, char type, NamedFunction function, Object[] arguments) {
+ this.index = (short)index;
+ this.type = type;
+ this.function = function;
+ this.arguments = arguments;
+ assert(this.index == index);
+ }
+ Name(MethodHandle function, Object... arguments) {
+ this(new NamedFunction(function), arguments);
+ }
+ Name(MemberName function, Object... arguments) {
+ this(new NamedFunction(function), arguments);
+ }
+ Name(NamedFunction function, Object... arguments) {
+ this(-1, function.returnType(), function, arguments = arguments.clone());
+ assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString();
+ for (int i = 0; i < arguments.length; i++)
+ assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString();
+ }
+ Name(int index, char type) {
+ this(index, type, null, null);
+ }
+ Name(char type) {
+ this(-1, type);
+ }
+
+ char type() { return type; }
+ int index() { return index; }
+ boolean initIndex(int i) {
+ if (index != i) {
+ if (index != -1) return false;
+ index = (short)i;
+ }
+ return true;
+ }
+
+
+ void resolve() {
+ if (function != null)
+ function.resolve();
+ }
+
+ Name newIndex(int i) {
+ if (initIndex(i)) return this;
+ return cloneWithIndex(i);
+ }
+ Name cloneWithIndex(int i) {
+ Object[] newArguments = (arguments == null) ? null : arguments.clone();
+ return new Name(i, type, function, newArguments);
+ }
+ Name replaceName(Name oldName, Name newName) { // FIXME: use replaceNames uniformly
+ if (oldName == newName) return this;
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ Object[] arguments = this.arguments;
+ if (arguments == null) return this;
+ boolean replaced = false;
+ for (int j = 0; j < arguments.length; j++) {
+ if (arguments[j] == oldName) {
+ if (!replaced) {
+ replaced = true;
+ arguments = arguments.clone();
+ }
+ arguments[j] = newName;
+ }
+ }
+ if (!replaced) return this;
+ return new Name(function, arguments);
+ }
+ Name replaceNames(Name[] oldNames, Name[] newNames, int start, int end) {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ Object[] arguments = this.arguments;
+ boolean replaced = false;
+ eachArg:
+ for (int j = 0; j < arguments.length; j++) {
+ if (arguments[j] instanceof Name) {
+ Name n = (Name) arguments[j];
+ int check = n.index;
+ // harmless check to see if the thing is already in newNames:
+ if (check >= 0 && check < newNames.length && n == newNames[check])
+ continue eachArg;
+ // n might not have the correct index: n != oldNames[n.index].
+ for (int i = start; i < end; i++) {
+ if (n == oldNames[i]) {
+ if (n == newNames[i])
+ continue eachArg;
+ if (!replaced) {
+ replaced = true;
+ arguments = arguments.clone();
+ }
+ arguments[j] = newNames[i];
+ continue eachArg;
+ }
+ }
+ }
+ }
+ if (!replaced) return this;
+ return new Name(function, arguments);
+ }
+ void internArguments() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ Object[] arguments = this.arguments;
+ for (int j = 0; j < arguments.length; j++) {
+ if (arguments[j] instanceof Name) {
+ Name n = (Name) arguments[j];
+ if (n.isParam() && n.index < INTERNED_ARGUMENT_LIMIT)
+ arguments[j] = internArgument(n);
+ }
+ }
+ }
+ boolean isParam() {
+ return function == null;
+ }
+ boolean isConstantZero() {
+ return !isParam() && arguments.length == 0 && function.equals(constantZero(0, type).function);
+ }
+
+ public String toString() {
+ return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+type;
+ }
+ public String debugString() {
+ String s = toString();
+ return (function == null) ? s : s + "=" + exprString();
+ }
+ public String exprString() {
+ if (function == null) return "null";
+ StringBuilder buf = new StringBuilder(function.toString());
+ buf.append("(");
+ String cma = "";
+ for (Object a : arguments) {
+ buf.append(cma); cma = ",";
+ if (a instanceof Name || a instanceof Integer)
+ buf.append(a);
+ else
+ buf.append("(").append(a).append(")");
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ private static boolean typesMatch(char parameterType, Object object) {
+ if (object instanceof Name) {
+ return ((Name)object).type == parameterType;
+ }
+ switch (parameterType) {
+ case 'I': return object instanceof Integer;
+ case 'J': return object instanceof Long;
+ case 'F': return object instanceof Float;
+ case 'D': return object instanceof Double;
+ }
+ assert(parameterType == 'L');
+ 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.
+ * @param binding
+ * @return
+ */
+ 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;
+ }
+
+ public boolean equals(Name that) {
+ if (this == that) return true;
+ if (isParam())
+ // each parameter is a unique atom
+ return false; // this != that
+ return
+ //this.index == that.index &&
+ this.type == that.type &&
+ this.function.equals(that.function) &&
+ Arrays.equals(this.arguments, that.arguments);
+ }
+ @Override
+ public boolean equals(Object x) {
+ return x instanceof Name && equals((Name)x);
+ }
+ @Override
+ public int hashCode() {
+ if (isParam())
+ return index | (type << 8);
+ return function.hashCode() ^ Arrays.hashCode(arguments);
+ }
+ }
+
+ static Name argument(int which, char type) {
+ int tn = ALL_TYPES.indexOf(type);
+ if (tn < 0 || which >= INTERNED_ARGUMENT_LIMIT)
+ return new Name(which, type);
+ return INTERNED_ARGUMENTS[tn][which];
+ }
+ static Name internArgument(Name n) {
+ assert(n.isParam()) : "not param: " + n;
+ assert(n.index < INTERNED_ARGUMENT_LIMIT);
+ return argument(n.index, n.type);
+ }
+ static Name[] arguments(int extra, String types) {
+ int length = types.length();
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, types.charAt(i));
+ return names;
+ }
+ static Name[] arguments(int extra, char... types) {
+ int length = types.length;
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, types[i]);
+ return names;
+ }
+ static Name[] arguments(int extra, List<Class<?>> types) {
+ int length = types.size();
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, basicType(types.get(i)));
+ return names;
+ }
+ static Name[] arguments(int extra, Class<?>... types) {
+ int length = types.length;
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, basicType(types[i]));
+ return names;
+ }
+ static Name[] arguments(int extra, MethodType types) {
+ int length = types.parameterCount();
+ Name[] names = new Name[length + extra];
+ for (int i = 0; i < length; i++)
+ names[i] = argument(i, basicType(types.parameterType(i)));
+ return names;
+ }
+ static final String ALL_TYPES = "LIJFD"; // omit V, not an argument type
+ static final int INTERNED_ARGUMENT_LIMIT = 10;
+ private static final Name[][] INTERNED_ARGUMENTS
+ = new Name[ALL_TYPES.length()][INTERNED_ARGUMENT_LIMIT];
+ static {
+ for (int tn = 0; tn < ALL_TYPES.length(); tn++) {
+ for (int i = 0; i < INTERNED_ARGUMENTS[tn].length; i++) {
+ char type = ALL_TYPES.charAt(tn);
+ INTERNED_ARGUMENTS[tn][i] = new Name(i, type);
+ }
+ }
+ }
+
+ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+
+ static Name constantZero(int which, char type) {
+ return CONSTANT_ZERO[ALL_TYPES.indexOf(type)].newIndex(which);
+ }
+ private static final Name[] CONSTANT_ZERO
+ = new Name[ALL_TYPES.length()];
+ static {
+ for (int tn = 0; tn < ALL_TYPES.length(); tn++) {
+ char bt = ALL_TYPES.charAt(tn);
+ Wrapper wrap = Wrapper.forBasicType(bt);
+ MemberName zmem = new MemberName(LambdaForm.class, "zero"+bt, MethodType.methodType(wrap.primitiveType()), REF_invokeStatic);
+ try {
+ zmem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zmem, null, NoSuchMethodException.class);
+ } catch (IllegalAccessException|NoSuchMethodException ex) {
+ throw new InternalError(ex);
+ }
+ NamedFunction zcon = new NamedFunction(zmem);
+ Name n = new Name(zcon).newIndex(0);
+ assert(n.type == ALL_TYPES.charAt(tn));
+ CONSTANT_ZERO[tn] = n;
+ assert(n.isConstantZero());
+ }
+ }
+
+ // Avoid appealing to ValueConversions at bootstrap time:
+ private static int zeroI() { return 0; }
+ private static long zeroJ() { return 0; }
+ private static float zeroF() { return 0; }
+ private static double zeroD() { return 0; }
+ private static Object zeroL() { return null; }
+
+ // Put this last, so that previous static inits can run before.
+ static {
+ if (USE_PREDEFINED_INTERPRET_METHODS)
+ PREPARED_FORMS.putAll(computeInitialPreparedForms());
+ }
+
+ /**
+ * Internal marker for byte-compiled LambdaForms.
+ */
+ /*non-public*/
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Compiled {
+ }
+
+ /**
+ * Internal marker for LambdaForm interpreter frames.
+ */
+ /*non-public*/
+ @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;
+ */
+
+ static { NamedFunction.initializeInvokers(); }
+}
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java Fri Aug 10 16:03:28 2012 -0700
@@ -26,6 +26,8 @@
package java.lang.invoke;
import sun.invoke.util.BytecodeDescriptor;
+import sun.invoke.util.VerifyAccess;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -38,6 +40,7 @@
import java.util.List;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.Objects;
/**
* A {@code MemberName} is a compact symbolic datum which fully characterizes
@@ -71,19 +74,14 @@
private String name; // may be null if not yet materialized
private Object type; // may be null if not yet materialized
private int flags; // modifier bits; see reflect.Modifier
-
- private Object vmtarget; // VM-specific target value
- private int vmindex; // method index within class or interface
-
- { vmindex = VM_INDEX_UNINITIALIZED; }
+ //@Injected JVM_Method* vmtarget;
+ //@Injected int vmindex;
+ private Object resolution; // if null, this guy is resolved
/** Return the declaring class of this member.
* In the case of a bare name and type, the declaring class will be null.
*/
public Class<?> getDeclaringClass() {
- if (clazz == null && isResolved()) {
- expandFromVM();
- }
return clazz;
}
@@ -105,6 +103,16 @@
return name;
}
+ public MethodType getMethodOrFieldType() {
+ if (isInvocable())
+ return getMethodType();
+ if (isGetter())
+ return MethodType.methodType(getFieldType());
+ if (isSetter())
+ return MethodType.methodType(void.class, getFieldType());
+ throw new InternalError("not a method or field: "+this);
+ }
+
/** Return the declared type of this member, which
* must be a method or constructor.
*/
@@ -140,9 +148,11 @@
* a reference to declaring class. For static methods, it is the same as the declared type.
*/
public MethodType getInvocationType() {
- MethodType itype = getMethodType();
+ MethodType itype = getMethodOrFieldType();
+ if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
+ return itype.changeReturnType(clazz);
if (!isStatic())
- itype = itype.insertParameterTypes(0, clazz);
+ return itype.insertParameterTypes(0, clazz);
return itype;
}
@@ -208,9 +218,98 @@
return (flags & RECOGNIZED_MODIFIERS);
}
+ /** Return the reference kind of this member, or zero if none.
+ */
+ public byte getReferenceKind() {
+ return (byte) ((flags >>> MN_REFERENCE_KIND_SHIFT) & MN_REFERENCE_KIND_MASK);
+ }
+ private boolean referenceKindIsConsistent() {
+ byte refKind = getReferenceKind();
+ if (refKind == REF_NONE) return isType();
+ if (isField()) {
+ assert(staticIsConsistent());
+ assert(MethodHandleNatives.refKindIsField(refKind));
+ } else if (isConstructor()) {
+ assert(refKind == REF_newInvokeSpecial || refKind == REF_invokeSpecial);
+ } else if (isMethod()) {
+ assert(staticIsConsistent());
+ assert(MethodHandleNatives.refKindIsMethod(refKind));
+ if (clazz.isInterface())
+ assert(refKind == REF_invokeInterface ||
+ refKind == REF_invokeVirtual && isObjectPublicMethod());
+ } else {
+ assert(false);
+ }
+ return true;
+ }
+ private boolean isObjectPublicMethod() {
+ if (clazz == Object.class) return true;
+ MethodType mtype = getMethodType();
+ if (name.equals("toString") && mtype.returnType() == String.class && mtype.parameterCount() == 0)
+ return true;
+ if (name.equals("hashCode") && mtype.returnType() == int.class && mtype.parameterCount() == 0)
+ return true;
+ if (name.equals("equals") && mtype.returnType() == boolean.class && mtype.parameterCount() == 1 && mtype.parameterType(0) == Object.class)
+ return true;
+ return false;
+ }
+ /*non-public*/ boolean referenceKindIsConsistentWith(int originalRefKind) {
+ int refKind = getReferenceKind();
+ if (refKind == originalRefKind) return true;
+ switch (originalRefKind) {
+ case REF_invokeInterface:
+ // Looking up an interface method, can get (e.g.) Object.hashCode
+ assert(refKind == REF_invokeVirtual ||
+ refKind == REF_invokeSpecial) : this;
+ return true;
+ case REF_invokeVirtual:
+ case REF_newInvokeSpecial:
+ // Looked up a virtual, can get (e.g.) final String.hashCode.
+ assert(refKind == REF_invokeSpecial) : this;
+ return true;
+ }
+ assert(false) : this;
+ return true;
+ }
+ private boolean staticIsConsistent() {
+ byte refKind = getReferenceKind();
+ return MethodHandleNatives.refKindIsStatic(refKind) == isStatic() || getModifiers() == 0;
+ }
+ private boolean vminfoIsConsistent() {
+ byte refKind = getReferenceKind();
+ assert(isResolved()); // else don't call
+ Object vminfo = MethodHandleNatives.getMemberVMInfo(this);
+ assert(vminfo instanceof Object[]);
+ long vmindex = (Long) ((Object[])vminfo)[0];
+ Object vmtarget = ((Object[])vminfo)[1];
+ if (MethodHandleNatives.refKindIsField(refKind)) {
+ assert(vmindex >= 0) : vmindex + ":" + this;
+ assert(vmtarget instanceof Class);
+ } else {
+ if (MethodHandleNatives.refKindDoesDispatch(refKind))
+ assert(vmindex >= 0) : vmindex + ":" + this;
+ else
+ assert(vmindex < 0) : vmindex;
+ assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
+ }
+ return true;
+ }
+
+ private MemberName changeReferenceKind(byte refKind, byte oldKind) {
+ assert(getReferenceKind() == oldKind);
+ assert(MethodHandleNatives.refKindIsValid(refKind));
+ flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
+// if (isConstructor() && refKind != REF_newInvokeSpecial)
+// flags += (IS_METHOD - IS_CONSTRUCTOR);
+// else if (refKind == REF_newInvokeSpecial && isMethod())
+// flags += (IS_CONSTRUCTOR - IS_METHOD);
+ return this;
+ }
+
private void setFlags(int flags) {
this.flags = flags;
assert(testAnyFlags(ALL_KINDS));
+ assert(referenceKindIsConsistent());
}
private boolean testFlags(int mask, int value) {
@@ -223,6 +322,17 @@
return !testFlags(mask, 0);
}
+ /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
+ public boolean isMethodHandleInvoke() {
+ final int bits = Modifier.NATIVE | Modifier.FINAL;
+ final int negs = Modifier.STATIC;
+ if (testFlags(bits | negs, bits) &&
+ clazz == MethodHandle.class) {
+ return name.equals("invoke") || name.equals("invokeExact");
+ }
+ return false;
+ }
+
/** Utility method to query the modifier flags of this member. */
public boolean isStatic() {
return Modifier.isStatic(flags);
@@ -243,10 +353,22 @@
public boolean isFinal() {
return Modifier.isFinal(flags);
}
+ /** Utility method to query whether this member or its defining class is final. */
+ public boolean canBeStaticallyBound() {
+ return Modifier.isFinal(flags | clazz.getModifiers());
+ }
+ /** Utility method to query the modifier flags of this member. */
+ public boolean isVolatile() {
+ return Modifier.isVolatile(flags);
+ }
/** Utility method to query the modifier flags of this member. */
public boolean isAbstract() {
return Modifier.isAbstract(flags);
}
+ /** Utility method to query the modifier flags of this member. */
+ public boolean isNative() {
+ return Modifier.isNative(flags);
+ }
// let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
// unofficial modifier flags, used by HotSpot:
@@ -279,15 +401,12 @@
IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
IS_FIELD = MN_IS_FIELD, // field
IS_TYPE = MN_IS_TYPE; // nested type
- static final int // for MethodHandleNatives.getMembers
- SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
- SEARCH_INTERFACES = MN_SEARCH_INTERFACES;
static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
- static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
+ static final int SEARCH_ALL_SUPERS = MN_SEARCH_SUPERCLASSES | MN_SEARCH_INTERFACES;
/** Utility method to query whether this member is a method or constructor. */
public boolean isInvocable() {
@@ -318,6 +437,12 @@
return !testAnyFlags(ALL_ACCESS);
}
+ /** Utility method to query whether this member is accessible from a given lookup class. */
+ public boolean isAccessibleFrom(Class<?> lookupClass) {
+ return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
+ lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE);
+ }
+
/** Initialize a query. It is not resolved. */
private void init(Class<?> defClass, String name, Object type, int flags) {
// defining class is allowed to be null (for a naked name/type pair)
@@ -328,7 +453,7 @@
this.name = name;
this.type = type;
setFlags(flags);
- assert(!isResolved());
+ assert(this.resolution == null); // nobody should have touched this yet
}
private void expandFromVM() {
@@ -339,39 +464,94 @@
}
// Capturing information from the Core Reflection API:
- private static int flagsMods(int flags, int mods) {
+ private static int flagsMods(int flags, int mods, byte refKind) {
assert((flags & RECOGNIZED_MODIFIERS) == 0);
assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
- return flags | mods;
+ assert((refKind & ~MN_REFERENCE_KIND_MASK) == 0);
+ return flags | mods | (refKind << MN_REFERENCE_KIND_SHIFT);
}
/** Create a name for the given reflected method. The resulting name will be in a resolved state. */
public MemberName(Method m) {
- Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
- init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
+ this(m, false);
+ }
+ @SuppressWarnings("LeakingThisInConstructor")
+ public MemberName(Method m, boolean wantSpecial) {
+ m.getClass(); // NPE check
// fill in vmtarget, vmindex while we have m in hand:
MethodHandleNatives.init(this, m);
- assert(isResolved());
+ assert(isResolved() && this.clazz != null);
+ this.name = m.getName();
+ if (this.type == null)
+ this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
+ if (wantSpecial) {
+ if (getReferenceKind() == REF_invokeVirtual)
+ changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+ }
+ }
+ public MemberName asSpecial() {
+ switch (getReferenceKind()) {
+ case REF_invokeSpecial: return this;
+ case REF_invokeVirtual: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
+ case REF_newInvokeSpecial: return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
+ }
+ throw new IllegalArgumentException(this.toString());
+ }
+ public MemberName asConstructor() {
+ switch (getReferenceKind()) {
+ case REF_invokeSpecial: return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial);
+ case REF_newInvokeSpecial: return this;
+ }
+ throw new IllegalArgumentException(this.toString());
}
/** Create a name for the given reflected constructor. The resulting name will be in a resolved state. */
+ @SuppressWarnings("LeakingThisInConstructor")
public MemberName(Constructor<?> ctor) {
- Object[] typeInfo = { void.class, ctor.getParameterTypes() };
- init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
+ ctor.getClass(); // NPE check
// fill in vmtarget, vmindex while we have ctor in hand:
MethodHandleNatives.init(this, ctor);
- assert(isResolved());
+ assert(isResolved() && this.clazz != null);
+ this.name = CONSTRUCTOR_NAME;
+ if (this.type == null)
+ this.type = new Object[] { void.class, ctor.getParameterTypes() };
}
- /** Create a name for the given reflected field. The resulting name will be in a resolved state. */
+ /** Create a name for the given reflected field. The resulting name will be in a resolved state.
+ */
public MemberName(Field fld) {
- init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
+ this(fld, false);
+ }
+ @SuppressWarnings("LeakingThisInConstructor")
+ public MemberName(Field fld, boolean makeSetter) {
+ fld.getClass(); // NPE check
// fill in vmtarget, vmindex while we have fld in hand:
MethodHandleNatives.init(this, fld);
- assert(isResolved());
+ assert(isResolved() && this.clazz != null);
+ this.name = fld.getName();
+ this.type = fld.getType();
+ assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+ byte refKind = this.getReferenceKind();
+ assert(refKind == (isStatic() ? REF_getStatic : REF_getField));
+ if (makeSetter) {
+ changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind);
+ }
+ }
+ public boolean isGetter() {
+ return MethodHandleNatives.refKindIsGetter(getReferenceKind());
+ }
+ public boolean isSetter() {
+ return MethodHandleNatives.refKindIsSetter(getReferenceKind());
+ }
+ public MemberName asSetter() {
+ byte refKind = getReferenceKind();
+ assert(MethodHandleNatives.refKindIsGetter(refKind));
+ assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField));
+ byte setterRefKind = (byte)(refKind + (REF_putField - REF_getField));
+ return clone().changeReferenceKind(setterRefKind, refKind);
}
/** Create a name for the given class. The resulting name will be in a resolved state. */
public MemberName(Class<?> type) {
- init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
- vmindex = 0; // isResolved
- assert(isResolved());
+ init(type.getDeclaringClass(), type.getSimpleName(), type,
+ flagsMods(IS_TYPE, type.getModifiers(), REF_NONE));
+ initResolved(true);
}
// bare-bones constructor; the JVM will fill it in
@@ -386,41 +566,89 @@
}
}
- // %%% define equals/hashcode?
+ /** Get the definition of this member name.
+ * This may be in a super-class of the declaring class of this member.
+ */
+ public MemberName getDefinition() {
+ if (!isResolved()) throw new IllegalStateException("must be resolved: "+this);
+ if (isType()) return this;
+ MemberName res = this.clone();
+ res.clazz = null;
+ res.type = null;
+ res.name = null;
+ res.resolution = res;
+ res.expandFromVM();
+ assert(res.getName().equals(this.getName()));
+ return res;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(clazz, flags, name, getType());
+ }
+ @Override
+ public boolean equals(Object that) {
+ return (that instanceof MemberName && this.equals((MemberName)that));
+ }
+
+ /** Decide if two member names have exactly the same symbolic content.
+ * Does not take into account any actual class members, so even if
+ * two member names resolve to the same actual member, they may
+ * be distinct references.
+ */
+ public boolean equals(MemberName that) {
+ if (this == that) return true;
+ if (that == null) return false;
+ return this.clazz == that.clazz
+ && this.flags == that.flags
+ && Objects.equals(this.name, that.name)
+ && Objects.equals(this.getType(), that.getType());
+ }
// Construction from symbolic parts, for queries:
- /** Create a field or type name from the given components: Declaring class, name, type, modifiers.
+ /** Create a field or type name from the given components: Declaring class, name, type, reference kind.
* The declaring class may be supplied as null if this is to be a bare name and type.
* The resulting name will in an unresolved state.
*/
- public MemberName(Class<?> defClass, String name, Class<?> type, int modifiers) {
- init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
+ public MemberName(Class<?> defClass, String name, Class<?> type, byte refKind) {
+ init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
+ initResolved(false);
}
/** Create a field or type name from the given components: Declaring class, name, type.
* The declaring class may be supplied as null if this is to be a bare name and type.
* The modifier flags default to zero.
* The resulting name will in an unresolved state.
*/
- public MemberName(Class<?> defClass, String name, Class<?> type) {
- this(defClass, name, type, 0);
+ public MemberName(Class<?> defClass, String name, Class<?> type, Void unused) {
+ this(defClass, name, type, REF_NONE);
+ initResolved(false);
}
/** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
* It will be a constructor if and only if the name is {@code "<init>"}.
* The declaring class may be supplied as null if this is to be a bare name and type.
+ * The last argument is optional, a boolean which requests REF_invokeSpecial.
* The resulting name will in an unresolved state.
*/
- public MemberName(Class<?> defClass, String name, MethodType type, int modifiers) {
- int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
- init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
+ public MemberName(Class<?> defClass, String name, MethodType type, byte refKind) {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ int flags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+ init(defClass, name, type, flagsMods(flags, 0, refKind));
+ initResolved(false);
}
- /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
- * It will be a constructor if and only if the name is {@code "<init>"}.
- * The declaring class may be supplied as null if this is to be a bare name and type.
- * The modifier flags default to zero.
- * The resulting name will in an unresolved state.
+// /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers.
+// * It will be a constructor if and only if the name is {@code "<init>"}.
+// * The declaring class may be supplied as null if this is to be a bare name and type.
+// * The modifier flags default to zero.
+// * The resulting name will in an unresolved state.
+// */
+// public MemberName(Class<?> defClass, String name, MethodType type, Void unused) {
+// this(defClass, name, type, REF_NONE);
+// }
+
+ /** Query whether this member name is resolved to a non-static, non-final method.
*/
- public MemberName(Class<?> defClass, String name, MethodType type) {
- this(defClass, name, type, 0);
+ public boolean hasReceiverTypeDispatch() {
+ return MethodHandleNatives.refKindDoesDispatch(getReferenceKind());
}
/** Query whether this member name is resolved.
@@ -429,15 +657,38 @@
* (Document?)
*/
public boolean isResolved() {
- return (vmindex != VM_INDEX_UNINITIALIZED);
+ return resolution == null;
+ }
+
+ private void initResolved(boolean isResolved) {
+ assert(this.resolution == null); // not initialized yet!
+ if (!isResolved)
+ this.resolution = this;
+ assert(isResolved() == isResolved);
}
- /** Query whether this member name is resolved to a non-static, non-final method.
- */
- public boolean hasReceiverTypeDispatch() {
- return (isMethod() && getVMIndex() >= 0);
+ void checkForTypeAlias() {
+ if (isInvocable()) {
+ MethodType type;
+ if (this.type instanceof MethodType)
+ type = (MethodType) this.type;
+ else
+ this.type = type = getMethodType();
+ if (type.erase() == type) return;
+ if (VerifyAccess.isTypeVisible(type, clazz)) return;
+ throw new LinkageError("bad method type alias: "+type+" not visible from "+clazz);
+ } else {
+ Class<?> type;
+ if (this.type instanceof Class<?>)
+ type = (Class<?>) this.type;
+ else
+ this.type = type = getFieldType();
+ if (VerifyAccess.isTypeVisible(type, clazz)) return;
+ throw new LinkageError("bad field type alias: "+type+" not visible from "+clazz);
+ }
}
+
/** Produce a string form of this member name.
* For types, it is simply the type's own string (as reported by {@code toString}).
* For fields, it is {@code "DeclaringClass.name/type"}.
@@ -445,6 +696,7 @@
* If the declaring class is null, the prefix {@code "DeclaringClass."} is omitted.
* If the member is unresolved, a prefix {@code "*."} is prepended.
*/
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
@Override
public String toString() {
if (isType())
@@ -464,22 +716,12 @@
} else {
buf.append(type == null ? "(*)*" : getName(type));
}
- /*
- buf.append('/');
- // key: Public, private, pRotected, sTatic, Final, sYnchronized,
- // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
- // (annotation), Enum, (unused)
- final String FIELD_MOD_CHARS = "PprTF?vt????Y?E?";
- final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
- String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
- for (int i = 0; i < modChars.length(); i++) {
- if ((flags & (1 << i)) != 0) {
- char mc = modChars.charAt(i);
- if (mc != '?')
- buf.append(mc);
- }
+ byte refKind = getReferenceKind();
+ if (refKind != REF_NONE) {
+ buf.append('/');
+ buf.append(MethodHandleNatives.refKindName(refKind));
}
- */
+ //buf.append("#").append(System.identityHashCode(this));
return buf.toString();
}
private static String getName(Object obj) {
@@ -488,19 +730,6 @@
return String.valueOf(obj);
}
- // Queries to the JVM:
- /** Document? */
- /*non-public*/ int getVMIndex() {
- if (!isResolved())
- throw newIllegalStateException("not resolved", this);
- return vmindex;
- }
-// /*non-public*/ Object getVMTarget() {
-// if (!isResolved())
-// throw newIllegalStateException("not resolved", this);
-// return vmtarget;
-// }
-
public IllegalAccessException makeAccessException(String message, Object from) {
message = message + ": "+ toString();
if (from != null) message += ", from " + from;
@@ -518,14 +747,19 @@
}
public ReflectiveOperationException makeAccessException() {
String message = message() + ": "+ toString();
- if (isResolved())
- return new IllegalAccessException(message);
+ ReflectiveOperationException ex;
+ if (isResolved() || !(resolution instanceof NoSuchMethodError ||
+ resolution instanceof NoSuchFieldError))
+ ex = new IllegalAccessException(message);
else if (isConstructor())
- return new NoSuchMethodException(message);
+ ex = new NoSuchMethodException(message);
else if (isMethod())
- return new NoSuchMethodException(message);
+ ex = new NoSuchMethodException(message);
else
- return new NoSuchFieldException(message);
+ ex = new NoSuchFieldException(message);
+ if (resolution instanceof Throwable)
+ ex.initCause((Throwable) resolution);
+ return ex;
}
/** Actually making a query requires an access check. */
@@ -539,7 +773,7 @@
private Factory() { } // singleton pattern
static Factory INSTANCE = new Factory();
- private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
+ private static int ALLOWED_FLAGS = ALL_KINDS;
/// Queries
List<MemberName> getMembers(Class<?> defc,
@@ -573,14 +807,14 @@
// JVM returned to us with an intentional overflow!
totalCount += buf.length;
int excess = bufCount - buf.length;
- if (bufs == null) bufs = new ArrayList<MemberName[]>(1);
+ if (bufs == null) bufs = new ArrayList<>(1);
bufs.add(buf);
int len2 = buf.length;
len2 = Math.max(len2, excess);
len2 = Math.max(len2, totalCount / 4);
buf = newMemberBuffer(Math.min(BUF_MAX, len2));
}
- ArrayList<MemberName> result = new ArrayList<MemberName>(totalCount);
+ ArrayList<MemberName> result = new ArrayList<>(totalCount);
if (bufs != null) {
for (MemberName[] buf0 : bufs) {
Collections.addAll(result, buf0);
@@ -599,43 +833,29 @@
}
return result;
}
- boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> lookupClass) {
- if (m.name == null || m.type == null) { // find unique non-overloaded name
- Class<?> defc = m.getDeclaringClass();
- List<MemberName> choices = null;
- if (m.isMethod())
- choices = getMethods(defc, searchSupers, m.name, (MethodType) m.type, lookupClass);
- else if (m.isConstructor())
- choices = getConstructors(defc, lookupClass);
- else if (m.isField())
- choices = getFields(defc, searchSupers, m.name, (Class<?>) m.type, lookupClass);
- //System.out.println("resolving "+m+" to "+choices);
- if (choices == null || choices.size() != 1)
- return false;
- if (m.name == null) m.name = choices.get(0).name;
- if (m.type == null) m.type = choices.get(0).type;
- }
- MethodHandleNatives.resolve(m, lookupClass);
- if (m.isResolved()) return true;
- int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
- String matchSig = m.getSignature();
- MemberName[] buf = { m };
- int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
- m.getName(), matchSig, matchFlags, lookupClass, 0, buf);
- if (n != 1) return false;
- return m.isResolved();
- }
/** Produce a resolved version of the given member.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
* Access checking is performed on behalf of the given {@code lookupClass}.
* If lookup fails or access is not permitted, null is returned.
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
*/
- public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> lookupClass) {
- MemberName result = m.clone();
- if (resolveInPlace(result, searchSupers, lookupClass))
- return result;
- return null;
+ private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass) {
+ MemberName m = ref.clone(); // JVM will side-effect the ref
+ assert(refKind == m.getReferenceKind());
+ try {
+ m = MethodHandleNatives.resolve(m, lookupClass);
+ m.checkForTypeAlias();
+ m.resolution = null;
+ } catch (LinkageError ex) {
+ // JVM reports that the "bytecode behavior" would get an error
+ assert(!m.isResolved());
+ m.resolution = ex;
+ return m;
+ }
+ assert(m.referenceKindIsConsistent());
+ m.initResolved(true);
+ assert(m.vminfoIsConsistent());
+ return m;
}
/** Produce a resolved version of the given member.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
@@ -645,16 +865,29 @@
*/
public
<NoSuchMemberException extends ReflectiveOperationException>
- MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
+ MemberName resolveOrFail(byte refKind, MemberName m, Class<?> lookupClass,
Class<NoSuchMemberException> nsmClass)
throws IllegalAccessException, NoSuchMemberException {
- MemberName result = resolveOrNull(m, searchSupers, lookupClass);
- if (result != null)
+ MemberName result = resolve(refKind, m, lookupClass);
+ if (result.isResolved())
return result;
- ReflectiveOperationException ex = m.makeAccessException();
+ ReflectiveOperationException ex = result.makeAccessException();
if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex;
throw nsmClass.cast(ex);
}
+ /** Produce a resolved version of the given member.
+ * Super types are searched (for inherited members) if {@code searchSupers} is true.
+ * Access checking is performed on behalf of the given {@code lookupClass}.
+ * If lookup fails or access is not permitted, return null.
+ * Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
+ */
+ public
+ MemberName resolveOrNull(byte refKind, MemberName m, Class<?> lookupClass) {
+ MemberName result = resolve(refKind, m, lookupClass);
+ if (result.isResolved())
+ return result;
+ return null;
+ }
/** Return a list of all methods defined by the given class.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
* Access checking is performed on behalf of the given {@code lookupClass}.
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Fri Aug 10 16:03:28 2012 -0700
@@ -26,9 +26,13 @@
package java.lang.invoke;
-import java.util.ArrayList;
-import sun.invoke.util.ValueConversions;
+import java.util.*;
+import sun.invoke.util.*;
+import sun.misc.Unsafe;
+
import static java.lang.invoke.MethodHandleStatics.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* A method handle is a typed, directly executable reference to an underlying method,
@@ -208,8 +212,8 @@
* refers directly to an associated {@code CONSTANT_Methodref},
* {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
* constant pool entry.
- * (For more details on method handle constants,
- * see the <a href="package-summary.html#mhcon">package summary</a>.)
+ * (For full details on method handle constants,
+ * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
* <p>
* Method handles produced by lookups or constant loads from methods or
* constructors with the variable arity modifier bit ({@code 0x0080})
@@ -224,6 +228,19 @@
* (E.g., if a non-static method handle is obtained via {@code ldc},
* the type of the receiver is the class named in the constant pool entry.)
* <p>
+ * Method handle constants are subject to the same link-time access checks
+ * their corresponding bytecode instructions, and the {@code ldc} instruction
+ * will throw corresponding linkage errors if the bytecode behaviors would
+ * throw such errors.
+ * <p>
+ * As a corollary of this, access to protected members is restricted
+ * to receivers only of the accessing class, or one of its subclasses,
+ * and the accessing class must in turn be a subclass (or package sibling)
+ * of the protected member's defining class.
+ * If a method reference refers to a protected non-static method or field
+ * of a class outside the current package, the receiver argument will
+ * be narrowed to the type of the accessing class.
+ * <p>
* When a method handle to a virtual method is invoked, the method is
* always looked up in the receiver (that is, the first argument).
* <p>
@@ -390,39 +407,8 @@
* @author John Rose, JSR 292 EG
*/
public abstract class MethodHandle {
- // { JVM internals:
-
- private byte vmentry; // adapter stub or method entry point
- //private int vmslots; // optionally, hoist type.form.vmslots
- /*non-public*/ Object vmtarget; // VM-specific, class-specific target value
-
- // TO DO: vmtarget should be invisible to Java, since the JVM puts internal
- // managed pointers into it. Making it visible exposes it to debuggers,
- // which can cause errors when they treat the pointer as an Object.
-
- // These two dummy fields are present to force 'I' and 'J' signatures
- // into this class's constant pool, so they can be transferred
- // to vmentry when this class is loaded.
- static final int INT_FIELD = 0;
- static final long LONG_FIELD = 0;
-
- // vmentry (a void* field) is used *only* by the JVM.
- // The JVM adjusts its type to int or long depending on system wordsize.
- // Since it is statically typed as neither int nor long, it is impossible
- // to use this field from Java bytecode. (Please don't try to, either.)
-
- // The vmentry is an assembly-language stub which is jumped to
- // immediately after the method type is verified.
- // For a direct MH, this stub loads the vmtarget's entry point
- // and jumps to it.
-
- // } End of JVM internals.
-
static { MethodHandleImpl.initStatics(); }
- // interface MethodHandle<R throws X extends Exception,A...>
- // { MethodType<R throws X,A...> type(); public R invokeExact(A...) throws X; }
-
/**
* Internal marker interface which distinguishes (to the Java compiler)
* those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
@@ -431,7 +417,9 @@
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@interface PolymorphicSignature { }
- private MethodType type;
+ private final MethodType type;
+ /*private*/ final LambdaForm form;
+ // form is not private so that invokers can easily fetch it
/**
* Reports the type of this method handle.
@@ -448,9 +436,13 @@
* the {@code java.lang.invoke} package.
*/
// @param type type (permanently assigned) of the new method handle
- /*non-public*/ MethodHandle(MethodType type) {
- type.getClass(); // elicit NPE
+ /*non-public*/ MethodHandle(MethodType type, LambdaForm form) {
+ type.getClass(); // explicit NPE
+ form.getClass(); // explicit NPE
this.type = type;
+ this.form = form;
+
+ form.prepare(); // TO DO: Try to delay this step until just before invocation.
}
/**
@@ -506,6 +498,46 @@
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
/**
+ * Private method for trusted invocation of a method handle respecting simplified signatures.
+ * Type mismatches will not throw {@code WrongMethodTypeException}, but could crash the JVM.
+ * <p>
+ * The caller signature is restricted to the following basic types:
+ * Object, int, long, float, double, and void return.
+ * <p>
+ * The caller is responsible for maintaining type correctness by ensuring
+ * that the each outgoing argument value is a member of the range of the corresponding
+ * callee argument type.
+ * (The caller should therefore issue appropriate casts and integer narrowing
+ * operations on outgoing argument values.)
+ * The caller can assume that the incoming result value is part of the range
+ * of the callee's return type.
+ */
+ /*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable;
+
+ /*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable;
+
+ /**
+ * Private method for trusted invocation of a MemberName of kind {@code REF_invokeStatic}.
+ * The caller signature is restricted to basic types as with {@code invokeBasic}.
+ * The trailing (not leading) argument must be a MemberName.
+ */
+ /*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable;
+
+ /**
+ * Private method for trusted invocation of a MemberName of kind {@code REF_invokeSpecial}.
+ * The caller signature is restricted to basic types as with {@code invokeBasic}.
+ * The trailing (not leading) argument must be a MemberName.
+ */
+ /*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable;
+
+ /**
+ * Private method for trusted invocation of a MemberName of kind {@code REF_invokeInterface}.
+ * The caller signature is restricted to basic types as with {@code invokeBasic}.
+ * The trailing (not leading) argument must be a MemberName.
+ */
+ /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
+
+ /**
* Performs a variable arity invocation, passing the arguments in the given array
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
* which mentions only the type {@code Object}, and whose arity is the length
@@ -557,6 +589,7 @@
*/
public Object invokeWithArguments(Object... arguments) throws Throwable {
int argc = arguments == null ? 0 : arguments.length;
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
MethodType type = type();
if (type.parameterCount() != argc || isVarargsCollector()) {
// simulate invoke
@@ -690,7 +723,7 @@
if (!type.isConvertibleTo(newType)) {
throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
}
- return MethodHandleImpl.convertArguments(this, newType, 1);
+ return convertArguments(newType);
}
/**
@@ -772,7 +805,8 @@
*/
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
asSpreaderChecks(arrayType, arrayLength);
- return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
+ int spreadArgPos = type.parameterCount() - arrayLength;
+ return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
}
private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
@@ -790,7 +824,7 @@
}
}
if (sawProblem) {
- ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>(type().parameterList());
+ ArrayList<Class<?>> ptypes = new ArrayList<>(type().parameterList());
for (int i = nargs - arrayLength; i < nargs; i++) {
ptypes.set(i, arrayElement);
}
@@ -885,8 +919,12 @@
*/
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
asCollectorChecks(arrayType, arrayLength);
+ int collectArgPos = type().parameterCount()-1;
+ MethodHandle target = this;
+ if (arrayType != type().parameterType(collectArgPos))
+ target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
- return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
+ return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
}
// private API: return true if last param exactly matches arrayType
@@ -1056,7 +1094,7 @@
boolean lastMatch = asCollectorChecks(arrayType, 0);
if (isVarargsCollector() && lastMatch)
return this;
- return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
+ return MethodHandleImpl.makeVarargsCollector(this, arrayType);
}
/**
@@ -1155,14 +1193,13 @@
*/
public MethodHandle bindTo(Object x) {
Class<?> ptype;
- if (type().parameterCount() == 0 ||
- (ptype = type().parameterType(0)).isPrimitive())
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ MethodType type = type();
+ if (type.parameterCount() == 0 ||
+ (ptype = type.parameterType(0)).isPrimitive())
throw newIllegalArgumentException("no leading reference parameter", x);
- x = MethodHandles.checkValue(ptype, x);
- // Cf. MethodHandles.insertArguments for the following logic:
- MethodHandle bmh = MethodHandleImpl.bindReceiver(this, x);
- if (bmh != null) return bmh;
- return MethodHandleImpl.bindArgument(this, 0, x);
+ x = ptype.cast(x); // throw CCE if needed
+ return bindReceiver(x);
}
/**
@@ -1183,11 +1220,178 @@
@Override
public String toString() {
if (DEBUG_METHOD_HANDLE_NAMES) return debugString();
+ return standardString();
+ }
+ String standardString() {
return "MethodHandle"+type;
}
+ String debugString() {
+ return standardString()+"="+internalForm()+internalValues();
+ }
+
+ //// Implementation methods.
+ //// Sub-classes can override these default implementations.
+ //// All these methods assume arguments are already validated.
+
+ // Other transforms to do: convert, explicitCast, permute, drop, filter, fold, GWT, catch
+
+ /*non-public*/
+ MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
+ if (!member.isVarargs()) return this;
+ int argc = type().parameterCount();
+ if (argc != 0) {
+ Class<?> arrayType = type().parameterType(argc-1);
+ if (arrayType.isArray()) {
+ return MethodHandleImpl.makeVarargsCollector(this, arrayType);
+ }
+ }
+ throw member.makeAccessException("cannot make variable arity", null);
+ }
+ /*non-public*/
+ MethodHandle viewAsType(MethodType newType) {
+ // No actual conversions, just a new view of the same method.
+ if (!type.isViewableAs(newType))
+ throw new InternalError();
+ return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
+ }
+
+ // Decoding
+
+ /*non-public*/
+ LambdaForm internalForm() {
+ return form;
+ }
+
+ /*non-public*/
+ MemberName internalMemberName() {
+ return null; // DMH returns DMH.member
+ }
+
+ /*non-public*/
+ Object internalValues() {
+ return "";
+ }
+
+ //// Method handle implementation methods.
+ //// Sub-classes can override these default implementations.
+ //// All these methods assume arguments are already validated.
+
+ /*non-public*/ MethodHandle convertArguments(MethodType newType) {
+ // Override this if it can be improved.
+ return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
+ }
+
+ /*non-public*/
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ // Override this if it can be improved.
+ return rebind().bindArgument(pos, basicType, value);
+ }
+
+ /*non-public*/
+ MethodHandle bindReceiver(Object receiver) {
+ // Override this if it can be improved.
+ return bindArgument(0, 'L', receiver);
+ }
+
+ /*non-public*/
+ MethodHandle bindImmediate(int pos, char basicType, Object value) {
+ // Bind an immediate value to a position in the arguments.
+ // This means, elide the respective argument,
+ // and replace all references to it in NamedFunction args with the specified value.
+
+ // CURRENT RESTRICTIONS
+ // * only for pos 0 and UNSAFE (position is adjusted in MHImpl to make API usable for others)
+ assert pos == 0 && basicType == 'L' && value instanceof Unsafe;
+ MethodType type2 = type.dropParameterTypes(pos, pos + 1); // adjustment: ignore receiver!
+ LambdaForm form2 = form.bindImmediate(pos + 1, basicType, value); // adjust pos to form-relative pos
+ return copyWith(type2, form2);
+ }
/*non-public*/
- String debugString() {
- return getNameString(this);
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ throw new InternalError("copyWith: " + this.getClass());
+ }
+
+ /*non-public*/
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ // Override this if it can be improved.
+ return rebind().dropArguments(srcType, pos, drops);
+ }
+
+ /*non-public*/
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ // Override this if it can be improved.
+ return rebind().permuteArguments(newType, reorder);
+ }
+
+ /*non-public*/
+ MethodHandle rebind() {
+ // Bind 'this' into a new invoker, of the known class BMH.
+ MethodType type2 = type();
+ LambdaForm form2 = reinvokerForm(type2.basicType());
+ // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
+ return BoundMethodHandle.bindSingle(type2, form2, this);
+ }
+
+ /*non-public*/
+ MethodHandle reinvokerTarget() {
+ throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
+ }
+
+ /** Create a LF which simply reinvokes a target of the given basic type.
+ * The target MH must override {@link #reinvokerTarget} to provide the target.
+ */
+ static LambdaForm reinvokerForm(MethodType mtype) {
+ mtype = mtype.basicType();
+ LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
+ if (reinvoker != null) return reinvoker;
+ MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
+ final int THIS_BMH = 0;
+ final int ARG_BASE = 1;
+ final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
+ int nameCursor = ARG_LIMIT;
+ final int NEXT_MH = nameCursor++;
+ final int REINVOKE = nameCursor++;
+ LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
+ names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
+ Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
+ targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
+ names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
+ return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
+ }
+
+ private static final LambdaForm.NamedFunction NF_reinvokerTarget;
+ static {
+ try {
+ NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
+ .getDeclaredMethod("reinvokerTarget"));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ /**
+ * Replace the old lambda form of this method handle with a new one.
+ * The new one must be functionally equivalent to the old one.
+ * Threads may continue running the old form indefinitely,
+ * but it is likely that the new one will be preferred for new executions.
+ * Use with discretion.
+ * @param newForm
+ */
+ /*non-public*/
+ void updateForm(LambdaForm newForm) {
+ if (form == newForm) return;
+ // ISSUE: Should we have a memory fence here?
+ UNSAFE.putObject(this, FORM_OFFSET, newForm);
+ this.form.prepare(); // as in MethodHandle.<init>
+ }
+
+ private static final long FORM_OFFSET;
+ static {
+ try {
+ FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class.getDeclaredField("form"));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
}
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Aug 10 16:03:28 2012 -0700
@@ -26,17 +26,14 @@
package java.lang.invoke;
import sun.invoke.util.VerifyType;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import sun.invoke.empty.Empty;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
-import sun.misc.Unsafe;
+import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
@@ -47,670 +44,471 @@
/*non-public*/ abstract class MethodHandleImpl {
/// Factory methods to create method handles:
- private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
+ static void initStatics() {
+ // Trigger selected static initializations.
+ MemberName.Factory.INSTANCE.getClass();
+ }
- static void initStatics() {
- // Trigger preceding sequence.
+ static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
+ if (!arrayClass.isArray())
+ throw newIllegalArgumentException("not an array: "+arrayClass);
+ MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
+ MethodType srcType = accessor.type().erase();
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(1, lambdaType);
+ Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
+ names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
+ LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
+ MethodHandle mh = new SimpleMethodHandle(srcType, form);
+ if (ArrayAccessor.needCast(arrayClass)) {
+ mh = mh.bindTo(arrayClass);
+ }
+ mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
+ return mh;
}
- /** Look up a given method.
- * Callable only from sun.invoke and related packages.
- * <p>
- * The resulting method handle type will be of the given type,
- * with a receiver type {@code rcvc} prepended if the member is not static.
- * <p>
- * Access checks are made as of the given lookup class.
- * In particular, if the method is protected and {@code defc} is in a
- * different package from the lookup class, then {@code rcvc} must be
- * the lookup class or a subclass.
- * @param token Proof that the lookup class has access to this package.
- * @param member Resolved method or constructor to call.
- * @param name Name of the desired method.
- * @param rcvc Receiver type of desired non-static method (else null)
- * @param doDispatch whether the method handle will test the receiver type
- * @param lookupClass access-check relative to this class
- * @return a direct handle to the matching method
- * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
- */
- static
- MethodHandle findMethod(MemberName method,
- boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
- MethodType mtype = method.getMethodType();
- if (!method.isStatic()) {
- // adjust the advertised receiver type to be exactly the one requested
- // (in the case of invokespecial, this will be the calling class)
- Class<?> recvType = method.getDeclaringClass();
- mtype = mtype.insertParameterTypes(0, recvType);
- }
- DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
- if (!mh.isValid())
- throw method.makeAccessException("no direct method handle", lookupClass);
- assert(mh.type() == mtype);
- if (!method.isVarargs())
- return mh;
- int argc = mtype.parameterCount();
- if (argc != 0) {
- Class<?> arrayType = mtype.parameterType(argc-1);
- if (arrayType.isArray())
- return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
+ static final class ArrayAccessor {
+ /// Support for array element access
+ static final HashMap<Class<?>, MethodHandle> GETTER_CACHE = new HashMap<>(); // TODO use it
+ static final HashMap<Class<?>, MethodHandle> SETTER_CACHE = new HashMap<>(); // TODO use it
+
+ static int getElementI(int[] a, int i) { return a[i]; }
+ static long getElementJ(long[] a, int i) { return a[i]; }
+ static float getElementF(float[] a, int i) { return a[i]; }
+ static double getElementD(double[] a, int i) { return a[i]; }
+ static boolean getElementZ(boolean[] a, int i) { return a[i]; }
+ static byte getElementB(byte[] a, int i) { return a[i]; }
+ static short getElementS(short[] a, int i) { return a[i]; }
+ static char getElementC(char[] a, int i) { return a[i]; }
+ static Object getElementL(Object[] a, int i) { return a[i]; }
+
+ static void setElementI(int[] a, int i, int x) { a[i] = x; }
+ static void setElementJ(long[] a, int i, long x) { a[i] = x; }
+ static void setElementF(float[] a, int i, float x) { a[i] = x; }
+ static void setElementD(double[] a, int i, double x) { a[i] = x; }
+ static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
+ static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
+ static void setElementS(short[] a, int i, short x) { a[i] = x; }
+ static void setElementC(char[] a, int i, char x) { a[i] = x; }
+ static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
+
+ static Object getElementL(Class<?> arrayClass, Object[] a, int i) { arrayClass.cast(a); return a[i]; }
+ static void setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; }
+
+ // Weakly typed wrappers of Object[] accessors:
+ static Object getElementL(Object a, int i) { return getElementL((Object[])a, i); }
+ static void setElementL(Object a, int i, Object x) { setElementL((Object[]) a, i, x); }
+ static Object getElementL(Object arrayClass, Object a, int i) { return getElementL((Class<?>) arrayClass, (Object[])a, i); }
+ static void setElementL(Object arrayClass, Object a, int i, Object x) { setElementL((Class<?>) arrayClass, (Object[])a, i, x); }
+
+ static boolean needCast(Class<?> arrayClass) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ return !elemClass.isPrimitive() && elemClass != Object.class;
}
- throw method.makeAccessException("cannot make variable arity", null);
- }
-
- static
- MethodHandle makeAllocator(MethodHandle rawConstructor) {
- MethodType rawConType = rawConstructor.type();
- Class<?> allocateClass = rawConType.parameterType(0);
- // Wrap the raw (unsafe) constructor with the allocation of a suitable object.
- assert(AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true));
- // allocator(arg...)
- // [fold]=> cookedConstructor(obj=allocate(C), arg...)
- // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...))
- MethodHandle returner = MethodHandles.identity(allocateClass);
- MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass);
- MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false);
- assert(cookedConstructor.type().equals(ctype));
- ctype = ctype.dropParameterTypes(0, 1);
- cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true);
- AllocateObject allocator = new AllocateObject(allocateClass);
- // allocate() => new C(void)
- assert(allocator.type().equals(MethodType.methodType(allocateClass)));
- ctype = ctype.dropParameterTypes(0, 1);
- MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator);
- return fold;
- }
-
- static final class AllocateObject /*<C>*/ extends BoundMethodHandle {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
-
- private final Class<?> /*<C>*/ allocateClass;
-
- // for allocation only:
- private AllocateObject(Class<?> /*<C>*/ allocateClass) {
- super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
- this.allocateClass = allocateClass;
+ static String name(Class<?> arrayClass, boolean isSetter) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ if (elemClass == null) throw new IllegalArgumentException();
+ return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
}
- @SuppressWarnings("unchecked")
- private Object /*C*/ allocate() throws InstantiationException {
- return unsafe.allocateInstance(allocateClass);
+ static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false; // FIXME: decide
+ static MethodType type(Class<?> arrayClass, boolean isSetter) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ Class<?> arrayArgClass = arrayClass;
+ if (!elemClass.isPrimitive()) {
+ arrayArgClass = Object[].class;
+ if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
+ arrayArgClass = Object.class;
+ }
+ if (!needCast(arrayClass)) {
+ return !isSetter ?
+ MethodType.methodType(elemClass, arrayArgClass, int.class) :
+ MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
+ } else {
+ Class<?> classArgClass = Class.class;
+ if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
+ classArgClass = Object.class;
+ return !isSetter ?
+ MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) :
+ MethodType.methodType(void.class, classArgClass, arrayArgClass, int.class, Object.class);
+ }
}
- static final MethodHandle ALLOCATE;
- static {
+ static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
+ Class<?> elemClass = arrayClass.getComponentType();
+ return !isSetter ?
+ MethodType.methodType(elemClass, arrayClass, int.class) :
+ MethodType.methodType(void.class, arrayClass, int.class, elemClass);
+ }
+ static MethodHandle getAccessor(Class<?> arrayClass, boolean isSetter) {
+ String name = name(arrayClass, isSetter);
+ MethodType type = type(arrayClass, isSetter);
try {
- ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0));
+ return IMPL_LOOKUP.findStatic(ArrayAccessor.class, name, type);
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
}
- static
- MethodHandle accessField(MemberName member, boolean isSetter,
- Class<?> lookupClass) {
- // Use sun. misc.Unsafe to dig up the dirt on the field.
- FieldAccessor accessor = new FieldAccessor(member, isSetter);
- return accessor;
- }
+ /**
+ * Create a JVM-level adapter method handle to conform the given method
+ * handle to the similar newType, using only pairwise argument conversions.
+ * For each argument, convert incoming argument to the exact type needed.
+ * The argument conversions allowed are casting, boxing and unboxing,
+ * integral widening or narrowing, and floating point widening or narrowing.
+ * @param srcType required call type
+ * @param target original method handle
+ * @param level which strength of conversion is allowed
+ * @return an adapter to the original handle with the desired new type,
+ * or the original target if the types are already identical
+ * or null if the adaptation cannot be made
+ */
+ static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) {
+ assert(level >= 0 && level <= 2);
+ MethodType dstType = target.type();
+ assert(dstType.parameterCount() == target.type().parameterCount());
+ if (srcType == dstType)
+ return target;
- static
- MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) {
- if (!arrayClass.isArray())
- throw newIllegalArgumentException("not an array: "+arrayClass);
- Class<?> elemClass = arrayClass.getComponentType();
- MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass);
- if (mhs == null) {
- if (!FieldAccessor.doCache(elemClass))
- return FieldAccessor.ahandle(arrayClass, isSetter);
- mhs = new MethodHandle[] {
- FieldAccessor.ahandle(arrayClass, false),
- FieldAccessor.ahandle(arrayClass, true)
- };
- if (mhs[0].type().parameterType(0) == Class.class) {
- mhs[0] = mhs[0].bindTo(elemClass);
- mhs[1] = mhs[1].bindTo(elemClass);
+ // Calculate extra arguments (temporaries) required in the names array.
+ // FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step.
+ int extra = 0;
+ for (int i = 0; i < srcType.parameterCount(); i++) {
+ Class<?> src = srcType.parameterType(i);
+ Class<?> dst = dstType.parameterType(i);
+ if (!VerifyType.isNullConversion(src, dst)) {
+ extra++;
}
- synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
- FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
}
- return mhs[isSetter ? 1 : 0];
- }
+
+ Class<?> needReturn = srcType.returnType();
+ Class<?> haveReturn = dstType.returnType();
+ boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+
+ // Now build a LambdaForm.
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(extra + 1, lambdaType);
+ int[] indexes = new int[lambdaType.parameterCount()];
- static final class FieldAccessor /*<C,V>*/ extends BoundMethodHandle {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- final Object base; // for static refs only
- final long offset;
- final String name;
+ MethodType midType = dstType;
+ for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
+ Class<?> src = srcType.parameterType(i);
+ Class<?> dst = midType.parameterType(i);
+
+ if (VerifyType.isNullConversion(src, dst)) {
+ // do nothing: difference is trivial
+ indexes[i] = argIndex;
+ continue;
+ }
+
+ // Work the current type backward toward the desired caller type:
+ midType = midType.changeParameterType(i, src);
- FieldAccessor(MemberName field, boolean isSetter) {
- super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
- this.offset = (long) field.getVMIndex();
- this.name = field.getName();
- this.base = staticBase(field);
+ // Tricky case analysis follows.
+ MethodHandle fn = null;
+ if (src.isPrimitive()) {
+ if (dst.isPrimitive()) {
+ fn = ValueConversions.convertPrimitive(src, dst);
+ } else {
+ Wrapper w = Wrapper.forPrimitiveType(src);
+ MethodHandle boxMethod = ValueConversions.box(w);
+ if (dst == w.wrapperType())
+ fn = boxMethod;
+ else
+ fn = boxMethod.asType(MethodType.methodType(dst, src));
+ }
+ } else {
+ if (dst.isPrimitive()) {
+ // Caller has boxed a primitive. Unbox it for the target.
+ Wrapper w = Wrapper.forPrimitiveType(dst);
+ if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) {
+ fn = ValueConversions.unbox(dst);
+ } else if (src == Object.class || !Wrapper.isWrapperType(src)) {
+ // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int
+ // must include additional conversions
+ // src must be examined at runtime, to detect Byte, Character, etc.
+ MethodHandle unboxMethod = (level == 1
+ ? ValueConversions.unbox(dst)
+ : ValueConversions.unboxCast(dst));
+ fn = unboxMethod;
+ } else {
+ // Example: Byte->int
+ // Do this by reformulating the problem to Byte->byte.
+ Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
+ MethodHandle unbox = ValueConversions.unbox(srcPrim);
+ // Compose the two conversions. FIXME: should make two Names for this job
+ fn = unbox.asType(MethodType.methodType(dst, src));
+ }
+ } else {
+ // Simple reference conversion.
+ // Note: Do not check for a class hierarchy relation
+ // between src and dst. In all cases a 'null' argument
+ // will pass the cast conversion.
+ fn = ValueConversions.cast(dst);
+ }
+ }
+ names[tmpIndex] = new Name(fn, names[argIndex]);
+ indexes[i] = tmpIndex;
+ tmpIndex++;
}
- @Override
- String debugString() { return addTypeString(name, this); }
-
- int getFieldI(Object /*C*/ obj) { return unsafe.getInt(obj, offset); }
- void setFieldI(Object /*C*/ obj, int x) { unsafe.putInt(obj, offset, x); }
- long getFieldJ(Object /*C*/ obj) { return unsafe.getLong(obj, offset); }
- void setFieldJ(Object /*C*/ obj, long x) { unsafe.putLong(obj, offset, x); }
- float getFieldF(Object /*C*/ obj) { return unsafe.getFloat(obj, offset); }
- void setFieldF(Object /*C*/ obj, float x) { unsafe.putFloat(obj, offset, x); }
- double getFieldD(Object /*C*/ obj) { return unsafe.getDouble(obj, offset); }
- void setFieldD(Object /*C*/ obj, double x) { unsafe.putDouble(obj, offset, x); }
- boolean getFieldZ(Object /*C*/ obj) { return unsafe.getBoolean(obj, offset); }
- void setFieldZ(Object /*C*/ obj, boolean x) { unsafe.putBoolean(obj, offset, x); }
- byte getFieldB(Object /*C*/ obj) { return unsafe.getByte(obj, offset); }
- void setFieldB(Object /*C*/ obj, byte x) { unsafe.putByte(obj, offset, x); }
- short getFieldS(Object /*C*/ obj) { return unsafe.getShort(obj, offset); }
- void setFieldS(Object /*C*/ obj, short x) { unsafe.putShort(obj, offset, x); }
- char getFieldC(Object /*C*/ obj) { return unsafe.getChar(obj, offset); }
- void setFieldC(Object /*C*/ obj, char x) { unsafe.putChar(obj, offset, x); }
- Object /*V*/ getFieldL(Object /*C*/ obj) { return unsafe.getObject(obj, offset); }
- void setFieldL(Object /*C*/ obj, Object /*V*/ x) { unsafe.putObject(obj, offset, x); }
- // cast (V) is OK here, since we wrap convertArguments around the MH.
-
- static Object staticBase(final MemberName field) {
- if (!field.isStatic()) return null;
- return AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- try {
- Class c = field.getDeclaringClass();
- // FIXME: Should not have to create 'f' to get this value.
- java.lang.reflect.Field f = c.getDeclaredField(field.getName());
- return unsafe.staticFieldBase(f);
- } catch (NoSuchFieldException ee) {
- throw uncaughtException(ee);
- }
- }
- });
+ if (retConv) {
+ MethodHandle adjustReturn;
+ if (haveReturn == void.class) {
+ // synthesize a zero value for the given void
+ Object zero = Wrapper.forBasicType(needReturn).zero();
+ adjustReturn = MethodHandles.constant(needReturn, zero);
+ } else {
+ MethodHandle identity = MethodHandles.identity(needReturn);
+ MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
+ adjustReturn = makePairwiseConvert(identity, needConversion, level);
+ }
+ target = makeCollectArguments(adjustReturn, target, 0, false);
}
- int getStaticI() { return unsafe.getInt(base, offset); }
- void setStaticI(int x) { unsafe.putInt(base, offset, x); }
- long getStaticJ() { return unsafe.getLong(base, offset); }
- void setStaticJ(long x) { unsafe.putLong(base, offset, x); }
- float getStaticF() { return unsafe.getFloat(base, offset); }
- void setStaticF(float x) { unsafe.putFloat(base, offset, x); }
- double getStaticD() { return unsafe.getDouble(base, offset); }
- void setStaticD(double x) { unsafe.putDouble(base, offset, x); }
- boolean getStaticZ() { return unsafe.getBoolean(base, offset); }
- void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); }
- byte getStaticB() { return unsafe.getByte(base, offset); }
- void setStaticB(byte x) { unsafe.putByte(base, offset, x); }
- short getStaticS() { return unsafe.getShort(base, offset); }
- void setStaticS(short x) { unsafe.putShort(base, offset, x); }
- char getStaticC() { return unsafe.getChar(base, offset); }
- void setStaticC(char x) { unsafe.putChar(base, offset, x); }
- @SuppressWarnings("unchecked") // (V) is for internal clarity but triggers warning
- Object /*V*/ getStaticL() { return unsafe.getObject(base, offset); }
- void setStaticL(Object /*V*/ x) { unsafe.putObject(base, offset, x); }
-
- static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) {
- String stem;
- if (!isStatic)
- stem = (!isSetter ? "getField" : "setField");
- else
- stem = (!isSetter ? "getStatic" : "setStatic");
- return stem + Wrapper.basicTypeChar(vclass);
+ // Build argument array for the call.
+ Name[] targetArgs = new Name[dstType.parameterCount()];
+ for (int i = 0; i < dstType.parameterCount(); i++) {
+ int idx = indexes[i];
+ targetArgs[i] = names[idx];
}
- static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
- MethodType type;
- if (!isStatic) {
- if (!isSetter)
- return MethodType.methodType(vclass, cclass);
- else
- return MethodType.methodType(void.class, cclass, vclass);
- } else {
- if (!isSetter)
- return MethodType.methodType(vclass);
- else
- return MethodType.methodType(void.class, vclass);
- }
+ names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+ LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(srcType, form);
+ }
+
+ static MethodHandle makeReferenceIdentity(Class<?> refType) {
+ MethodType lambdaType = MethodType.genericMethodType(1).invokerType();
+ Name[] names = arguments(1, lambdaType);
+ names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
+ LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(MethodType.methodType(refType, refType), form);
+ }
+
+ static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
+ MethodType type = target.type();
+ int last = type.parameterCount() - 1;
+ if (type.parameterType(last) != arrayType)
+ target = target.asType(type.changeParameterType(last, arrayType));
+ target = target.asFixedArity(); // make sure this attribute is turned off
+ return new AsVarargsCollector(target, target.type(), arrayType);
+ }
+
+ static class AsVarargsCollector extends MethodHandle {
+ MethodHandle target;
+ final Class<?> arrayType;
+ MethodHandle cache;
+
+ AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
+ super(type, reinvokerForm(type));
+ this.target = target;
+ this.arrayType = arrayType;
+ this.cache = target.asCollector(arrayType, 0);
}
- static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
- String name = FieldAccessor.fname(vclass, isSetter, isStatic);
- if (cclass.isPrimitive()) throw newIllegalArgumentException("primitive "+cclass);
- Class<?> ecclass = Object.class; //erase this type
- Class<?> evclass = vclass;
- if (!evclass.isPrimitive()) evclass = Object.class;
- MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic);
- MethodHandle mh;
- try {
- mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
- } catch (ReflectiveOperationException ex) {
- throw uncaughtException(ex);
- }
- if (evclass != vclass || (!isStatic && ecclass != cclass)) {
- MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
- strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
- mh = convertArguments(mh, strongType, 0);
- }
- return mh;
+
+ @Override MethodHandle reinvokerTarget() { return target; }
+
+ @Override
+ public boolean isVarargsCollector() {
+ return true;
+ }
+
+ @Override
+ public MethodHandle asFixedArity() {
+ return target;
}
- /// Support for array element access
- static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE =
- new HashMap<Class<?>, MethodHandle[]>();
- // FIXME: Cache on the classes themselves, not here.
- static boolean doCache(Class<?> elemClass) {
- if (elemClass.isPrimitive()) return true;
- ClassLoader cl = elemClass.getClassLoader();
- return cl == null || cl == ClassLoader.getSystemClassLoader();
+ @Override
+ public MethodHandle asType(MethodType newType) {
+ MethodType type = this.type();
+ int collectArg = type.parameterCount() - 1;
+ int newArity = newType.parameterCount();
+ if (newArity == collectArg+1 &&
+ type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
+ // if arity and trailing parameter are compatible, do normal thing
+ return asFixedArity().asType(newType);
+ }
+ // check cache
+ if (cache.type().parameterCount() == newArity)
+ return cache.asType(newType);
+ // build and cache a collector
+ int arrayLength = newArity - collectArg;
+ MethodHandle collector;
+ try {
+ collector = asFixedArity().asCollector(arrayType, arrayLength);
+ } catch (IllegalArgumentException ex) {
+ throw new WrongMethodTypeException("cannot build collector");
+ }
+ cache = collector;
+ return collector.asType(newType);
}
- static int getElementI(int[] a, int i) { return a[i]; }
- static void setElementI(int[] a, int i, int x) { a[i] = x; }
- static long getElementJ(long[] a, int i) { return a[i]; }
- static void setElementJ(long[] a, int i, long x) { a[i] = x; }
- static float getElementF(float[] a, int i) { return a[i]; }
- static void setElementF(float[] a, int i, float x) { a[i] = x; }
- static double getElementD(double[] a, int i) { return a[i]; }
- static void setElementD(double[] a, int i, double x) { a[i] = x; }
- static boolean getElementZ(boolean[] a, int i) { return a[i]; }
- static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
- static byte getElementB(byte[] a, int i) { return a[i]; }
- static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
- static short getElementS(short[] a, int i) { return a[i]; }
- static void setElementS(short[] a, int i, short x) { a[i] = x; }
- static char getElementC(char[] a, int i) { return a[i]; }
- static void setElementC(char[] a, int i, char x) { a[i] = x; }
- static Object getElementL(Object[] a, int i) { return a[i]; }
- static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
- static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; }
- static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; }
+
+ @Override
+ MethodHandle setVarargs(MemberName member) {
+ if (member.isVarargs()) return this;
+ return asFixedArity();
+ }
- static String aname(Class<?> aclass, boolean isSetter) {
- Class<?> vclass = aclass.getComponentType();
- if (vclass == null) throw new IllegalArgumentException();
- return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass);
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ MethodHandle mh = super.viewAsType(newType);
+ // put back the varargs bit:
+ MethodType type = mh.type();
+ int arity = type.parameterCount();
+ return mh.asVarargsCollector(type.parameterType(arity-1));
}
- static MethodType atype(Class<?> aclass, boolean isSetter) {
- Class<?> vclass = aclass.getComponentType();
- if (!isSetter)
- return MethodType.methodType(vclass, aclass, int.class);
- else
- return MethodType.methodType(void.class, aclass, int.class, vclass);
+
+ @Override
+ MemberName internalMemberName() {
+ return asFixedArity().internalMemberName();
}
- static MethodHandle ahandle(Class<?> aclass, boolean isSetter) {
- Class<?> vclass = aclass.getComponentType();
- String name = FieldAccessor.aname(aclass, isSetter);
- Class<?> caclass = null;
- if (!vclass.isPrimitive() && vclass != Object.class) {
- caclass = aclass;
- aclass = Object[].class;
- vclass = Object.class;
- }
- MethodType type = FieldAccessor.atype(aclass, isSetter);
- if (caclass != null)
- type = type.insertParameterTypes(0, Class.class);
- MethodHandle mh;
- try {
- mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
- } catch (ReflectiveOperationException ex) {
- throw uncaughtException(ex);
- }
- if (caclass != null) {
- MethodType strongType = FieldAccessor.atype(caclass, isSetter);
- mh = mh.bindTo(caclass);
- mh = convertArguments(mh, strongType, 0);
- }
- return mh;
+
+
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ return asFixedArity().bindArgument(pos, basicType, value);
+ }
+
+ @Override
+ MethodHandle bindReceiver(Object receiver) {
+ return asFixedArity().bindReceiver(receiver);
+ }
+
+ @Override
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ return asFixedArity().dropArguments(srcType, pos, drops);
+ }
+
+ @Override
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ return asFixedArity().permuteArguments(newType, reorder);
}
}
- /** Bind a predetermined first argument to the given direct method handle.
- * Callable only from MethodHandles.
- * @param token Proof that the caller has access to this package.
- * @param target Any direct method handle.
- * @param receiver Receiver (or first static method argument) to pre-bind.
- * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
- */
- static
- MethodHandle bindReceiver(MethodHandle target, Object receiver) {
- if (receiver == null) return null;
- if (target instanceof AdapterMethodHandle &&
- ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY
- ) {
- Object info = MethodHandleNatives.getTargetInfo(target);
- if (info instanceof DirectMethodHandle) {
- DirectMethodHandle dmh = (DirectMethodHandle) info;
- if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
- MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
- MethodType newType = target.type().dropParameterTypes(0, 1);
- return convertArguments(bmh, newType, bmh.type(), 0);
- }
- }
+ /** Factory method: Spread selected argument. */
+ static MethodHandle makeSpreadArguments(MethodHandle target,
+ Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ MethodType targetType = target.type();
+
+ for (int i = 0; i < spreadArgCount; i++) {
+ Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
+ if (arg == null) arg = Object.class;
+ targetType = targetType.changeParameterType(spreadArgPos + i, arg);
}
- if (target instanceof DirectMethodHandle)
- return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
- return null; // let caller try something else
- }
-
- /** Bind a predetermined argument to the given arbitrary method handle.
- * Callable only from MethodHandles.
- * @param token Proof that the caller has access to this package.
- * @param target Any method handle.
- * @param receiver Argument (which can be a boxed primitive) to pre-bind.
- * @return a suitable BoundMethodHandle
- */
- static
- MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) {
- return new BoundMethodHandle(target, receiver, argnum);
- }
+ target = target.asType(targetType);
- static MethodHandle permuteArguments(MethodHandle target,
- MethodType newType,
- MethodType oldType,
- int[] permutationOrNull) {
- assert(oldType.parameterCount() == target.type().parameterCount());
- int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
- if (permutationOrNull.length != outargs)
- throw newIllegalArgumentException("wrong number of arguments in permutation");
- // Make the individual outgoing argument types match up first.
- Class<?>[] callTypeArgs = new Class<?>[outargs];
- for (int i = 0; i < outargs; i++)
- callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
- MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
- target = convertArguments(target, callType, oldType, 0);
- assert(target != null);
- oldType = target.type();
- List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN
- List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
- List<Integer> drops = new ArrayList<Integer>(); // not tokens
- List<Integer> dups = new ArrayList<Integer>(); // not tokens
- final int TOKEN = 10; // to mark items which are symbolic only
- // state represents the argument values coming into target
- for (int i = 0; i < outargs; i++) {
- state.add(permutationOrNull[i] * TOKEN);
- }
- // goal represents the desired state
- for (int i = 0; i < inargs; i++) {
- if (state.contains(i * TOKEN)) {
- goal.add(i * TOKEN);
- } else {
- // adapter must initially drop all unused arguments
- drops.add(i);
- }
- }
- // detect duplications
- while (state.size() > goal.size()) {
- for (int i2 = 0; i2 < state.size(); i2++) {
- int arg1 = state.get(i2);
- int i1 = state.indexOf(arg1);
- if (i1 != i2) {
- // found duplicate occurrence at i2
- int arg2 = (inargs++) * TOKEN;
- state.set(i2, arg2);
- dups.add(goal.indexOf(arg1));
- goal.add(arg2);
+ MethodType srcType = targetType
+ .replaceParameterTypes(spreadArgPos, spreadArgPos + spreadArgCount, spreadArgType);
+ // Now build a LambdaForm.
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(spreadArgCount + 2, lambdaType);
+ int nameCursor = lambdaType.parameterCount();
+ int[] indexes = new int[targetType.parameterCount()];
+
+ for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) {
+ Class<?> src = lambdaType.parameterType(i);
+ if (i == spreadArgPos) {
+ // Spread the array.
+ MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
+ Name array = names[argIndex];
+ names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
+ for (int j = 0; j < spreadArgCount; i++, j++) {
+ indexes[i] = nameCursor;
+ names[nameCursor++] = new Name(aload, array, j);
}
+ } else if (i < indexes.length) {
+ indexes[i] = argIndex;
}
}
- assert(state.size() == goal.size());
- int size = goal.size();
- while (!state.equals(goal)) {
- // Look for a maximal sequence of adjacent misplaced arguments,
- // and try to rotate them into place.
- int bestRotArg = -10 * TOKEN, bestRotLen = 0;
- int thisRotArg = -10 * TOKEN, thisRotLen = 0;
- for (int i = 0; i < size; i++) {
- int arg = state.get(i);
- // Does this argument match the current run?
- if (arg == thisRotArg + TOKEN) {
- thisRotArg = arg;
- thisRotLen += 1;
- if (bestRotLen < thisRotLen) {
- bestRotLen = thisRotLen;
- bestRotArg = thisRotArg;
- }
- } else {
- // The old sequence (if any) stops here.
- thisRotLen = 0;
- thisRotArg = -10 * TOKEN;
- // But maybe a new one starts here also.
- int wantArg = goal.get(i);
- final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
- if (arg != wantArg &&
- arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
- arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
- thisRotArg = arg;
- thisRotLen = 1;
- }
- }
- }
- if (bestRotLen >= 2) {
- // Do a rotation if it can improve argument positioning
- // by at least 2 arguments. This is not always optimal,
- // but it seems to catch common cases.
- int dstEnd = state.indexOf(bestRotArg);
- int srcEnd = goal.indexOf(bestRotArg);
- int rotBy = dstEnd - srcEnd;
- int dstBeg = dstEnd - (bestRotLen - 1);
- int srcBeg = srcEnd - (bestRotLen - 1);
- assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
- // Make a span which covers both source and destination.
- int rotBeg = Math.min(dstBeg, srcBeg);
- int rotEnd = Math.max(dstEnd, srcEnd);
- int score = 0;
- for (int i = rotBeg; i <= rotEnd; i++) {
- if ((int)state.get(i) != (int)goal.get(i))
- score += 1;
- }
- List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
- Collections.rotate(rotSpan, -rotBy); // reverse direction
- for (int i = rotBeg; i <= rotEnd; i++) {
- if ((int)state.get(i) != (int)goal.get(i))
- score -= 1;
- }
- if (score >= 2) {
- // Improved at least two argument positions. Do it.
- List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
- Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
- MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
- MethodHandle nextTarget
- = AdapterMethodHandle.makeRotateArguments(rotType, target,
- rotBeg, rotSpan.size(), rotBy);
- if (nextTarget != null) {
- //System.out.println("Rot: "+rotSpan+" by "+rotBy);
- target = nextTarget;
- oldType = rotType;
- continue;
- }
- }
- // Else de-rotate, and drop through to the swap-fest.
- Collections.rotate(rotSpan, rotBy);
- }
+ assert(nameCursor == names.length-1); // leave room for the final call
+
+ // Build argument array for the call.
+ Name[] targetArgs = new Name[targetType.parameterCount()];
+ for (int i = 0; i < targetType.parameterCount(); i++) {
+ int idx = indexes[i];
+ targetArgs[i] = names[idx];
+ }
+ names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+
+ LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(srcType, form);
+ }
- // Now swap like the wind!
- List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
- for (int i = 0; i < size; i++) {
- // What argument do I want here?
- int arg = goal.get(i);
- if (arg != state.get(i)) {
- // Where is it now?
- int j = state.indexOf(arg);
- Collections.swap(ptypes, i, j);
- MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
- target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
- if (target == null) throw newIllegalArgumentException("cannot swap");
- assert(target.type() == swapType);
- oldType = swapType;
- Collections.swap(state, i, j);
- }
- }
- // One pass of swapping must finish the job.
- assert(state.equals(goal));
+ static void checkSpreadArgument(Object av, int n) {
+ if (av == null) {
+ if (n == 0) return;
+ } else if (av instanceof Object[]) {
+ int len = ((Object[])av).length;
+ if (len == n) return;
+ } else {
+ int len = java.lang.reflect.Array.getLength(av);
+ if (len == n) return;
}
- while (!dups.isEmpty()) {
- // Grab a contiguous trailing sequence of dups.
- int grab = dups.size() - 1;
- int dupArgPos = dups.get(grab), dupArgCount = 1;
- while (grab - 1 >= 0) {
- int dup0 = dups.get(grab - 1);
- if (dup0 != dupArgPos - 1) break;
- dupArgPos -= 1;
- dupArgCount += 1;
- grab -= 1;
- }
- //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size()));
- dups.subList(grab, dups.size()).clear();
- // In the new target type drop that many args from the tail:
- List<Class<?>> ptypes = oldType.parameterList();
- ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
- MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
- target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
- if (target == null)
- throw newIllegalArgumentException("cannot dup");
- oldType = target.type();
+ // fall through to error:
+ throw newIllegalArgumentException("Array is not of length "+n);
+ }
+
+ private static final NamedFunction NF_checkSpreadArgument;
+ static {
+ try {
+ NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
+ .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
+ NF_checkSpreadArgument.resolve();
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
}
- while (!drops.isEmpty()) {
- // Grab a contiguous initial sequence of drops.
- int dropArgPos = drops.get(0), dropArgCount = 1;
- while (dropArgCount < drops.size()) {
- int drop1 = drops.get(dropArgCount);
- if (drop1 != dropArgPos + dropArgCount) break;
- dropArgCount += 1;
- }
- //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount));
- drops.subList(0, dropArgCount).clear();
- List<Class<?>> dropTypes = newType.parameterList()
- .subList(dropArgPos, dropArgPos + dropArgCount);
- MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
- target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
- if (target == null) throw newIllegalArgumentException("cannot drop");
- oldType = target.type();
- }
- target = convertArguments(target, newType, oldType, 0);
- assert(target != null);
- return target;
}
- /*non-public*/ static
- MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
- MethodType oldType = target.type();
- if (oldType.equals(newType))
- return target;
- assert(level > 1 || oldType.isConvertibleTo(newType));
- MethodHandle retFilter = null;
- Class<?> oldRT = oldType.returnType();
- Class<?> newRT = newType.returnType();
- if (!VerifyType.isNullConversion(oldRT, newRT)) {
- if (oldRT == void.class) {
- Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
- retFilter = ValueConversions.zeroConstantFunction(wrap);
- } else {
- retFilter = MethodHandles.identity(newRT);
- retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
- }
- newType = newType.changeReturnType(oldRT);
+ /** Factory method: Collect or filter selected argument(s). */
+ static MethodHandle makeCollectArguments(MethodHandle target,
+ MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
+ MethodType targetType = target.type(); // (a..., c, [b...])=>r
+ MethodType collectorType = collector.type(); // (b...)=>c
+ int collectArgCount = collectorType.parameterCount();
+ Class<?> collectValType = collectorType.returnType();
+ int collectValCount = (collectValType == void.class ? 0 : 1);
+ MethodType srcType = targetType // (a..., [b...])=>r
+ .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
+ if (!retainOriginalArgs) { // (a..., b...)=>r
+ srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList());
}
- MethodHandle res = null;
- Exception ex = null;
- try {
- res = convertArguments(target, newType, oldType, level);
- } catch (IllegalArgumentException ex1) {
- ex = ex1;
- }
- if (res == null) {
- WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
- wmt.initCause(ex);
- throw wmt;
- }
- if (retFilter != null)
- res = MethodHandles.filterReturnValue(res, retFilter);
- return res;
- }
+ // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
+ // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
+ // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
- static MethodHandle convertArguments(MethodHandle target,
- MethodType newType,
- MethodType oldType,
- int level) {
- assert(oldType.parameterCount() == target.type().parameterCount());
- if (newType == oldType)
- return target;
- if (oldType.parameterCount() != newType.parameterCount())
- throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
- return AdapterMethodHandle.makePairwiseConvert(newType, target, level);
- }
+ // Now build a LambdaForm.
+ MethodType lambdaType = srcType.invokerType();
+ Name[] names = arguments(2, lambdaType);
+ final int collectNamePos = names.length - 2;
+ final int targetNamePos = names.length - 1;
+
+ Name[] collectorArgs = Arrays.copyOfRange(names, 1 + collectArgPos, 1 + collectArgPos + collectArgCount);
+ names[collectNamePos] = new Name(collector, (Object[]) collectorArgs);
- static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
- MethodType oldType = target.type();
- int nargs = oldType.parameterCount();
- int keepPosArgs = nargs - arrayLength;
- MethodType newType = oldType
- .dropParameterTypes(keepPosArgs, nargs)
- .insertParameterTypes(keepPosArgs, arrayType);
- return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
- }
- // called internally only
- static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) {
- int arrayLength = target.type().parameterCount() - spreadArgPos;
- return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
- }
- static MethodHandle spreadArguments(MethodHandle target,
- MethodType newType,
- int spreadArgPos,
- Class<?> arrayType,
- int arrayLength) {
- // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
- MethodType oldType = target.type();
- // spread the last argument of newType to oldType
- assert(arrayLength == oldType.parameterCount() - spreadArgPos);
- assert(newType.parameterType(spreadArgPos) == arrayType);
- return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
- }
+ // Build argument array for the target.
+ // Incoming LF args to copy are: [ (mh) headArgs collectArgs tailArgs ].
+ // Output argument array is [ headArgs (collectVal)? (collectArgs)? tailArgs ].
+ Name[] targetArgs = new Name[targetType.parameterCount()];
+ int inputArgPos = 1; // incoming LF args to copy to target
+ int targetArgPos = 0; // fill pointer for targetArgs
+ int chunk = collectArgPos; // |headArgs|
+ System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+ inputArgPos += chunk;
+ targetArgPos += chunk;
+ if (collectValType != void.class) {
+ targetArgs[targetArgPos++] = names[collectNamePos];
+ }
+ chunk = collectArgCount;
+ if (retainOriginalArgs) {
+ System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+ targetArgPos += chunk; // optionally pass on the collected chunk
+ }
+ inputArgPos += chunk;
+ chunk = targetArgs.length - targetArgPos; // all the rest
+ System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk);
+ assert(inputArgPos + chunk == collectNamePos); // use of rest of input args also
+ names[targetNamePos] = new Name(target, (Object[]) targetArgs);
- static MethodHandle collectArguments(MethodHandle target,
- int collectArg,
- MethodHandle collector) {
- MethodType type = target.type();
- Class<?> collectType = collector.type().returnType();
- assert(collectType != void.class); // else use foldArguments
- if (collectType != type.parameterType(collectArg))
- target = target.asType(type.changeParameterType(collectArg, collectType));
- MethodType newType = type
- .dropParameterTypes(collectArg, collectArg+1)
- .insertParameterTypes(collectArg, collector.type().parameterArray());
- return collectArguments(target, newType, collectArg, collector);
- }
- static MethodHandle collectArguments(MethodHandle target,
- MethodType newType,
- int collectArg,
- MethodHandle collector) {
- MethodType oldType = target.type(); // (a...,c)=>r
- // newType // (a..., b...)=>r
- MethodType colType = collector.type(); // (b...)=>c
- // oldType // (a..., b...)=>r
- assert(newType.parameterCount() == collectArg + colType.parameterCount());
- assert(oldType.parameterCount() == collectArg + 1);
- assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false));
- return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
- }
-
- static MethodHandle filterArgument(MethodHandle target,
- int pos,
- MethodHandle filter) {
- MethodType ttype = target.type();
- MethodType ftype = filter.type();
- assert(ftype.parameterCount() == 1);
- return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
- }
-
- static MethodHandle foldArguments(MethodHandle target,
- MethodType newType,
- int foldPos,
- MethodHandle combiner) {
- MethodType oldType = target.type();
- MethodType ctype = combiner.type();
- assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true));
- return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
- }
-
- static
- MethodHandle dropArguments(MethodHandle target,
- MethodType newType, int argnum) {
- int drops = newType.parameterCount() - target.type().parameterCount();
- return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops);
+ LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(srcType, form);
}
static
@@ -735,47 +533,42 @@
MethodHandle makeGuardWithTest(MethodHandle test,
MethodHandle target,
MethodHandle fallback) {
- // gwt(arg...)
- // [fold]=> continueAfterTest(z=test(arg...), arg...)
- // [filter]=> (tf=select(z))(arg...)
- // where select(z) = select(z, t, f).bindTo(t, f) => z ? t f
- // [tailcall]=> tf(arg...)
- assert(test.type().returnType() == boolean.class);
- MethodType targetType = target.type();
- MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class);
- assert(AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true));
- // working backwards, as usual:
- assert(target.type().equals(fallback.type()));
- MethodHandle tailcall = MethodHandles.exactInvoker(target.type());
- MethodHandle select = selectAlternative();
- select = bindArgument(select, 2, CountingMethodHandle.wrap(fallback));
- select = bindArgument(select, 1, CountingMethodHandle.wrap(target));
- // select(z: boolean) => (z ? target : fallback)
- MethodHandle filter = filterArgument(tailcall, 0, select);
- assert(filter.type().parameterType(0) == boolean.class);
- MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test);
- return fold;
+ MethodType basicType = target.type().basicType();
+ MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType);
+ int arity = basicType.parameterCount();
+ int extraNames = 3;
+ MethodType lambdaType = basicType.invokerType();
+ Name[] names = arguments(extraNames, lambdaType);
+
+ Object[] testArgs = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class);
+ Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class);
+
+ // call test
+ names[arity + 1] = new Name(test, testArgs);
+
+ // call selectAlternative
+ Object[] selectArgs = { names[arity + 1], target, fallback };
+ names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs);
+ targetArgs[0] = names[arity + 2];
+
+ // call target or fallback
+ names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs);
+
+ LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names);
+ return new SimpleMethodHandle(target.type(), form);
}
- private static class GuardWithCatch extends BoundMethodHandle {
+ private static class GuardWithCatch {
private final MethodHandle target;
private final Class<? extends Throwable> exType;
private final MethodHandle catcher;
+ // FIXME: Build the control flow out of foldArguments.
GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
- this(INVOKES[target.type().parameterCount()], target, exType, catcher);
- }
- // FIXME: Build the control flow out of foldArguments.
- GuardWithCatch(MethodHandle invoker,
- MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
- super(invoker);
this.target = target;
this.exType = exType;
this.catcher = catcher;
}
- @Override
- String debugString() {
- return addTypeString(target, this);
- }
+ @LambdaForm.Hidden
private Object invoke_V(Object... av) throws Throwable {
try {
return target.invokeExact(av);
@@ -784,6 +577,7 @@
return catcher.invokeExact(t, av);
}
}
+ @LambdaForm.Hidden
private Object invoke_L0() throws Throwable {
try {
return target.invokeExact();
@@ -792,6 +586,7 @@
return catcher.invokeExact(t);
}
}
+ @LambdaForm.Hidden
private Object invoke_L1(Object a0) throws Throwable {
try {
return target.invokeExact(a0);
@@ -800,6 +595,7 @@
return catcher.invokeExact(t, a0);
}
}
+ @LambdaForm.Hidden
private Object invoke_L2(Object a0, Object a1) throws Throwable {
try {
return target.invokeExact(a0, a1);
@@ -808,6 +604,7 @@
return catcher.invokeExact(t, a0, a1);
}
}
+ @LambdaForm.Hidden
private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
try {
return target.invokeExact(a0, a1, a2);
@@ -816,6 +613,7 @@
return catcher.invokeExact(t, a0, a1, a2);
}
}
+ @LambdaForm.Hidden
private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3);
@@ -824,6 +622,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3);
}
}
+ @LambdaForm.Hidden
private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4);
@@ -832,6 +631,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3, a4);
}
}
+ @LambdaForm.Hidden
private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4, a5);
@@ -840,6 +640,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
}
}
+ @LambdaForm.Hidden
private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
@@ -848,6 +649,7 @@
return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
}
}
+ @LambdaForm.Hidden
private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
try {
return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
@@ -857,7 +659,7 @@
}
}
static MethodHandle[] makeInvokes() {
- ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
+ ArrayList<MethodHandle> invokes = new ArrayList<>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = invokes.size();
@@ -898,39 +700,60 @@
MethodType gtype = type.generic();
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
// Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
- MethodHandle gtarget = convertArguments(target, gtype, type, 2);
- MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2);
- MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
- if (gtarget == null || gcatcher == null || gguard == null) return null;
- return convertArguments(gguard, type, gtype, 2);
+ MethodHandle gtarget = makePairwiseConvert(target, gtype, 2);
+ MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2);
+ GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+ if (gtarget == null || gcatcher == null) throw new InternalError();
+ MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard);
+ return makePairwiseConvert(ginvoker, type, 2);
} else {
- MethodType gtype = MethodType.genericMethodType(0, true);
- MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
- MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0);
+ MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs);
catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class));
- MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1);
- MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher);
- if (gtarget == null || gcatcher == null || gguard == null) return null;
- return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
+ MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs);
+ GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
+ if (gtarget == null || gcatcher == null) throw new InternalError();
+ MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard);
+ return makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
}
}
static
MethodHandle throwException(MethodType type) {
- return AdapterMethodHandle.makeRetypeRaw(type, throwException());
+ assert(Throwable.class.isAssignableFrom(type.parameterType(0)));
+ int arity = type.parameterCount();
+ if (arity > 1) {
+ return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1);
+ }
+ return makePairwiseConvert(throwException(), type, 2);
}
static MethodHandle THROW_EXCEPTION;
static MethodHandle throwException() {
- if (THROW_EXCEPTION != null) return THROW_EXCEPTION;
+ MethodHandle mh = THROW_EXCEPTION;
+ if (mh != null) return mh;
try {
- THROW_EXCEPTION
+ mh
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
MethodType.methodType(Empty.class, Throwable.class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
- return THROW_EXCEPTION;
+ THROW_EXCEPTION = mh;
+ return mh;
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
+
+ static MethodHandle FAKE_METHOD_HANDLE_INVOKE;
+ static
+ MethodHandle fakeMethodHandleInvoke(MemberName method) {
+ MethodType type = method.getInvocationType();
+ assert(type.equals(MethodType.methodType(Object.class, Object[].class)));
+ MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE;
+ if (mh != null) return mh;
+ mh = throwException(type.insertParameterTypes(0, UnsupportedOperationException.class));
+ mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
+ FAKE_METHOD_HANDLE_INVOKE = mh;
+ return mh;
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleInfo.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, 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.lang.invoke.MethodHandleNatives.Constants;
+
+//Not yet public: public
+class MethodHandleInfo {
+ public static final int
+ REF_NONE = Constants.REF_NONE,
+ REF_getField = Constants.REF_getField,
+ REF_getStatic = Constants.REF_getStatic,
+ REF_putField = Constants.REF_putField,
+ REF_putStatic = Constants.REF_putStatic,
+ REF_invokeVirtual = Constants.REF_invokeVirtual,
+ REF_invokeStatic = Constants.REF_invokeStatic,
+ REF_invokeSpecial = Constants.REF_invokeSpecial,
+ REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
+ REF_invokeInterface = Constants.REF_invokeInterface;
+
+ private final Class<?> declaringClass;
+ private final String name;
+ private final MethodType methodType;
+ private final int referenceKind;
+
+ public MethodHandleInfo(MethodHandle mh) throws ReflectiveOperationException {
+ MemberName mn = mh.internalMemberName();
+ this.declaringClass = mn.getDeclaringClass();
+ this.name = mn.getName();
+ this.methodType = mn.getMethodType();
+ this.referenceKind = mn.getReferenceKind();
+ }
+
+ public Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public MethodType getMethodType() {
+ return methodType;
+ }
+
+ public int getReferenceKind() {
+ return referenceKind;
+ }
+}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Fri Aug 10 16:03:28 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -29,6 +29,7 @@
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
@@ -41,56 +42,21 @@
private MethodHandleNatives() { } // static only
- /// MethodName support
+ /// MemberName support
static native void init(MemberName self, Object ref);
static native void expand(MemberName self);
- static native void resolve(MemberName self, Class<?> caller);
+ static native MemberName resolve(MemberName self, Class<?> caller) throws LinkageError;
static native int getMembers(Class<?> defc, String matchName, String matchSig,
int matchFlags, Class<?> caller, int skip, MemberName[] results);
- /// MethodHandle support
-
- /** Initialize the method handle to adapt the call. */
- static native void init(AdapterMethodHandle self, MethodHandle target, int argnum);
- /** Initialize the method handle to call the correct method, directly. */
- static native void init(BoundMethodHandle self, Object target, int argnum);
- /** Initialize the method handle to call as if by an invoke* instruction. */
- static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class<?> caller);
-
- /** Initialize a method type, once per form. */
- static native void init(MethodType self);
-
- /** Fetch the vmtarget field.
- * It will be sanitized as necessary to avoid exposing non-Java references.
- * This routine is for debugging and reflection.
- */
- static native Object getTarget(MethodHandle self, int format);
+ /// Field layout queries parallel to sun.misc.Unsafe:
+ static native long objectFieldOffset(MemberName self); // e.g., returns vmindex
+ static native long staticFieldOffset(MemberName self); // e.g., returns vmindex
+ static native Object staticFieldBase(MemberName self); // e.g., returns clazz
+ static native Object getMemberVMInfo(MemberName self); // returns {vmindex,vmtarget}
- /** Fetch the name of the handled method, if available.
- * This routine is for debugging and reflection.
- */
- static MemberName getMethodName(MethodHandle self) {
- return (MemberName) getTarget(self, ETF_METHOD_NAME);
- }
-
- /** Fetch the reflective version of the handled method, if available.
- */
- static AccessibleObject getTargetMethod(MethodHandle self) {
- return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD);
- }
-
- /** Fetch the target of this method handle.
- * If it directly targets a method, return a MemberName for the method.
- * If it is chained to another method handle, return that handle.
- */
- static Object getTargetInfo(MethodHandle self) {
- return getTarget(self, ETF_HANDLE_OR_METHOD_NAME);
- }
-
- static Object[] makeTarget(Class<?> defc, String name, String sig, int mods, Class<?> refc) {
- return new Object[] { defc, name, sig, mods, refc };
- }
+ /// MethodHandle support
/** Fetch MH-related JVM parameter.
* which=0 retrieves MethodHandlePushLimit
@@ -98,19 +64,6 @@
*/
static native int getConstant(int which);
- /** Java copy of MethodHandlePushLimit in range 2..255. */
- static final int JVM_PUSH_LIMIT;
- /** JVM stack motion (in words) after one slot is pushed, usually -1.
- */
- static final int JVM_STACK_MOVE_UNIT;
-
- /** Which conv-ops are implemented by the JVM? */
- static final int CONV_OP_IMPLEMENTED_MASK;
- /** Derived mode flag. Only false on some old JVM implementations. */
- static final boolean HAVE_RICOCHET_FRAMES;
-
- static final int OP_ROT_ARGS_DOWN_LIMIT_BIAS;
-
static final boolean COUNT_GWT;
/// CallSite support
@@ -122,17 +75,11 @@
private static native void registerNatives();
static {
registerNatives();
- int k;
- JVM_PUSH_LIMIT = getConstant(Constants.GC_JVM_PUSH_LIMIT);
- JVM_STACK_MOVE_UNIT = getConstant(Constants.GC_JVM_STACK_MOVE_UNIT);
- k = getConstant(Constants.GC_CONV_OP_IMPLEMENTED_MASK);
- CONV_OP_IMPLEMENTED_MASK = (k != 0) ? k : DEFAULT_CONV_OP_IMPLEMENTED_MASK;
- k = getConstant(Constants.GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS);
- OP_ROT_ARGS_DOWN_LIMIT_BIAS = (k != 0) ? (byte)k : -1;
- HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
COUNT_GWT = getConstant(Constants.GC_COUNT_GWT) != 0;
- //sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init");
- }
+
+ // The JVM calls MethodHandleNatives.<clinit>. Cascade the <clinit> calls as needed:
+ MethodHandleImpl.initStatics();
+}
// All compile-time constants go here.
// There is an opportunity to check them against the JVM's idea of them.
@@ -140,16 +87,8 @@
Constants() { } // static only
// MethodHandleImpl
static final int // for getConstant
- GC_JVM_PUSH_LIMIT = 0,
- GC_JVM_STACK_MOVE_UNIT = 1,
- GC_CONV_OP_IMPLEMENTED_MASK = 2,
- GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS = 3,
- GC_COUNT_GWT = 4;
- static final int
- ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
- ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
- ETF_METHOD_NAME = 2, // ultimate method as MemberName
- ETF_REFLECT_METHOD = 3; // ultimate method as java.lang.reflect object (sans refClass)
+ GC_COUNT_GWT = 4,
+ GC_LAMBDA_SUPPORT = 5;
// MemberName
// The JVM uses values of -2 and above for vtable indexes.
@@ -162,65 +101,11 @@
MN_IS_CONSTRUCTOR = 0x00020000, // constructor
MN_IS_FIELD = 0x00040000, // field
MN_IS_TYPE = 0x00080000, // nested type
- MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
- MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers
- VM_INDEX_UNINITIALIZED = -99;
-
- // BoundMethodHandle
- /** Constants for decoding the vmargslot field, which contains 2 values. */
- static final int
- ARG_SLOT_PUSH_SHIFT = 16,
- ARG_SLOT_MASK = (1<<ARG_SLOT_PUSH_SHIFT)-1;
-
- // AdapterMethodHandle
- /** Conversions recognized by the JVM.
- * They must align with the constants in java.lang.invoke.AdapterMethodHandle,
- * in the JVM file hotspot/src/share/vm/classfile/javaClasses.hpp.
- */
- static final int
- OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
- OP_RETYPE_RAW = 0x1, // straight retype, trusted (void->int, Object->T)
- OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
- OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
- OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
- OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper
- OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
- OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
- OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
- OP_DROP_ARGS = 0x9, // remove one or more argument slots
- OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function
- OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
- OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
- //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
- CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
- /** Shift and mask values for decoding the AMH.conversion field.
- * These numbers are shared with the JVM for creating AMHs.
- */
- static final int
- CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
- CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below
- CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
- CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
- CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
- CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed)
- CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed)
- CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
- CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1;
-
- /** Which conv-ops are implemented by the JVM? */
- static final int DEFAULT_CONV_OP_IMPLEMENTED_MASK =
- // Value to use if the corresponding JVM query fails.
- ((1<<OP_RETYPE_ONLY)
- |(1<<OP_RETYPE_RAW)
- |(1<<OP_CHECK_CAST)
- |(1<<OP_PRIM_TO_PRIM)
- |(1<<OP_REF_TO_PRIM)
- |(1<<OP_SWAP_ARGS)
- |(1<<OP_ROT_ARGS)
- |(1<<OP_DUP_ARGS)
- |(1<<OP_DROP_ARGS)
- //|(1<<OP_SPREAD_ARGS)
- );
+ MN_REFERENCE_KIND_SHIFT = 24, // refKind
+ MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
+ // The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
+ MN_SEARCH_SUPERCLASSES = 0x00100000,
+ MN_SEARCH_INTERFACES = 0x00200000;
/**
* Basic types as encoded in the JVM. These code values are not
@@ -243,9 +128,54 @@
T_ILLEGAL = 99;
/**
+ * Constant pool entry types.
+ */
+ static final byte
+ CONSTANT_Utf8 = 1,
+ CONSTANT_Integer = 3,
+ CONSTANT_Float = 4,
+ CONSTANT_Long = 5,
+ CONSTANT_Double = 6,
+ CONSTANT_Class = 7,
+ CONSTANT_String = 8,
+ CONSTANT_Fieldref = 9,
+ CONSTANT_Methodref = 10,
+ CONSTANT_InterfaceMethodref = 11,
+ CONSTANT_NameAndType = 12,
+ CONSTANT_MethodHandle = 15, // JSR 292
+ CONSTANT_MethodType = 16, // JSR 292
+ CONSTANT_InvokeDynamic = 18,
+ CONSTANT_LIMIT = 19; // Limit to tags found in classfiles
+
+ /**
+ * Access modifier flags.
+ */
+ static final char
+ ACC_PUBLIC = 0x0001,
+ ACC_PRIVATE = 0x0002,
+ ACC_PROTECTED = 0x0004,
+ ACC_STATIC = 0x0008,
+ ACC_FINAL = 0x0010,
+ ACC_SYNCHRONIZED = 0x0020,
+ ACC_VOLATILE = 0x0040,
+ ACC_TRANSIENT = 0x0080,
+ ACC_NATIVE = 0x0100,
+ ACC_INTERFACE = 0x0200,
+ ACC_ABSTRACT = 0x0400,
+ ACC_STRICT = 0x0800,
+ ACC_SYNTHETIC = 0x1000,
+ ACC_ANNOTATION = 0x2000,
+ ACC_ENUM = 0x4000,
+ // aliases:
+ ACC_SUPER = ACC_SYNCHRONIZED,
+ ACC_BRIDGE = ACC_VOLATILE,
+ ACC_VARARGS = ACC_TRANSIENT;
+
+ /**
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
*/
- static final int
+ static final byte
+ REF_NONE = 0, // null value
REF_getField = 1,
REF_getStatic = 2,
REF_putField = 3,
@@ -254,8 +184,66 @@
REF_invokeStatic = 6,
REF_invokeSpecial = 7,
REF_newInvokeSpecial = 8,
- REF_invokeInterface = 9;
+ REF_invokeInterface = 9,
+ REF_LIMIT = 10;
+ }
+
+ static boolean refKindIsValid(int refKind) {
+ return (refKind > REF_NONE && refKind < REF_LIMIT);
+ }
+ static boolean refKindIsField(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind <= REF_putStatic);
+ }
+ static boolean refKindIsGetter(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind <= REF_getStatic);
+ }
+ static boolean refKindIsSetter(byte refKind) {
+ return refKindIsField(refKind) && !refKindIsGetter(refKind);
+ }
+ static boolean refKindIsMethod(byte refKind) {
+ return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
+ }
+ static boolean refKindHasReceiver(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind & 1) != 0;
+ }
+ static boolean refKindIsStatic(byte refKind) {
+ return !refKindHasReceiver(refKind) && (refKind != REF_newInvokeSpecial);
}
+ static boolean refKindDoesDispatch(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return (refKind == REF_invokeVirtual ||
+ refKind == REF_invokeInterface);
+ }
+ static {
+ final int HR_MASK = ((1 << REF_getField) |
+ (1 << REF_putField) |
+ (1 << REF_invokeVirtual) |
+ (1 << REF_invokeSpecial) |
+ (1 << REF_invokeInterface)
+ );
+ for (byte refKind = REF_NONE+1; refKind < REF_LIMIT; refKind++) {
+ assert(refKindHasReceiver(refKind) == (((1<<refKind) & HR_MASK) != 0)) : refKind;
+ }
+ }
+ static String refKindName(byte refKind) {
+ assert(refKindIsValid(refKind));
+ return REFERENCE_KIND_NAME[refKind];
+ }
+ private static String[] REFERENCE_KIND_NAME = {
+ null,
+ "getField",
+ "getStatic",
+ "putField",
+ "putStatic",
+ "invokeVirtual",
+ "invokeStatic",
+ "invokeSpecial",
+ "newInvokeSpecial",
+ "invokeInterface"
+ };
private static native int getNamedCon(int which, Object[] name);
static boolean verifyConstants() {
@@ -275,16 +263,11 @@
continue;
}
throw new InternalError(err);
- } catch (Exception ex) {
- if (ex instanceof NoSuchFieldException) {
- String err = (name+": JVM has "+vmval+" which Java does not define");
- // ignore exotic ops the JVM cares about; we just wont issue them
- if (name.startsWith("OP_") || name.startsWith("GC_")) {
- System.err.println("warning: "+err);
- continue;
- }
- }
- throw new InternalError(name+": access failed, got "+ex);
+ } catch (NoSuchFieldException | IllegalAccessException ex) {
+ String err = (name+": JVM has "+vmval+" which Java does not define");
+ // ignore exotic ops the JVM cares about; we just wont issue them
+ //System.err.println("warning: "+err);
+ continue;
}
}
return true;
@@ -299,18 +282,21 @@
/**
* The JVM is linking an invokedynamic instruction. Create a reified call site for it.
*/
- static CallSite makeDynamicCallSite(MethodHandle bootstrapMethod,
- String name, MethodType type,
- Object info,
- MemberName callerMethod, int callerBCI) {
- return CallSite.makeSite(bootstrapMethod, name, type, info, callerMethod, callerBCI);
- }
-
- /**
- * Called by the JVM to check the length of a spread array.
- */
- static void checkSpreadArgument(Object av, int n) {
- MethodHandleStatics.checkSpreadArgument(av, n);
+ static MemberName linkCallSite(Object callerObj,
+ Object bootstrapMethodObj,
+ Object nameObj, Object typeObj,
+ Object staticArguments,
+ Object[] appendixResult) {
+ MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
+ Class<?> caller = (Class<?>)callerObj;
+ String name = nameObj.toString().intern();
+ MethodType type = (MethodType)typeObj;
+ appendixResult[0] = CallSite.makeSite(bootstrapMethod,
+ name,
+ type,
+ staticArguments,
+ caller);
+ return Invokers.linkToCallSiteMethod(type);
}
/**
@@ -321,71 +307,64 @@
}
/**
- * The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning.
- */
- static void notifyGenericMethodType(MethodType type) {
- type.form().notifyGenericMethodType();
- }
-
- /**
- * The JVM wants to raise an exception. Here's the path.
+ * The JVM wants to link a call site that requires a dynamic type check.
+ * Name is a type-checking invoker, invokeExact or invoke.
+ * Return a JVM method (MemberName) to handle the invoking.
+ * The method assumes the following arguments on the stack:
+ * 0: the method handle being invoked
+ * 1-N: the arguments to the method handle invocation
+ * N+1: an implicitly added type argument (the given MethodType)
*/
- static void raiseException(int code, Object actual, Object required) {
- String message = null;
- switch (code) {
- case 190: // arraylength
- try {
- String reqLength = "";
- if (required instanceof AdapterMethodHandle) {
- int conv = ((AdapterMethodHandle)required).getConversion();
- int spChange = AdapterMethodHandle.extractStackMove(conv);
- reqLength = " of length "+(spChange+1);
- }
- int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual);
- message = "required array"+reqLength+", but encountered wrong length "+actualLength;
- break;
- } catch (IllegalArgumentException ex) {
- }
- required = Object[].class; // should have been an array
- code = 192; // checkcast
- break;
- case 191: // athrow
- // JVM is asking us to wrap an exception which happened during resolving
- if (required == BootstrapMethodError.class) {
- throw new BootstrapMethodError((Throwable) actual);
- }
- break;
+ static MemberName linkMethod(Class<?> callerClass, int refKind,
+ Class<?> defc, String name, Object type,
+ Object[] appendixResult) {
+ if (!TRACE_METHOD_LINKAGE)
+ return linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
+ return linkMethodTracing(callerClass, refKind, defc, name, type, appendixResult);
+ }
+ static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
+ Class<?> defc, String name, Object type,
+ Object[] appendixResult) {
+ if (defc != MethodHandle.class || refKind != REF_invokeVirtual)
+ throw new LinkageError("no such method "+defc.getName()+"."+name+type);
+ switch (name) {
+ case "invoke":
+ return Invokers.genericInvokerMethod(callerClass, type, appendixResult);
+ case "invokeExact":
+ return Invokers.exactInvokerMethod(callerClass, type, appendixResult);
}
- // disregard the identity of the actual object, if it is not a class:
- if (message == null) {
- if (!(actual instanceof Class) && !(actual instanceof MethodType))
- actual = actual.getClass();
- if (actual != null)
- message = "required "+required+" but encountered "+actual;
- else
- message = "required "+required;
- }
- switch (code) {
- case 190: // arraylength
- throw new ArrayIndexOutOfBoundsException(message);
- case 50: //_aaload
- throw new ClassCastException(message);
- case 192: // checkcast
- throw new ClassCastException(message);
- default:
- throw new InternalError("unexpected code "+code+": "+message);
+ throw new UnsupportedOperationException("linkMethod "+name);
+ }
+ // Tracing logic:
+ static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
+ Class<?> defc, String name, Object type,
+ Object[] appendixResult) {
+ System.out.println("linkMethod "+defc.getName()+"."+
+ name+type+"/"+Integer.toHexString(refKind));
+ try {
+ MemberName res = linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
+ System.out.println("linkMethod => "+res+" + "+appendixResult[0]);
+ return res;
+ } catch (Throwable ex) {
+ System.out.println("linkMethod => throw "+ex);
+ throw ex;
}
}
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
+ * The type argument is a Class for field requests and a MethodType for non-fields.
+ * <p>
+ * Recent versions of the JVM may also pass a resolved MemberName for the type.
+ * In that case, the name is ignored and may be null.
*/
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type) {
try {
Lookup lookup = IMPL_LOOKUP.in(callerClass);
- return lookup.linkMethodHandleConstant(refKind, defc, name, type);
+ assert(refKindIsValid(refKind));
+ return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
} catch (ReflectiveOperationException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Fri Aug 10 16:03:28 2012 -0700
@@ -27,6 +27,7 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
+import sun.misc.Unsafe;
/**
* This class consists exclusively of static names internal to the
@@ -38,16 +39,30 @@
private MethodHandleStatics() { } // do not instantiate
+ static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
static final boolean DEBUG_METHOD_HANDLE_NAMES;
+ static final boolean DUMP_CLASS_FILES;
+ static final boolean TRACE_INTERPRETER;
+ static final boolean TRACE_METHOD_LINKAGE;
+ static final Integer COMPILE_THRESHOLD;
static {
- final Object[] values = { false };
+ final Object[] values = { false, false, false, false, null };
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+ values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
+ values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
+ values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
+ values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
return null;
}
});
DEBUG_METHOD_HANDLE_NAMES = (Boolean) values[0];
+ DUMP_CLASS_FILES = (Boolean) values[1];
+ TRACE_INTERPRETER = (Boolean) values[2];
+ TRACE_METHOD_LINKAGE = (Boolean) values[3];
+ COMPILE_THRESHOLD = (Integer) values[4];
}
/*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
@@ -55,7 +70,7 @@
type = target.type();
MemberName name = null;
if (target != null)
- name = MethodHandleNatives.getMethodName(target);
+ name = target.internalMemberName();
if (name == null)
return "invoke" + type;
return name.getName() + type;
@@ -77,20 +92,6 @@
return str + target.type();
}
- static void checkSpreadArgument(Object av, int n) {
- if (av == null) {
- if (n == 0) return;
- } else if (av instanceof Object[]) {
- int len = ((Object[])av).length;
- if (len == n) return;
- } else {
- int len = java.lang.reflect.Array.getLength(av);
- if (len == n) return;
- }
- // fall through to error:
- throw newIllegalArgumentException("Array is not of length "+n);
- }
-
// handy shared exception makers (they simplify the common case code)
/*non-public*/ static RuntimeException newIllegalStateException(String message) {
return new IllegalStateException(message);
@@ -110,6 +111,9 @@
/*non-public*/ static Error uncaughtException(Exception ex) {
throw new InternalError("uncaught exception", ex);
}
+ static Error NYI() {
+ throw new AssertionError("NYI");
+ }
private static String message(String message, Object obj) {
if (obj != null) message = message + ": " + obj;
return message;
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Fri Aug 10 16:03:28 2012 -0700
@@ -26,7 +26,6 @@
package java.lang.invoke;
import java.lang.reflect.*;
-import sun.invoke.WrapperInstance;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyAccess;
import sun.invoke.util.Wrapper;
@@ -174,6 +173,8 @@
* Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}.
* The formal parameter {@code this} stands for the self-reference of type {@code C};
* if it is present, it is always the leading argument to the method handle invocation.
+ * (In the case of some {@code protected} members, {@code this} may be
+ * restricted in type to the lookup class; see below.)
* The name {@code arg} stands for all the other method handle arguments.
* In the code examples for the Core Reflection API, the name {@code thisOrNull}
* stands for a null reference if the accessed method or field is static,
@@ -244,6 +245,18 @@
* is exactly equivalent to executing the compiled and resolved call to {@code M}.
* The same point is true of fields and constructors.
* <p>
+ * If the desired member is {@code protected}, the usual JVM rules apply,
+ * including the requirement that the lookup class must be either be in the
+ * same package as the desired member, or must inherit that member.
+ * (See the Java Virtual Machine Specification, sections 4.9.2, 5.4.3.5, and 6.4.)
+ * In addition, if the desired member is a non-static field or method
+ * in a different package, the resulting method handle may only be applied
+ * to objects of the lookup class or one of its subclasses.
+ * This requirement is enforced by narrowing the type of the leading
+ * {@code this} parameter from {@code C}
+ * (which will necessarily be a superclass of the lookup class)
+ * to the lookup class itself.
+ * <p>
* In some cases, access between nested classes is obtained by the Java compiler by creating
* an wrapper method to access a private method of another class
* in the same top-level declaration.
@@ -582,19 +595,9 @@
*/
public
MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, true);
+ MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- return accessStatic(refc, method);
- }
- private
- MethodHandle accessStatic(Class<?> refc, MemberName method) throws IllegalAccessException {
- checkMethod(refc, method, true);
- return MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
- }
- private
- MethodHandle resolveStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, true);
- return accessStatic(refc, method);
+ return getDirectMethod(REF_invokeStatic, refc, method);
}
/**
@@ -609,6 +612,11 @@
* (The dispatching action is identical with that performed by an
* {@code invokevirtual} or {@code invokeinterface} instruction.)
* <p>
+ * The first argument will be of type {@code refc} if the lookup
+ * class has full privileges to access the member. Otherwise
+ * the member must be {@code protected} and the first argument
+ * will be restricted in type to the lookup class.
+ * <p>
* The returned method handle will have
* {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
* the method's variable arity modifier bit ({@code 0x0080}) is set.
@@ -636,18 +644,22 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, false);
+ if (refc == MethodHandle.class) {
+ MethodHandle mh = findVirtualForMH(name, type);
+ if (mh != null) return mh;
+ }
+ byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual);
+ MemberName method = resolveOrFail(refKind, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- return accessVirtual(refc, method);
+ return getDirectMethod(refKind, refc, method);
}
- private MethodHandle resolveVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- MemberName method = resolveOrFail(refc, name, type, false);
- return accessVirtual(refc, method);
- }
- private MethodHandle accessVirtual(Class<?> refc, MemberName method) throws IllegalAccessException {
- checkMethod(refc, method, false);
- MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
- return restrictProtectedReceiver(method, mh);
+ private MethodHandle findVirtualForMH(String name, MethodType type) {
+ // these names require special lookups because of the implicit MethodType argument
+ if ("invoke".equals(name))
+ return invoker(type);
+ if ("invokeExact".equals(name))
+ return exactInvoker(type);
+ return null;
}
/**
@@ -678,36 +690,9 @@
*/
public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
String name = "<init>";
- MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
+ MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
checkSecurityManager(refc, ctor); // stack walk magic: do not refactor
- return accessConstructor(refc, ctor);
- }
- private MethodHandle accessConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
- assert(ctor.isConstructor());
- checkAccess(refc, ctor);
- MethodHandle rawMH = MethodHandleImpl.findMethod(ctor, false, lookupClassOrNull());
- MethodHandle allocMH = MethodHandleImpl.makeAllocator(rawMH);
- return fixVarargs(allocMH, rawMH);
- }
- private MethodHandle resolveConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- String name = "<init>";
- MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
- return accessConstructor(refc, ctor);
- }
-
- /** Return a version of MH which matches matchMH w.r.t. isVarargsCollector. */
- private static MethodHandle fixVarargs(MethodHandle mh, MethodHandle matchMH) {
- boolean va1 = mh.isVarargsCollector();
- boolean va2 = matchMH.isVarargsCollector();
- if (va1 == va2) {
- return mh;
- } else if (va2) {
- MethodType type = mh.type();
- int arity = type.parameterCount();
- return mh.asVarargsCollector(type.parameterType(arity-1));
- } else {
- return mh.asFixedArity();
- }
+ return getDirectConstructor(refc, ctor);
}
/**
@@ -747,21 +732,10 @@
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
checkSpecialCaller(specialCaller);
- MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
+ Lookup specialLookup = this.in(specialCaller);
+ MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- return accessSpecial(refc, method, specialCaller);
- }
- private MethodHandle accessSpecial(Class<?> refc, MemberName method,
- Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
- checkMethod(refc, method, false);
- MethodHandle mh = MethodHandleImpl.findMethod(method, false, specialCaller);
- return restrictReceiver(method, mh, specialCaller);
- }
- private MethodHandle resolveSpecial(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
- Class<?> specialCaller = lookupClass();
- checkSpecialCaller(specialCaller);
- MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
- return accessSpecial(refc, method, specialCaller);
+ return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method);
}
/**
@@ -782,13 +756,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
+ MemberName field = resolveOrFail(REF_getField, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, false, 0);
- }
- private MethodHandle resolveGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
- return makeAccessor(refc, field, false, false, 0);
+ return getDirectField(REF_getField, refc, field);
}
/**
@@ -809,13 +779,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
+ MemberName field = resolveOrFail(REF_putField, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, true, 0);
- }
- private MethodHandle resolveSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, false);
- return makeAccessor(refc, field, false, true, 0);
+ return getDirectField(REF_putField, refc, field);
}
/**
@@ -835,13 +801,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
+ MemberName field = resolveOrFail(REF_getStatic, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, false, 1);
- }
- private MethodHandle resolveStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
- return makeAccessor(refc, field, false, false, 1);
+ return getDirectField(REF_getStatic, refc, field);
}
/**
@@ -861,13 +823,9 @@
* @throws NullPointerException if any argument is null
*/
public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
+ MemberName field = resolveOrFail(REF_putStatic, refc, name, type);
checkSecurityManager(refc, field); // stack walk magic: do not refactor
- return makeAccessor(refc, field, false, true, 1);
- }
- private MethodHandle resolveStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
- MemberName field = resolveOrFail(refc, name, type, true);
- return makeAccessor(refc, field, false, true, 1);
+ return getDirectField(REF_putStatic, refc, field);
}
/**
@@ -918,14 +876,10 @@
*/
public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
Class<? extends Object> refc = receiver.getClass(); // may get NPE
- MemberName method = resolveOrFail(refc, name, type, false);
+ MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor
- checkMethod(refc, method, false);
- MethodHandle dmh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
- MethodHandle bmh = MethodHandleImpl.bindReceiver(dmh, receiver);
- if (bmh == null)
- throw method.makeAccessException("no access", this);
- return fixVarargs(bmh, dmh);
+ MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method);
+ return mh.bindReceiver(receiver).setVarargs(method);
}
/**
@@ -951,12 +905,12 @@
*/
public MethodHandle unreflect(Method m) throws IllegalAccessException {
MemberName method = new MemberName(m);
+ byte refKind = method.getReferenceKind();
+ if (refKind == REF_invokeSpecial)
+ refKind = REF_invokeVirtual;
assert(method.isMethod());
- if (m.isAccessible())
- return MethodHandleImpl.findMethod(method, true, /*no lookupClass*/ null);
- checkMethod(method.getDeclaringClass(), method, method.isStatic());
- MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
- return restrictProtectedReceiver(method, mh);
+ Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
+ return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method);
}
/**
@@ -982,12 +936,11 @@
*/
public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
checkSpecialCaller(specialCaller);
- MemberName method = new MemberName(m);
+ Lookup specialLookup = this.in(specialCaller);
+ MemberName method = new MemberName(m, true);
assert(method.isMethod());
// ignore m.isAccessible: this is a new kind of access
- checkMethod(m.getDeclaringClass(), method, false);
- MethodHandle mh = MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
- return restrictReceiver(method, mh, specialCaller);
+ return specialLookup.getDirectMethod(REF_invokeSpecial, method.getDeclaringClass(), method);
}
/**
@@ -1015,15 +968,8 @@
public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException {
MemberName ctor = new MemberName(c);
assert(ctor.isConstructor());
- MethodHandle rawCtor;
- if (c.isAccessible()) {
- rawCtor = MethodHandleImpl.findMethod(ctor, false, /*no lookupClass*/ null);
- } else {
- checkAccess(c.getDeclaringClass(), ctor);
- rawCtor = MethodHandleImpl.findMethod(ctor, false, lookupClassOrNull());
- }
- MethodHandle allocator = MethodHandleImpl.makeAllocator(rawCtor);
- return fixVarargs(allocator, rawCtor);
+ Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this;
+ return lookup.getDirectConstructor(ctor.getDeclaringClass(), ctor);
}
/**
@@ -1041,7 +987,15 @@
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
- return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false, -1);
+ return unreflectField(f, false);
+ }
+ private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
+ MemberName field = new MemberName(f, isSetter);
+ assert(isSetter
+ ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
+ : MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
+ Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this;
+ return lookup.getDirectField(field.getReferenceKind(), f.getDeclaringClass(), field);
}
/**
@@ -1059,33 +1013,22 @@
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
- return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true, -1);
+ return unreflectField(f, true);
}
/// Helper methods, all package-private.
- MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoSuchFieldException, IllegalAccessException {
+ MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
name.getClass(); type.getClass(); // NPE
- int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
+ return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
NoSuchFieldException.class);
}
- MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoSuchMethodException, IllegalAccessException {
+ MemberName resolveOrFail(byte refKind, Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
name.getClass(); type.getClass(); // NPE
- int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
- NoSuchMethodException.class);
- }
-
- MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
- boolean searchSupers, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
- checkSymbolicClass(refc); // do this before attempting to resolve
- name.getClass(); type.getClass(); // NPE
- int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller,
+ return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
NoSuchMethodException.class);
}
@@ -1141,7 +1084,8 @@
// SecurityManager.checkMemberAccess [0]
}
- void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws IllegalAccessException {
+ void checkMethod(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
+ boolean wantStatic = (refKind == REF_invokeStatic);
String message;
if (m.isConstructor())
message = "expected a method, not a constructor";
@@ -1150,26 +1094,43 @@
else if (wantStatic != m.isStatic())
message = wantStatic ? "expected a static method" : "expected a non-static method";
else
- { checkAccess(refc, m); return; }
+ { checkAccess(refKind, refc, m); return; }
throw m.makeAccessException(message, this);
}
- void checkAccess(Class<?> refc, MemberName m) throws IllegalAccessException {
+ void checkField(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
+ boolean wantStatic = !MethodHandleNatives.refKindHasReceiver(refKind);
+ String message;
+ if (wantStatic != m.isStatic())
+ message = wantStatic ? "expected a static field" : "expected a non-static field";
+ else
+ { checkAccess(refKind, refc, m); return; }
+ throw m.makeAccessException(message, this);
+ }
+
+ void checkAccess(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
+ assert(m.referenceKindIsConsistentWith(refKind) &&
+ MethodHandleNatives.refKindIsValid(refKind) &&
+ (MethodHandleNatives.refKindIsField(refKind) == m.isField()));
int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
int mods = m.getModifiers();
+ if (Modifier.isFinal(mods) &&
+ MethodHandleNatives.refKindIsSetter(refKind))
+ throw m.makeAccessException("unexpected set of a final field", this);
if (Modifier.isPublic(mods) && Modifier.isPublic(refc.getModifiers()) && allowedModes != 0)
return; // common case
int requestedModes = fixmods(mods); // adjust 0 => PACKAGE
- if ((requestedModes & allowedModes) != 0
- && VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
- mods, lookupClass(), allowedModes))
- return;
- if (((requestedModes & ~allowedModes) & PROTECTED) != 0
- && (allowedModes & PACKAGE) != 0
- && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
+ if ((requestedModes & allowedModes) != 0) {
+ if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
+ mods, lookupClass(), allowedModes))
+ return;
+ } else {
// Protected members can also be checked as if they were package-private.
- return;
+ if ((requestedModes & PROTECTED) != 0 && (allowedModes & PACKAGE) != 0
+ && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
+ return;
+ }
throw m.makeAccessException(accessFailedMessage(refc, m), this);
}
@@ -1198,7 +1159,8 @@
private static final boolean ALLOW_NESTMATE_ACCESS = false;
- void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+ private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+ int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
if ((allowedModes & PRIVATE) == 0
|| (specialCaller != lookupClass()
@@ -1208,7 +1170,7 @@
makeAccessException("no private access for invokespecial", this);
}
- MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws IllegalAccessException {
+ private boolean restrictProtectedReceiver(MemberName method) {
// The accessing class only has the right to use a protected member
// on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc.
if (!method.isProtected() || method.isStatic()
@@ -1217,52 +1179,82 @@
|| VerifyAccess.isSamePackage(method.getDeclaringClass(), lookupClass())
|| (ALLOW_NESTMATE_ACCESS &&
VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass())))
- return mh;
- else
- return restrictReceiver(method, mh, lookupClass());
+ return false;
+ return true;
}
- MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
+ private MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
assert(!method.isStatic());
- Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide
- if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
+ // receiver type of mh is too wide; narrow to caller
+ if (!method.getDeclaringClass().isAssignableFrom(caller)) {
throw method.makeAccessException("caller class must be a subclass below the method", caller);
}
MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh;
MethodType narrowType = rawType.changeParameterType(0, caller);
- MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
- return fixVarargs(narrowMH, mh);
+ return mh.viewAsType(narrowType);
}
- MethodHandle makeAccessor(Class<?> refc, MemberName field,
- boolean trusted, boolean isSetter,
- int checkStatic) throws IllegalAccessException {
- assert(field.isField());
- if (checkStatic >= 0 && (checkStatic != 0) != field.isStatic())
- throw field.makeAccessException((checkStatic != 0)
- ? "expected a static field"
- : "expected a non-static field", this);
- if (trusted)
- return MethodHandleImpl.accessField(field, isSetter, /*no lookupClass*/ null);
- checkAccess(refc, field);
- MethodHandle mh = MethodHandleImpl.accessField(field, isSetter, lookupClassOrNull());
- return restrictProtectedReceiver(field, mh);
+ private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method) throws IllegalAccessException {
+ return getDirectMethodCommon(refKind, refc, method,
+ (refKind == REF_invokeSpecial ||
+ (MethodHandleNatives.refKindHasReceiver(refKind) &&
+ restrictProtectedReceiver(method))));
+ }
+ private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method) throws IllegalAccessException {
+ return getDirectMethodCommon(refKind, refc, method, false);
+ }
+ private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
+ boolean doRestrict) throws IllegalAccessException {
+ checkMethod(refKind, refc, method);
+ if (method.isMethodHandleInvoke())
+ return fakeMethodHandleInvoke(method);
+ MethodHandle mh = DirectMethodHandle.make(refc, method);
+ mh = mh.setVarargs(method);
+ if (doRestrict)
+ mh = restrictReceiver(method, mh, lookupClass());
+ return mh;
+ }
+ private MethodHandle fakeMethodHandleInvoke(MemberName method) {
+ return throwException(method.getReturnType(), UnsupportedOperationException.class);
+ }
+ private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
+ checkField(refKind, refc, field);
+ MethodHandle mh = DirectMethodHandle.make(refc, field);
+ boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) &&
+ restrictProtectedReceiver(field));
+ if (doRestrict)
+ mh = restrictReceiver(field, mh, lookupClass());
+ return mh;
+ }
+ private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
+ assert(ctor.isConstructor());
+ checkAccess(REF_newInvokeSpecial, refc, ctor);
+ return DirectMethodHandle.make(ctor).setVarargs(ctor);
}
/** Hook called from the JVM (via MethodHandleNatives) to link MH constants:
*/
/*non-public*/
- MethodHandle linkMethodHandleConstant(int refKind, Class<?> defc, String name, Object type) throws ReflectiveOperationException {
- switch (refKind) {
- case REF_getField: return resolveGetter( defc, name, (Class<?>) type );
- case REF_getStatic: return resolveStaticGetter( defc, name, (Class<?>) type );
- case REF_putField: return resolveSetter( defc, name, (Class<?>) type );
- case REF_putStatic: return resolveStaticSetter( defc, name, (Class<?>) type );
- case REF_invokeVirtual: return resolveVirtual( defc, name, (MethodType) type );
- case REF_invokeStatic: return resolveStatic( defc, name, (MethodType) type );
- case REF_invokeSpecial: return resolveSpecial( defc, name, (MethodType) type );
- case REF_newInvokeSpecial: return resolveConstructor( defc, (MethodType) type );
- case REF_invokeInterface: return resolveVirtual( defc, name, (MethodType) type );
+ MethodHandle linkMethodHandleConstant(byte refKind, Class<?> defc, String name, Object type) throws ReflectiveOperationException {
+ MemberName resolved = null;
+ if (type instanceof MemberName) {
+ resolved = (MemberName) type;
+ if (!resolved.isResolved()) throw new InternalError("unresolved MemberName");
+ assert(name == null || name.equals(resolved.getName()));
+ }
+ if (MethodHandleNatives.refKindIsField(refKind)) {
+ MemberName field = (resolved != null) ? resolved
+ : resolveOrFail(refKind, defc, name, (Class<?>) type);
+ return getDirectField(refKind, defc, field);
+ } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
+ MemberName method = (resolved != null) ? resolved
+ : resolveOrFail(refKind, defc, name, (MethodType) type);
+ return getDirectMethod(refKind, defc, method);
+ } else if (refKind == REF_newInvokeSpecial) {
+ assert(name == null || name.equals("<init>"));
+ MemberName ctor = (resolved != null) ? resolved
+ : resolveOrFail(REF_newInvokeSpecial, defc, name, (MethodType) type);
+ return getDirectConstructor(defc, ctor);
}
// oops
throw new ReflectiveOperationException("bad MethodHandle constant #"+refKind+" "+name+" : "+type);
@@ -1281,7 +1273,7 @@
*/
public static
MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
- return MethodHandleImpl.accessArrayElement(arrayClass, false);
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, false);
}
/**
@@ -1295,7 +1287,7 @@
*/
public static
MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
- return MethodHandleImpl.accessArrayElement(arrayClass, true);
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, true);
}
/// method handle invocation (reflective style)
@@ -1422,78 +1414,12 @@
return type.invokers().generalInvoker();
}
- /**
- * Perform value checking, exactly as if for an adapted method handle.
- * It is assumed that the given value is either null, of type T0,
- * or (if T0 is primitive) of the wrapper class corresponding to T0.
- * The following checks and conversions are made:
- * <ul>
- * <li>If T0 and T1 are references, then a cast to T1 is applied.
- * (The types do not need to be related in any particular way.)
- * <li>If T0 and T1 are primitives, then a widening or narrowing
- * conversion is applied, if one exists.
- * <li>If T0 is a primitive and T1 a reference, and
- * T0 has a wrapper class TW, a boxing conversion to TW is applied,
- * possibly followed by a reference conversion.
- * T1 must be TW or a supertype.
- * <li>If T0 is a reference and T1 a primitive, and
- * T1 has a wrapper class TW, an unboxing conversion is applied,
- * possibly preceded by a reference conversion.
- * T0 must be TW or a supertype.
- * <li>If T1 is void, the return value is discarded
- * <li>If T0 is void and T1 a reference, a null value is introduced.
- * <li>If T0 is void and T1 a primitive, a zero value is introduced.
- * </ul>
- * If the value is discarded, null will be returned.
- * @param valueType
- * @param value
- * @return the value, converted if necessary
- * @throws java.lang.ClassCastException if a cast fails
- */
- // FIXME: This is used in just one place. Refactor away.
- static
- <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value)
- throws ClassCastException
- {
- if (t0 == t1) {
- // no conversion needed; just reassert the same type
- if (t0.isPrimitive())
- return Wrapper.asPrimitiveType(t1).cast(value);
- else
- return Wrapper.OBJECT.convert(value, t1);
- }
- boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive();
- if (!prim0) {
- // check contract with caller
- Wrapper.OBJECT.convert(value, t0);
- if (!prim1) {
- return Wrapper.OBJECT.convert(value, t1);
- }
- // convert reference to primitive by unboxing
- Wrapper w1 = Wrapper.forPrimitiveType(t1);
- return w1.convert(value, t1);
- }
- // check contract with caller:
- Wrapper.asWrapperType(t0).cast(value);
- Wrapper w1 = Wrapper.forPrimitiveType(t1);
- return w1.convert(value, t1);
+ static /*non-public*/
+ MethodHandle basicInvoker(MethodType type) {
+ return type.form().basicInvoker();
}
- // FIXME: Delete this. It is used only for insertArguments & bindTo.
- // Replace by a more standard check.
- static
- Object checkValue(Class<?> T1, Object value)
- throws ClassCastException
- {
- Class<?> T0;
- if (value == null)
- T0 = Object.class;
- else
- T0 = value.getClass();
- return checkValue(T0, T1, value);
- }
-
- /// method handle modification (creation from other method handles)
+ /// method handle modification (creation from other method handles)
/**
* Produces a method handle which adapts the type of the
@@ -1541,7 +1467,10 @@
*/
public static
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
- return MethodHandleImpl.convertArguments(target, newType, 2);
+ if (!target.type().isCastableTo(newType)) {
+ throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType);
+ }
+ return MethodHandleImpl.makePairwiseConvert(target, newType, 2);
}
/**
@@ -1605,11 +1534,8 @@
*/
public static
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
- MethodType oldType = target.type();
- checkReorder(reorder, newType, oldType);
- return MethodHandleImpl.permuteArguments(target,
- newType, oldType,
- reorder);
+ checkReorder(reorder, newType, target.type());
+ return target.permuteArguments(newType, reorder);
}
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
@@ -1678,8 +1604,7 @@
else if (type.isPrimitive())
return ValueConversions.identity(Wrapper.forPrimitiveType(type));
else
- return AdapterMethodHandle.makeRetypeRaw(
- MethodType.methodType(type, type), ValueConversions.identity());
+ return MethodHandleImpl.makeReferenceIdentity(type);
}
/**
@@ -1725,18 +1650,26 @@
MethodHandle result = target;
for (int i = 0; i < insCount; i++) {
Object value = values[i];
- Class<?> valueType = oldType.parameterType(pos+i);
- value = checkValue(valueType, value);
- if (pos == 0 && !valueType.isPrimitive()) {
- // At least for now, make bound method handles a special case.
- MethodHandle bmh = MethodHandleImpl.bindReceiver(result, value);
- if (bmh != null) {
- result = bmh;
- continue;
+ Class<?> ptype = oldType.parameterType(pos+i);
+ if (ptype.isPrimitive()) {
+ char btype = 'I';
+ Wrapper w = Wrapper.forPrimitiveType(ptype);
+ switch (w) {
+ case LONG: btype = 'J'; break;
+ case FLOAT: btype = 'F'; break;
+ case DOUBLE: btype = 'D'; break;
}
- // else fall through to general adapter machinery
+ // perform unboxing and/or primitive conversion
+ value = w.convert(value, ptype);
+ result = result.bindArgument(pos, btype, value);
+ continue;
}
- result = MethodHandleImpl.bindArgument(result, pos, value);
+ value = ptype.cast(value); // throw CCE if needed
+ if (pos == 0) {
+ result = result.bindReceiver(value);
+ } else {
+ result = result.bindArgument(pos, 'L', value);
+ }
}
return result;
}
@@ -1786,16 +1719,17 @@
public static
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
MethodType oldType = target.type(); // get NPE
- if (valueTypes.size() == 0) return target;
+ int dropped = valueTypes.size();
+ MethodType.checkSlotCount(dropped);
+ if (dropped == 0) return target;
int outargs = oldType.parameterCount();
- int inargs = outargs + valueTypes.size();
+ int inargs = outargs + dropped;
if (pos < 0 || pos >= inargs)
throw newIllegalArgumentException("no argument type to remove");
- ArrayList<Class<?>> ptypes =
- new ArrayList<Class<?>>(oldType.parameterList());
+ ArrayList<Class<?>> ptypes = new ArrayList<>(oldType.parameterList());
ptypes.addAll(pos, valueTypes);
MethodType newType = MethodType.methodType(oldType.returnType(), ptypes);
- return MethodHandleImpl.dropArguments(target, newType, pos);
+ return target.dropArguments(newType, pos, dropped);
}
/**
@@ -1939,7 +1873,7 @@
if (filterType.parameterCount() != 1
|| filterType.returnType() != targetType.parameterType(pos))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
- return MethodHandleImpl.filterArgument(target, pos, filter);
+ return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
}
/**
@@ -2011,10 +1945,7 @@
throw newIllegalArgumentException("target and filter types do not match", target, filter);
// result = fold( lambda(retval, arg...) { filter(retval) },
// lambda( arg...) { target(arg...) } )
- MethodType newType = targetType.changeReturnType(filterType.returnType());
- MethodHandle result = null;
- assert(AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false));
- return AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
+ return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
}
/**
@@ -2112,9 +2043,7 @@
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType);
MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
- MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
- if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
- return res;
+ return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
}
/**
@@ -2255,6 +2184,8 @@
*/
public static
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
+ if (!Throwable.class.isAssignableFrom(exType))
+ throw new ClassCastException(exType.getName());
return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
}
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java Fri Aug 10 16:03:28 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -26,12 +26,14 @@
package java.lang.invoke;
import sun.invoke.util.Wrapper;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import sun.invoke.util.BytecodeDescriptor;
import static java.lang.invoke.MethodHandleStatics.*;
+import sun.invoke.util.VerifyType;
/**
* A method type represents the arguments and return type accepted and
@@ -107,6 +109,8 @@
/*trusted*/ Class<?> rtype() { return rtype; }
/*trusted*/ Class<?>[] ptypes() { return ptypes; }
+ void setForm(MethodTypeForm f) { form = f; }
+
private static void checkRtype(Class<?> rtype) {
rtype.equals(rtype); // null check
}
@@ -126,7 +130,7 @@
checkSlotCount(ptypes.length + slots);
return slots;
}
- private static void checkSlotCount(int count) {
+ static void checkSlotCount(int count) {
if ((count & 0xFF) != count)
throw newIllegalArgumentException("bad parameter count "+count);
}
@@ -135,8 +139,7 @@
return new IndexOutOfBoundsException(num.toString());
}
- static final HashMap<MethodType,MethodType> internTable
- = new HashMap<MethodType, MethodType>();
+ static final WeakInternSet internTable = new WeakInternSet();
static final Class<?>[] NO_PTYPES = {};
@@ -238,31 +241,17 @@
ptypes = NO_PTYPES; trusted = true;
}
MethodType mt1 = new MethodType(rtype, ptypes);
- MethodType mt0;
- synchronized (internTable) {
- mt0 = internTable.get(mt1);
- if (mt0 != null)
- return mt0;
- }
+ MethodType mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
if (!trusted)
// defensively copy the array passed in by the user
mt1 = new MethodType(rtype, ptypes.clone());
// promote the object to the Real Thing, and reprobe
MethodTypeForm form = MethodTypeForm.findForm(mt1);
mt1.form = form;
- if (form.erasedType == mt1) {
- // This is a principal (erased) type; show it to the JVM.
- MethodHandleNatives.init(mt1);
- }
- synchronized (internTable) {
- mt0 = internTable.get(mt1);
- if (mt0 != null)
- return mt0;
- internTable.put(mt1, mt1);
- }
- return mt1;
+ return internTable.add(mt1);
}
-
private static final MethodType[] objectOnlyTypes = new MethodType[20];
/**
@@ -394,6 +383,32 @@
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
+ /**
+ * Finds or creates a method type with modified parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param start the position (zero-based) of the first replaced parameter type(s)
+ * @param end the position (zero-based) after the last replaced parameter type(s)
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
+ * @return the same type, except with the selected parameter(s) replaced
+ * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
+ * or if {@code end} is negative or greater than {@code parameterCount()}
+ * or if {@code start} is greater than {@code end}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ /*non-public*/ MethodType replaceParameterTypes(int start, int end, Class<?>... ptypesToInsert) {
+ if (start == end)
+ return insertParameterTypes(start, ptypesToInsert);
+ int len = ptypes.length;
+ if (!(0 <= start && start <= end && end <= len))
+ throw newIndexOutOfBoundsException("start="+start+" end="+end);
+ int ilen = ptypesToInsert.length;
+ if (ilen == 0)
+ return dropParameterTypes(start, end);
+ return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
+ }
+
/**
* Finds or creates a method type with some parameter types omitted.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
@@ -474,6 +489,23 @@
}
/**
+ * Erases all reference types to {@code Object}, and all subword types to {@code int}.
+ * This is the reduced type polymorphism used by private methods
+ * such as {@link MethodHandle#invokeBasic invokeBasic}.
+ * @return a version of the original type with all reference and subword types replaced
+ */
+ /*non-public*/ MethodType basicType() {
+ return form.basicType();
+ }
+
+ /**
+ * @return a version of the original type with MethodHandle prepended as the first argument
+ */
+ /*non-public*/ MethodType invokerType() {
+ return insertParameterTypes(0, MethodHandle.class);
+ }
+
+ /**
* Converts all types, both reference and primitive, to {@code Object}.
* Convenience method for {@link #genericMethodType(int) genericMethodType}.
* The expression {@code type.wrap().erase()} produces the same value
@@ -567,6 +599,11 @@
return Collections.unmodifiableList(Arrays.asList(ptypes));
}
+ /*non-public*/ Class<?> lastParameterType() {
+ int len = ptypes.length;
+ return len == 0 ? void.class : ptypes[len-1];
+ }
+
/**
* Presents the parameter types as an array (a convenience method).
* Changes to the array will not result in changes to the type.
@@ -636,6 +673,26 @@
/*non-public*/
+ boolean isViewableAs(MethodType newType) {
+ if (!VerifyType.isNullConversion(returnType(), newType.returnType()))
+ return false;
+ int argc = parameterCount();
+ if (argc != newType.parameterCount())
+ return false;
+ for (int i = 0; i < argc; i++) {
+ if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i)))
+ return false;
+ }
+ return true;
+ }
+ /*non-public*/
+ boolean isCastableTo(MethodType newType) {
+ int argc = parameterCount();
+ if (argc != newType.parameterCount())
+ return false;
+ return true;
+ }
+ /*non-public*/
boolean isConvertibleTo(MethodType newType) {
if (!canConvert(returnType(), newType.returnType()))
return false;
@@ -818,6 +875,10 @@
return BytecodeDescriptor.unparse(this);
}
+ /*non-public*/ static String toFieldDescriptorString(Class<?> cls) {
+ return BytecodeDescriptor.unparse(cls);
+ }
+
/// Serialization.
/**
@@ -890,18 +951,17 @@
// store them into the implementation-specific final fields.
checkRtype(rtype);
checkPtypes(ptypes);
- unsafe.putObject(this, rtypeOffset, rtype);
- unsafe.putObject(this, ptypesOffset, ptypes);
+ UNSAFE.putObject(this, rtypeOffset, rtype);
+ UNSAFE.putObject(this, ptypesOffset, ptypes);
}
// Support for resetting final fields while deserializing
- private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
private static final long rtypeOffset, ptypesOffset;
static {
try {
- rtypeOffset = unsafe.objectFieldOffset
+ rtypeOffset = UNSAFE.objectFieldOffset
(MethodType.class.getDeclaredField("rtype"));
- ptypesOffset = unsafe.objectFieldOffset
+ ptypesOffset = UNSAFE.objectFieldOffset
(MethodType.class.getDeclaredField("ptypes"));
} catch (Exception ex) {
throw new Error(ex);
@@ -919,4 +979,269 @@
// Verify all operands, and make sure ptypes is unshared:
return methodType(rtype, ptypes);
}
+
+ /**
+ * Weak intern set based on implementation of the <tt>HashSet</tt> and
+ * <tt>WeakHashMap</tt>, with <em>weak values</em>. Note: <tt>null</tt>
+ * values will yield <tt>NullPointerException</tt>
+ * Refer to implementation of WeakInternSet for details.
+ *
+ * @see java.util.HashMap
+ * @see java.util.HashSet
+ * @see java.util.WeakHashMap
+ * @see java.lang.ref.WeakReference
+ */
+ private static class WeakInternSet {
+ // The default initial capacity -- MUST be a power of two.
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ // The maximum capacity, used if a higher value is implicitly specified
+ // by either of the constructors with arguments.
+ // MUST be a power of two <= 1<<30.
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ // The load factor used when none specified in constructor.
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ // The table, resized as necessary. Length MUST Always be a power of two.
+ private Entry[] table;
+
+ // The number of entries contained in this set.
+ private int size;
+
+ // The next size value at which to resize (capacity * load factor).
+ private int threshold;
+
+ // The load factor for the hash table.
+ private final float loadFactor;
+
+ // Reference queue for cleared WeakEntries
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+
+ private Entry[] newTable(int n) {
+ return new Entry[n];
+ }
+
+ /**
+ * Constructs a new, empty <tt>WeakInternSet</tt> with the default initial
+ * capacity (16) and load factor (0.75).
+ */
+ WeakInternSet() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ threshold = DEFAULT_INITIAL_CAPACITY;
+ table = newTable(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Applies a supplemental hash function to a given hashCode, which
+ * defends against poor quality hash functions. This is critical
+ * because hashing uses power-of-two length hash tables, that
+ * otherwise encounter collisions for hashCodes that do not differ
+ * in lower bits.
+ * @param h preliminary hash code value
+ * @return supplemental hash code value
+ */
+ private static int hash(int h) {
+ // This function ensures that hashCodes that differ only by
+ // constant multiples at each bit position have a bounded
+ // number of collisions (approximately 8 at default load factor).
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /**
+ * Checks for equality of non-null reference x and possibly-null y. By
+ * default uses Object.equals.
+ * @param x first object to compare
+ * @param y second object to compare
+ * @return <tt>true</tt> if objects are equal
+ */
+ private static boolean eq(Object x, Object y) {
+ return x == y || x.equals(y);
+ }
+
+ /**
+ * Returns index for hash code h.
+ * @param h raw hash code
+ * @param length length of table (power of 2)
+ * @return index in table
+ */
+ private static int indexFor(int h, int length) {
+ return h & (length-1);
+ }
+
+ /**
+ * Expunges stale entries from the table.
+ */
+ private void expungeStaleEntries() {
+ for (Object x; (x = queue.poll()) != null; ) {
+ synchronized (queue) {
+ Entry entry = (Entry) x;
+ int i = indexFor(entry.hash, table.length);
+ Entry prev = table[i];
+ Entry p = prev;
+ while (p != null) {
+ Entry next = p.next;
+ if (p == entry) {
+ if (prev == entry)
+ table[i] = next;
+ else
+ prev.next = next;
+ entry.next = null;
+ size--;
+ break;
+ }
+ prev = p;
+ p = next;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the table after first expunging stale entries.
+ * @return an expunged hash table
+ */
+ private Entry[] getTable() {
+ expungeStaleEntries();
+ return table;
+ }
+
+ /**
+ * Returns the entry to which the specified value is mapped,
+ * or {@code null} if this set contains no entry for the value.
+ *
+ * <p>More formally, if this set contains an entry for value
+ * {@code entry} to a value {@code value} such that
+ * {@code entry.equals(value)}, then this method returns {@code entry};
+ * otherwise it returns {@code null}.
+ *
+ * @param value value to search for in set
+ * @return interned value if in set, otherwise <tt>null</tt>
+ */
+ synchronized MethodType get(MethodType value) {
+ int h = hash(value.hashCode());
+ Entry[] tab = getTable();
+ int index = indexFor(h, tab.length);
+ Entry e = tab[index];
+ MethodType g;
+ while (e != null) {
+ if (e.hash == h && eq(value, g = e.get()))
+ return g;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Attempts to add the specified value to the set and returns same value.
+ * If the set previously contained an entry for this value, the old
+ * value is left untouched and returned as the result.
+ *
+ * @param value value to be added
+ * @return the previous entry associated with <tt>value</tt>, or
+ * <tt>value</tt> if there was no previous entry found
+ */
+ synchronized MethodType add(MethodType value) {
+ int h = hash(value.hashCode());
+ Entry[] tab = getTable();
+ int i = indexFor(h, tab.length);
+ MethodType g;
+ for (Entry e = tab[i]; e != null; e = e.next) {
+ if (h == e.hash && eq(value, g = e.get())) {
+ return g;
+ }
+ }
+ Entry e = tab[i];
+ tab[i] = new Entry(value, queue, h, e);
+ if (++size >= threshold)
+ resize(tab.length * 2);
+ return value;
+ }
+
+ /**
+ * Rehashes the contents of this set into a new array with a
+ * larger capacity. This method is called automatically when the
+ * number of keys in this set reaches its threshold.
+ *
+ * If current capacity is MAXIMUM_CAPACITY, this method does not
+ * resize the set, but sets threshold to Integer.MAX_VALUE.
+ * This has the effect of preventing future calls.
+ *
+ * @param newCapacity the new capacity, MUST be a power of two;
+ * must be greater than current capacity unless current
+ * capacity is MAXIMUM_CAPACITY (in which case value
+ * is irrelevant)
+ */
+ private void resize(int newCapacity) {
+ Entry[] oldTable = getTable();
+ int oldCapacity = oldTable.length;
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ Entry[] newTable = newTable(newCapacity);
+ transfer(oldTable, newTable);
+ table = newTable;
+
+ /*
+ * If ignoring null elements and processing ref queue caused massive
+ * shrinkage, then restore old table. This should be rare, but avoids
+ * unbounded expansion of garbage-filled tables.
+ */
+ if (size >= threshold / 2) {
+ threshold = (int)(newCapacity * loadFactor);
+ } else {
+ expungeStaleEntries();
+ transfer(newTable, oldTable);
+ table = oldTable;
+ }
+ }
+
+ /**
+ * Transfers all entries from src to dest tables
+ * @param src original table
+ * @param dest new table
+ */
+ private void transfer(Entry[] src, Entry[] dest) {
+ for (int j = 0; j < src.length; ++j) {
+ Entry e = src[j];
+ src[j] = null;
+ while (e != null) {
+ Entry next = e.next;
+ MethodType key = e.get();
+ if (key == null) {
+ e.next = null; // Help GC
+ size--;
+ } else {
+ int i = indexFor(e.hash, dest.length);
+ e.next = dest[i];
+ dest[i] = e;
+ }
+ e = next;
+ }
+ }
+ }
+
+ /**
+ * The entries in this hash table extend WeakReference, using its main ref
+ * field as the key.
+ */
+ private static class Entry extends WeakReference<MethodType> {
+ final int hash;
+ Entry next;
+
+ /**
+ * Creates new entry.
+ */
+ Entry(MethodType key,
+ ReferenceQueue<Object> queue,
+ int hash, Entry next) {
+ super(key, queue);
+ this.hash = hash;
+ this.next = next;
+ }
+ }
+ }
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Fri Aug 10 16:03:28 2012 -0700
@@ -27,6 +27,7 @@
import sun.invoke.util.Wrapper;
import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
/**
* Shared information for a group of method types, which differ
@@ -41,27 +42,70 @@
* No more than half of these are likely to be loaded at once.
* @author John Rose
*/
-class MethodTypeForm {
+final class MethodTypeForm {
final int[] argToSlotTable, slotToArgTable;
final long argCounts; // packed slot & value counts
final long primCounts; // packed prim & double counts
final int vmslots; // total number of parameter slots
- private Object vmlayout; // vm-specific information for calls
final MethodType erasedType; // the canonical erasure
-
- /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
- /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return
- /*lazy*/ MethodType primsAsInts; // replace prims by int/long
- /*lazy*/ MethodType primsAsLongs; // replace prims by long
- /*lazy*/ MethodType primsAtEnd; // reorder primitives to the end
+ final MethodType basicType; // the canonical erasure, with primitives simplified
// Cached adapter information:
- /*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke
+ /*lazy*/ MethodHandle genericInvoker; // JVM hook for inexact invoke
+ /*lazy*/ MethodHandle basicInvoker; // cached instance of MH.invokeBasic
+ /*lazy*/ MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
+
+ // Cached lambda form information, for basic types only:
+ final LambdaForm[] lambdaForms;
+ // Indexes into lambdaForms:
+ static final int
+ LF_INVVIRTUAL = 0, // DMH invokeVirtual
+ LF_INVSTATIC = 1,
+ LF_INVSPECIAL = 2,
+ LF_NEWINVSPECIAL = 3,
+ LF_INVINTERFACE = 4,
+ LF_INVSTATIC_INIT = 5, // DMH invokeStatic with <clinit> barrier
+ LF_INTERPRET = 6, // LF interpreter
+ LF_COUNTER = 7, // CMH wrapper
+ LF_REINVOKE = 8, // other wrapper
+ LF_EX_LINKER = 9, // invokeExact_MT
+ LF_EX_INVOKER = 10, // invokeExact MH
+ LF_GEN_LINKER = 11,
+ LF_GEN_INVOKER = 12,
+ LF_CS_LINKER = 13, // linkToCallSite_CS
+ LF_LIMIT = 14;
public MethodType erasedType() {
return erasedType;
}
+ public MethodType basicType() {
+ return basicType;
+ }
+
+ public LambdaForm cachedLambdaForm(int which) {
+ return lambdaForms[which];
+ }
+
+ public LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
+ // Should we perform some sort of CAS, to avoid racy duplication?
+ return lambdaForms[which] = form;
+ }
+
+ public MethodHandle basicInvoker() {
+ assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also
+ MethodHandle invoker = basicInvoker;
+ if (invoker != null) return invoker;
+ invoker = basicType.invokers().makeBasicInvoker();
+ basicInvoker = invoker;
+ return invoker;
+ }
+
+ /**
+ * Build an MTF for a given type, which must have all references erased to Object.
+ * This MTF will stand for that type and all un-erased variations.
+ * Eagerly compute some basic properties of the type, common to all variations.
+ */
protected MethodTypeForm(MethodType erasedType) {
this.erasedType = erasedType;
@@ -75,26 +119,41 @@
// Walk the argument types, looking for primitives.
int pac = 0, lac = 0, prc = 0, lrc = 0;
- Class<?> epts[] = ptypes;
+ Class<?>[] epts = ptypes;
+ Class<?>[] bpts = epts;
for (int i = 0; i < epts.length; i++) {
Class<?> pt = epts[i];
if (pt != Object.class) {
- assert(pt.isPrimitive());
++pac;
- if (hasTwoArgSlots(pt)) ++lac;
+ Wrapper w = Wrapper.forPrimitiveType(pt);
+ if (w.isDoubleWord()) ++lac;
+ if (w.isSubwordOrInt() && pt != int.class) {
+ if (bpts == epts)
+ bpts = bpts.clone();
+ bpts[i] = int.class;
+ }
}
}
pslotCount += lac; // #slots = #args + #longs
Class<?> rt = erasedType.returnType();
+ Class<?> bt = rt;
if (rt != Object.class) {
++prc; // even void.class counts as a prim here
- if (hasTwoArgSlots(rt)) ++lrc;
+ Wrapper w = Wrapper.forPrimitiveType(rt);
+ if (w.isDoubleWord()) ++lrc;
+ if (w.isSubwordOrInt() && rt != int.class)
+ bt = int.class;
// adjust #slots, #args
if (rt == void.class)
rtypeCount = rslotCount = 0;
else
rslotCount += lrc;
}
+ if (epts == bpts && bt == rt) {
+ this.basicType = erasedType;
+ } else {
+ this.basicType = MethodType.makeImpl(bt, bpts, true);
+ }
if (lac != 0) {
int slot = ptypeCount + lac;
slotToArgTab = new int[slot+1];
@@ -102,7 +161,8 @@
argToSlotTab[0] = slot; // argument "-1" is past end of slots
for (int i = 0; i < epts.length; i++) {
Class<?> pt = epts[i];
- if (hasTwoArgSlots(pt)) --slot;
+ Wrapper w = Wrapper.forBasicType(pt);
+ if (w.isDoubleWord()) --slot;
--slot;
slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
argToSlotTab[1+i] = slot;
@@ -130,164 +190,13 @@
// send a few bits down to the JVM:
this.vmslots = parameterSlotCount();
- // short circuit some no-op canonicalizations:
- if (!hasPrimitives()) {
- primsAsBoxes = erasedType;
- primArgsAsBoxes = erasedType;
- primsAsInts = erasedType;
- primsAsLongs = erasedType;
- primsAtEnd = erasedType;
+ if (basicType == erasedType) {
+ lambdaForms = new LambdaForm[LF_LIMIT];
+ } else {
+ lambdaForms = null; // could be basicType.form().lambdaForms;
}
}
- /** Turn all primitive types to corresponding wrapper types.
- */
- public MethodType primsAsBoxes() {
- MethodType ct = primsAsBoxes;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(erasedType, WRAP, WRAP);
- if (ct == null) ct = t; // no prims to box
- return primsAsBoxes = ct;
- }
-
- /** Turn all primitive argument types to corresponding wrapper types.
- * Subword and void return types are promoted to int.
- */
- public MethodType primArgsAsBoxes() {
- MethodType ct = primArgsAsBoxes;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(erasedType, RAW_RETURN, WRAP);
- if (ct == null) ct = t; // no prims to box
- return primArgsAsBoxes = ct;
- }
-
- /** Turn all primitive types to either int or long.
- * Floating point return types are not changed, because
- * they may require special calling sequences.
- * A void return value is turned to int.
- */
- public MethodType primsAsInts() {
- MethodType ct = primsAsInts;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(t, RAW_RETURN, INTS);
- if (ct == null) ct = t; // no prims to int-ify
- return primsAsInts = ct;
- }
-
- /** Turn all primitive types to either int or long.
- * Floating point return types are not changed, because
- * they may require special calling sequences.
- * A void return value is turned to int.
- */
- public MethodType primsAsLongs() {
- MethodType ct = primsAsLongs;
- if (ct != null) return ct;
- MethodType t = erasedType;
- ct = canonicalize(t, RAW_RETURN, LONGS);
- if (ct == null) ct = t; // no prims to int-ify
- return primsAsLongs = ct;
- }
-
- /** Stably sort parameters into 3 buckets: ref, int, long. */
- public MethodType primsAtEnd() {
- MethodType ct = primsAtEnd;
- if (ct != null) return ct;
- MethodType t = erasedType;
-
- int pac = primitiveParameterCount();
- if (pac == 0)
- return primsAtEnd = t;
-
- int argc = parameterCount();
- int lac = longPrimitiveParameterCount();
- if (pac == argc && (lac == 0 || lac == argc))
- return primsAtEnd = t;
-
- // known to have a mix of 2 or 3 of ref, int, long
- int[] reorder = primsAtEndOrder(t);
- ct = reorderParameters(t, reorder, null);
- //System.out.println("t="+t+" / reorder="+java.util.Arrays.toString(reorder)+" => "+ct);
- return primsAtEnd = ct;
- }
-
- /** Compute a new ordering of parameters so that all references
- * are before all ints or longs, and all ints are before all longs.
- * For this ordering, doubles count as longs, and all other primitive
- * values count as ints.
- * As a special case, if the parameters are already in the specified
- * order, this method returns a null reference, rather than an array
- * specifying a null permutation.
- * <p>
- * For example, the type {@code (int,boolean,int,Object,String)void}
- * produces the order {@code {3,4,0,1,2}}, the type
- * {@code (long,int,String)void} produces {@code {2,1,2}}, and
- * the type {@code (Object,int)Object} produces {@code null}.
- */
- public static int[] primsAtEndOrder(MethodType mt) {
- MethodTypeForm form = mt.form();
- if (form.primsAtEnd == form.erasedType)
- // quick check shows no reordering is necessary
- return null;
-
- int argc = form.parameterCount();
- int[] paramOrder = new int[argc];
-
- // 3-way bucket sort:
- int pac = form.primitiveParameterCount();
- int lac = form.longPrimitiveParameterCount();
- int rfill = 0, ifill = argc - pac, lfill = argc - lac;
-
- Class<?>[] ptypes = mt.ptypes();
- boolean changed = false;
- for (int i = 0; i < ptypes.length; i++) {
- Class<?> pt = ptypes[i];
- int ord;
- if (!pt.isPrimitive()) ord = rfill++;
- else if (!hasTwoArgSlots(pt)) ord = ifill++;
- else ord = lfill++;
- if (ord != i) changed = true;
- assert(paramOrder[ord] == 0);
- paramOrder[ord] = i;
- }
- assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
- if (!changed) {
- form.primsAtEnd = form.erasedType;
- return null;
- }
- return paramOrder;
- }
-
- /** Put the existing parameters of mt into a new order, given by newParamOrder.
- * The third argument is logically appended to mt.parameterArray,
- * so that elements of newParamOrder can index either pre-existing or
- * new parameter types.
- */
- public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class<?>[] moreParams) {
- if (newParamOrder == null) return mt; // no-op reordering
- Class<?>[] ptypes = mt.ptypes();
- Class<?>[] ntypes = new Class<?>[newParamOrder.length];
- int maxParam = ptypes.length + (moreParams == null ? 0 : moreParams.length);
- boolean changed = (ntypes.length != ptypes.length);
- for (int i = 0; i < newParamOrder.length; i++) {
- int param = newParamOrder[i];
- if (param != i) changed = true;
- Class<?> nt;
- if (param < ptypes.length) nt = ptypes[param];
- else if (param == maxParam) nt = mt.returnType();
- else nt = moreParams[param - ptypes.length];
- ntypes[i] = nt;
- }
- if (!changed) return mt;
- return MethodType.makeImpl(mt.returnType(), ntypes, true);
- }
-
- private static boolean hasTwoArgSlots(Class<?> type) {
- return type == long.class || type == double.class;
- }
-
private static long pack(int a, int b, int c, int d) {
assert(((a|b|c|d) & ~0xFFFF) == 0);
long hw = ((a << 16) | b), lw = ((c << 16) | d);
@@ -325,11 +234,11 @@
public boolean hasPrimitives() {
return primCounts != 0;
}
-// public boolean hasNonVoidPrimitives() {
-// if (primCounts == 0) return false;
-// if (primitiveParameterCount() != 0) return true;
-// return (primitiveReturnCount() != 0 && returnCount() != 0);
-// }
+ public boolean hasNonVoidPrimitives() {
+ if (primCounts == 0) return false;
+ if (primitiveParameterCount() != 0) return true;
+ return (primitiveReturnCount() != 0 && returnCount() != 0);
+ }
public boolean hasLongPrimitives() {
return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
}
@@ -455,18 +364,6 @@
return cs;
}
- /*non-public*/ void notifyGenericMethodType() {
- if (genericInvoker != null) return;
- try {
- // Trigger adapter creation.
- genericInvoker = InvokeGeneric.generalInvokerOf(erasedType);
- } catch (Exception ex) {
- Error err = new InternalError("Exception while resolving inexact invoke", ex);
- err.initCause(ex);
- throw err;
- }
- }
-
@Override
public String toString() {
return "Form"+erasedType;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/SimpleMethodHandle.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2008, 2011, 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 static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A method handle whose behavior is determined only by its LambdaForm.
+ * @author jrose
+ */
+final class SimpleMethodHandle extends MethodHandle {
+ SimpleMethodHandle(MethodType type, LambdaForm form) {
+ super(type, form);
+ }
+
+ @Override
+ MethodHandle bindArgument(int pos, char basicType, Object value) {
+ MethodType type2 = type().dropParameterTypes(pos, pos+1);
+ LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
+ return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
+ }
+
+ @Override
+ MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
+ LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
+ return new SimpleMethodHandle(srcType, newForm);
+ }
+
+ @Override
+ MethodHandle permuteArguments(MethodType newType, int[] reorder) {
+ LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList()));
+ return new SimpleMethodHandle(newType, form2);
+ }
+
+ @Override
+ MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+ return new SimpleMethodHandle(mt, lf);
+ }
+
+}
--- a/jdk/src/share/classes/java/lang/invoke/package-info.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/package-info.java Fri Aug 10 16:03:28 2012 -0700
@@ -191,6 +191,13 @@
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
* resulting in a {@code BootstrapMethodError}.)
* <p>
+ * Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
+ * argument, if it can be represented by a constant pool entry.
+ * However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
+ * cannot be created for bootstrap methods, since such constants cannot be directly
+ * represented in the constant pool, and the invocation of the bootstrap method will
+ * not perform the necessary narrowing primitive conversions.
+ * <p>
* Extra bootstrap method arguments are intended to allow language implementors
* to safely and compactly encode metadata.
* In principle, the name and extra arguments are redundant,
--- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Fri Aug 10 16:03:28 2012 -0700
@@ -118,42 +118,15 @@
return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
}
- /// Converting references to "raw" values.
- /// A raw primitive value is always an int or long.
-
- static int unboxByteRaw(Object x, boolean cast) {
- return unboxByte(x, cast);
- }
-
- static int unboxShortRaw(Object x, boolean cast) {
- return unboxShort(x, cast);
- }
-
- static int unboxBooleanRaw(Object x, boolean cast) {
- return unboxBoolean(x, cast) ? 1 : 0;
- }
-
- static int unboxCharacterRaw(Object x, boolean cast) {
- return unboxCharacter(x, cast);
- }
-
- static int unboxFloatRaw(Object x, boolean cast) {
- return Float.floatToIntBits(unboxFloat(x, cast));
- }
-
- static long unboxDoubleRaw(Object x, boolean cast) {
- return Double.doubleToRawLongBits(unboxDouble(x, cast));
- }
-
- private static MethodType unboxType(Wrapper wrap, boolean raw) {
- return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class);
+ private static MethodType unboxType(Wrapper wrap) {
+ return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
}
private static final EnumMap<Wrapper, MethodHandle>[]
- UNBOX_CONVERSIONS = newWrapperCaches(4);
+ UNBOX_CONVERSIONS = newWrapperCaches(2);
- private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) {
- EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)];
+ private static MethodHandle unbox(Wrapper wrap, boolean cast) {
+ EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)];
MethodHandle mh = cache.get(wrap);
if (mh != null) {
return mh;
@@ -163,19 +136,15 @@
case OBJECT:
mh = IDENTITY; break;
case VOID:
- mh = raw ? ALWAYS_ZERO : IGNORE; break;
- case INT: case LONG:
- // these guys don't need separate raw channels
- if (raw) mh = unbox(wrap, false, cast);
- break;
+ mh = IGNORE; break;
}
if (mh != null) {
cache.put(wrap, mh);
return mh;
}
// look up the method
- String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
- MethodType type = unboxType(wrap, raw);
+ String name = "unbox" + wrap.wrapperSimpleName();
+ MethodType type = unboxType(wrap);
try {
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
} catch (ReflectiveOperationException ex) {
@@ -187,32 +156,30 @@
return mh;
}
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
- + (cast ? " (cast)" : "") + (raw ? " (raw)" : ""));
+ + (cast ? " (cast)" : ""));
}
public static MethodHandle unboxCast(Wrapper type) {
- return unbox(type, false, true);
- }
-
- public static MethodHandle unboxRaw(Wrapper type) {
- return unbox(type, true, false);
+ return unbox(type, true);
}
public static MethodHandle unbox(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), false, false);
+ return unbox(Wrapper.forPrimitiveType(type), false);
}
public static MethodHandle unboxCast(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), false, true);
- }
-
- public static MethodHandle unboxRaw(Class<?> type) {
- return unbox(Wrapper.forPrimitiveType(type), true, false);
+ return unbox(Wrapper.forPrimitiveType(type), true);
}
static private final Integer ZERO_INT = 0, ONE_INT = 1;
/// Primitive conversions
+ /**
+ * Produce a Number which represents the given value {@code x}
+ * according to the primitive type of the given wrapper {@code wrap}.
+ * Caller must invoke intValue, byteValue, longValue (etc.) on the result
+ * to retrieve the desired primitive value.
+ */
public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
// Maybe merge this code with Wrapper.convert/cast.
Number res = null;
@@ -237,6 +204,27 @@
return res;
}
+ /**
+ * The JVM verifier allows boolean, byte, short, or char to widen to int.
+ * Support exactly this conversion, from a boxed value type Boolean,
+ * Byte, Short, Character, or Integer.
+ */
+ public static int widenSubword(Object x) {
+ if (x instanceof Integer)
+ return (int) x;
+ else if (x instanceof Boolean)
+ return fromBoolean((boolean) x);
+ else if (x instanceof Character)
+ return (char) x;
+ else if (x instanceof Short)
+ return (short) x;
+ else if (x instanceof Byte)
+ return (byte) x;
+ else
+ // Fail with a ClassCastException.
+ return (int) x;
+ }
+
/// Converting primitives to references
static Integer boxInteger(int x) {
@@ -271,53 +259,17 @@
return x;
}
- /// Converting raw primitives to references
-
- static Byte boxByteRaw(int x) {
- return boxByte((byte)x);
- }
-
- static Short boxShortRaw(int x) {
- return boxShort((short)x);
- }
-
- static Boolean boxBooleanRaw(int x) {
- return boxBoolean(x != 0);
- }
-
- static Character boxCharacterRaw(int x) {
- return boxCharacter((char)x);
- }
-
- static Float boxFloatRaw(int x) {
- return boxFloat(Float.intBitsToFloat(x));
- }
-
- static Double boxDoubleRaw(long x) {
- return boxDouble(Double.longBitsToDouble(x));
- }
-
- // a raw void value is (arbitrarily) a garbage int
- static Void boxVoidRaw(int x) {
- return null;
- }
-
- private static MethodType boxType(Wrapper wrap, boolean raw) {
+ private static MethodType boxType(Wrapper wrap) {
// be exact, since return casts are hard to compose
Class<?> boxType = wrap.wrapperType();
- return MethodType.methodType(boxType, rawWrapper(wrap, raw).primitiveType());
- }
-
- private static Wrapper rawWrapper(Wrapper wrap, boolean raw) {
- if (raw) return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT;
- return wrap;
+ return MethodType.methodType(boxType, wrap.primitiveType());
}
private static final EnumMap<Wrapper, MethodHandle>[]
- BOX_CONVERSIONS = newWrapperCaches(4);
+ BOX_CONVERSIONS = newWrapperCaches(2);
- private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) {
- EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+ private static MethodHandle box(Wrapper wrap, boolean exact) {
+ EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)];
MethodHandle mh = cache.get(wrap);
if (mh != null) {
return mh;
@@ -327,11 +279,7 @@
case OBJECT:
mh = IDENTITY; break;
case VOID:
- if (!raw) mh = ZERO_OBJECT;
- break;
- case INT: case LONG:
- // these guys don't need separate raw channels
- if (raw) mh = box(wrap, exact, false);
+ mh = ZERO_OBJECT;
break;
}
if (mh != null) {
@@ -339,8 +287,8 @@
return mh;
}
// look up the method
- String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
- MethodType type = boxType(wrap, raw);
+ String name = "box" + wrap.wrapperSimpleName();
+ MethodType type = boxType(wrap);
if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
@@ -348,160 +296,26 @@
mh = null;
}
} else {
- mh = box(wrap, !exact, raw).asType(type.erase());
+ mh = box(wrap, !exact).asType(type.erase());
}
if (mh != null) {
cache.put(wrap, mh);
return mh;
}
throw new IllegalArgumentException("cannot find box adapter for "
- + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : ""));
+ + wrap + (exact ? " (exact)" : ""));
}
public static MethodHandle box(Class<?> type) {
boolean exact = false;
// e.g., boxShort(short)Short if exact,
// e.g., boxShort(short)Object if !exact
- return box(Wrapper.forPrimitiveType(type), exact, false);
- }
-
- public static MethodHandle boxRaw(Class<?> type) {
- boolean exact = false;
- // e.g., boxShortRaw(int)Short if exact
- // e.g., boxShortRaw(int)Object if !exact
- return box(Wrapper.forPrimitiveType(type), exact, true);
+ return box(Wrapper.forPrimitiveType(type), exact);
}
public static MethodHandle box(Wrapper type) {
boolean exact = false;
- return box(type, exact, false);
- }
-
- public static MethodHandle boxRaw(Wrapper type) {
- boolean exact = false;
- return box(type, exact, true);
- }
-
- /// Kludges for when raw values get accidentally boxed.
-
- static int unboxRawInteger(Object x) {
- if (x instanceof Integer)
- return (int) x;
- else
- return (int) unboxLong(x, false);
- }
-
- static Integer reboxRawInteger(Object x) {
- if (x instanceof Integer)
- return (Integer) x;
- else
- return (int) unboxLong(x, false);
- }
-
- static Byte reboxRawByte(Object x) {
- if (x instanceof Byte) return (Byte) x;
- return boxByteRaw(unboxRawInteger(x));
- }
-
- static Short reboxRawShort(Object x) {
- if (x instanceof Short) return (Short) x;
- return boxShortRaw(unboxRawInteger(x));
- }
-
- static Boolean reboxRawBoolean(Object x) {
- if (x instanceof Boolean) return (Boolean) x;
- return boxBooleanRaw(unboxRawInteger(x));
- }
-
- static Character reboxRawCharacter(Object x) {
- if (x instanceof Character) return (Character) x;
- return boxCharacterRaw(unboxRawInteger(x));
- }
-
- static Float reboxRawFloat(Object x) {
- if (x instanceof Float) return (Float) x;
- return boxFloatRaw(unboxRawInteger(x));
- }
-
- static Long reboxRawLong(Object x) {
- return (Long) x; //never a rebox
- }
-
- static Double reboxRawDouble(Object x) {
- if (x instanceof Double) return (Double) x;
- return boxDoubleRaw(unboxLong(x, true));
- }
-
- private static MethodType reboxType(Wrapper wrap) {
- Class<?> boxType = wrap.wrapperType();
- return MethodType.methodType(boxType, Object.class);
- }
-
- private static final EnumMap<Wrapper, MethodHandle>[]
- REBOX_CONVERSIONS = newWrapperCaches(1);
-
- /**
- * Because we normalize primitive types to reduce the number of signatures,
- * primitives are sometimes manipulated under an "erased" type,
- * either int (for types other than long/double) or long (for all types).
- * When the erased primitive value is then boxed into an Integer or Long,
- * the final boxed primitive is sometimes required. This transformation
- * is called a "rebox". It takes an Integer or Long and produces some
- * other boxed value, typed (inexactly) as an Object
- */
- public static MethodHandle rebox(Wrapper wrap) {
- EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[0];
- MethodHandle mh = cache.get(wrap);
- if (mh != null) {
- return mh;
- }
- // slow path
- switch (wrap) {
- case OBJECT:
- mh = IDENTITY; break;
- case VOID:
- throw new IllegalArgumentException("cannot rebox a void");
- }
- if (mh != null) {
- cache.put(wrap, mh);
- return mh;
- }
- // look up the method
- String name = "reboxRaw" + wrap.simpleName();
- MethodType type = reboxType(wrap);
- try {
- mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
- mh = mh.asType(IDENTITY.type());
- } catch (ReflectiveOperationException ex) {
- mh = null;
- }
- if (mh != null) {
- cache.put(wrap, mh);
- return mh;
- }
- throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
- }
-
- public static MethodHandle rebox(Class<?> type) {
- return rebox(Wrapper.forPrimitiveType(type));
- }
-
- /// Width-changing conversions between int and long.
-
- static long widenInt(int x) {
- return (long) x;
- }
-
- static Long widenBoxedInt(Integer x) {
- return (long)(int)x;
- }
-
- static int narrowLong(long x) {
- return (int) x;
- }
-
- static Integer narrowBoxedLong(Long x) {
- return (int)(long) x;
+ return box(type, exact);
}
/// Constant functions
@@ -553,7 +367,7 @@
case OBJECT:
case INT: case LONG: case FLOAT: case DOUBLE:
try {
- mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type);
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type);
} catch (ReflectiveOperationException ex) {
mh = null;
}
@@ -564,12 +378,9 @@
return mh;
}
- // use the raw method
- Wrapper rawWrap = wrap.rawPrimitive();
- if (mh == null && rawWrap != wrap) {
- mh = MethodHandles.explicitCastArguments(zeroConstantFunction(rawWrap), type);
- }
- if (mh != null) {
+ // use zeroInt and cast the result
+ if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
+ mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
cache.put(wrap, mh);
return mh;
}
@@ -657,7 +468,7 @@
return t.cast(x);
}
- private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
+ private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
static {
try {
MethodType idType = MethodType.genericMethodType(1);
@@ -666,8 +477,6 @@
MethodType ignoreType = idType.changeReturnType(void.class);
MethodType zeroObjectType = MethodType.genericMethodType(0);
IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
- IDENTITY_I = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(int.class, int.class));
- IDENTITY_J = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(long.class, long.class));
//CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
@@ -723,7 +532,6 @@
if (exact) {
MethodType xmt = MethodType.methodType(type, Object.class);
mh = MethodHandles.explicitCastArguments(mh, xmt);
- //mh = AdapterMethodHandle.makeRetypeRaw(IMPL_TOKEN, xmt, mh);
}
if (cache != null)
cache.put(wrap, mh);
@@ -735,8 +543,10 @@
}
public static MethodHandle identity(Class<?> type) {
- // This stuff has been moved into MethodHandles:
- return MethodHandles.identity(type);
+ if (!type.isPrimitive())
+ // Reference identity has been moved into MethodHandles:
+ return MethodHandles.identity(type);
+ return identity(Wrapper.findPrimitiveType(type));
}
public static MethodHandle identity(Wrapper wrap) {
@@ -769,95 +579,203 @@
throw new IllegalArgumentException("cannot find identity for " + wrap);
}
- /// Float/non-float conversions.
+ /// Primitive conversions.
+ // These are supported directly by the JVM, usually by a single instruction.
+ // In the case of narrowing to a subword, there may be a pair of instructions.
+ // In the case of booleans, there may be a helper routine to manage a 1-bit value.
+ // This is the full 8x8 matrix (minus the diagonal).
- static float doubleToFloat(double x) {
+ // narrow double to all other types:
+ static float doubleToFloat(double x) { // bytecode: d2f
return (float) x;
}
- static double floatToDouble(float x) {
- return x;
- }
-
- // narrow double to integral type
- static long doubleToLong(double x) {
+ static long doubleToLong(double x) { // bytecode: d2l
return (long) x;
}
- static int doubleToInt(double x) {
+ static int doubleToInt(double x) { // bytecode: d2i
return (int) x;
}
- static short doubleToShort(double x) {
+ static short doubleToShort(double x) { // bytecodes: d2i, i2s
return (short) x;
}
- static char doubleToChar(double x) {
+ static char doubleToChar(double x) { // bytecodes: d2i, i2c
return (char) x;
}
- static byte doubleToByte(double x) {
+ static byte doubleToByte(double x) { // bytecodes: d2i, i2b
return (byte) x;
}
static boolean doubleToBoolean(double x) {
return toBoolean((byte) x);
}
- // narrow float to integral type
- static long floatToLong(float x) {
+ // widen float:
+ static double floatToDouble(float x) { // bytecode: f2d
+ return x;
+ }
+ // narrow float:
+ static long floatToLong(float x) { // bytecode: f2l
return (long) x;
}
- static int floatToInt(float x) {
+ static int floatToInt(float x) { // bytecode: f2i
return (int) x;
}
- static short floatToShort(float x) {
+ static short floatToShort(float x) { // bytecodes: f2i, i2s
return (short) x;
}
- static char floatToChar(float x) {
+ static char floatToChar(float x) { // bytecodes: f2i, i2c
return (char) x;
}
- static byte floatToByte(float x) {
+ static byte floatToByte(float x) { // bytecodes: f2i, i2b
return (byte) x;
}
static boolean floatToBoolean(float x) {
return toBoolean((byte) x);
}
- // widen integral type to double
- static double longToDouble(long x) {
+ // widen long:
+ static double longToDouble(long x) { // bytecode: l2d
+ return x;
+ }
+ static float longToFloat(long x) { // bytecode: l2f
+ return x;
+ }
+ // narrow long:
+ static int longToInt(long x) { // bytecode: l2i
+ return (int) x;
+ }
+ static short longToShort(long x) { // bytecodes: f2i, i2s
+ return (short) x;
+ }
+ static char longToChar(long x) { // bytecodes: f2i, i2c
+ return (char) x;
+ }
+ static byte longToByte(long x) { // bytecodes: f2i, i2b
+ return (byte) x;
+ }
+ static boolean longToBoolean(long x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen int:
+ static double intToDouble(int x) { // bytecode: i2d
return x;
}
- static double intToDouble(int x) {
+ static float intToFloat(int x) { // bytecode: i2f
+ return x;
+ }
+ static long intToLong(int x) { // bytecode: i2l
+ return x;
+ }
+ // narrow int:
+ static short intToShort(int x) { // bytecode: i2s
+ return (short) x;
+ }
+ static char intToChar(int x) { // bytecode: i2c
+ return (char) x;
+ }
+ static byte intToByte(int x) { // bytecode: i2b
+ return (byte) x;
+ }
+ static boolean intToBoolean(int x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen short:
+ static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i')
+ return x;
+ }
+ static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i')
+ return x;
+ }
+ static long shortToLong(short x) { // bytecode: i2l (implicit 's2i')
+ return x;
+ }
+ static int shortToInt(short x) { // (implicit 's2i')
return x;
}
- static double shortToDouble(short x) {
+ // narrow short:
+ static char shortToChar(short x) { // bytecode: i2c (implicit 's2i')
+ return (char)x;
+ }
+ static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i')
+ return (byte)x;
+ }
+ static boolean shortToBoolean(short x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen char:
+ static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i')
+ return x;
+ }
+ static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i')
+ return x;
+ }
+ static long charToLong(char x) { // bytecode: i2l (implicit 'c2i')
+ return x;
+ }
+ static int charToInt(char x) { // (implicit 'c2i')
return x;
}
- static double charToDouble(char x) {
+ // narrow char:
+ static short charToShort(char x) { // bytecode: i2s (implicit 'c2i')
+ return (short)x;
+ }
+ static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i')
+ return (byte)x;
+ }
+ static boolean charToBoolean(char x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen byte:
+ static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i')
+ return x;
+ }
+ static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i')
return x;
}
- static double byteToDouble(byte x) {
+ static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i')
+ return x;
+ }
+ static int byteToInt(byte x) { // (implicit 'b2i')
return x;
}
+ static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i')
+ return (short)x;
+ }
+ static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i')
+ return (char)x;
+ }
+ // narrow byte to boolean:
+ static boolean byteToBoolean(byte x) {
+ return toBoolean(x);
+ }
+
+ // widen boolean to all types:
static double booleanToDouble(boolean x) {
return fromBoolean(x);
}
-
- // widen integral type to float
- static float longToFloat(long x) {
- return x;
- }
- static float intToFloat(int x) {
- return x;
- }
- static float shortToFloat(short x) {
- return x;
- }
- static float charToFloat(char x) {
- return x;
- }
- static float byteToFloat(byte x) {
- return x;
- }
static float booleanToFloat(boolean x) {
return fromBoolean(x);
}
+ static long booleanToLong(boolean x) {
+ return fromBoolean(x);
+ }
+ static int booleanToInt(boolean x) {
+ return fromBoolean(x);
+ }
+ static short booleanToShort(boolean x) {
+ return fromBoolean(x);
+ }
+ static char booleanToChar(boolean x) {
+ return (char)fromBoolean(x);
+ }
+ static byte booleanToByte(boolean x) {
+ return fromBoolean(x);
+ }
+ // helpers to force boolean into the conversion scheme:
static boolean toBoolean(byte x) {
// see javadoc for MethodHandles.explicitCastArguments
return ((x & 1) != 0);
@@ -868,62 +786,48 @@
}
private static final EnumMap<Wrapper, MethodHandle>[]
- CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4);
+ CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
- static MethodHandle convertFloatFunction(Wrapper wrap, boolean toFloat, boolean doubleSize) {
- EnumMap<Wrapper, MethodHandle> cache = CONVERT_FLOAT_FUNCTIONS[(toFloat?1:0)+(doubleSize?2:0)];
- MethodHandle mh = cache.get(wrap);
+ public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
+ EnumMap<Wrapper, MethodHandle> cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
+ MethodHandle mh = cache.get(wdst);
if (mh != null) {
return mh;
}
// slow path
- Wrapper fwrap = (doubleSize ? Wrapper.DOUBLE : Wrapper.FLOAT);
- Class<?> fix = wrap.primitiveType();
- Class<?> flt = (doubleSize ? double.class : float.class);
- Class<?> src = toFloat ? fix : flt;
- Class<?> dst = toFloat ? flt : fix;
- if (src == dst) return identity(wrap);
- MethodType type = MethodType.methodType(dst, src);
- switch (wrap) {
- case VOID:
- mh = toFloat ? zeroConstantFunction(fwrap) : MethodHandles.dropArguments(EMPTY, 0, flt);
- break;
- case OBJECT:
- mh = toFloat ? unbox(flt) : box(flt);
- break;
- default:
- try {
- mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
- } catch (ReflectiveOperationException ex) {
- mh = null;
- }
- break;
+ Class<?> src = wsrc.primitiveType();
+ Class<?> dst = wdst.primitiveType();
+ MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src);
+ if (wsrc == wdst) {
+ mh = identity(src);
+ } else if (wsrc == Wrapper.VOID) {
+ mh = zeroConstantFunction(wdst);
+ } else if (wdst == Wrapper.VOID) {
+ mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles.
+ } else if (wsrc == Wrapper.OBJECT) {
+ mh = unboxCast(dst);
+ } else if (wdst == Wrapper.OBJECT) {
+ mh = box(src);
+ } else {
+ assert(src.isPrimitive() && dst.isPrimitive());
+ try {
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
+ } catch (ReflectiveOperationException ex) {
+ mh = null;
+ }
}
if (mh != null) {
assert(mh.type() == type) : mh;
- cache.put(wrap, mh);
+ cache.put(wdst, mh);
return mh;
}
- throw new IllegalArgumentException("cannot find float conversion constant for " +
+ throw new IllegalArgumentException("cannot find primitive conversion function for " +
src.getSimpleName()+" -> "+dst.getSimpleName());
}
- public static MethodHandle convertFromFloat(Class<?> fixType) {
- Wrapper wrap = Wrapper.forPrimitiveType(fixType);
- return convertFloatFunction(wrap, false, false);
- }
- public static MethodHandle convertFromDouble(Class<?> fixType) {
- Wrapper wrap = Wrapper.forPrimitiveType(fixType);
- return convertFloatFunction(wrap, false, true);
- }
- public static MethodHandle convertToFloat(Class<?> fixType) {
- Wrapper wrap = Wrapper.forPrimitiveType(fixType);
- return convertFloatFunction(wrap, true, false);
- }
- public static MethodHandle convertToDouble(Class<?> fixType) {
- Wrapper wrap = Wrapper.forPrimitiveType(fixType);
- return convertFloatFunction(wrap, true, true);
+ public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {
+ return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));
}
private static String capitalize(String x) {
--- a/jdk/src/share/classes/sun/invoke/util/VerifyAccess.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/VerifyAccess.java Fri Aug 10 16:03:28 2012 -0700
@@ -169,6 +169,46 @@
}
/**
+ * Decide if the given method type, attributed to a member or symbolic
+ * reference of a given reference class, is really visible to that class.
+ * @param type the supposed type of a member or symbolic reference of refc
+ * @param refc
+ */
+ public static boolean isTypeVisible(Class<?> type, Class<?> refc) {
+ if (type == refc) return true; // easy check
+ while (type.isArray()) type = type.getComponentType();
+ if (type.isPrimitive() || type == Object.class) return true;
+ ClassLoader parent = type.getClassLoader();
+ if (parent == null) return true;
+ ClassLoader child = refc.getClassLoader();
+ if (child == null) return false;
+ if (parent == child || loadersAreRelated(parent, child, true))
+ return true;
+ // Do it the hard way: Look up the type name from the refc loader.
+ try {
+ Class<?> res = child.loadClass(type.getName());
+ return (type == res);
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Decide if the given method type, attributed to a member or symbolic
+ * reference of a given reference class, is really visible to that class.
+ * @param type the supposed type of a member or symbolic reference of refc
+ * @param refc
+ */
+ public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {
+ for (int n = -1, max = type.parameterCount(); n < max; n++) {
+ Class<?> ptype = (n < 0 ? type.returnType() : type.parameterType(n));
+ if (!isTypeVisible(ptype, refc))
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Test if two classes have the same class loader and package qualifier.
* @param class1
* @param class2
--- a/jdk/src/share/classes/sun/invoke/util/VerifyType.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/VerifyType.java Fri Aug 10 16:03:28 2012 -0700
@@ -122,8 +122,6 @@
return isNullConversion(recv.returnType(), call.returnType());
}
- //TO DO: isRawConversion
-
/**
* Determine if the JVM verifier allows a value of type call to be
* passed to a formal parameter (or return variable) of type recv.
@@ -188,40 +186,6 @@
return -1;
}
- public static int canPassRaw(Class<?> src, Class<?> dst) {
- if (dst.isPrimitive()) {
- if (dst == void.class)
- // As above, return anything to a caller expecting void.
- return 1;
- if (src == void.class)
- // Special permission for raw conversions: allow a void
- // to be captured as a garbage int.
- // Caller promises that the actual value will be disregarded.
- return dst == int.class ? 1 : 0;
- if (isNullType(src))
- // Special permission for raw conversions: allow a null
- // to be reinterpreted as anything. For objects, it is safe,
- // and for primitives you get a garbage value (probably zero).
- return 1;
- if (!src.isPrimitive())
- return 0;
- Wrapper sw = Wrapper.forPrimitiveType(src);
- Wrapper dw = Wrapper.forPrimitiveType(dst);
- if (sw.stackSlots() == dw.stackSlots())
- return 1; // can do a reinterpret-cast on a stacked primitive
- if (sw.isSubwordOrInt() && dw == Wrapper.VOID)
- return 1; // can drop an outgoing int value
- return 0;
- } else if (src.isPrimitive()) {
- return 0;
- }
-
- // Both references.
- if (isNullReferenceConversion(src, dst))
- return 1;
- return -1;
- }
-
public static boolean isSpreadArgType(Class<?> spreadArg) {
return spreadArg.isArray();
}
--- a/jdk/src/share/classes/sun/invoke/util/Wrapper.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/Wrapper.java Fri Aug 10 16:03:28 2012 -0700
@@ -47,7 +47,8 @@
private final Object zero;
private final Object emptyArray;
private final int format;
- private final String simpleName;
+ private final String wrapperSimpleName;
+ private final String primitiveSimpleName;
private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
this.wrapperType = wtype;
@@ -56,12 +57,13 @@
this.zero = zero;
this.emptyArray = emptyArray;
this.format = format;
- this.simpleName = wtype.getSimpleName();
+ this.wrapperSimpleName = wtype.getSimpleName();
+ this.primitiveSimpleName = ptype.getSimpleName();
}
/** For debugging, give the details of this wrapper. */
public String detailString() {
- return simpleName+
+ return wrapperSimpleName+
java.util.Arrays.asList(wrapperType, primitiveType,
basicTypeChar, zero,
"0x"+Integer.toHexString(format));
@@ -418,7 +420,11 @@
/** What is the simple name of the wrapper type?
*/
- public String simpleName() { return simpleName; }
+ public String wrapperSimpleName() { return wrapperSimpleName; }
+
+ /** What is the simple name of the primitive type?
+ */
+ public String primitiveSimpleName() { return primitiveSimpleName; }
// /** Wrap a value in the given type, which may be either a primitive or wrapper type.
// * Performs standard primitive conversions, including truncation and float conversions.
@@ -456,26 +462,31 @@
// If the target type is an interface, perform no runtime check.
// (This loophole is safe, and is allowed by the JVM verifier.)
// If the target type is a primitive, change it to a wrapper.
+ assert(!type.isPrimitive());
+ if (!type.isInterface())
+ type.cast(x);
@SuppressWarnings("unchecked")
T result = (T) x; // unchecked warning is expected here
return result;
}
Class<T> wtype = wrapperType(type);
if (wtype.isInstance(x)) {
- @SuppressWarnings("unchecked")
- T result = (T) x; // unchecked warning is expected here
- return result;
+ return wtype.cast(x);
}
- Class<?> sourceType = x.getClass(); // throw NPE if x is null
if (!isCast) {
+ Class<?> sourceType = x.getClass(); // throw NPE if x is null
Wrapper source = findWrapperType(sourceType);
if (source == null || !this.isConvertibleFrom(source)) {
throw newClassCastException(wtype, sourceType);
}
+ } else if (x == null) {
+ @SuppressWarnings("unchecked")
+ T z = (T) zero;
+ return z;
}
@SuppressWarnings("unchecked")
T result = (T) wrap(x); // unchecked warning is expected here
- assert result.getClass() == wtype;
+ assert (result == null ? Void.class : result.getClass()) == wtype;
return result;
}
@@ -523,7 +534,7 @@
case 'S': return Short.valueOf((short) xn.intValue());
case 'B': return Byte.valueOf((byte) xn.intValue());
case 'C': return Character.valueOf((char) xn.intValue());
- case 'Z': return Boolean.valueOf(boolValue(xn.longValue()));
+ case 'Z': return Boolean.valueOf(boolValue(xn.byteValue()));
}
throw new InternalError("bad wrapper");
}
@@ -546,72 +557,11 @@
case 'S': return Short.valueOf((short) x);
case 'B': return Byte.valueOf((byte) x);
case 'C': return Character.valueOf((char) x);
- case 'Z': return Boolean.valueOf(boolValue(x));
+ case 'Z': return Boolean.valueOf(boolValue((byte) x));
}
throw new InternalError("bad wrapper");
}
- /** Wrap a value (a long or smaller value) in this wrapper's type.
- * Does not perform floating point conversion.
- * Produces a {@code Long} for {@code OBJECT}, although the exact type
- * of the operand is not known.
- * Returns null for {@code VOID}.
- */
- public Object wrapRaw(long x) {
- switch (basicTypeChar) {
- case 'F': return Float.valueOf(Float.intBitsToFloat((int)x));
- case 'D': return Double.valueOf(Double.longBitsToDouble(x));
- case 'L': // same as 'J':
- case 'J': return (Long) x;
- }
- // Other wrapping operations are just the same, given that the
- // operand is already promoted to an int.
- return wrap((int)x);
- }
-
- /** Produce bitwise value which encodes the given wrapped value.
- * Does not perform floating point conversion.
- * Returns zero for {@code VOID}.
- */
- public long unwrapRaw(Object x) {
- switch (basicTypeChar) {
- case 'F': return Float.floatToRawIntBits((Float) x);
- case 'D': return Double.doubleToRawLongBits((Double) x);
-
- case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type");
- case 'V': return 0;
- case 'I': return (int)(Integer) x;
- case 'J': return (long)(Long) x;
- case 'S': return (short)(Short) x;
- case 'B': return (byte)(Byte) x;
- case 'C': return (char)(Character) x;
- case 'Z': return (boolean)(Boolean) x ? 1 : 0;
- }
- throw new InternalError("bad wrapper");
- }
-
- /** Report what primitive type holds this guy's raw value. */
- public Class<?> rawPrimitiveType() {
- return rawPrimitive().primitiveType();
- }
-
- /** Report, as a wrapper, what primitive type holds this guy's raw value.
- * Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE,
- * else returns INT.
- */
- public Wrapper rawPrimitive() {
- switch (basicTypeChar) {
- case 'S': case 'B':
- case 'C': case 'Z':
- case 'V':
- case 'F':
- return INT;
- case 'D':
- return LONG;
- }
- return this;
- }
-
private static Number numberValue(Object x) {
if (x instanceof Number) return (Number)x;
if (x instanceof Character) return (int)(Character)x;
@@ -620,7 +570,10 @@
return (Number)x;
}
- private static boolean boolValue(long bits) {
+ // Parameter type of boolValue must be byte, because
+ // MethodHandles.explicitCastArguments defines boolean
+ // conversion as first converting to byte.
+ private static boolean boolValue(byte bits) {
bits &= 1; // simple 31-bit zero extension
return (bits != 0);
}
--- a/jdk/src/share/classes/sun/misc/Unsafe.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/src/share/classes/sun/misc/Unsafe.java Fri Aug 10 16:03:28 2012 -0700
@@ -678,6 +678,14 @@
public native Object staticFieldBase(Field f);
/**
+ * Detect if the given class may need to be initialized. This is often
+ * needed in conjunction with obtaining the static field base of a
+ * class.
+ * @return false only if a call to {@code ensureClassInitialized} would have no effect
+ */
+ public native boolean shouldBeInitialized(Class<?> c);
+
+ /**
* Ensure the given class has been initialized. This is often
* needed in conjunction with obtaining the static field base of a
* class.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/7157574/Test7157574.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+7157574 method handles returned by reflective lookup API sometimes have wrong receiver type
+
+When an inherited non-static field or method is looked up in a class C using Lookup.findVirtual(C...), etc., the JSR 292 API, the first argument of the resulting method handle must be the receiver ('this'), and must be the requested class (or more specific, in the case of findSpecial or a lookup of a protected method).
+
+But currently, if a supertype T defines the looked-up method or field and C inherits it, the returned method handle might have the more specific initial type T.
+
+The relevant javadoc (and 292 spec.) is as follows:
+ * The formal parameter {@code this} stands for the self-reference of type {@code C};
+ * if it is present, it is always the leading argument to the method handle invocation.
+ * (In the case of some {@code protected} members, {@code this} may be
+ * restricted in type to the lookup class; see below.)
+
+Because of this bug, all of the assertions fail in the following example:
+*/
+
+/* @test
+ * @bug 7157574
+ * @summary method handles returned by reflective lookup API sometimes have wrong receiver type
+ *
+ * @run main Test7157574
+ */
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+public class Test7157574 {
+ interface Intf { void ig1(); void ig2(); void ig3(); void ig4(); void m1(); }
+ static abstract class Super implements Intf { public abstract void m2(); public int f2; }
+ static abstract class Sub extends Super { }
+ public static void main(String... av) throws Throwable {
+ MethodHandle m1 = lookup().findVirtual(Sub.class, "m1", methodType(void.class));
+ System.out.println(m1);
+ MethodHandle m2 = lookup().findVirtual(Sub.class, "m2", methodType(void.class));
+ System.out.println(m2);
+ MethodHandle f2 = lookup().findGetter(Sub.class, "f2", int.class);
+ System.out.println(f2);
+ MethodHandle f2s = lookup().findSetter(Sub.class, "f2", int.class);
+ System.out.println(f2s);
+ MethodHandle chc = lookup().findVirtual(Sub.class, "hashCode", methodType(int.class));
+ System.out.println(chc);
+ MethodHandle ihc = lookup().findVirtual(Intf.class, "hashCode", methodType(int.class));
+ System.out.println(ihc);
+ assertEquals(Sub.class, m1.type().parameterType(0));
+ assertEquals(Sub.class, m2.type().parameterType(0));
+ assertEquals(Sub.class, f2.type().parameterType(0));
+ assertEquals(Sub.class, f2s.type().parameterType(0));
+ assertEquals(Sub.class, chc.type().parameterType(0));
+ assertEquals(Intf.class, ihc.type().parameterType(0));
+ // test the MHs on a concrete version of Sub
+ class C extends Sub {
+ public void m1() { this.f2 = -1; }
+ public void m2() { this.f2 = -2; }
+ // Pack the vtable of Intf with leading junk:
+ private void ig() { throw new RuntimeException(); }
+ public void ig1() { ig(); }
+ public void ig2() { ig(); }
+ public void ig3() { ig(); }
+ public void ig4() { ig(); }
+ }
+ testConcrete(new C(), m1, m2, f2, f2s, chc, ihc);
+ }
+ private static void testConcrete(Sub s,
+ MethodHandle m1, MethodHandle m2,
+ MethodHandle f2, MethodHandle f2s,
+ MethodHandle chc, MethodHandle ihc
+ ) throws Throwable {
+ s.f2 = 0;
+ m1.invokeExact(s);
+ assertEquals(-1, s.f2);
+ m2.invokeExact(s);
+ assertEquals(-2, s.f2);
+ s.f2 = 2;
+ assertEquals(2, (int) f2.invokeExact(s));
+ f2s.invokeExact(s, 0);
+ assertEquals(0, s.f2);
+ assertEquals(s.hashCode(), (int) chc.invokeExact(s));
+ assertEquals(s.hashCode(), (int) ihc.invokeExact((Intf)s));
+ }
+
+ private static void assertEquals(Object expect, Object observe) {
+ if (java.util.Objects.equals(expect, observe)) return;
+ String msg = ("expected "+expect+" but observed "+observe);
+ System.out.println("FAILED: "+msg);
+ throw new AssertionError(msg);
+ }
+}
--- a/jdk/test/java/lang/invoke/InvokeGenericTest.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -68,24 +68,6 @@
public InvokeGenericTest() {
}
- @Before
- public void checkImplementedPlatform() {
- boolean platformOK = false;
- Properties properties = System.getProperties();
- String vers = properties.getProperty("java.vm.version");
- String name = properties.getProperty("java.vm.name");
- String arch = properties.getProperty("os.arch");
- if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") ||
- arch.equals("x86_64") || arch.equals("sparc") || arch.equals("sparcv9")) &&
- (name.contains("Client") || name.contains("Server"))
- ) {
- platformOK = true;
- } else {
- System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch));
- }
- assumeTrue(platformOK);
- }
-
String testName;
static int allPosTests, allNegTests;
int posTests, negTests;
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -54,7 +54,6 @@
/**
* @author jrose
*/
-@SuppressWarnings("LocalVariableHidesMemberVariable")
public class JavaDocExamplesTest {
/** Wrapper for running the JUnit tests in this module.
* Put JUnit on the classpath!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/MaxTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/* @test
+ * @summary BoundMethodHandle tests with primitive types
+ * @compile MaxTest.java
+ * @run junit/othervm test.java.lang.invoke.MaxTest
+ */
+
+package test.java.lang.invoke;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import org.junit.Test;
+
+public class MaxTest {
+
+ static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ private MethodHandle getMax(Class<?> t) throws Throwable {
+ return LOOKUP.findStatic(Math.class, "max", MethodType.methodType(t, t, t));
+ }
+
+ static int ITERATION_COUNT = 40000;
+ static {
+ String iterations = System.getProperty(MaxTest.class.getSimpleName() + ".ITERATION_COUNT");
+ if (iterations == null) {
+ iterations = System.getProperty(MaxTest.class.getName() + ".ITERATION_COUNT");
+ }
+ if (iterations != null) {
+ ITERATION_COUNT = Integer.parseInt(iterations);
+ }
+ }
+
+ @Test
+ public void testMaxLong() throws Throwable {
+ final Class<?> C = long.class;
+ final long P = 23L;
+ final long Q = 42L;
+ final long R = Math.max(P, Q);
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((long) h.invokeExact(P, Q), R);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((long) bh.invokeExact(Q), R);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((long) bbh.invokeExact(), R);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((long) b2h.invokeExact(P), R);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((long) bb2h.invokeExact(), R);
+ }
+ }
+
+ @Test
+ public void testMaxInt() throws Throwable {
+ final Class<?> C = int.class;
+ final int P = 23;
+ final int Q = 42;
+ final int R = Math.max(P, Q);
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((int) h.invokeExact(P, Q), R);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((int) bh.invokeExact(Q), R);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((int) bbh.invokeExact(), R);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((int) b2h.invokeExact(P), R);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((int) bb2h.invokeExact(), R);
+ }
+ }
+
+ @Test
+ public void testMaxFloat() throws Throwable {
+ final Class<?> C = float.class;
+ final float P = 23F;
+ final float Q = 42F;
+ final float R = Math.max(P, Q);
+ final float D = 0.1F;
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((float) h.invokeExact(P, Q), R, D);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((float) bh.invokeExact(Q), R, D);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((float) bbh.invokeExact(), R, D);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((float) b2h.invokeExact(P), R, D);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((float) bb2h.invokeExact(), R, D);
+ }
+ }
+
+ @Test
+ public void testMaxDouble() throws Throwable {
+ final Class<?> C = double.class;
+ final double P = 23F;
+ final double Q = 42F;
+ final double R = Math.max(P, Q);
+ final double D = 0.1;
+ for (int i = 0; i < ITERATION_COUNT; ++i) {
+ MethodHandle h = getMax(C);
+ assertEquals((double) h.invokeExact(P, Q), R, D);
+ MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
+ assertEquals((double) bh.invokeExact(Q), R, D);
+ MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
+ assertEquals((double) bbh.invokeExact(), R, D);
+ MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
+ assertEquals((double) b2h.invokeExact(P), R, D);
+ MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
+ assertEquals((double) bb2h.invokeExact(), R, D);
+ }
+ }
+
+}
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -25,12 +25,13 @@
/* @test
* @summary unit tests for java.lang.invoke.MethodHandles
- * @compile -source 7 -target 7 MethodHandlesTest.java
+ * @compile MethodHandlesTest.java remote/RemoteExample.java
* @run junit/othervm test.java.lang.invoke.MethodHandlesTest
*/
package test.java.lang.invoke;
+import test.java.lang.invoke.remote.RemoteExample;
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.*;
@@ -57,8 +58,13 @@
// Set this true during development if you want to fast-forward to
// a particular new, non-working test. Tests which are known to
// work (or have recently worked) test this flag and return on true.
- static boolean CAN_SKIP_WORKING = false;
- //static { CAN_SKIP_WORKING = true; }
+ static final boolean CAN_SKIP_WORKING;
+ static {
+ String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".CAN_SKIP_WORKING");
+ if (vstr == null)
+ vstr = System.getProperty(THIS_CLASS.getName()+".CAN_SKIP_WORKING");
+ CAN_SKIP_WORKING = Boolean.parseBoolean(vstr);
+ }
// Set 'true' to do about 15x fewer tests, especially those redundant with RicochetTest.
// This might be useful with -Xcomp stress tests that compile all method handles.
@@ -71,62 +77,6 @@
} finally { printCounts(); verbosity -= 9; }
}
- // current failures
- @Test //@Ignore("failure in call to makeRawRetypeOnly in ToGeneric")
- public void testFail_1() throws Throwable {
- // AMH.<init>: IllegalArgumentException: bad adapter (conversion=0xfffab300): adapter pushes too many parameters
- testSpreadArguments(int.class, 0, 6);
- }
- @Test //@Ignore("failure in JVM when expanding the stack using asm stub for _adapter_spread_args")
- public void testFail_2() throws Throwable {
- // if CONV_OP_IMPLEMENTED_MASK includes OP_SPREAD_ARGS, this crashes:
- testSpreadArguments(Object.class, 0, 2);
- }
- @Test //@Ignore("IllArgEx failure in call to ToGeneric.make")
- public void testFail_3() throws Throwable {
- // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
- testSpreadArguments(int.class, 1, 2);
- }
- @Test //@Ignore("IllArgEx failure in call to ToGeneric.make")
- public void testFail_4() throws Throwable {
- // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
- testCollectArguments(int.class, 1, 2);
- }
- @Test //@Ignore("cannot collect leading primitive types")
- public void testFail_5() throws Throwable {
- // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
- testInvokers(MethodType.genericMethodType(2).changeParameterType(0, int.class));
- }
- @Test //@Ignore("should not insert arguments beyond MethodHandlePushLimit")
- public void testFail_6() throws Throwable {
- // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
- testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
- }
- @Test //@Ignore("permuteArguments has trouble with double slots")
- public void testFail_7() throws Throwable {
- testPermuteArguments(new Object[]{10, 200L},
- new Class<?>[]{Integer.class, long.class},
- new int[]{1,0});
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,0,1}); //rot
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{1,2,0}); //rot
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,1,0}); //swap
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{0,1,2,2}); //dup
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,0,1,2});
- testPermuteArguments(new Object[]{10, 200L, 5000L},
- new Class<?>[]{Integer.class, long.class, long.class},
- new int[]{2,2,0,1});
- //testPermuteArguments(4, Integer.class, 2, long.class, 6);
- }
static final int MAX_ARG_INCREASE = 3;
public MethodHandlesTest() {
@@ -180,7 +130,7 @@
static Object logEntry(String name, Object... args) {
return Arrays.asList(name, Arrays.asList(args));
}
- static Object called(String name, Object... args) {
+ public static Object called(String name, Object... args) {
Object entry = logEntry(name, args);
calledLog.add(entry);
return entry;
@@ -280,6 +230,8 @@
{ param = c; break; }
}
}
+ if (param.isInterface() && param.isAssignableFrom(List.class))
+ return Arrays.asList("#"+nextArg());
if (param.isInterface() || param.isAssignableFrom(String.class))
return "#"+nextArg();
else
@@ -399,6 +351,8 @@
static final Lookup PRIVATE = MethodHandles.lookup();
// This lookup is good for package-private members but not private ones.
static final Lookup PACKAGE = PackageSibling.lookup();
+ // This lookup is good for public members and protected members of PubExample
+ static final Lookup SUBCLASS = RemoteExample.lookup();
// This lookup is good only for public members.
static final Lookup PUBLIC = MethodHandles.publicLookup();
@@ -412,9 +366,11 @@
@Override public String toString() { return name; }
public void v0() { called("v0", this); }
+ protected void pro_v0() { called("pro_v0", this); }
void pkg_v0() { called("pkg_v0", this); }
private void pri_v0() { called("pri_v0", this); }
public static void s0() { called("s0"); }
+ protected static void pro_s0() { called("pro_s0"); }
static void pkg_s0() { called("pkg_s0"); }
private static void pri_s0() { called("pri_s0"); }
@@ -434,12 +390,21 @@
// for testing findConstructor:
public Example(String x, int y) { this.name = x+y; called("Example.<init>", x, y); }
public Example(int x, String y) { this.name = x+y; called("Example.<init>", x, y); }
+ public Example(int x, int y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, long y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, float y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, double y) { this.name = x+""+y; called("Example.<init>", x, y); }
+ public Example(int x, int y, int z) { this.name = x+""+y+""+z; called("Example.<init>", x, y, z); }
+ public Example(int x, int y, int z, int a) { this.name = x+""+y+""+z+""+a; called("Example.<init>", x, y, z, a); }
static final Lookup EXAMPLE = MethodHandles.lookup(); // for testing findSpecial
}
static final Lookup EXAMPLE = Example.EXAMPLE;
public static class PubExample extends Example {
- public PubExample() { super("PubExample#"+nextArg()); }
+ public PubExample() { this("PubExample"); }
+ protected PubExample(String prefix) { super(prefix+"#"+nextArg()); }
+ protected void pro_v0() { called("Pub/pro_v0", this); }
+ protected static void pro_s0() { called("Pub/pro_s0"); }
}
static class SubExample extends Example {
@Override public void v0() { called("Sub/v0", this); }
@@ -457,12 +422,14 @@
@Override public String toString() { return name; }
}
}
+ static interface SubIntExample extends IntExample { }
static final Object[][][] ACCESS_CASES = {
- { { false, PUBLIC }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
- { { false, PUBLIC }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE
- { { false, PUBLIC }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false
- { { true, PUBLIC }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: all true
+ { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false
+ { { false, PUBLIC }, { false, SUBCLASS }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE
+ { { false, PUBLIC }, { false, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false
+ { { false, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: subclass OK
+ { { true, PUBLIC }, { true, SUBCLASS }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[4]: all true
};
static Object[][] accessCases(Class<?> defc, String name, boolean isSpecial) {
@@ -471,11 +438,13 @@
cases = ACCESS_CASES[1]; // PRIVATE only
} else if (name.contains("pkg_") || !Modifier.isPublic(defc.getModifiers())) {
cases = ACCESS_CASES[2]; // not PUBLIC
+ } else if (name.contains("pro_")) {
+ cases = ACCESS_CASES[3]; // PUBLIC class, protected member
} else {
- assertTrue(name.indexOf('_') < 0);
+ assertTrue(name.indexOf('_') < 0 || name.contains("fin_"));
boolean pubc = Modifier.isPublic(defc.getModifiers());
if (pubc)
- cases = ACCESS_CASES[3]; // all access levels
+ cases = ACCESS_CASES[4]; // all access levels
else
cases = ACCESS_CASES[2]; // PACKAGE but not PUBLIC
}
@@ -487,6 +456,13 @@
return accessCases(defc, name, false);
}
+ static Lookup maybeMoveIn(Lookup lookup, Class<?> defc) {
+ if (lookup == PUBLIC || lookup == SUBCLASS || lookup == PACKAGE)
+ // external views stay external
+ return lookup;
+ return lookup.in(defc);
+ }
+
@Test
public void testFindStatic() throws Throwable {
if (CAN_SKIP_WORKING) return;
@@ -495,6 +471,8 @@
testFindStatic(Example.class, void.class, "s0");
testFindStatic(Example.class, void.class, "pkg_s0");
testFindStatic(Example.class, void.class, "pri_s0");
+ testFindStatic(Example.class, void.class, "pro_s0");
+ testFindStatic(PubExample.class, void.class, "Pub/pro_s0");
testFindStatic(Example.class, Object.class, "s1", Object.class);
testFindStatic(Example.class, Object.class, "s2", int.class);
@@ -505,6 +483,7 @@
testFindStatic(Example.class, Object.class, "s7", float.class, double.class);
testFindStatic(false, PRIVATE, Example.class, void.class, "bogus");
+ testFindStatic(false, PRIVATE, Example.class, void.class, "v0");
}
void testFindStatic(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
@@ -517,14 +496,16 @@
}
void testFindStatic(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
+ String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- target = lookup.in(defc).findStatic(defc, name, type);
+ target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -537,7 +518,7 @@
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type, target.type());
- assertNameStringContains(target, name);
+ assertNameStringContains(target, methodName);
Object[] args = randomArgs(params);
printCalled(target, name, args);
target.invokeWithArguments(args);
@@ -571,7 +552,12 @@
testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class);
testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class);
testFindVirtual(Example.class, Object.class, "v2", int.class, int.class);
+ testFindVirtual(Example.class, void.class, "pro_v0");
+ testFindVirtual(PubExample.class, void.class, "Pub/pro_v0");
+
testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus");
+ testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "s0");
+
// test dispatch
testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/v0");
testFindVirtual(SubExample.class, Example.class, void.class, "Sub/v0");
@@ -602,9 +588,10 @@
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- target = lookup.in(defc).findVirtual(defc, methodName, type);
+ target = maybeMoveIn(lookup, defc).findVirtual(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -616,12 +603,20 @@
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
- Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params);
+ Class<?> selfc = defc;
+ // predict receiver type narrowing:
+ if (lookup == SUBCLASS &&
+ name.contains("pro_") &&
+ selfc.isAssignableFrom(lookup.lookupClass())) {
+ selfc = lookup.lookupClass();
+ if (name.startsWith("Pub/")) name = "Rem/"+name.substring(4);
+ }
+ Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)selfc), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
assertEquals(typeWithSelf, target.type());
assertNameStringContains(target, methodName);
Object[] argsWithSelf = randomArgs(paramsWithSelf);
- if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc);
+ if (selfc.isAssignableFrom(rcvc) && rcvc != selfc) argsWithSelf[0] = randomArg(rcvc);
printCalled(target, name, argsWithSelf);
target.invokeWithArguments(argsWithSelf);
assertCalled(name, argsWithSelf);
@@ -635,6 +630,7 @@
startTest("findSpecial");
testFindSpecial(SubExample.class, Example.class, void.class, "v0");
testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
+ testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
// Do some negative testing:
testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus");
testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus");
@@ -647,23 +643,34 @@
void testFindSpecial(Class<?> specialCaller,
Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
- testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params);
- testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params);
- testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
- testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params);
+ if (specialCaller == RemoteExample.class) {
+ testFindSpecial(false, EXAMPLE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PRIVATE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
+ testFindSpecial(true, SUBCLASS, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params);
+ return;
+ }
+ testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params);
+ testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params);
+ testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params);
}
void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller,
Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
+ String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller));
- target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller);
+ if (verbosity >= 5) System.out.println(" lookup => "+maybeMoveIn(lookup, specialCaller));
+ target = maybeMoveIn(lookup, specialCaller).findSpecial(defc, methodName, type, specialCaller);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -680,7 +687,7 @@
assertEquals(type, target.type().dropParameterTypes(0,1));
Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
- assertNameStringContains(target, name);
+ assertNameStringContains(target, methodName);
Object[] args = randomArgs(paramsWithSelf);
printCalled(target, name, args);
target.invokeWithArguments(args);
@@ -693,7 +700,13 @@
startTest("findConstructor");
testFindConstructor(true, EXAMPLE, Example.class);
testFindConstructor(true, EXAMPLE, Example.class, int.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, long.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, float.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, double.class);
testFindConstructor(true, EXAMPLE, Example.class, String.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class);
+ testFindConstructor(true, EXAMPLE, Example.class, int.class, int.class, int.class, int.class);
}
void testFindConstructor(boolean positive, Lookup lookup,
Class<?> defc, Class<?>... params) throws Throwable {
@@ -757,9 +770,10 @@
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- target = lookup.in(defc).bind(receiver, methodName, type);
+ target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -786,6 +800,7 @@
if (CAN_SKIP_WORKING) return;
startTest("unreflect");
testUnreflect(Example.class, true, void.class, "s0");
+ testUnreflect(Example.class, true, void.class, "pro_s0");
testUnreflect(Example.class, true, void.class, "pkg_s0");
testUnreflect(Example.class, true, void.class, "pri_s0");
@@ -804,6 +819,9 @@
testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class);
testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class);
testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class);
+
+ // Test a public final member in another package:
+ testUnreflect(RemoteExample.class, false, void.class, "Rem/fin_v0");
}
void testUnreflect(Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable {
@@ -820,8 +838,9 @@
boolean positive, Lookup lookup,
Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
+ String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
- Method rmethod = defc.getDeclaredMethod(name, params);
+ Method rmethod = defc.getDeclaredMethod(methodName, params);
MethodHandle target = null;
Exception noAccess = null;
boolean isStatic = (rcvc == null);
@@ -829,11 +848,12 @@
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
if (isSpecial)
- target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller);
+ target = maybeMoveIn(lookup, specialCaller).unreflectSpecial(rmethod, specialCaller);
else
- target = lookup.in(defc).unreflect(rmethod);
+ target = maybeMoveIn(lookup, defc).unreflect(rmethod);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
@@ -960,7 +980,7 @@
}
}
- static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10;
+ static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10, TEST_BOUND = 0x20, TEST_NPE = 0x40;
static boolean testModeMatches(int testMode, boolean isStatic) {
switch (testMode) {
case TEST_FIND_STATIC: return isStatic;
@@ -972,16 +992,20 @@
@Test
public void testUnreflectGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("unreflectGetter");
testGetter(TEST_UNREFLECT);
}
@Test
public void testFindGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findGetter");
testGetter(TEST_FIND_FIELD);
+ testGetter(TEST_FIND_FIELD | TEST_BOUND);
}
@Test
public void testFindStaticGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findStaticGetter");
testGetter(TEST_FIND_STATIC);
}
@@ -990,6 +1014,8 @@
for (Object[] c : HasFields.CASES) {
boolean positive = (c[1] != Error.class);
testGetter(positive, lookup, c[0], c[1], testMode);
+ if (positive)
+ testGetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
}
testGetter(true, lookup,
new Object[]{ true, System.class, "out", java.io.PrintStream.class },
@@ -1005,12 +1031,15 @@
testAccessor(positive, lookup, fieldRef, value, testMode);
}
- public void testAccessor(boolean positive, MethodHandles.Lookup lookup,
+ public void testAccessor(boolean positive0, MethodHandles.Lookup lookup,
Object fieldRef, Object value, int testMode0) throws Throwable {
if (verbosity >= 4)
- System.out.println("testAccessor"+Arrays.asList(positive, lookup, fieldRef, value, testMode0));
+ System.out.println("testAccessor"+Arrays.deepToString(new Object[]{positive0, lookup, fieldRef, value, testMode0}));
boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
- int testMode = testMode0 & ~TEST_SETTER;
+ boolean doBound = ((testMode0 & TEST_BOUND) != 0);
+ boolean testNPE = ((testMode0 & TEST_NPE) != 0);
+ int testMode = testMode0 & ~(TEST_SETTER | TEST_BOUND | TEST_NPE);
+ boolean positive = positive0 && !testNPE;
boolean isStatic;
Class<?> fclass;
String fname;
@@ -1035,6 +1064,7 @@
}
if (!testModeMatches(testMode, isStatic)) return;
if (f == null && testMode == TEST_UNREFLECT) return;
+ if (testNPE && isStatic) return;
countTest(positive);
MethodType expType;
if (isGetter)
@@ -1045,7 +1075,7 @@
Exception noAccess = null;
MethodHandle mh;
try {
- switch (testMode0) {
+ switch (testMode0 & ~(TEST_BOUND | TEST_NPE)) {
case TEST_UNREFLECT: mh = lookup.unreflectGetter(f); break;
case TEST_FIND_FIELD: mh = lookup.findGetter(fclass, fname, ftype); break;
case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype); break;
@@ -1061,6 +1091,7 @@
} catch (ReflectiveOperationException ex) {
mh = null;
noAccess = ex;
+ if (verbosity >= 5) ex.printStackTrace(System.out);
if (fname.contains("bogus"))
assertTrue(noAccess instanceof NoSuchFieldException);
else
@@ -1070,15 +1101,19 @@
System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
+" => "+mh
+(noAccess == null ? "" : " !! "+noAccess));
- if (positive && noAccess != null) throw new RuntimeException(noAccess);
- assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null);
- if (!positive) return; // negative test failed as expected
+ if (positive && !testNPE && noAccess != null) throw new RuntimeException(noAccess);
+ assertEquals(positive0 ? "positive test" : "negative test erroneously passed", positive0, mh != null);
+ if (!positive && !testNPE) return; // negative access test failed as expected
assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
assertSame(mh.type(), expType);
- assertNameStringContains(mh, fname);
+ //assertNameStringContains(mh, fname); // This does not hold anymore with LFs
HasFields fields = new HasFields();
+ HasFields fieldsForMH = fields;
+ if (testNPE) fieldsForMH = null; // perturb MH argument to elicit expected error
+ if (doBound)
+ mh = mh.bindTo(fieldsForMH);
Object sawValue;
Class<?> vtype = ftype;
if (ftype != int.class) vtype = Object.class;
@@ -1094,19 +1129,28 @@
if (f != null && f.getDeclaringClass() == HasFields.class) {
assertEquals(f.get(fields), value); // clean to start with
}
+ Throwable caughtEx = null;
if (isGetter) {
Object expValue = value;
for (int i = 0; i <= 1; i++) {
- if (isStatic) {
- if (ftype == int.class)
- sawValue = (int) mh.invokeExact(); // do these exactly
- else
- sawValue = mh.invokeExact();
- } else {
- if (ftype == int.class)
- sawValue = (int) mh.invokeExact((Object) fields);
- else
- sawValue = mh.invokeExact((Object) fields);
+ sawValue = null; // make DA rules happy under try/catch
+ try {
+ if (isStatic || doBound) {
+ if (ftype == int.class)
+ sawValue = (int) mh.invokeExact(); // do these exactly
+ else
+ sawValue = mh.invokeExact();
+ } else {
+ if (ftype == int.class)
+ sawValue = (int) mh.invokeExact((Object) fieldsForMH);
+ else
+ sawValue = mh.invokeExact((Object) fieldsForMH);
+ }
+ } catch (RuntimeException ex) {
+ if (ex instanceof NullPointerException && testNPE) {
+ caughtEx = ex;
+ break;
+ }
}
assertEquals(sawValue, expValue);
if (f != null && f.getDeclaringClass() == HasFields.class
@@ -1121,16 +1165,23 @@
} else {
for (int i = 0; i <= 1; i++) {
Object putValue = randomArg(ftype);
- if (isStatic) {
- if (ftype == int.class)
- mh.invokeExact((int)putValue); // do these exactly
- else
- mh.invokeExact(putValue);
- } else {
- if (ftype == int.class)
- mh.invokeExact((Object) fields, (int)putValue);
- else
- mh.invokeExact((Object) fields, putValue);
+ try {
+ if (isStatic || doBound) {
+ if (ftype == int.class)
+ mh.invokeExact((int)putValue); // do these exactly
+ else
+ mh.invokeExact(putValue);
+ } else {
+ if (ftype == int.class)
+ mh.invokeExact((Object) fieldsForMH, (int)putValue);
+ else
+ mh.invokeExact((Object) fieldsForMH, putValue);
+ }
+ } catch (RuntimeException ex) {
+ if (ex instanceof NullPointerException && testNPE) {
+ caughtEx = ex;
+ break;
+ }
}
if (f != null && f.getDeclaringClass() == HasFields.class) {
assertEquals(f.get(fields), putValue);
@@ -1140,21 +1191,33 @@
if (f != null && f.getDeclaringClass() == HasFields.class) {
f.set(fields, value); // put it back
}
+ if (testNPE) {
+ if (caughtEx == null || !(caughtEx instanceof NullPointerException))
+ throw new RuntimeException("failed to catch NPE exception"+(caughtEx == null ? " (caughtEx=null)" : ""), caughtEx);
+ caughtEx = null; // nullify expected exception
+ }
+ if (caughtEx != null) {
+ throw new RuntimeException("unexpected exception", caughtEx);
+ }
}
@Test
public void testUnreflectSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("unreflectSetter");
testSetter(TEST_UNREFLECT);
}
@Test
public void testFindSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findSetter");
testSetter(TEST_FIND_FIELD);
+ testSetter(TEST_FIND_FIELD | TEST_BOUND);
}
@Test
public void testFindStaticSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("findStaticSetter");
testSetter(TEST_FIND_STATIC);
}
@@ -1164,6 +1227,8 @@
for (Object[] c : HasFields.CASES) {
boolean positive = (c[1] != Error.class);
testSetter(positive, lookup, c[0], c[1], testMode);
+ if (positive)
+ testSetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
}
for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
testSetter(false, lookup,
@@ -1178,34 +1243,84 @@
@Test
public void testArrayElementGetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("arrayElementGetter");
testArrayElementGetterSetter(false);
}
@Test
public void testArrayElementSetter() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
startTest("arrayElementSetter");
testArrayElementGetterSetter(true);
}
+ private static final int TEST_ARRAY_NONE = 0, TEST_ARRAY_NPE = 1, TEST_ARRAY_OOB = 2, TEST_ARRAY_ASE = 3;
+
public void testArrayElementGetterSetter(boolean testSetter) throws Throwable {
- testArrayElementGetterSetter(new Object[10], testSetter);
- testArrayElementGetterSetter(new String[10], testSetter);
- testArrayElementGetterSetter(new boolean[10], testSetter);
- testArrayElementGetterSetter(new byte[10], testSetter);
- testArrayElementGetterSetter(new char[10], testSetter);
- testArrayElementGetterSetter(new short[10], testSetter);
- testArrayElementGetterSetter(new int[10], testSetter);
- testArrayElementGetterSetter(new float[10], testSetter);
- testArrayElementGetterSetter(new long[10], testSetter);
- testArrayElementGetterSetter(new double[10], testSetter);
+ testArrayElementGetterSetter(testSetter, TEST_ARRAY_NONE);
+ }
+
+ @Test
+ public void testArrayElementErrors() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
+ startTest("arrayElementErrors");
+ testArrayElementGetterSetter(false, TEST_ARRAY_NPE);
+ testArrayElementGetterSetter(true, TEST_ARRAY_NPE);
+ testArrayElementGetterSetter(false, TEST_ARRAY_OOB);
+ testArrayElementGetterSetter(true, TEST_ARRAY_OOB);
+ testArrayElementGetterSetter(new Object[10], true, TEST_ARRAY_ASE);
+ testArrayElementGetterSetter(new Example[10], true, TEST_ARRAY_ASE);
+ testArrayElementGetterSetter(new IntExample[10], true, TEST_ARRAY_ASE);
}
- public void testArrayElementGetterSetter(Object array, boolean testSetter) throws Throwable {
- countTest(true);
- if (verbosity > 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+Array.getLength(array)+"]");
+ public void testArrayElementGetterSetter(boolean testSetter, int negTest) throws Throwable {
+ testArrayElementGetterSetter(new String[10], testSetter, negTest);
+ testArrayElementGetterSetter(new Iterable<?>[10], testSetter, negTest);
+ testArrayElementGetterSetter(new Example[10], testSetter, negTest);
+ testArrayElementGetterSetter(new IntExample[10], testSetter, negTest);
+ testArrayElementGetterSetter(new Object[10], testSetter, negTest);
+ testArrayElementGetterSetter(new boolean[10], testSetter, negTest);
+ testArrayElementGetterSetter(new byte[10], testSetter, negTest);
+ testArrayElementGetterSetter(new char[10], testSetter, negTest);
+ testArrayElementGetterSetter(new short[10], testSetter, negTest);
+ testArrayElementGetterSetter(new int[10], testSetter, negTest);
+ testArrayElementGetterSetter(new float[10], testSetter, negTest);
+ testArrayElementGetterSetter(new long[10], testSetter, negTest);
+ testArrayElementGetterSetter(new double[10], testSetter, negTest);
+ }
+
+ public void testArrayElementGetterSetter(Object array, boolean testSetter, int negTest) throws Throwable {
+ boolean positive = (negTest == TEST_ARRAY_NONE);
+ int length = java.lang.reflect.Array.getLength(array);
Class<?> arrayType = array.getClass();
Class<?> elemType = arrayType.getComponentType();
+ Object arrayToMH = array;
+ // this stanza allows negative tests to make argument perturbations:
+ switch (negTest) {
+ case TEST_ARRAY_NPE:
+ arrayToMH = null;
+ break;
+ case TEST_ARRAY_OOB:
+ assert(length > 0);
+ arrayToMH = java.lang.reflect.Array.newInstance(elemType, 0);
+ break;
+ case TEST_ARRAY_ASE:
+ assert(testSetter && !elemType.isPrimitive());
+ if (elemType == Object.class)
+ arrayToMH = new StringBuffer[length]; // very random subclass of Object!
+ else if (elemType == Example.class)
+ arrayToMH = new SubExample[length];
+ else if (elemType == IntExample.class)
+ arrayToMH = new SubIntExample[length];
+ else
+ return; // can't make an ArrayStoreException test
+ assert(arrayType.isInstance(arrayToMH))
+ : Arrays.asList(arrayType, arrayToMH.getClass(), testSetter, negTest);
+ break;
+ }
+ countTest(positive);
+ if (verbosity > 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+length+"]"+(positive ? "" : " negative test #"+negTest+" using "+Arrays.deepToString(new Object[]{arrayToMH})));
MethodType expType = !testSetter
? MethodType.methodType(elemType, arrayType, int.class)
: MethodType.methodType(void.class, arrayType, int.class, elemType);
@@ -1214,25 +1329,29 @@
: MethodHandles.arrayElementSetter(arrayType);
assertSame(mh.type(), expType);
if (elemType != int.class && elemType != boolean.class) {
- // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
- MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
+ MethodType gtype = mh.type().generic().changeParameterType(1, int.class);
if (testSetter) gtype = gtype.changeReturnType(void.class);
mh = mh.asType(gtype);
}
Object sawValue, expValue;
List<Object> model = array2list(array);
- int length = Array.getLength(array);
+ Throwable caughtEx = null;
for (int i = 0; i < length; i++) {
// update array element
Object random = randomArg(elemType);
model.set(i, random);
if (testSetter) {
- if (elemType == int.class)
- mh.invokeExact((int[]) array, i, (int)random);
- else if (elemType == boolean.class)
- mh.invokeExact((boolean[]) array, i, (boolean)random);
- else
- mh.invokeExact(array, (Integer)i, random);
+ try {
+ if (elemType == int.class)
+ mh.invokeExact((int[]) arrayToMH, i, (int)random);
+ else if (elemType == boolean.class)
+ mh.invokeExact((boolean[]) arrayToMH, i, (boolean)random);
+ else
+ mh.invokeExact(arrayToMH, i, random);
+ } catch (RuntimeException ex) {
+ caughtEx = ex;
+ break;
+ }
assertEquals(model, array2list(array));
} else {
Array.set(array, i, random);
@@ -1247,16 +1366,39 @@
sawValue = Array.get(array, i);
if (!testSetter) {
expValue = sawValue;
- if (elemType == int.class)
- sawValue = (int) mh.invokeExact((int[]) array, i);
- else if (elemType == boolean.class)
- sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
- else
- sawValue = mh.invokeExact(array, (Integer)i);
+ try {
+ if (elemType == int.class)
+ sawValue = (int) mh.invokeExact((int[]) arrayToMH, i);
+ else if (elemType == boolean.class)
+ sawValue = (boolean) mh.invokeExact((boolean[]) arrayToMH, i);
+ else
+ sawValue = mh.invokeExact(arrayToMH, i);
+ } catch (RuntimeException ex) {
+ caughtEx = ex;
+ break;
+ }
assertEquals(sawValue, expValue);
assertEquals(model, array2list(array));
}
}
+ if (!positive) {
+ if (caughtEx == null)
+ throw new RuntimeException("failed to catch exception for negTest="+negTest);
+ // test the kind of exception
+ Class<?> reqType = null;
+ switch (negTest) {
+ case TEST_ARRAY_ASE: reqType = ArrayStoreException.class; break;
+ case TEST_ARRAY_OOB: reqType = ArrayIndexOutOfBoundsException.class; break;
+ case TEST_ARRAY_NPE: reqType = NullPointerException.class; break;
+ default: assert(false);
+ }
+ if (reqType.isInstance(caughtEx)) {
+ caughtEx = null; // nullify expected exception
+ }
+ }
+ if (caughtEx != null) {
+ throw new RuntimeException("unexpected exception", caughtEx);
+ }
}
List<Object> array2list(Object array) {
@@ -1363,6 +1505,8 @@
@Test
public void testVarargsCollector() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
+ startTest("varargsCollector");
MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
MethodType.methodType(Object.class, String.class, Object[].class));
vac0 = vac0.bindTo("vac");
@@ -1375,7 +1519,7 @@
}
}
- @Test
+ @Test // SLOW
public void testPermuteArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("permuteArguments");
@@ -1514,7 +1658,7 @@
}
- @Test
+ @Test // SLOW
public void testSpreadArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("spreadArguments");
@@ -1522,7 +1666,7 @@
if (verbosity >= 3)
System.out.println("spreadArguments "+argType);
for (int nargs = 0; nargs < 50; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int pos = 0; pos <= nargs; pos++) {
if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue;
if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
@@ -1608,7 +1752,7 @@
}
}
- @Test
+ @Test // SLOW
public void testCollectArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("collectArguments");
@@ -1616,7 +1760,7 @@
if (verbosity >= 3)
System.out.println("collectArguments "+argType);
for (int nargs = 0; nargs < 50; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int pos = 0; pos <= nargs; pos++) {
if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue;
if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
@@ -1650,12 +1794,12 @@
assertArrayEquals(collectedArgs, returnValue);
}
- @Test
+ @Test // SLOW
public void testInsertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("insertArguments");
for (int nargs = 0; nargs < 50; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int ins = 0; ins <= nargs; ins++) {
if (nargs > 10 && ins > 4 && ins < nargs-4 && ins % 10 != 3)
continue;
@@ -1677,7 +1821,7 @@
List<Object> argsToPass = new ArrayList<>(resList);
List<Object> argsToInsert = argsToPass.subList(pos, pos + ins);
if (verbosity >= 3)
- System.out.println("insert: "+argsToInsert+" into "+target);
+ System.out.println("insert: "+argsToInsert+" @"+pos+" into "+target);
@SuppressWarnings("cast") // cast to spread Object... is helpful
MethodHandle target2 = MethodHandles.insertArguments(target, pos,
(Object[]/*...*/) argsToInsert.toArray());
@@ -1840,7 +1984,7 @@
assertEquals(resList, res2List);
}
- @Test
+ @Test // SLOW
public void testInvokers() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker");
@@ -2109,7 +2253,7 @@
if (CAN_SKIP_WORKING) return;
startTest("catchException");
for (int nargs = 0; nargs < 40; nargs++) {
- if (CAN_TEST_LIGHTLY && nargs > 7) break;
+ if (CAN_TEST_LIGHTLY && nargs > 11) break;
for (int throwMode = 0; throwMode < THROW_MODE_LIMIT; throwMode++) {
testCatchException(int.class, new ClassCastException("testing"), throwMode, nargs);
if (CAN_TEST_LIGHTLY && nargs > 3) continue;
@@ -2237,8 +2381,10 @@
assertSame(thrown, caught);
}
- @Test
+ //@Test
public void testInterfaceCast() throws Throwable {
+ //if (CAN_SKIP_WORKING) return;
+ startTest("interfaceCast");
for (Class<?> ctype : new Class<?>[]{ Object.class, String.class, CharSequence.class, Number.class, Iterable.class}) {
testInterfaceCast(ctype, false, false);
testInterfaceCast(ctype, true, false);
@@ -2279,11 +2425,12 @@
}
}
- @Test
+ @Test // SLOW
public void testCastFailure() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testCastFailure");
testCastFailure("cast/argument", 11000);
+ if (CAN_TEST_LIGHTLY) return;
testCastFailure("unbox/argument", 11000);
testCastFailure("cast/return", 11000);
testCastFailure("unbox/return", 11000);
@@ -2380,7 +2527,7 @@
if (verbosity > 2)
System.out.println("caught "+ex);
if (verbosity > 3)
- ex.printStackTrace();
+ ex.printStackTrace(System.out);
assertTrue(true); // all is well
}
}
@@ -2444,7 +2591,7 @@
@Test
public void testAsInterfaceInstance() throws Throwable {
if (CAN_SKIP_WORKING) return;
- startTest("testAsInterfaceInstance");
+ startTest("asInterfaceInstance");
Lookup lookup = MethodHandles.lookup();
// test typical case: Runnable.run
{
@@ -2550,7 +2697,7 @@
} else {
assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
if (!(ex1 instanceof UndeclaredThrowableException) || ex1.getCause() != ex) {
- ex1.printStackTrace();
+ ex1.printStackTrace(System.out);
}
assertSame(ex, ex1.getCause());
UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
@@ -2630,6 +2777,8 @@
}
}
// Local abbreviated copy of sun.invoke.util.ValueConversions
+// This guy tests access from outside the same package member, but inside
+// the package itself.
class ValueConversions {
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
private static final Object[] NO_ARGS_ARRAY = {};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/PrivateInvokeTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2009, 2011, 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.
+ */
+
+/* @test
+ * @summary white-box testing of method handle sub-primitives
+ * @run junit test.java.lang.invoke.PrivateInvokeTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.junit.*;
+import static org.junit.Assert.*;
+
+public class PrivateInvokeTest {
+ // Utility functions
+ private static final Lookup LOOKUP = lookup();
+ private static final Class<?> THIS_CLASS = PrivateInvokeTest.class;
+ private static final int
+ REF_NONE = 0, // null value
+ REF_getField = 1,
+ REF_getStatic = 2,
+ REF_putField = 3,
+ REF_putStatic = 4,
+ REF_invokeVirtual = 5,
+ REF_invokeStatic = 6,
+ REF_invokeSpecial = 7,
+ REF_newInvokeSpecial = 8,
+ REF_invokeInterface = 9,
+ REF_LIMIT = 10,
+ REF_MH_invokeBasic = REF_NONE;;
+ private static final String[] REF_KIND_NAMES = {
+ "MH::invokeBasic",
+ "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
+ "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
+ "REF_newInvokeSpecial", "REF_invokeInterface"
+ };
+ private int verbose;
+ //{ verbose = 99; } // for debugging
+ {
+ String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbose");
+ if (vstr == null)
+ vstr = System.getProperty(THIS_CLASS.getName()+".verbose");
+ if (vstr == null)
+ vstr = System.getProperty("test.verbose");
+ if (vstr != null) verbose = Integer.parseInt(vstr);
+ }
+ private static int referenceKind(Method m) {
+ if (Modifier.isStatic(m.getModifiers()))
+ return REF_invokeStatic;
+ else if (m.getDeclaringClass().isInterface())
+ return REF_invokeInterface;
+ else if (Modifier.isFinal(m.getModifiers()) ||
+ Modifier.isFinal(m.getDeclaringClass().getModifiers()))
+ return REF_invokeSpecial;
+ else
+ return REF_invokeVirtual;
+ }
+ private static MethodType basicType(MethodType mtype) {
+ MethodType btype = mtype.erase();
+ if (btype.hasPrimitives()) {
+ for (int i = -1; i < mtype.parameterCount(); i++) {
+ Class<?> type = (i < 0 ? mtype.returnType() : mtype.parameterType(i));
+ if (type == boolean.class ||
+ type == byte.class ||
+ type == char.class ||
+ type == short.class) {
+ type = int.class;
+ if (i < 0)
+ btype = btype.changeReturnType(type);
+ else
+ btype = btype.changeParameterType(i, type);
+ }
+ }
+ }
+ return btype;
+ }
+ private static Method getMethod(Class<?> defc, String name, Class<?>... ptypes) {
+ try {
+ return defc.getDeclaredMethod(name, ptypes);
+ } catch (NoSuchMethodException ex) {
+ }
+ try {
+ return defc.getMethod(name, ptypes);
+ } catch (NoSuchMethodException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private static MethodHandle unreflect(Method m) {
+ try {
+ MethodHandle mh = LOOKUP.unreflect(m);
+ if (Modifier.isTransient(m.getModifiers()))
+ mh = mh.asFixedArity(); // remove varargs wrapper
+ return mh;
+ } catch (IllegalAccessException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private static final Lookup DIRECT_INVOKER_LOOKUP;
+ private static final Class<?> MEMBER_NAME_CLASS;
+ private static final MethodHandle MH_INTERNAL_MEMBER_NAME;
+ private static final MethodHandle MH_DEBUG_STRING;
+ static {
+ try {
+ // This is white box testing. Use reflection to grab private implementation bits.
+ String magicName = "IMPL_LOOKUP";
+ Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
+ // This unit test will fail if a security manager is installed.
+ magicLookup.setAccessible(true);
+ // Forbidden fruit...
+ DIRECT_INVOKER_LOOKUP = (Lookup) magicLookup.get(null);
+ MEMBER_NAME_CLASS = Class.forName("java.lang.invoke.MemberName", false, MethodHandle.class.getClassLoader());
+ MH_INTERNAL_MEMBER_NAME = DIRECT_INVOKER_LOOKUP
+ .findVirtual(MethodHandle.class, "internalMemberName", methodType(MEMBER_NAME_CLASS))
+ .asType(methodType(Object.class, MethodHandle.class));
+ MH_DEBUG_STRING = DIRECT_INVOKER_LOOKUP
+ .findVirtual(MethodHandle.class, "debugString", methodType(String.class));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ }
+ private Object internalMemberName(MethodHandle mh) {
+ try {
+ return MH_INTERNAL_MEMBER_NAME.invokeExact(mh);
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+ private String debugString(MethodHandle mh) {
+ try {
+ return (String) MH_DEBUG_STRING.invokeExact(mh);
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+ private static MethodHandle directInvoker(int refKind, MethodType mtype) {
+ return directInvoker(REF_KIND_NAMES[refKind], mtype);
+ }
+ private static MethodHandle directInvoker(String name, MethodType mtype) {
+ boolean isStatic;
+ mtype = mtype.erase();
+ if (name.startsWith("MH::")) {
+ isStatic = false;
+ name = strip("MH::", name);
+ } else if (name.startsWith("REF_")) {
+ isStatic = true;
+ name = strip("REF_", name);
+ if (name.startsWith("invoke"))
+ name = "linkTo"+strip("invoke", name);
+ mtype = mtype.appendParameterTypes(MEMBER_NAME_CLASS);
+ } else {
+ throw new AssertionError("name="+name);
+ }
+ //System.out.println("directInvoker = "+name+mtype);
+ try {
+ if (isStatic)
+ return DIRECT_INVOKER_LOOKUP
+ .findStatic(MethodHandle.class, name, mtype);
+ else
+ return DIRECT_INVOKER_LOOKUP
+ .findVirtual(MethodHandle.class, name, mtype);
+ } catch (ReflectiveOperationException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private Object invokeWithArguments(Method m, Object... args) {
+ Object recv = null;
+ if (!Modifier.isStatic(m.getModifiers())) {
+ recv = args[0];
+ args = pop(1, args);
+ }
+ try {
+ return m.invoke(recv, args);
+ } catch (IllegalAccessException|IllegalArgumentException|InvocationTargetException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private Object invokeWithArguments(MethodHandle mh, Object... args) {
+ try {
+ return mh.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ private int counter;
+ private Object makeArgument(Class<?> type) {
+ final String cname = type.getSimpleName();
+ final int n = ++counter;
+ final int nn = (n << 10) + 13;
+ if (type.isAssignableFrom(String.class)) {
+ return "<"+cname+"#"+nn+">";
+ }
+ if (type == THIS_CLASS) return this.withCounter(nn);
+ if (type == Integer.class || type == int.class) return nn;
+ if (type == Character.class || type == char.class) return (char)(n % 100+' ');
+ if (type == Byte.class || type == byte.class) return (byte)-(n % 100);
+ if (type == Long.class || type == long.class) return (long)nn;
+ throw new IllegalArgumentException("don't know how to make argument of type: "+type);
+ }
+ private Object[] makeArguments(Class<?>... ptypes) {
+ Object[] args = new Object[ptypes.length];
+ for (int i = 0; i < args.length; i++)
+ args[i] = makeArgument(ptypes[i]);
+ return args;
+ }
+ private Object[] makeArguments(MethodType mtype) {
+ return makeArguments(mtype.parameterArray());
+ }
+ private Object[] pop(int n, Object[] args) {
+ if (n >= 0)
+ return Arrays.copyOfRange(args, n, args.length);
+ else
+ return Arrays.copyOfRange(args, 0, args.length+n);
+ }
+ private Object[] pushAtFront(Object arg1, Object[] args) {
+ Object[] res = new Object[1+args.length];
+ res[0] = arg1;
+ System.arraycopy(args, 0, res, 1, args.length);
+ return res;
+ }
+ private Object[] pushAtBack(Object[] args, Object argN) {
+ Object[] res = new Object[1+args.length];
+ System.arraycopy(args, 0, res, 0, args.length);
+ res[args.length] = argN;
+ return res;
+ }
+ private static String strip(String prefix, String s) {
+ assert(s.startsWith(prefix));
+ return s.substring(prefix.length());
+ }
+
+ private final int[] refKindTestCounts = new int[REF_KIND_NAMES.length];
+ @After
+ public void printCounts() {
+ ArrayList<String> zeroes = new ArrayList<>();
+ for (int i = 0; i < refKindTestCounts.length; i++) {
+ final int count = refKindTestCounts[i];
+ final String name = REF_KIND_NAMES[i];
+ if (count == 0) {
+ if (name != null) zeroes.add(name);
+ continue;
+ }
+ if (verbose >= 0)
+ System.out.println("test count for "+name+" : "+count);
+ else if (name != null)
+ zeroes.add(name);
+ }
+ if (verbose >= 0)
+ System.out.println("test counts zero for "+zeroes);
+ }
+
+ // Test subjects
+ public static String makeString(Object x) { return "makeString("+x+")"; }
+ public static String dupString(String x) { return "("+x+"+"+x+")"; }
+ public static String intString(int x) { return "intString("+x+")"; }
+ public static String byteString(byte x) { return "byteString("+x+")"; }
+ public static String longString(String x, long y, String z) { return "longString("+x+y+z+")"; }
+
+ public final String toString() {
+ return "<"+getClass().getSimpleName()+"#"+counter+">";
+ }
+ public final String hello() { return "hello from "+this; }
+ private PrivateInvokeTest withCounter(int counter) {
+ PrivateInvokeTest res = new PrivateInvokeTest();
+ res.counter = counter;
+ return res;
+ }
+
+ public static void main(String... av) throws Throwable {
+ new PrivateInvokeTest().run();
+ }
+ public void run() throws Throwable {
+ testFirst();
+ testInvokeDirect();
+ }
+
+ @Test
+ public void testFirst() throws Throwable {
+ if (true) return; // nothing here
+ try {
+ System.out.println("start of testFirst");
+ } finally {
+ System.out.println("end of testFirst");
+ }
+ }
+
+ @Test
+ public void testInvokeDirect() {
+ testInvokeDirect(getMethod(THIS_CLASS, "hello"));
+ testInvokeDirect(getMethod(Object.class, "toString"));
+ testInvokeDirect(getMethod(Comparable.class, "compareTo", Object.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "makeString", Object.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "dupString", String.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "intString", int.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "byteString", byte.class));
+ testInvokeDirect(getMethod(THIS_CLASS, "longString", String.class, long.class, String.class));
+ }
+
+ void testInvokeDirect(Method m) {
+ final int refKind = referenceKind(m);
+ testInvokeDirect(m, refKind);
+ testInvokeDirect(m, REF_MH_invokeBasic);
+ }
+ void testInvokeDirect(Method m, int refKind) {
+ if (verbose >= 1)
+ System.out.println("testInvoke m="+m+" : "+REF_KIND_NAMES[refKind]);
+ final MethodHandle mh = unreflect(m);
+ Object[] args = makeArguments(mh.type());
+ Object res1 = invokeWithArguments(m, args);
+ // res1 comes from java.lang.reflect.Method::invoke
+ if (verbose >= 1)
+ System.out.println("m"+Arrays.asList(args)+" => "+res1);
+ // res2 comes from java.lang.invoke.MethodHandle::invoke
+ Object res2 = invokeWithArguments(mh, args);
+ assertEquals(res1, res2);
+ MethodType mtype = mh.type();
+ testInvokeVia("DMH invoker", refKind, directInvoker(refKind, mtype), mh, res1, args);
+ MethodType etype = mtype.erase();
+ if (etype != mtype) {
+ // Try a detuned invoker.
+ testInvokeVia("erased DMH invoker", refKind, directInvoker(refKind, etype), mh, res1, args);
+ }
+ MethodType btype = basicType(mtype);
+ if (btype != mtype && btype != etype) {
+ // Try a detuned invoker.
+ testInvokeVia("basic DMH invoker", refKind, directInvoker(refKind, btype), mh, res1, args);
+ }
+ if (false) {
+ // this can crash the JVM
+ testInvokeVia("generic DMH invoker", refKind, directInvoker(refKind, mtype.generic()), mh, res1, args);
+ }
+ refKindTestCounts[refKind] += 1;
+ }
+
+ void testInvokeVia(String kind, int refKind, MethodHandle invoker, MethodHandle mh, Object res1, Object... args) {
+ Object[] args1;
+ if (refKind == REF_MH_invokeBasic)
+ args1 = pushAtFront(mh, args);
+ else
+ args1 = pushAtBack(args, internalMemberName(mh));
+ if (verbose >= 2) {
+ System.out.println(kind+" invoker="+invoker+" mh="+debugString(mh)+" args="+Arrays.asList(args1));
+ }
+ Object res3 = invokeWithArguments(invoker, args1);
+ assertEquals(res1, res3);
+ }
+}
--- a/jdk/test/java/lang/invoke/ThrowExceptionsTest.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/test/java/lang/invoke/ThrowExceptionsTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -222,7 +222,7 @@
return savedEx;
}
- private static void assertEquals(Object x, Object y) {
+ private static void assertEquals(Object x, Object y) {
if (x == y || x != null && x.equals(y)) return;
throw new RuntimeException(x+" != "+y);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/remote/RemoteExample.java Fri Aug 10 16:03:28 2012 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009-2010 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package test.java.lang.invoke.remote;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import test.java.lang.invoke.MethodHandlesTest;
+
+/**
+ * Out-of-package access into protected members of test.java.lang.invoke.remote.MethodHandle.PubExample.
+ */
+public class RemoteExample extends MethodHandlesTest.PubExample {
+ public RemoteExample() { super("RemoteExample"); }
+ public static Lookup lookup() { return MethodHandles.lookup(); }
+ public final void fin_v0() { MethodHandlesTest.called("Rem/fin_v0", this); }
+ protected void pro_v0() { MethodHandlesTest.called("Rem/pro_v0", this); }
+ protected static void pro_s0() { MethodHandlesTest.called("Rem/pro_s0"); }
+}
--- a/jdk/test/sun/invoke/util/ValueConversionsTest.java Fri Aug 10 10:15:52 2012 -0700
+++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java Fri Aug 10 16:03:28 2012 -0700
@@ -122,36 +122,6 @@
}
@Test
- public void testUnboxRaw() throws Throwable {
- //System.out.println("unboxRaw");
- for (Wrapper w : Wrapper.values()) {
- if (w == Wrapper.OBJECT) continue; // skip this; no raw form
- //System.out.println(w);
- for (int n = -5; n < 10; n++) {
- Object box = w.wrap(n);
- long expResult = w.unwrapRaw(box);
- Object box2 = w.wrapRaw(expResult);
- assertEquals(box, box2);
- MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType());
- long result = -1;
- switch (w) {
- case INT: result = (int) unboxer.invokeExact(box); break;
- case LONG: result = (long) unboxer.invokeExact(box); break;
- case FLOAT: result = (int) unboxer.invokeExact(box); break;
- case DOUBLE: result = (long) unboxer.invokeExact(box); break;
- case CHAR: result = (int) unboxer.invokeExact(box); break;
- case BYTE: result = (int) unboxer.invokeExact(box); break;
- case SHORT: result = (int) unboxer.invokeExact(box); break;
- case BOOLEAN: result = (int) unboxer.invokeExact(box); break;
- case VOID: result = (int) unboxer.invokeExact(box); break;
- }
- assertEquals("(w,n,box)="+Arrays.asList(w,n,box),
- expResult, result);
- }
- }
- }
-
- @Test
public void testBox() throws Throwable {
//System.out.println("box");
for (Wrapper w : Wrapper.values()) {
@@ -180,65 +150,6 @@
}
@Test
- public void testBoxRaw() throws Throwable {
- //System.out.println("boxRaw");
- for (Wrapper w : Wrapper.values()) {
- if (w == Wrapper.VOID) continue; // skip this; no unboxed form
- if (w == Wrapper.OBJECT) continue; // skip this; no raw form
- //System.out.println(w);
- for (int n = -5; n < 10; n++) {
- Object box = w.wrap(n);
- long raw = w.unwrapRaw(box);
- Object expResult = box;
- MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType());
- Object result = null;
- switch (w) {
- case INT: result = boxer.invokeExact((int)raw); break;
- case LONG: result = boxer.invokeExact(raw); break;
- case FLOAT: result = boxer.invokeExact((int)raw); break;
- case DOUBLE: result = boxer.invokeExact(raw); break;
- case CHAR: result = boxer.invokeExact((int)raw); break;
- case BYTE: result = boxer.invokeExact((int)raw); break;
- case SHORT: result = boxer.invokeExact((int)raw); break;
- case BOOLEAN: result = boxer.invokeExact((int)raw); break;
- }
- assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
- expResult, result);
- }
- }
- }
-
- @Test
- public void testReboxRaw() throws Throwable {
- //System.out.println("reboxRaw");
- for (Wrapper w : Wrapper.values()) {
- Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType());
- if (w == Wrapper.VOID) continue; // skip this; no unboxed form
- if (w == Wrapper.OBJECT) continue; // skip this; no raw form
- //System.out.println(w);
- for (int n = -5; n < 10; n++) {
- Object box = w.wrap(n);
- Object raw = pw.wrap(w.unwrapRaw(box));
- Object expResult = box;
- MethodHandle boxer = ValueConversions.rebox(w.primitiveType());
- Object result = null;
- switch (w) {
- case INT: result = boxer.invokeExact(raw); break;
- case LONG: result = boxer.invokeExact(raw); break;
- case FLOAT: result = boxer.invokeExact(raw); break;
- case DOUBLE: result = boxer.invokeExact(raw); break;
- case CHAR: result = boxer.invokeExact(raw); break;
- case BYTE: result = boxer.invokeExact(raw); break;
- case SHORT: result = boxer.invokeExact(raw); break;
- case BOOLEAN: result = boxer.invokeExact(raw); break;
- }
- assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
- expResult, result);
- }
- }
- }
-
- @Test
public void testCast() throws Throwable {
//System.out.println("cast");
Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
@@ -281,6 +192,91 @@
}
@Test
+ public void testConvert() throws Throwable {
+ //System.out.println("convert");
+ for (long tval = 0, ctr = 0;;) {
+ if (++ctr > 99999) throw new AssertionError("too many test values");
+ // next test value:
+ //System.out.println(Long.toHexString(tval)); // prints 3776 test patterns
+ tval = nextTestValue(tval);
+ if (tval == 0) {
+ //System.out.println("test value count = "+ctr); // 3776 = 8*59*8
+ break; // repeat
+ }
+ }
+ for (Wrapper src : Wrapper.values()) {
+ for (Wrapper dst : Wrapper.values()) {
+ testConvert(src, dst, 0);
+ }
+ }
+ }
+ static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable {
+ //System.out.println(src+" => "+dst);
+ boolean testSingleCase = (tval != 0);
+ final long tvalInit = tval;
+ MethodHandle conv = ValueConversions.convertPrimitive(src, dst);
+ MethodType convType;
+ if (src == Wrapper.VOID)
+ convType = MethodType.methodType(dst.primitiveType() /* , void */);
+ else
+ convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
+ assertEquals(convType, conv.type());
+ MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class));
+ for (;;) {
+ long n = tval;
+ Object testValue = src.wrap(n);
+ Object expResult = dst.cast(testValue, dst.primitiveType());
+ Object result;
+ switch (src) {
+ case INT: result = converter.invokeExact((int)n); break;
+ case LONG: result = converter.invokeExact(/*long*/n); break;
+ case FLOAT: result = converter.invokeExact((float)n); break;
+ case DOUBLE: result = converter.invokeExact((double)n); break;
+ case CHAR: result = converter.invokeExact((char)n); break;
+ case BYTE: result = converter.invokeExact((byte)n); break;
+ case SHORT: result = converter.invokeExact((short)n); break;
+ case OBJECT: result = converter.invokeExact((Object)n); break;
+ case BOOLEAN: result = converter.invokeExact((n & 1) != 0); break;
+ case VOID: result = converter.invokeExact(); break;
+ default: throw new AssertionError();
+ }
+ assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue),
+ expResult, result);
+ if (testSingleCase) break;
+ // next test value:
+ tval = nextTestValue(tval);
+ if (tval == tvalInit) break; // repeat
+ }
+ }
+ static long tweakSign(long x) {
+ // Assuming that x is mostly zeroes, make those zeroes follow bit #62 (just below the sign).
+ // This function is self-inverse.
+ final long MID_SIGN_BIT = 62;
+ long sign = -((x >>> MID_SIGN_BIT) & 1); // all ones or all zeroes
+ long flip = (sign >>> -MID_SIGN_BIT); // apply the sign below the mid-bit
+ return x ^ flip;
+ }
+ static long nextTestValue(long x) {
+ // Produce 64 bits with three component bitfields: [ high:3 | mid:58 | low:3 ].
+ // The high and low fields vary through all possible bit patterns.
+ // The middle field is either all zero or has a single bit set.
+ // For better coverage of the neighborhood of zero, an internal sign bit is xored downward also.
+ long ux = tweakSign(x); // unsign the middle field
+ final long LOW_BITS = 3, LOW_BITS_MASK = (1L << LOW_BITS)-1;
+ final long HIGH_BITS = 3, HIGH_BITS_MASK = ~(-1L >>> HIGH_BITS);
+ if ((ux & LOW_BITS_MASK) != LOW_BITS_MASK) {
+ ++ux;
+ } else {
+ ux &= ~LOW_BITS_MASK;
+ long midBit = (ux & ~HIGH_BITS_MASK);
+ if (midBit == 0)
+ midBit = (1L<<LOW_BITS); // introduce a low bit
+ ux += midBit;
+ }
+ return tweakSign(ux);
+ }
+
+ @Test
public void testVarargsArray() throws Throwable {
//System.out.println("varargsArray");
final int MIN = START_ARITY;
@@ -332,7 +328,7 @@
}
private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
- System.out.println(arrayType.getSimpleName());
+ //System.out.println(arrayType.getSimpleName());
Class<?> elemType = arrayType.getComponentType();
int MIN = START_ARITY;
int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added