24 */ |
24 */ |
25 |
25 |
26 package java.lang.invoke; |
26 package java.lang.invoke; |
27 |
27 |
28 import sun.invoke.util.VerifyType; |
28 import sun.invoke.util.VerifyType; |
29 import java.security.AccessController; |
29 |
30 import java.security.PrivilegedAction; |
|
31 import java.util.ArrayList; |
30 import java.util.ArrayList; |
32 import java.util.Arrays; |
31 import java.util.Arrays; |
33 import java.util.Collections; |
|
34 import java.util.HashMap; |
32 import java.util.HashMap; |
35 import java.util.List; |
|
36 import sun.invoke.empty.Empty; |
33 import sun.invoke.empty.Empty; |
37 import sun.invoke.util.ValueConversions; |
34 import sun.invoke.util.ValueConversions; |
38 import sun.invoke.util.Wrapper; |
35 import sun.invoke.util.Wrapper; |
39 import sun.misc.Unsafe; |
36 import static java.lang.invoke.LambdaForm.*; |
40 import static java.lang.invoke.MethodHandleStatics.*; |
37 import static java.lang.invoke.MethodHandleStatics.*; |
41 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; |
38 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; |
42 |
39 |
43 /** |
40 /** |
44 * Trusted implementation code for MethodHandle. |
41 * Trusted implementation code for MethodHandle. |
45 * @author jrose |
42 * @author jrose |
46 */ |
43 */ |
47 /*non-public*/ abstract class MethodHandleImpl { |
44 /*non-public*/ abstract class MethodHandleImpl { |
48 /// Factory methods to create method handles: |
45 /// Factory methods to create method handles: |
49 |
46 |
50 private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE; |
|
51 |
|
52 static void initStatics() { |
47 static void initStatics() { |
53 // Trigger preceding sequence. |
48 // Trigger selected static initializations. |
54 } |
49 MemberName.Factory.INSTANCE.getClass(); |
55 |
50 } |
56 /** Look up a given method. |
51 |
57 * Callable only from sun.invoke and related packages. |
52 static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) { |
58 * <p> |
53 if (!arrayClass.isArray()) |
59 * The resulting method handle type will be of the given type, |
54 throw newIllegalArgumentException("not an array: "+arrayClass); |
60 * with a receiver type {@code rcvc} prepended if the member is not static. |
55 MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter); |
61 * <p> |
56 MethodType srcType = accessor.type().erase(); |
62 * Access checks are made as of the given lookup class. |
57 MethodType lambdaType = srcType.invokerType(); |
63 * In particular, if the method is protected and {@code defc} is in a |
58 Name[] names = arguments(1, lambdaType); |
64 * different package from the lookup class, then {@code rcvc} must be |
59 Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount()); |
65 * the lookup class or a subclass. |
60 names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args); |
66 * @param token Proof that the lookup class has access to this package. |
61 LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names); |
67 * @param member Resolved method or constructor to call. |
62 MethodHandle mh = new SimpleMethodHandle(srcType, form); |
68 * @param name Name of the desired method. |
63 if (ArrayAccessor.needCast(arrayClass)) { |
69 * @param rcvc Receiver type of desired non-static method (else null) |
64 mh = mh.bindTo(arrayClass); |
70 * @param doDispatch whether the method handle will test the receiver type |
65 } |
71 * @param lookupClass access-check relative to this class |
66 mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter)); |
72 * @return a direct handle to the matching method |
67 return mh; |
73 * @throws IllegalAccessException if the given method cannot be accessed by the lookup class |
68 } |
74 */ |
69 |
75 static |
70 static final class ArrayAccessor { |
76 MethodHandle findMethod(MemberName method, |
71 /// Support for array element access |
77 boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException { |
72 static final HashMap<Class<?>, MethodHandle> GETTER_CACHE = new HashMap<>(); // TODO use it |
78 MethodType mtype = method.getMethodType(); |
73 static final HashMap<Class<?>, MethodHandle> SETTER_CACHE = new HashMap<>(); // TODO use it |
79 if (!method.isStatic()) { |
74 |
80 // adjust the advertised receiver type to be exactly the one requested |
75 static int getElementI(int[] a, int i) { return a[i]; } |
81 // (in the case of invokespecial, this will be the calling class) |
76 static long getElementJ(long[] a, int i) { return a[i]; } |
82 Class<?> recvType = method.getDeclaringClass(); |
77 static float getElementF(float[] a, int i) { return a[i]; } |
83 mtype = mtype.insertParameterTypes(0, recvType); |
78 static double getElementD(double[] a, int i) { return a[i]; } |
84 } |
79 static boolean getElementZ(boolean[] a, int i) { return a[i]; } |
85 DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass); |
80 static byte getElementB(byte[] a, int i) { return a[i]; } |
86 if (!mh.isValid()) |
81 static short getElementS(short[] a, int i) { return a[i]; } |
87 throw method.makeAccessException("no direct method handle", lookupClass); |
82 static char getElementC(char[] a, int i) { return a[i]; } |
88 assert(mh.type() == mtype); |
83 static Object getElementL(Object[] a, int i) { return a[i]; } |
89 if (!method.isVarargs()) |
84 |
90 return mh; |
85 static void setElementI(int[] a, int i, int x) { a[i] = x; } |
91 int argc = mtype.parameterCount(); |
86 static void setElementJ(long[] a, int i, long x) { a[i] = x; } |
92 if (argc != 0) { |
87 static void setElementF(float[] a, int i, float x) { a[i] = x; } |
93 Class<?> arrayType = mtype.parameterType(argc-1); |
88 static void setElementD(double[] a, int i, double x) { a[i] = x; } |
94 if (arrayType.isArray()) |
89 static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; } |
95 return AdapterMethodHandle.makeVarargsCollector(mh, arrayType); |
90 static void setElementB(byte[] a, int i, byte x) { a[i] = x; } |
96 } |
91 static void setElementS(short[] a, int i, short x) { a[i] = x; } |
97 throw method.makeAccessException("cannot make variable arity", null); |
92 static void setElementC(char[] a, int i, char x) { a[i] = x; } |
98 } |
93 static void setElementL(Object[] a, int i, Object x) { a[i] = x; } |
99 |
94 |
100 static |
95 static Object getElementL(Class<?> arrayClass, Object[] a, int i) { arrayClass.cast(a); return a[i]; } |
101 MethodHandle makeAllocator(MethodHandle rawConstructor) { |
96 static void setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; } |
102 MethodType rawConType = rawConstructor.type(); |
97 |
103 Class<?> allocateClass = rawConType.parameterType(0); |
98 // Weakly typed wrappers of Object[] accessors: |
104 // Wrap the raw (unsafe) constructor with the allocation of a suitable object. |
99 static Object getElementL(Object a, int i) { return getElementL((Object[])a, i); } |
105 assert(AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true)); |
100 static void setElementL(Object a, int i, Object x) { setElementL((Object[]) a, i, x); } |
106 // allocator(arg...) |
101 static Object getElementL(Object arrayClass, Object a, int i) { return getElementL((Class<?>) arrayClass, (Object[])a, i); } |
107 // [fold]=> cookedConstructor(obj=allocate(C), arg...) |
102 static void setElementL(Object arrayClass, Object a, int i, Object x) { setElementL((Class<?>) arrayClass, (Object[])a, i, x); } |
108 // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...)) |
103 |
109 MethodHandle returner = MethodHandles.identity(allocateClass); |
104 static boolean needCast(Class<?> arrayClass) { |
110 MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass); |
105 Class<?> elemClass = arrayClass.getComponentType(); |
111 MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false); |
106 return !elemClass.isPrimitive() && elemClass != Object.class; |
112 assert(cookedConstructor.type().equals(ctype)); |
107 } |
113 ctype = ctype.dropParameterTypes(0, 1); |
108 static String name(Class<?> arrayClass, boolean isSetter) { |
114 cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true); |
109 Class<?> elemClass = arrayClass.getComponentType(); |
115 AllocateObject allocator = new AllocateObject(allocateClass); |
110 if (elemClass == null) throw new IllegalArgumentException(); |
116 // allocate() => new C(void) |
111 return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass); |
117 assert(allocator.type().equals(MethodType.methodType(allocateClass))); |
112 } |
118 ctype = ctype.dropParameterTypes(0, 1); |
113 static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false; // FIXME: decide |
119 MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator); |
114 static MethodType type(Class<?> arrayClass, boolean isSetter) { |
120 return fold; |
115 Class<?> elemClass = arrayClass.getComponentType(); |
121 } |
116 Class<?> arrayArgClass = arrayClass; |
122 |
117 if (!elemClass.isPrimitive()) { |
123 static final class AllocateObject /*<C>*/ extends BoundMethodHandle { |
118 arrayArgClass = Object[].class; |
124 private static final Unsafe unsafe = Unsafe.getUnsafe(); |
119 if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS) |
125 |
120 arrayArgClass = Object.class; |
126 private final Class<?> /*<C>*/ allocateClass; |
121 } |
127 |
122 if (!needCast(arrayClass)) { |
128 // for allocation only: |
123 return !isSetter ? |
129 private AllocateObject(Class<?> /*<C>*/ allocateClass) { |
124 MethodType.methodType(elemClass, arrayArgClass, int.class) : |
130 super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class))); |
125 MethodType.methodType(void.class, arrayArgClass, int.class, elemClass); |
131 this.allocateClass = allocateClass; |
126 } else { |
132 } |
127 Class<?> classArgClass = Class.class; |
133 @SuppressWarnings("unchecked") |
128 if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS) |
134 private Object /*C*/ allocate() throws InstantiationException { |
129 classArgClass = Object.class; |
135 return unsafe.allocateInstance(allocateClass); |
130 return !isSetter ? |
136 } |
131 MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) : |
137 static final MethodHandle ALLOCATE; |
132 MethodType.methodType(void.class, classArgClass, arrayArgClass, int.class, Object.class); |
138 static { |
133 } |
139 try { |
134 } |
140 ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0)); |
135 static MethodType correctType(Class<?> arrayClass, boolean isSetter) { |
|
136 Class<?> elemClass = arrayClass.getComponentType(); |
|
137 return !isSetter ? |
|
138 MethodType.methodType(elemClass, arrayClass, int.class) : |
|
139 MethodType.methodType(void.class, arrayClass, int.class, elemClass); |
|
140 } |
|
141 static MethodHandle getAccessor(Class<?> arrayClass, boolean isSetter) { |
|
142 String name = name(arrayClass, isSetter); |
|
143 MethodType type = type(arrayClass, isSetter); |
|
144 try { |
|
145 return IMPL_LOOKUP.findStatic(ArrayAccessor.class, name, type); |
141 } catch (ReflectiveOperationException ex) { |
146 } catch (ReflectiveOperationException ex) { |
142 throw uncaughtException(ex); |
147 throw uncaughtException(ex); |
143 } |
148 } |
144 } |
149 } |
145 } |
150 } |
146 |
151 |
147 static |
152 /** |
148 MethodHandle accessField(MemberName member, boolean isSetter, |
153 * Create a JVM-level adapter method handle to conform the given method |
149 Class<?> lookupClass) { |
154 * handle to the similar newType, using only pairwise argument conversions. |
150 // Use sun. misc.Unsafe to dig up the dirt on the field. |
155 * For each argument, convert incoming argument to the exact type needed. |
151 FieldAccessor accessor = new FieldAccessor(member, isSetter); |
156 * The argument conversions allowed are casting, boxing and unboxing, |
152 return accessor; |
157 * integral widening or narrowing, and floating point widening or narrowing. |
153 } |
158 * @param srcType required call type |
154 |
159 * @param target original method handle |
155 static |
160 * @param level which strength of conversion is allowed |
156 MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) { |
161 * @return an adapter to the original handle with the desired new type, |
157 if (!arrayClass.isArray()) |
162 * or the original target if the types are already identical |
158 throw newIllegalArgumentException("not an array: "+arrayClass); |
163 * or null if the adaptation cannot be made |
159 Class<?> elemClass = arrayClass.getComponentType(); |
164 */ |
160 MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass); |
165 static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) { |
161 if (mhs == null) { |
166 assert(level >= 0 && level <= 2); |
162 if (!FieldAccessor.doCache(elemClass)) |
167 MethodType dstType = target.type(); |
163 return FieldAccessor.ahandle(arrayClass, isSetter); |
168 assert(dstType.parameterCount() == target.type().parameterCount()); |
164 mhs = new MethodHandle[] { |
169 if (srcType == dstType) |
165 FieldAccessor.ahandle(arrayClass, false), |
170 return target; |
166 FieldAccessor.ahandle(arrayClass, true) |
171 |
167 }; |
172 // Calculate extra arguments (temporaries) required in the names array. |
168 if (mhs[0].type().parameterType(0) == Class.class) { |
173 // FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step. |
169 mhs[0] = mhs[0].bindTo(elemClass); |
174 int extra = 0; |
170 mhs[1] = mhs[1].bindTo(elemClass); |
175 for (int i = 0; i < srcType.parameterCount(); i++) { |
171 } |
176 Class<?> src = srcType.parameterType(i); |
172 synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier |
177 Class<?> dst = dstType.parameterType(i); |
173 FieldAccessor.ARRAY_CACHE.put(elemClass, mhs); |
178 if (!VerifyType.isNullConversion(src, dst)) { |
174 } |
179 extra++; |
175 return mhs[isSetter ? 1 : 0]; |
180 } |
176 } |
181 } |
177 |
182 |
178 static final class FieldAccessor /*<C,V>*/ extends BoundMethodHandle { |
183 Class<?> needReturn = srcType.returnType(); |
179 private static final Unsafe unsafe = Unsafe.getUnsafe(); |
184 Class<?> haveReturn = dstType.returnType(); |
180 final Object base; // for static refs only |
185 boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn); |
181 final long offset; |
186 |
182 final String name; |
187 // Now build a LambdaForm. |
183 |
188 MethodType lambdaType = srcType.invokerType(); |
184 FieldAccessor(MemberName field, boolean isSetter) { |
189 Name[] names = arguments(extra + 1, lambdaType); |
185 super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic())); |
190 int[] indexes = new int[lambdaType.parameterCount()]; |
186 this.offset = (long) field.getVMIndex(); |
191 |
187 this.name = field.getName(); |
192 MethodType midType = dstType; |
188 this.base = staticBase(field); |
193 for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) { |
189 } |
194 Class<?> src = srcType.parameterType(i); |
190 @Override |
195 Class<?> dst = midType.parameterType(i); |
191 String debugString() { return addTypeString(name, this); } |
196 |
192 |
197 if (VerifyType.isNullConversion(src, dst)) { |
193 private static Object nullCheck(Object obj) { |
198 // do nothing: difference is trivial |
194 obj.getClass(); // NPE |
199 indexes[i] = argIndex; |
195 return obj; |
200 continue; |
196 } |
201 } |
197 |
202 |
198 int getFieldI(Object /*C*/ obj) { return unsafe.getInt(nullCheck(obj), offset); } |
203 // Work the current type backward toward the desired caller type: |
199 void setFieldI(Object /*C*/ obj, int x) { unsafe.putInt(nullCheck(obj), offset, x); } |
204 midType = midType.changeParameterType(i, src); |
200 long getFieldJ(Object /*C*/ obj) { return unsafe.getLong(nullCheck(obj), offset); } |
205 |
201 void setFieldJ(Object /*C*/ obj, long x) { unsafe.putLong(nullCheck(obj), offset, x); } |
206 // Tricky case analysis follows. |
202 float getFieldF(Object /*C*/ obj) { return unsafe.getFloat(nullCheck(obj), offset); } |
207 MethodHandle fn = null; |
203 void setFieldF(Object /*C*/ obj, float x) { unsafe.putFloat(nullCheck(obj), offset, x); } |
208 if (src.isPrimitive()) { |
204 double getFieldD(Object /*C*/ obj) { return unsafe.getDouble(nullCheck(obj), offset); } |
209 if (dst.isPrimitive()) { |
205 void setFieldD(Object /*C*/ obj, double x) { unsafe.putDouble(nullCheck(obj), offset, x); } |
210 fn = ValueConversions.convertPrimitive(src, dst); |
206 boolean getFieldZ(Object /*C*/ obj) { return unsafe.getBoolean(nullCheck(obj), offset); } |
211 } else { |
207 void setFieldZ(Object /*C*/ obj, boolean x) { unsafe.putBoolean(nullCheck(obj), offset, x); } |
212 Wrapper w = Wrapper.forPrimitiveType(src); |
208 byte getFieldB(Object /*C*/ obj) { return unsafe.getByte(nullCheck(obj), offset); } |
213 MethodHandle boxMethod = ValueConversions.box(w); |
209 void setFieldB(Object /*C*/ obj, byte x) { unsafe.putByte(nullCheck(obj), offset, x); } |
214 if (dst == w.wrapperType()) |
210 short getFieldS(Object /*C*/ obj) { return unsafe.getShort(nullCheck(obj), offset); } |
215 fn = boxMethod; |
211 void setFieldS(Object /*C*/ obj, short x) { unsafe.putShort(nullCheck(obj), offset, x); } |
216 else |
212 char getFieldC(Object /*C*/ obj) { return unsafe.getChar(nullCheck(obj), offset); } |
217 fn = boxMethod.asType(MethodType.methodType(dst, src)); |
213 void setFieldC(Object /*C*/ obj, char x) { unsafe.putChar(nullCheck(obj), offset, x); } |
218 } |
214 Object /*V*/ getFieldL(Object /*C*/ obj) { return unsafe.getObject(nullCheck(obj), offset); } |
|
215 void setFieldL(Object /*C*/ obj, Object /*V*/ x) { unsafe.putObject(nullCheck(obj), offset, x); } |
|
216 |
|
217 static Object staticBase(final MemberName field) { |
|
218 if (!field.isStatic()) return null; |
|
219 return AccessController.doPrivileged(new PrivilegedAction<Object>() { |
|
220 public Object run() { |
|
221 try { |
|
222 Class<?> c = field.getDeclaringClass(); |
|
223 // FIXME: Should not have to create 'f' to get this value. |
|
224 java.lang.reflect.Field f = c.getDeclaredField(field.getName()); |
|
225 return unsafe.staticFieldBase(f); |
|
226 } catch (NoSuchFieldException ee) { |
|
227 throw uncaughtException(ee); |
|
228 } |
|
229 } |
|
230 }); |
|
231 } |
|
232 |
|
233 int getStaticI() { return unsafe.getInt(base, offset); } |
|
234 void setStaticI(int x) { unsafe.putInt(base, offset, x); } |
|
235 long getStaticJ() { return unsafe.getLong(base, offset); } |
|
236 void setStaticJ(long x) { unsafe.putLong(base, offset, x); } |
|
237 float getStaticF() { return unsafe.getFloat(base, offset); } |
|
238 void setStaticF(float x) { unsafe.putFloat(base, offset, x); } |
|
239 double getStaticD() { return unsafe.getDouble(base, offset); } |
|
240 void setStaticD(double x) { unsafe.putDouble(base, offset, x); } |
|
241 boolean getStaticZ() { return unsafe.getBoolean(base, offset); } |
|
242 void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); } |
|
243 byte getStaticB() { return unsafe.getByte(base, offset); } |
|
244 void setStaticB(byte x) { unsafe.putByte(base, offset, x); } |
|
245 short getStaticS() { return unsafe.getShort(base, offset); } |
|
246 void setStaticS(short x) { unsafe.putShort(base, offset, x); } |
|
247 char getStaticC() { return unsafe.getChar(base, offset); } |
|
248 void setStaticC(char x) { unsafe.putChar(base, offset, x); } |
|
249 Object /*V*/ getStaticL() { return unsafe.getObject(base, offset); } |
|
250 void setStaticL(Object /*V*/ x) { unsafe.putObject(base, offset, x); } |
|
251 |
|
252 static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) { |
|
253 String stem; |
|
254 if (!isStatic) |
|
255 stem = (!isSetter ? "getField" : "setField"); |
|
256 else |
|
257 stem = (!isSetter ? "getStatic" : "setStatic"); |
|
258 return stem + Wrapper.basicTypeChar(vclass); |
|
259 } |
|
260 static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) { |
|
261 MethodType type; |
|
262 if (!isStatic) { |
|
263 if (!isSetter) |
|
264 return MethodType.methodType(vclass, cclass); |
|
265 else |
|
266 return MethodType.methodType(void.class, cclass, vclass); |
|
267 } else { |
219 } else { |
268 if (!isSetter) |
220 if (dst.isPrimitive()) { |
269 return MethodType.methodType(vclass); |
221 // Caller has boxed a primitive. Unbox it for the target. |
270 else |
222 Wrapper w = Wrapper.forPrimitiveType(dst); |
271 return MethodType.methodType(void.class, vclass); |
223 if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) { |
272 } |
224 fn = ValueConversions.unbox(dst); |
273 } |
225 } else if (src == Object.class || !Wrapper.isWrapperType(src)) { |
274 static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) { |
226 // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int |
275 String name = FieldAccessor.fname(vclass, isSetter, isStatic); |
227 // must include additional conversions |
276 if (cclass.isPrimitive()) throw newIllegalArgumentException("primitive "+cclass); |
228 // src must be examined at runtime, to detect Byte, Character, etc. |
277 Class<?> ecclass = Object.class; //erase this type |
229 MethodHandle unboxMethod = (level == 1 |
278 Class<?> evclass = vclass; |
230 ? ValueConversions.unbox(dst) |
279 if (!evclass.isPrimitive()) evclass = Object.class; |
231 : ValueConversions.unboxCast(dst)); |
280 MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic); |
232 fn = unboxMethod; |
281 MethodHandle mh; |
233 } else { |
282 try { |
234 // Example: Byte->int |
283 mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type); |
235 // Do this by reformulating the problem to Byte->byte. |
284 } catch (ReflectiveOperationException ex) { |
236 Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType(); |
285 throw uncaughtException(ex); |
237 MethodHandle unbox = ValueConversions.unbox(srcPrim); |
286 } |
238 // Compose the two conversions. FIXME: should make two Names for this job |
287 if (evclass != vclass || (!isStatic && ecclass != cclass)) { |
239 fn = unbox.asType(MethodType.methodType(dst, src)); |
288 MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); |
|
289 strongType = strongType.insertParameterTypes(0, FieldAccessor.class); |
|
290 mh = convertArguments(mh, strongType, 0); |
|
291 } |
|
292 return mh; |
|
293 } |
|
294 |
|
295 /// Support for array element access |
|
296 static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE = |
|
297 new HashMap<Class<?>, MethodHandle[]>(); |
|
298 // FIXME: Cache on the classes themselves, not here. |
|
299 static boolean doCache(Class<?> elemClass) { |
|
300 if (elemClass.isPrimitive()) return true; |
|
301 ClassLoader cl = elemClass.getClassLoader(); |
|
302 return cl == null || cl == ClassLoader.getSystemClassLoader(); |
|
303 } |
|
304 static int getElementI(int[] a, int i) { return a[i]; } |
|
305 static void setElementI(int[] a, int i, int x) { a[i] = x; } |
|
306 static long getElementJ(long[] a, int i) { return a[i]; } |
|
307 static void setElementJ(long[] a, int i, long x) { a[i] = x; } |
|
308 static float getElementF(float[] a, int i) { return a[i]; } |
|
309 static void setElementF(float[] a, int i, float x) { a[i] = x; } |
|
310 static double getElementD(double[] a, int i) { return a[i]; } |
|
311 static void setElementD(double[] a, int i, double x) { a[i] = x; } |
|
312 static boolean getElementZ(boolean[] a, int i) { return a[i]; } |
|
313 static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; } |
|
314 static byte getElementB(byte[] a, int i) { return a[i]; } |
|
315 static void setElementB(byte[] a, int i, byte x) { a[i] = x; } |
|
316 static short getElementS(short[] a, int i) { return a[i]; } |
|
317 static void setElementS(short[] a, int i, short x) { a[i] = x; } |
|
318 static char getElementC(char[] a, int i) { return a[i]; } |
|
319 static void setElementC(char[] a, int i, char x) { a[i] = x; } |
|
320 static Object getElementL(Object[] a, int i) { return a[i]; } |
|
321 static void setElementL(Object[] a, int i, Object x) { a[i] = x; } |
|
322 static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; } |
|
323 static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; } |
|
324 |
|
325 static String aname(Class<?> aclass, boolean isSetter) { |
|
326 Class<?> vclass = aclass.getComponentType(); |
|
327 if (vclass == null) throw new IllegalArgumentException(); |
|
328 return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass); |
|
329 } |
|
330 static MethodType atype(Class<?> aclass, boolean isSetter) { |
|
331 Class<?> vclass = aclass.getComponentType(); |
|
332 if (!isSetter) |
|
333 return MethodType.methodType(vclass, aclass, int.class); |
|
334 else |
|
335 return MethodType.methodType(void.class, aclass, int.class, vclass); |
|
336 } |
|
337 static MethodHandle ahandle(Class<?> aclass, boolean isSetter) { |
|
338 Class<?> vclass = aclass.getComponentType(); |
|
339 String name = FieldAccessor.aname(aclass, isSetter); |
|
340 Class<?> caclass = null; |
|
341 if (!vclass.isPrimitive() && vclass != Object.class) { |
|
342 caclass = aclass; |
|
343 aclass = Object[].class; |
|
344 vclass = Object.class; |
|
345 } |
|
346 MethodType type = FieldAccessor.atype(aclass, isSetter); |
|
347 if (caclass != null) |
|
348 type = type.insertParameterTypes(0, Class.class); |
|
349 MethodHandle mh; |
|
350 try { |
|
351 mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type); |
|
352 } catch (ReflectiveOperationException ex) { |
|
353 throw uncaughtException(ex); |
|
354 } |
|
355 if (caclass != null) { |
|
356 MethodType strongType = FieldAccessor.atype(caclass, isSetter); |
|
357 mh = mh.bindTo(caclass); |
|
358 mh = convertArguments(mh, strongType, 0); |
|
359 } |
|
360 return mh; |
|
361 } |
|
362 } |
|
363 |
|
364 /** Bind a predetermined first argument to the given direct method handle. |
|
365 * Callable only from MethodHandles. |
|
366 * @param token Proof that the caller has access to this package. |
|
367 * @param target Any direct method handle. |
|
368 * @param receiver Receiver (or first static method argument) to pre-bind. |
|
369 * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist |
|
370 */ |
|
371 static |
|
372 MethodHandle bindReceiver(MethodHandle target, Object receiver) { |
|
373 if (receiver == null) return null; |
|
374 if (target instanceof AdapterMethodHandle && |
|
375 ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY |
|
376 ) { |
|
377 Object info = MethodHandleNatives.getTargetInfo(target); |
|
378 if (info instanceof DirectMethodHandle) { |
|
379 DirectMethodHandle dmh = (DirectMethodHandle) info; |
|
380 if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) { |
|
381 MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0); |
|
382 MethodType newType = target.type().dropParameterTypes(0, 1); |
|
383 return convertArguments(bmh, newType, bmh.type(), 0); |
|
384 } |
|
385 } |
|
386 } |
|
387 if (target instanceof DirectMethodHandle) |
|
388 return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0); |
|
389 return null; // let caller try something else |
|
390 } |
|
391 |
|
392 /** Bind a predetermined argument to the given arbitrary method handle. |
|
393 * Callable only from MethodHandles. |
|
394 * @param token Proof that the caller has access to this package. |
|
395 * @param target Any method handle. |
|
396 * @param receiver Argument (which can be a boxed primitive) to pre-bind. |
|
397 * @return a suitable BoundMethodHandle |
|
398 */ |
|
399 static |
|
400 MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) { |
|
401 return new BoundMethodHandle(target, receiver, argnum); |
|
402 } |
|
403 |
|
404 static MethodHandle permuteArguments(MethodHandle target, |
|
405 MethodType newType, |
|
406 MethodType oldType, |
|
407 int[] permutationOrNull) { |
|
408 assert(oldType.parameterCount() == target.type().parameterCount()); |
|
409 int outargs = oldType.parameterCount(), inargs = newType.parameterCount(); |
|
410 if (permutationOrNull.length != outargs) |
|
411 throw newIllegalArgumentException("wrong number of arguments in permutation"); |
|
412 // Make the individual outgoing argument types match up first. |
|
413 Class<?>[] callTypeArgs = new Class<?>[outargs]; |
|
414 for (int i = 0; i < outargs; i++) |
|
415 callTypeArgs[i] = newType.parameterType(permutationOrNull[i]); |
|
416 MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs); |
|
417 target = convertArguments(target, callType, oldType, 0); |
|
418 assert(target != null); |
|
419 oldType = target.type(); |
|
420 List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN |
|
421 List<Integer> state = new ArrayList<Integer>(); // i*TOKEN |
|
422 List<Integer> drops = new ArrayList<Integer>(); // not tokens |
|
423 List<Integer> dups = new ArrayList<Integer>(); // not tokens |
|
424 final int TOKEN = 10; // to mark items which are symbolic only |
|
425 // state represents the argument values coming into target |
|
426 for (int i = 0; i < outargs; i++) { |
|
427 state.add(permutationOrNull[i] * TOKEN); |
|
428 } |
|
429 // goal represents the desired state |
|
430 for (int i = 0; i < inargs; i++) { |
|
431 if (state.contains(i * TOKEN)) { |
|
432 goal.add(i * TOKEN); |
|
433 } else { |
|
434 // adapter must initially drop all unused arguments |
|
435 drops.add(i); |
|
436 } |
|
437 } |
|
438 // detect duplications |
|
439 while (state.size() > goal.size()) { |
|
440 for (int i2 = 0; i2 < state.size(); i2++) { |
|
441 int arg1 = state.get(i2); |
|
442 int i1 = state.indexOf(arg1); |
|
443 if (i1 != i2) { |
|
444 // found duplicate occurrence at i2 |
|
445 int arg2 = (inargs++) * TOKEN; |
|
446 state.set(i2, arg2); |
|
447 dups.add(goal.indexOf(arg1)); |
|
448 goal.add(arg2); |
|
449 } |
|
450 } |
|
451 } |
|
452 assert(state.size() == goal.size()); |
|
453 int size = goal.size(); |
|
454 while (!state.equals(goal)) { |
|
455 // Look for a maximal sequence of adjacent misplaced arguments, |
|
456 // and try to rotate them into place. |
|
457 int bestRotArg = -10 * TOKEN, bestRotLen = 0; |
|
458 int thisRotArg = -10 * TOKEN, thisRotLen = 0; |
|
459 for (int i = 0; i < size; i++) { |
|
460 int arg = state.get(i); |
|
461 // Does this argument match the current run? |
|
462 if (arg == thisRotArg + TOKEN) { |
|
463 thisRotArg = arg; |
|
464 thisRotLen += 1; |
|
465 if (bestRotLen < thisRotLen) { |
|
466 bestRotLen = thisRotLen; |
|
467 bestRotArg = thisRotArg; |
|
468 } |
240 } |
469 } else { |
241 } else { |
470 // The old sequence (if any) stops here. |
242 // Simple reference conversion. |
471 thisRotLen = 0; |
243 // Note: Do not check for a class hierarchy relation |
472 thisRotArg = -10 * TOKEN; |
244 // between src and dst. In all cases a 'null' argument |
473 // But maybe a new one starts here also. |
245 // will pass the cast conversion. |
474 int wantArg = goal.get(i); |
246 fn = ValueConversions.cast(dst); |
475 final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION; |
|
476 if (arg != wantArg && |
|
477 arg >= wantArg - TOKEN * MAX_ARG_ROTATION && |
|
478 arg <= wantArg + TOKEN * MAX_ARG_ROTATION) { |
|
479 thisRotArg = arg; |
|
480 thisRotLen = 1; |
|
481 } |
|
482 } |
247 } |
483 } |
248 } |
484 if (bestRotLen >= 2) { |
249 names[tmpIndex] = new Name(fn, names[argIndex]); |
485 // Do a rotation if it can improve argument positioning |
250 indexes[i] = tmpIndex; |
486 // by at least 2 arguments. This is not always optimal, |
251 tmpIndex++; |
487 // but it seems to catch common cases. |
252 } |
488 int dstEnd = state.indexOf(bestRotArg); |
253 if (retConv) { |
489 int srcEnd = goal.indexOf(bestRotArg); |
254 MethodHandle adjustReturn; |
490 int rotBy = dstEnd - srcEnd; |
255 if (haveReturn == void.class) { |
491 int dstBeg = dstEnd - (bestRotLen - 1); |
256 // synthesize a zero value for the given void |
492 int srcBeg = srcEnd - (bestRotLen - 1); |
257 Object zero = Wrapper.forBasicType(needReturn).zero(); |
493 assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs |
258 adjustReturn = MethodHandles.constant(needReturn, zero); |
494 // Make a span which covers both source and destination. |
259 } else { |
495 int rotBeg = Math.min(dstBeg, srcBeg); |
260 MethodHandle identity = MethodHandles.identity(needReturn); |
496 int rotEnd = Math.max(dstEnd, srcEnd); |
261 MethodType needConversion = identity.type().changeParameterType(0, haveReturn); |
497 int score = 0; |
262 adjustReturn = makePairwiseConvert(identity, needConversion, level); |
498 for (int i = rotBeg; i <= rotEnd; i++) { |
263 } |
499 if ((int)state.get(i) != (int)goal.get(i)) |
264 target = makeCollectArguments(adjustReturn, target, 0, false); |
500 score += 1; |
265 } |
|
266 |
|
267 // Build argument array for the call. |
|
268 Name[] targetArgs = new Name[dstType.parameterCount()]; |
|
269 for (int i = 0; i < dstType.parameterCount(); i++) { |
|
270 int idx = indexes[i]; |
|
271 targetArgs[i] = names[idx]; |
|
272 } |
|
273 names[names.length - 1] = new Name(target, (Object[]) targetArgs); |
|
274 LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names); |
|
275 return new SimpleMethodHandle(srcType, form); |
|
276 } |
|
277 |
|
278 static MethodHandle makeReferenceIdentity(Class<?> refType) { |
|
279 MethodType lambdaType = MethodType.genericMethodType(1).invokerType(); |
|
280 Name[] names = arguments(1, lambdaType); |
|
281 names[names.length - 1] = new Name(ValueConversions.identity(), names[1]); |
|
282 LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names); |
|
283 return new SimpleMethodHandle(MethodType.methodType(refType, refType), form); |
|
284 } |
|
285 |
|
286 static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) { |
|
287 MethodType type = target.type(); |
|
288 int last = type.parameterCount() - 1; |
|
289 if (type.parameterType(last) != arrayType) |
|
290 target = target.asType(type.changeParameterType(last, arrayType)); |
|
291 target = target.asFixedArity(); // make sure this attribute is turned off |
|
292 return new AsVarargsCollector(target, target.type(), arrayType); |
|
293 } |
|
294 |
|
295 static class AsVarargsCollector extends MethodHandle { |
|
296 MethodHandle target; |
|
297 final Class<?> arrayType; |
|
298 MethodHandle cache; |
|
299 |
|
300 AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) { |
|
301 super(type, reinvokerForm(type)); |
|
302 this.target = target; |
|
303 this.arrayType = arrayType; |
|
304 this.cache = target.asCollector(arrayType, 0); |
|
305 } |
|
306 |
|
307 @Override MethodHandle reinvokerTarget() { return target; } |
|
308 |
|
309 @Override |
|
310 public boolean isVarargsCollector() { |
|
311 return true; |
|
312 } |
|
313 |
|
314 @Override |
|
315 public MethodHandle asFixedArity() { |
|
316 return target; |
|
317 } |
|
318 |
|
319 @Override |
|
320 public MethodHandle asType(MethodType newType) { |
|
321 MethodType type = this.type(); |
|
322 int collectArg = type.parameterCount() - 1; |
|
323 int newArity = newType.parameterCount(); |
|
324 if (newArity == collectArg+1 && |
|
325 type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) { |
|
326 // if arity and trailing parameter are compatible, do normal thing |
|
327 return asFixedArity().asType(newType); |
|
328 } |
|
329 // check cache |
|
330 if (cache.type().parameterCount() == newArity) |
|
331 return cache.asType(newType); |
|
332 // build and cache a collector |
|
333 int arrayLength = newArity - collectArg; |
|
334 MethodHandle collector; |
|
335 try { |
|
336 collector = asFixedArity().asCollector(arrayType, arrayLength); |
|
337 } catch (IllegalArgumentException ex) { |
|
338 throw new WrongMethodTypeException("cannot build collector"); |
|
339 } |
|
340 cache = collector; |
|
341 return collector.asType(newType); |
|
342 } |
|
343 |
|
344 @Override |
|
345 MethodHandle setVarargs(MemberName member) { |
|
346 if (member.isVarargs()) return this; |
|
347 return asFixedArity(); |
|
348 } |
|
349 |
|
350 @Override |
|
351 MethodHandle viewAsType(MethodType newType) { |
|
352 MethodHandle mh = super.viewAsType(newType); |
|
353 // put back the varargs bit: |
|
354 MethodType type = mh.type(); |
|
355 int arity = type.parameterCount(); |
|
356 return mh.asVarargsCollector(type.parameterType(arity-1)); |
|
357 } |
|
358 |
|
359 @Override |
|
360 MemberName internalMemberName() { |
|
361 return asFixedArity().internalMemberName(); |
|
362 } |
|
363 |
|
364 |
|
365 @Override |
|
366 MethodHandle bindArgument(int pos, char basicType, Object value) { |
|
367 return asFixedArity().bindArgument(pos, basicType, value); |
|
368 } |
|
369 |
|
370 @Override |
|
371 MethodHandle bindReceiver(Object receiver) { |
|
372 return asFixedArity().bindReceiver(receiver); |
|
373 } |
|
374 |
|
375 @Override |
|
376 MethodHandle dropArguments(MethodType srcType, int pos, int drops) { |
|
377 return asFixedArity().dropArguments(srcType, pos, drops); |
|
378 } |
|
379 |
|
380 @Override |
|
381 MethodHandle permuteArguments(MethodType newType, int[] reorder) { |
|
382 return asFixedArity().permuteArguments(newType, reorder); |
|
383 } |
|
384 } |
|
385 |
|
386 /** Factory method: Spread selected argument. */ |
|
387 static MethodHandle makeSpreadArguments(MethodHandle target, |
|
388 Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) { |
|
389 MethodType targetType = target.type(); |
|
390 |
|
391 for (int i = 0; i < spreadArgCount; i++) { |
|
392 Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i); |
|
393 if (arg == null) arg = Object.class; |
|
394 targetType = targetType.changeParameterType(spreadArgPos + i, arg); |
|
395 } |
|
396 target = target.asType(targetType); |
|
397 |
|
398 MethodType srcType = targetType |
|
399 .replaceParameterTypes(spreadArgPos, spreadArgPos + spreadArgCount, spreadArgType); |
|
400 // Now build a LambdaForm. |
|
401 MethodType lambdaType = srcType.invokerType(); |
|
402 Name[] names = arguments(spreadArgCount + 2, lambdaType); |
|
403 int nameCursor = lambdaType.parameterCount(); |
|
404 int[] indexes = new int[targetType.parameterCount()]; |
|
405 |
|
406 for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) { |
|
407 Class<?> src = lambdaType.parameterType(i); |
|
408 if (i == spreadArgPos) { |
|
409 // Spread the array. |
|
410 MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType); |
|
411 Name array = names[argIndex]; |
|
412 names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount); |
|
413 for (int j = 0; j < spreadArgCount; i++, j++) { |
|
414 indexes[i] = nameCursor; |
|
415 names[nameCursor++] = new Name(aload, array, j); |
501 } |
416 } |
502 List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1); |
417 } else if (i < indexes.length) { |
503 Collections.rotate(rotSpan, -rotBy); // reverse direction |
418 indexes[i] = argIndex; |
504 for (int i = rotBeg; i <= rotEnd; i++) { |
419 } |
505 if ((int)state.get(i) != (int)goal.get(i)) |
420 } |
506 score -= 1; |
421 assert(nameCursor == names.length-1); // leave room for the final call |
507 } |
422 |
508 if (score >= 2) { |
423 // Build argument array for the call. |
509 // Improved at least two argument positions. Do it. |
424 Name[] targetArgs = new Name[targetType.parameterCount()]; |
510 List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray()); |
425 for (int i = 0; i < targetType.parameterCount(); i++) { |
511 Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy); |
426 int idx = indexes[i]; |
512 MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes); |
427 targetArgs[i] = names[idx]; |
513 MethodHandle nextTarget |
428 } |
514 = AdapterMethodHandle.makeRotateArguments(rotType, target, |
429 names[names.length - 1] = new Name(target, (Object[]) targetArgs); |
515 rotBeg, rotSpan.size(), rotBy); |
430 |
516 if (nextTarget != null) { |
431 LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names); |
517 //System.out.println("Rot: "+rotSpan+" by "+rotBy); |
432 return new SimpleMethodHandle(srcType, form); |
518 target = nextTarget; |
433 } |
519 oldType = rotType; |
434 |
520 continue; |
435 static void checkSpreadArgument(Object av, int n) { |
521 } |
436 if (av == null) { |
522 } |
437 if (n == 0) return; |
523 // Else de-rotate, and drop through to the swap-fest. |
438 } else if (av instanceof Object[]) { |
524 Collections.rotate(rotSpan, rotBy); |
439 int len = ((Object[])av).length; |
525 } |
440 if (len == n) return; |
526 |
441 } else { |
527 // Now swap like the wind! |
442 int len = java.lang.reflect.Array.getLength(av); |
528 List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray()); |
443 if (len == n) return; |
529 for (int i = 0; i < size; i++) { |
444 } |
530 // What argument do I want here? |
445 // fall through to error: |
531 int arg = goal.get(i); |
446 throw newIllegalArgumentException("Array is not of length "+n); |
532 if (arg != state.get(i)) { |
447 } |
533 // Where is it now? |
448 |
534 int j = state.indexOf(arg); |
449 private static final NamedFunction NF_checkSpreadArgument; |
535 Collections.swap(ptypes, i, j); |
450 static { |
536 MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes); |
|
537 target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); |
|
538 if (target == null) throw newIllegalArgumentException("cannot swap"); |
|
539 assert(target.type() == swapType); |
|
540 oldType = swapType; |
|
541 Collections.swap(state, i, j); |
|
542 } |
|
543 } |
|
544 // One pass of swapping must finish the job. |
|
545 assert(state.equals(goal)); |
|
546 } |
|
547 while (!dups.isEmpty()) { |
|
548 // Grab a contiguous trailing sequence of dups. |
|
549 int grab = dups.size() - 1; |
|
550 int dupArgPos = dups.get(grab), dupArgCount = 1; |
|
551 while (grab - 1 >= 0) { |
|
552 int dup0 = dups.get(grab - 1); |
|
553 if (dup0 != dupArgPos - 1) break; |
|
554 dupArgPos -= 1; |
|
555 dupArgCount += 1; |
|
556 grab -= 1; |
|
557 } |
|
558 //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size())); |
|
559 dups.subList(grab, dups.size()).clear(); |
|
560 // In the new target type drop that many args from the tail: |
|
561 List<Class<?>> ptypes = oldType.parameterList(); |
|
562 ptypes = ptypes.subList(0, ptypes.size() - dupArgCount); |
|
563 MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes); |
|
564 target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); |
|
565 if (target == null) |
|
566 throw newIllegalArgumentException("cannot dup"); |
|
567 oldType = target.type(); |
|
568 } |
|
569 while (!drops.isEmpty()) { |
|
570 // Grab a contiguous initial sequence of drops. |
|
571 int dropArgPos = drops.get(0), dropArgCount = 1; |
|
572 while (dropArgCount < drops.size()) { |
|
573 int drop1 = drops.get(dropArgCount); |
|
574 if (drop1 != dropArgPos + dropArgCount) break; |
|
575 dropArgCount += 1; |
|
576 } |
|
577 //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount)); |
|
578 drops.subList(0, dropArgCount).clear(); |
|
579 List<Class<?>> dropTypes = newType.parameterList() |
|
580 .subList(dropArgPos, dropArgPos + dropArgCount); |
|
581 MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes); |
|
582 target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); |
|
583 if (target == null) throw newIllegalArgumentException("cannot drop"); |
|
584 oldType = target.type(); |
|
585 } |
|
586 target = convertArguments(target, newType, oldType, 0); |
|
587 assert(target != null); |
|
588 return target; |
|
589 } |
|
590 |
|
591 /*non-public*/ static |
|
592 MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) { |
|
593 MethodType oldType = target.type(); |
|
594 if (oldType.equals(newType)) |
|
595 return target; |
|
596 assert(level > 1 || oldType.isConvertibleTo(newType)); |
|
597 MethodHandle retFilter = null; |
|
598 Class<?> oldRT = oldType.returnType(); |
|
599 Class<?> newRT = newType.returnType(); |
|
600 if (!VerifyType.isNullConversion(oldRT, newRT)) { |
|
601 if (oldRT == void.class) { |
|
602 Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT; |
|
603 retFilter = ValueConversions.zeroConstantFunction(wrap); |
|
604 } else { |
|
605 retFilter = MethodHandles.identity(newRT); |
|
606 retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level); |
|
607 } |
|
608 newType = newType.changeReturnType(oldRT); |
|
609 } |
|
610 MethodHandle res = null; |
|
611 Exception ex = null; |
|
612 try { |
451 try { |
613 res = convertArguments(target, newType, oldType, level); |
452 NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class |
614 } catch (IllegalArgumentException ex1) { |
453 .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); |
615 ex = ex1; |
454 NF_checkSpreadArgument.resolve(); |
616 } |
455 } catch (ReflectiveOperationException ex) { |
617 if (res == null) { |
456 throw new InternalError(ex); |
618 WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target); |
457 } |
619 wmt.initCause(ex); |
458 } |
620 throw wmt; |
459 |
621 } |
460 /** Factory method: Collect or filter selected argument(s). */ |
622 if (retFilter != null) |
461 static MethodHandle makeCollectArguments(MethodHandle target, |
623 res = MethodHandles.filterReturnValue(res, retFilter); |
462 MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) { |
624 return res; |
463 MethodType targetType = target.type(); // (a..., c, [b...])=>r |
625 } |
464 MethodType collectorType = collector.type(); // (b...)=>c |
626 |
465 int collectArgCount = collectorType.parameterCount(); |
627 static MethodHandle convertArguments(MethodHandle target, |
466 Class<?> collectValType = collectorType.returnType(); |
628 MethodType newType, |
467 int collectValCount = (collectValType == void.class ? 0 : 1); |
629 MethodType oldType, |
468 MethodType srcType = targetType // (a..., [b...])=>r |
630 int level) { |
469 .dropParameterTypes(collectArgPos, collectArgPos+collectValCount); |
631 assert(oldType.parameterCount() == target.type().parameterCount()); |
470 if (!retainOriginalArgs) { // (a..., b...)=>r |
632 if (newType == oldType) |
471 srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList()); |
633 return target; |
472 } |
634 if (oldType.parameterCount() != newType.parameterCount()) |
473 // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ] |
635 throw newIllegalArgumentException("mismatched parameter count", oldType, newType); |
474 // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ] |
636 return AdapterMethodHandle.makePairwiseConvert(newType, target, level); |
475 // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ] |
637 } |
476 |
638 |
477 // Now build a LambdaForm. |
639 static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) { |
478 MethodType lambdaType = srcType.invokerType(); |
640 MethodType oldType = target.type(); |
479 Name[] names = arguments(2, lambdaType); |
641 int nargs = oldType.parameterCount(); |
480 final int collectNamePos = names.length - 2; |
642 int keepPosArgs = nargs - arrayLength; |
481 final int targetNamePos = names.length - 1; |
643 MethodType newType = oldType |
482 |
644 .dropParameterTypes(keepPosArgs, nargs) |
483 Name[] collectorArgs = Arrays.copyOfRange(names, 1 + collectArgPos, 1 + collectArgPos + collectArgCount); |
645 .insertParameterTypes(keepPosArgs, arrayType); |
484 names[collectNamePos] = new Name(collector, (Object[]) collectorArgs); |
646 return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); |
485 |
647 } |
486 // Build argument array for the target. |
648 // called internally only |
487 // Incoming LF args to copy are: [ (mh) headArgs collectArgs tailArgs ]. |
649 static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) { |
488 // Output argument array is [ headArgs (collectVal)? (collectArgs)? tailArgs ]. |
650 int arrayLength = target.type().parameterCount() - spreadArgPos; |
489 Name[] targetArgs = new Name[targetType.parameterCount()]; |
651 return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); |
490 int inputArgPos = 1; // incoming LF args to copy to target |
652 } |
491 int targetArgPos = 0; // fill pointer for targetArgs |
653 static MethodHandle spreadArguments(MethodHandle target, |
492 int chunk = collectArgPos; // |headArgs| |
654 MethodType newType, |
493 System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk); |
655 int spreadArgPos, |
494 inputArgPos += chunk; |
656 Class<?> arrayType, |
495 targetArgPos += chunk; |
657 int arrayLength) { |
496 if (collectValType != void.class) { |
658 // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[] |
497 targetArgs[targetArgPos++] = names[collectNamePos]; |
659 MethodType oldType = target.type(); |
498 } |
660 // spread the last argument of newType to oldType |
499 chunk = collectArgCount; |
661 assert(arrayLength == oldType.parameterCount() - spreadArgPos); |
500 if (retainOriginalArgs) { |
662 assert(newType.parameterType(spreadArgPos) == arrayType); |
501 System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk); |
663 return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); |
502 targetArgPos += chunk; // optionally pass on the collected chunk |
664 } |
503 } |
665 |
504 inputArgPos += chunk; |
666 static MethodHandle collectArguments(MethodHandle target, |
505 chunk = targetArgs.length - targetArgPos; // all the rest |
667 int collectArg, |
506 System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk); |
668 MethodHandle collector) { |
507 assert(inputArgPos + chunk == collectNamePos); // use of rest of input args also |
669 MethodType type = target.type(); |
508 names[targetNamePos] = new Name(target, (Object[]) targetArgs); |
670 Class<?> collectType = collector.type().returnType(); |
509 |
671 assert(collectType != void.class); // else use foldArguments |
510 LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names); |
672 if (collectType != type.parameterType(collectArg)) |
511 return new SimpleMethodHandle(srcType, form); |
673 target = target.asType(type.changeParameterType(collectArg, collectType)); |
|
674 MethodType newType = type |
|
675 .dropParameterTypes(collectArg, collectArg+1) |
|
676 .insertParameterTypes(collectArg, collector.type().parameterArray()); |
|
677 return collectArguments(target, newType, collectArg, collector); |
|
678 } |
|
679 static MethodHandle collectArguments(MethodHandle target, |
|
680 MethodType newType, |
|
681 int collectArg, |
|
682 MethodHandle collector) { |
|
683 MethodType oldType = target.type(); // (a...,c)=>r |
|
684 // newType // (a..., b...)=>r |
|
685 MethodType colType = collector.type(); // (b...)=>c |
|
686 // oldType // (a..., b...)=>r |
|
687 assert(newType.parameterCount() == collectArg + colType.parameterCount()); |
|
688 assert(oldType.parameterCount() == collectArg + 1); |
|
689 assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)); |
|
690 return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false); |
|
691 } |
|
692 |
|
693 static MethodHandle filterArgument(MethodHandle target, |
|
694 int pos, |
|
695 MethodHandle filter) { |
|
696 MethodType ttype = target.type(); |
|
697 MethodType ftype = filter.type(); |
|
698 assert(ftype.parameterCount() == 1); |
|
699 return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false); |
|
700 } |
|
701 |
|
702 static MethodHandle foldArguments(MethodHandle target, |
|
703 MethodType newType, |
|
704 int foldPos, |
|
705 MethodHandle combiner) { |
|
706 MethodType oldType = target.type(); |
|
707 MethodType ctype = combiner.type(); |
|
708 assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)); |
|
709 return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true); |
|
710 } |
|
711 |
|
712 static |
|
713 MethodHandle dropArguments(MethodHandle target, |
|
714 MethodType newType, int argnum) { |
|
715 int drops = newType.parameterCount() - target.type().parameterCount(); |
|
716 return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops); |
|
717 } |
512 } |
718 |
513 |
719 static |
514 static |
720 MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { |
515 MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { |
721 return testResult ? target : fallback; |
516 return testResult ? target : fallback; |
736 |
531 |
737 static |
532 static |
738 MethodHandle makeGuardWithTest(MethodHandle test, |
533 MethodHandle makeGuardWithTest(MethodHandle test, |
739 MethodHandle target, |
534 MethodHandle target, |
740 MethodHandle fallback) { |
535 MethodHandle fallback) { |
741 // gwt(arg...) |
536 MethodType basicType = target.type().basicType(); |
742 // [fold]=> continueAfterTest(z=test(arg...), arg...) |
537 MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType); |
743 // [filter]=> (tf=select(z))(arg...) |
538 int arity = basicType.parameterCount(); |
744 // where select(z) = select(z, t, f).bindTo(t, f) => z ? t f |
539 int extraNames = 3; |
745 // [tailcall]=> tf(arg...) |
540 MethodType lambdaType = basicType.invokerType(); |
746 assert(test.type().returnType() == boolean.class); |
541 Name[] names = arguments(extraNames, lambdaType); |
747 MethodType targetType = target.type(); |
542 |
748 MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class); |
543 Object[] testArgs = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class); |
749 assert(AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true)); |
544 Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class); |
750 // working backwards, as usual: |
545 |
751 assert(target.type().equals(fallback.type())); |
546 // call test |
752 MethodHandle tailcall = MethodHandles.exactInvoker(target.type()); |
547 names[arity + 1] = new Name(test, testArgs); |
753 MethodHandle select = selectAlternative(); |
548 |
754 select = bindArgument(select, 2, CountingMethodHandle.wrap(fallback)); |
549 // call selectAlternative |
755 select = bindArgument(select, 1, CountingMethodHandle.wrap(target)); |
550 Object[] selectArgs = { names[arity + 1], target, fallback }; |
756 // select(z: boolean) => (z ? target : fallback) |
551 names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs); |
757 MethodHandle filter = filterArgument(tailcall, 0, select); |
552 targetArgs[0] = names[arity + 2]; |
758 assert(filter.type().parameterType(0) == boolean.class); |
553 |
759 MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test); |
554 // call target or fallback |
760 return fold; |
555 names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs); |
761 } |
556 |
762 |
557 LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names); |
763 private static class GuardWithCatch extends BoundMethodHandle { |
558 return new SimpleMethodHandle(target.type(), form); |
|
559 } |
|
560 |
|
561 private static class GuardWithCatch { |
764 private final MethodHandle target; |
562 private final MethodHandle target; |
765 private final Class<? extends Throwable> exType; |
563 private final Class<? extends Throwable> exType; |
766 private final MethodHandle catcher; |
564 private final MethodHandle catcher; |
|
565 // FIXME: Build the control flow out of foldArguments. |
767 GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { |
566 GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { |
768 this(INVOKES[target.type().parameterCount()], target, exType, catcher); |
|
769 } |
|
770 // FIXME: Build the control flow out of foldArguments. |
|
771 GuardWithCatch(MethodHandle invoker, |
|
772 MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { |
|
773 super(invoker); |
|
774 this.target = target; |
567 this.target = target; |
775 this.exType = exType; |
568 this.exType = exType; |
776 this.catcher = catcher; |
569 this.catcher = catcher; |
777 } |
570 } |
778 @Override |
571 @LambdaForm.Hidden |
779 String debugString() { |
|
780 return addTypeString(target, this); |
|
781 } |
|
782 private Object invoke_V(Object... av) throws Throwable { |
572 private Object invoke_V(Object... av) throws Throwable { |
783 try { |
573 try { |
784 return target.invokeExact(av); |
574 return target.invokeExact(av); |
785 } catch (Throwable t) { |
575 } catch (Throwable t) { |
786 if (!exType.isInstance(t)) throw t; |
576 if (!exType.isInstance(t)) throw t; |
787 return catcher.invokeExact(t, av); |
577 return catcher.invokeExact(t, av); |
788 } |
578 } |
789 } |
579 } |
|
580 @LambdaForm.Hidden |
790 private Object invoke_L0() throws Throwable { |
581 private Object invoke_L0() throws Throwable { |
791 try { |
582 try { |
792 return target.invokeExact(); |
583 return target.invokeExact(); |
793 } catch (Throwable t) { |
584 } catch (Throwable t) { |
794 if (!exType.isInstance(t)) throw t; |
585 if (!exType.isInstance(t)) throw t; |
795 return catcher.invokeExact(t); |
586 return catcher.invokeExact(t); |
796 } |
587 } |
797 } |
588 } |
|
589 @LambdaForm.Hidden |
798 private Object invoke_L1(Object a0) throws Throwable { |
590 private Object invoke_L1(Object a0) throws Throwable { |
799 try { |
591 try { |
800 return target.invokeExact(a0); |
592 return target.invokeExact(a0); |
801 } catch (Throwable t) { |
593 } catch (Throwable t) { |
802 if (!exType.isInstance(t)) throw t; |
594 if (!exType.isInstance(t)) throw t; |
803 return catcher.invokeExact(t, a0); |
595 return catcher.invokeExact(t, a0); |
804 } |
596 } |
805 } |
597 } |
|
598 @LambdaForm.Hidden |
806 private Object invoke_L2(Object a0, Object a1) throws Throwable { |
599 private Object invoke_L2(Object a0, Object a1) throws Throwable { |
807 try { |
600 try { |
808 return target.invokeExact(a0, a1); |
601 return target.invokeExact(a0, a1); |
809 } catch (Throwable t) { |
602 } catch (Throwable t) { |
810 if (!exType.isInstance(t)) throw t; |
603 if (!exType.isInstance(t)) throw t; |
811 return catcher.invokeExact(t, a0, a1); |
604 return catcher.invokeExact(t, a0, a1); |
812 } |
605 } |
813 } |
606 } |
|
607 @LambdaForm.Hidden |
814 private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { |
608 private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { |
815 try { |
609 try { |
816 return target.invokeExact(a0, a1, a2); |
610 return target.invokeExact(a0, a1, a2); |
817 } catch (Throwable t) { |
611 } catch (Throwable t) { |
818 if (!exType.isInstance(t)) throw t; |
612 if (!exType.isInstance(t)) throw t; |
819 return catcher.invokeExact(t, a0, a1, a2); |
613 return catcher.invokeExact(t, a0, a1, a2); |
820 } |
614 } |
821 } |
615 } |
|
616 @LambdaForm.Hidden |
822 private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { |
617 private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { |
823 try { |
618 try { |
824 return target.invokeExact(a0, a1, a2, a3); |
619 return target.invokeExact(a0, a1, a2, a3); |
825 } catch (Throwable t) { |
620 } catch (Throwable t) { |
826 if (!exType.isInstance(t)) throw t; |
621 if (!exType.isInstance(t)) throw t; |
827 return catcher.invokeExact(t, a0, a1, a2, a3); |
622 return catcher.invokeExact(t, a0, a1, a2, a3); |
828 } |
623 } |
829 } |
624 } |
|
625 @LambdaForm.Hidden |
830 private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { |
626 private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { |
831 try { |
627 try { |
832 return target.invokeExact(a0, a1, a2, a3, a4); |
628 return target.invokeExact(a0, a1, a2, a3, a4); |
833 } catch (Throwable t) { |
629 } catch (Throwable t) { |
834 if (!exType.isInstance(t)) throw t; |
630 if (!exType.isInstance(t)) throw t; |
835 return catcher.invokeExact(t, a0, a1, a2, a3, a4); |
631 return catcher.invokeExact(t, a0, a1, a2, a3, a4); |
836 } |
632 } |
837 } |
633 } |
|
634 @LambdaForm.Hidden |
838 private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { |
635 private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { |
839 try { |
636 try { |
840 return target.invokeExact(a0, a1, a2, a3, a4, a5); |
637 return target.invokeExact(a0, a1, a2, a3, a4, a5); |
841 } catch (Throwable t) { |
638 } catch (Throwable t) { |
842 if (!exType.isInstance(t)) throw t; |
639 if (!exType.isInstance(t)) throw t; |
843 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); |
640 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); |
844 } |
641 } |
845 } |
642 } |
|
643 @LambdaForm.Hidden |
846 private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { |
644 private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { |
847 try { |
645 try { |
848 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); |
646 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); |
849 } catch (Throwable t) { |
647 } catch (Throwable t) { |
850 if (!exType.isInstance(t)) throw t; |
648 if (!exType.isInstance(t)) throw t; |
851 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); |
649 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); |
852 } |
650 } |
853 } |
651 } |
|
652 @LambdaForm.Hidden |
854 private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { |
653 private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { |
855 try { |
654 try { |
856 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); |
655 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); |
857 } catch (Throwable t) { |
656 } catch (Throwable t) { |
858 if (!exType.isInstance(t)) throw t; |
657 if (!exType.isInstance(t)) throw t; |
859 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); |
658 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); |
860 } |
659 } |
861 } |
660 } |
862 static MethodHandle[] makeInvokes() { |
661 static MethodHandle[] makeInvokes() { |
863 ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>(); |
662 ArrayList<MethodHandle> invokes = new ArrayList<>(); |
864 MethodHandles.Lookup lookup = IMPL_LOOKUP; |
663 MethodHandles.Lookup lookup = IMPL_LOOKUP; |
865 for (;;) { |
664 for (;;) { |
866 int nargs = invokes.size(); |
665 int nargs = invokes.size(); |
867 String name = "invoke_L"+nargs; |
666 String name = "invoke_L"+nargs; |
868 MethodHandle invoke = null; |
667 MethodHandle invoke = null; |