177 * For each argument, convert incoming argument to the exact type needed. |
177 * For each argument, convert incoming argument to the exact type needed. |
178 * The argument conversions allowed are casting, boxing and unboxing, |
178 * The argument conversions allowed are casting, boxing and unboxing, |
179 * integral widening or narrowing, and floating point widening or narrowing. |
179 * integral widening or narrowing, and floating point widening or narrowing. |
180 * @param srcType required call type |
180 * @param srcType required call type |
181 * @param target original method handle |
181 * @param target original method handle |
182 * @param level which strength of conversion is allowed |
182 * @param strict if true, only asType conversions are allowed; if false, explicitCastArguments conversions allowed |
|
183 * @param monobox if true, unboxing conversions are assumed to be exactly typed (Integer to int only, not long or double) |
183 * @return an adapter to the original handle with the desired new type, |
184 * @return an adapter to the original handle with the desired new type, |
184 * or the original target if the types are already identical |
185 * or the original target if the types are already identical |
185 * or null if the adaptation cannot be made |
186 * or null if the adaptation cannot be made |
186 */ |
187 */ |
187 static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) { |
188 static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, |
188 assert(level >= 0 && level <= 2); |
189 boolean strict, boolean monobox) { |
189 MethodType dstType = target.type(); |
190 MethodType dstType = target.type(); |
190 assert(dstType.parameterCount() == target.type().parameterCount()); |
191 assert(dstType.parameterCount() == target.type().parameterCount()); |
191 if (srcType == dstType) |
192 if (srcType == dstType) |
192 return target; |
193 return target; |
193 |
194 return makePairwiseConvertIndirect(target, srcType, strict, monobox); |
|
195 } |
|
196 |
|
197 private static int countNonNull(Object[] array) { |
|
198 int count = 0; |
|
199 for (Object x : array) { |
|
200 if (x != null) ++count; |
|
201 } |
|
202 return count; |
|
203 } |
|
204 |
|
205 static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType, |
|
206 boolean strict, boolean monobox) { |
194 // Calculate extra arguments (temporaries) required in the names array. |
207 // Calculate extra arguments (temporaries) required in the names array. |
195 // FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step. |
208 Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox); |
196 final int INARG_COUNT = srcType.parameterCount(); |
209 final int INARG_COUNT = srcType.parameterCount(); |
197 int conversions = 0; |
210 int convCount = countNonNull(convSpecs); |
198 boolean[] needConv = new boolean[1+INARG_COUNT]; |
211 boolean retConv = (convSpecs[INARG_COUNT] != null); |
199 for (int i = 0; i <= INARG_COUNT; i++) { |
212 boolean retVoid = srcType.returnType() == void.class; |
200 Class<?> src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i); |
213 if (retConv && retVoid) { |
201 Class<?> dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i); |
214 convCount -= 1; |
202 if (!VerifyType.isNullConversion(src, dst, false) || |
|
203 level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) { |
|
204 needConv[i] = true; |
|
205 conversions++; |
|
206 } |
|
207 } |
|
208 boolean retConv = needConv[INARG_COUNT]; |
|
209 if (retConv && srcType.returnType() == void.class) { |
|
210 retConv = false; |
215 retConv = false; |
211 conversions--; |
|
212 } |
216 } |
213 |
217 |
214 final int IN_MH = 0; |
218 final int IN_MH = 0; |
215 final int INARG_BASE = 1; |
219 final int INARG_BASE = 1; |
216 final int INARG_LIMIT = INARG_BASE + INARG_COUNT; |
220 final int INARG_LIMIT = INARG_BASE + INARG_COUNT; |
217 final int NAME_LIMIT = INARG_LIMIT + conversions + 1; |
221 final int NAME_LIMIT = INARG_LIMIT + convCount + 1; |
218 final int RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1); |
222 final int RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1); |
219 final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1; |
223 final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1; |
220 final int RESULT = (srcType.returnType() == void.class ? -1 : NAME_LIMIT - 1); |
224 final int RESULT = (retVoid ? -1 : NAME_LIMIT - 1); |
221 |
225 |
222 // Now build a LambdaForm. |
226 // Now build a LambdaForm. |
223 MethodType lambdaType = srcType.basicType().invokerType(); |
227 MethodType lambdaType = srcType.basicType().invokerType(); |
224 Name[] names = arguments(NAME_LIMIT - INARG_LIMIT, lambdaType); |
228 Name[] names = arguments(NAME_LIMIT - INARG_LIMIT, lambdaType); |
225 |
229 |
227 final int OUTARG_BASE = 0; // target MH is Name.function, name Name.arguments[0] |
231 final int OUTARG_BASE = 0; // target MH is Name.function, name Name.arguments[0] |
228 Object[] outArgs = new Object[OUTARG_BASE + INARG_COUNT]; |
232 Object[] outArgs = new Object[OUTARG_BASE + INARG_COUNT]; |
229 |
233 |
230 int nameCursor = INARG_LIMIT; |
234 int nameCursor = INARG_LIMIT; |
231 for (int i = 0; i < INARG_COUNT; i++) { |
235 for (int i = 0; i < INARG_COUNT; i++) { |
232 Class<?> src = srcType.parameterType(i); |
236 Object convSpec = convSpecs[i]; |
233 Class<?> dst = dstType.parameterType(i); |
237 if (convSpec == null) { |
234 |
|
235 if (!needConv[i]) { |
|
236 // do nothing: difference is trivial |
238 // do nothing: difference is trivial |
237 outArgs[OUTARG_BASE + i] = names[INARG_BASE + i]; |
239 outArgs[OUTARG_BASE + i] = names[INARG_BASE + i]; |
238 continue; |
240 continue; |
239 } |
241 } |
240 |
242 |
241 // Tricky case analysis follows. |
243 Name conv; |
242 MethodHandle fn = null; |
244 if (convSpec instanceof Class) { |
243 if (src.isPrimitive()) { |
245 Class<?> convClass = (Class<?>) convSpec; |
244 if (dst.isPrimitive()) { |
246 conv = new Name(Lazy.MH_castReference, convClass, names[INARG_BASE + i]); |
245 fn = ValueConversions.convertPrimitive(src, dst); |
|
246 } else { |
|
247 Wrapper w = Wrapper.forPrimitiveType(src); |
|
248 MethodHandle boxMethod = ValueConversions.box(w); |
|
249 if (dst == w.wrapperType()) |
|
250 fn = boxMethod; |
|
251 else |
|
252 fn = boxMethod.asType(MethodType.methodType(dst, src)); |
|
253 } |
|
254 } else { |
247 } else { |
255 if (dst.isPrimitive()) { |
248 MethodHandle fn = (MethodHandle) convSpec; |
256 // Caller has boxed a primitive. Unbox it for the target. |
249 conv = new Name(fn, names[INARG_BASE + i]); |
257 Wrapper w = Wrapper.forPrimitiveType(dst); |
250 } |
258 if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType(), false)) { |
|
259 fn = ValueConversions.unbox(dst); |
|
260 } else if (src == Object.class || !Wrapper.isWrapperType(src)) { |
|
261 // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int |
|
262 // must include additional conversions |
|
263 // src must be examined at runtime, to detect Byte, Character, etc. |
|
264 MethodHandle unboxMethod = (level == 1 |
|
265 ? ValueConversions.unbox(dst) |
|
266 : ValueConversions.unboxCast(dst)); |
|
267 fn = unboxMethod; |
|
268 } else { |
|
269 // Example: Byte->int |
|
270 // Do this by reformulating the problem to Byte->byte. |
|
271 Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType(); |
|
272 MethodHandle unbox = ValueConversions.unbox(srcPrim); |
|
273 // Compose the two conversions. FIXME: should make two Names for this job |
|
274 fn = unbox.asType(MethodType.methodType(dst, src)); |
|
275 } |
|
276 } else { |
|
277 // Simple reference conversion. |
|
278 // Note: Do not check for a class hierarchy relation |
|
279 // between src and dst. In all cases a 'null' argument |
|
280 // will pass the cast conversion. |
|
281 fn = ValueConversions.cast(dst, Lazy.MH_castReference); |
|
282 } |
|
283 } |
|
284 Name conv = new Name(fn, names[INARG_BASE + i]); |
|
285 assert(names[nameCursor] == null); |
251 assert(names[nameCursor] == null); |
286 names[nameCursor++] = conv; |
252 names[nameCursor++] = conv; |
287 assert(outArgs[OUTARG_BASE + i] == null); |
253 assert(outArgs[OUTARG_BASE + i] == null); |
288 outArgs[OUTARG_BASE + i] = conv; |
254 outArgs[OUTARG_BASE + i] = conv; |
289 } |
255 } |
290 |
256 |
291 // Build argument array for the call. |
257 // Build argument array for the call. |
292 assert(nameCursor == OUT_CALL); |
258 assert(nameCursor == OUT_CALL); |
293 names[OUT_CALL] = new Name(target, outArgs); |
259 names[OUT_CALL] = new Name(target, outArgs); |
294 |
260 |
295 if (RETURN_CONV < 0) { |
261 Object convSpec = convSpecs[INARG_COUNT]; |
|
262 if (!retConv) { |
296 assert(OUT_CALL == names.length-1); |
263 assert(OUT_CALL == names.length-1); |
297 } else { |
264 } else { |
298 Class<?> needReturn = srcType.returnType(); |
265 Name conv; |
299 Class<?> haveReturn = dstType.returnType(); |
266 if (convSpec == void.class) { |
300 MethodHandle fn; |
267 conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType()))); |
301 Object[] arg = { names[OUT_CALL] }; |
268 } else if (convSpec instanceof Class) { |
302 if (haveReturn == void.class) { |
269 Class<?> convClass = (Class<?>) convSpec; |
303 // synthesize a zero value for the given void |
270 conv = new Name(Lazy.MH_castReference, convClass, names[OUT_CALL]); |
304 Object zero = Wrapper.forBasicType(needReturn).zero(); |
|
305 fn = MethodHandles.constant(needReturn, zero); |
|
306 arg = new Object[0]; // don't pass names[OUT_CALL] to conversion |
|
307 } else { |
271 } else { |
308 MethodHandle identity = MethodHandles.identity(needReturn); |
272 MethodHandle fn = (MethodHandle) convSpec; |
309 MethodType needConversion = identity.type().changeParameterType(0, haveReturn); |
273 if (fn.type().parameterCount() == 0) |
310 fn = makePairwiseConvert(identity, needConversion, level); |
274 conv = new Name(fn); // don't pass retval to void conversion |
|
275 else |
|
276 conv = new Name(fn, names[OUT_CALL]); |
311 } |
277 } |
312 assert(names[RETURN_CONV] == null); |
278 assert(names[RETURN_CONV] == null); |
313 names[RETURN_CONV] = new Name(fn, arg); |
279 names[RETURN_CONV] = conv; |
314 assert(RETURN_CONV == names.length-1); |
280 assert(RETURN_CONV == names.length-1); |
315 } |
281 } |
316 |
282 |
317 LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names, RESULT); |
283 LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names, RESULT); |
318 return SimpleMethodHandle.make(srcType, form); |
284 return SimpleMethodHandle.make(srcType, form); |
341 MethodType lambdaType = MethodType.genericMethodType(1).invokerType(); |
307 MethodType lambdaType = MethodType.genericMethodType(1).invokerType(); |
342 Name[] names = arguments(1, lambdaType); |
308 Name[] names = arguments(1, lambdaType); |
343 names[names.length - 1] = new Name(ValueConversions.identity(), names[1]); |
309 names[names.length - 1] = new Name(ValueConversions.identity(), names[1]); |
344 LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names); |
310 LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names); |
345 return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form); |
311 return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form); |
|
312 } |
|
313 |
|
314 static Object[] computeValueConversions(MethodType srcType, MethodType dstType, |
|
315 boolean strict, boolean monobox) { |
|
316 final int INARG_COUNT = srcType.parameterCount(); |
|
317 Object[] convSpecs = new Object[INARG_COUNT+1]; |
|
318 for (int i = 0; i <= INARG_COUNT; i++) { |
|
319 boolean isRet = (i == INARG_COUNT); |
|
320 Class<?> src = isRet ? dstType.returnType() : srcType.parameterType(i); |
|
321 Class<?> dst = isRet ? srcType.returnType() : dstType.parameterType(i); |
|
322 if (!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)) { |
|
323 convSpecs[i] = valueConversion(src, dst, strict, monobox); |
|
324 } |
|
325 } |
|
326 return convSpecs; |
|
327 } |
|
328 static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, |
|
329 boolean strict) { |
|
330 return makePairwiseConvert(target, srcType, strict, /*monobox=*/ false); |
|
331 } |
|
332 |
|
333 /** |
|
334 * Find a conversion function from the given source to the given destination. |
|
335 * This conversion function will be used as a LF NamedFunction. |
|
336 * Return a Class object if a simple cast is needed. |
|
337 * Return void.class if void is involved. |
|
338 */ |
|
339 static Object valueConversion(Class<?> src, Class<?> dst, boolean strict, boolean monobox) { |
|
340 assert(!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)); // caller responsibility |
|
341 if (dst == void.class) |
|
342 return dst; |
|
343 MethodHandle fn; |
|
344 if (src.isPrimitive()) { |
|
345 if (src == void.class) { |
|
346 return void.class; // caller must recognize this specially |
|
347 } else if (dst.isPrimitive()) { |
|
348 // Examples: int->byte, byte->int, boolean->int (!strict) |
|
349 fn = ValueConversions.convertPrimitive(src, dst); |
|
350 } else { |
|
351 // Examples: int->Integer, boolean->Object, float->Number |
|
352 Wrapper wsrc = Wrapper.forPrimitiveType(src); |
|
353 fn = ValueConversions.boxExact(wsrc); |
|
354 assert(fn.type().parameterType(0) == wsrc.primitiveType()); |
|
355 assert(fn.type().returnType() == wsrc.wrapperType()); |
|
356 if (!VerifyType.isNullConversion(wsrc.wrapperType(), dst, strict)) { |
|
357 // Corner case, such as int->Long, which will probably fail. |
|
358 MethodType mt = MethodType.methodType(dst, src); |
|
359 if (strict) |
|
360 fn = fn.asType(mt); |
|
361 else |
|
362 fn = MethodHandleImpl.makePairwiseConvert(fn, mt, /*strict=*/ false); |
|
363 } |
|
364 } |
|
365 } else if (dst.isPrimitive()) { |
|
366 Wrapper wdst = Wrapper.forPrimitiveType(dst); |
|
367 if (monobox || src == wdst.wrapperType()) { |
|
368 // Use a strongly-typed unboxer, if possible. |
|
369 fn = ValueConversions.unboxExact(wdst, strict); |
|
370 } else { |
|
371 // Examples: Object->int, Number->int, Comparable->int, Byte->int |
|
372 // must include additional conversions |
|
373 // src must be examined at runtime, to detect Byte, Character, etc. |
|
374 fn = (strict |
|
375 ? ValueConversions.unboxWiden(wdst) |
|
376 : ValueConversions.unboxCast(wdst)); |
|
377 } |
|
378 } else { |
|
379 // Simple reference conversion. |
|
380 // Note: Do not check for a class hierarchy relation |
|
381 // between src and dst. In all cases a 'null' argument |
|
382 // will pass the cast conversion. |
|
383 return dst; |
|
384 } |
|
385 assert(fn.type().parameterCount() <= 1) : "pc"+Arrays.asList(src.getSimpleName(), dst.getSimpleName(), fn); |
|
386 return fn; |
346 } |
387 } |
347 |
388 |
348 static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) { |
389 static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) { |
349 MethodType type = target.type(); |
390 MethodType type = target.type(); |
350 int last = type.parameterCount() - 1; |
391 int last = type.parameterCount() - 1; |
718 // Box arguments and wrap them into Object[]: ValueConversions.array(). |
759 // Box arguments and wrap them into Object[]: ValueConversions.array(). |
719 MethodType varargsType = type.changeReturnType(Object[].class); |
760 MethodType varargsType = type.changeReturnType(Object[].class); |
720 MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); |
761 MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); |
721 // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). |
762 // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). |
722 MethodHandle unboxResult; |
763 MethodHandle unboxResult; |
723 if (type.returnType().isPrimitive()) { |
764 Class<?> rtype = type.returnType(); |
724 unboxResult = ValueConversions.unbox(type.returnType()); |
765 if (rtype.isPrimitive()) { |
|
766 if (rtype == void.class) { |
|
767 unboxResult = ValueConversions.ignore(); |
|
768 } else { |
|
769 Wrapper w = Wrapper.forPrimitiveType(type.returnType()); |
|
770 unboxResult = ValueConversions.unboxExact(w); |
|
771 } |
725 } else { |
772 } else { |
726 unboxResult = ValueConversions.identity(); |
773 unboxResult = MethodHandles.identity(Object.class); |
727 } |
774 } |
728 |
775 |
729 BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); |
776 BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); |
730 BoundMethodHandle mh; |
777 BoundMethodHandle mh; |
731 try { |
778 try { |