1// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5package com.android.tools.r8.ir.desugar;
6
7import com.android.tools.r8.errors.Unreachable;
8import com.android.tools.r8.graph.DexField;
9import com.android.tools.r8.graph.DexItemFactory;
10import com.android.tools.r8.graph.DexMethod;
11import com.android.tools.r8.graph.DexProto;
12import com.android.tools.r8.graph.DexString;
13import com.android.tools.r8.graph.DexType;
14import com.android.tools.r8.ir.code.Invoke;
15import com.android.tools.r8.ir.code.MemberType;
16import com.android.tools.r8.ir.code.MoveType;
17import com.android.tools.r8.ir.code.NumericType;
18import com.android.tools.r8.ir.conversion.IRBuilder;
19import com.google.common.collect.Lists;
20import java.util.ArrayList;
21import java.util.Collections;
22import java.util.List;
23
24// Source code representing synthesized lambda main method
25final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
26
27  LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod) {
28    super(lambda, mainMethod);
29  }
30
31  private boolean checkSignatures(
32      DexType[] captures, DexType[] enforcedParams, DexType enforcedReturnType,
33      List<DexType> implReceiverAndArgs, DexType implReturnType) {
34    List<DexType> capturesAndParams = new ArrayList<>();
35    capturesAndParams.addAll(Lists.newArrayList(captures));
36    capturesAndParams.addAll(Lists.newArrayList(enforcedParams));
37
38    int size = capturesAndParams.size();
39    if (size != implReceiverAndArgs.size()) {
40      return false;
41    }
42
43    for (int i = 0; i < size; i++) {
44      if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i))) {
45        return false;
46      }
47    }
48
49    if (!enforcedReturnType.isVoidType()
50        && !isSameOrAdaptableTo(implReturnType, enforcedReturnType)) {
51      return false;
52    }
53    return true;
54  }
55
56  private DexType getPrimitiveFromBoxed(DexType boxedPrimitive) {
57    DexString descriptor = boxedPrimitive.descriptor;
58    DexItemFactory factory = factory();
59    if (descriptor == factory.boxedBooleanDescriptor) {
60      return factory.booleanType;
61    }
62    if (descriptor == factory.boxedByteDescriptor) {
63      return factory.byteType;
64    }
65    if (descriptor == factory.boxedCharDescriptor) {
66      return factory.charType;
67    }
68    if (descriptor == factory.boxedShortDescriptor) {
69      return factory.shortType;
70    }
71    if (descriptor == factory.boxedIntDescriptor) {
72      return factory.intType;
73    }
74    if (descriptor == factory.boxedLongDescriptor) {
75      return factory.longType;
76    }
77    if (descriptor == factory.boxedFloatDescriptor) {
78      return factory.floatType;
79    }
80    if (descriptor == factory.boxedDoubleDescriptor) {
81      return factory.doubleType;
82    }
83    return null;
84  }
85
86  private DexType getBoxedForPrimitiveType(DexType primitive) {
87    switch (primitive.descriptor.content[0]) {
88      case 'Z':  // byte
89        return factory().boxedBooleanType;
90      case 'B':  // byte
91        return factory().boxedByteType;
92      case 'S':  // short
93        return factory().boxedShortType;
94      case 'C':  // char
95        return factory().boxedCharType;
96      case 'I':  // int
97        return factory().boxedIntType;
98      case 'J':  // long
99        return factory().boxedLongType;
100      case 'F':  // float
101        return factory().boxedFloatType;
102      case 'D':  // double
103        return factory().boxedDoubleType;
104      default:
105        throw new Unreachable("Invalid primitive type descriptor: " + primitive);
106    }
107  }
108
109  // Checks if the types are the same OR type `a` is adaptable to type `b`.
110  private boolean isSameOrAdaptableTo(DexType a, DexType b) {
111    if (a == b) {
112      return true;
113    }
114
115    DexItemFactory factory = factory();
116    if (a.isArrayType()) {
117      // Arrays are only adaptable to java.lang.Object.
118      return b == factory.objectType;
119    }
120
121    if (a.isPrimitiveType()) {
122      if (b.isPrimitiveType()) {
123        return isSameOrAdaptableTo(a.descriptor.content[0], b.descriptor.content[0]);
124      }
125
126      // `a` is primitive and `b` is a supertype of the boxed type `a`.
127      DexType boxedPrimitiveType = getBoxedForPrimitiveType(a);
128      if (b == boxedPrimitiveType || b == factory.objectType) {
129        return true;
130      }
131      return boxedPrimitiveType != factory.boxedCharType
132          && boxedPrimitiveType != factory.boxedBooleanType
133          && b.descriptor == factory.boxedNumberDescriptor;
134    }
135
136    if (b.isPrimitiveType()) {
137      // `a` is a boxed type for `a*` which can be
138      // widened to primitive type `b`.
139      DexType unboxedA = getPrimitiveFromBoxed(a);
140      return unboxedA != null &&
141          isSameOrAdaptableTo(unboxedA.descriptor.content[0], b.descriptor.content[0]);
142    }
143
144    // Otherwise `a` should be a reference type derived from `b`.
145    // NOTE: we don't check `b` for being actually a supertype, since we
146    // might not have full classpath with inheritance information to do that.
147    // We assume this requirement stands and will be caught by cast
148    // instruction anyways in most cases.
149    return a.isClassType() && b.isClassType();
150  }
151
152  // For two primitive types `a` is adjustable to `b` iff `a` is the same as `b`
153  // or can be converted to `b` via a primitive widening conversion.
154  private boolean isSameOrAdaptableTo(byte from, byte to) {
155    if (from == to) {
156      return true;
157    }
158    switch (from) {
159      case 'B':  // byte
160        return to == 'S' || to == 'I' || to == 'J' || to == 'F' || to == 'D';
161      case 'S':  // short
162      case 'C':  // char
163        return to == 'I' || to == 'J' || to == 'F' || to == 'D';
164      case 'I':  // int
165        return to == 'J' || to == 'F' || to == 'D';
166      case 'J':  // long
167        return to == 'F' || to == 'D';
168      case 'F':  // float
169        return to == 'D';
170      case 'Z':  // boolean
171      case 'D':  // double
172        return false;
173      default:
174        throw new Unreachable("Invalid primitive type descriptor: " + from);
175    }
176  }
177
178  @Override
179  protected void prepareInstructions() {
180    DexType[] capturedTypes = captures();
181    DexType[] erasedParams = descriptor().erasedProto.parameters.values;
182    DexType erasedReturnType = descriptor().erasedProto.returnType;
183    DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
184    DexType enforcedReturnType = descriptor().enforcedProto.returnType;
185
186    LambdaClass.Target target = lambda.target;
187    DexMethod methodToCall = target.callTarget;
188
189    // Only constructor call should use direct invoke type since super
190    // and private methods require accessor methods.
191    boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
192    assert !constructorTarget || methodToCall.name == factory().constructorMethodName;
193
194    List<DexType> implReceiverAndArgs = new ArrayList<>();
195    if (target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE) {
196      implReceiverAndArgs.add(methodToCall.holder);
197    }
198    implReceiverAndArgs.addAll(Lists.newArrayList(methodToCall.proto.parameters.values));
199    DexType implReturnType = methodToCall.proto.returnType;
200
201    assert target.invokeType == Invoke.Type.STATIC
202        || target.invokeType == Invoke.Type.VIRTUAL
203        || target.invokeType == Invoke.Type.DIRECT
204        || target.invokeType == Invoke.Type.INTERFACE;
205    assert checkSignatures(capturedTypes, enforcedParams,
206        enforcedReturnType, implReceiverAndArgs,
207        constructorTarget ? target.callTarget.holder : implReturnType);
208
209    // Prepare call arguments.
210    List<MoveType> argMoveTypes = new ArrayList<>();
211    List<Integer> argRegisters = new ArrayList<>();
212
213    // If the target is a constructor, we need to create the instance first.
214    // This instance will be the first argument to the call.
215    if (constructorTarget) {
216      int instance = nextRegister(MoveType.OBJECT);
217      add(builder -> builder.addNewInstance(instance, methodToCall.holder));
218      argMoveTypes.add(MoveType.OBJECT);
219      argRegisters.add(instance);
220    }
221
222    // Load captures if needed.
223    int capturedValues = capturedTypes.length;
224    for (int i = 0; i < capturedValues; i++) {
225      MemberType memberType = MemberType.fromDexType(capturedTypes[i]);
226      MoveType moveType = MemberType.moveTypeFor(memberType);
227      int register = nextRegister(moveType);
228
229      argMoveTypes.add(moveType);
230      argRegisters.add(register);
231
232      // Read field into tmp local.
233      DexField field = lambda.getCaptureField(i);
234      add(builder -> builder.addInstanceGet(
235          memberType, register, getReceiverRegister(), field));
236    }
237
238    // Prepare arguments.
239    for (int i = 0; i < erasedParams.length; i++) {
240      DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues);
241      argMoveTypes.add(MoveType.fromDexType(expectedParamType));
242      argRegisters.add(prepareParameterValue(
243          getParamRegister(i), erasedParams[i], enforcedParams[i], expectedParamType));
244    }
245
246    // Method call to the method implementing lambda or method-ref.
247    add(builder -> builder.addInvoke(target.invokeType,
248        methodToCall, methodToCall.proto, argMoveTypes, argRegisters));
249
250    // Does the method have return value?
251    if (enforcedReturnType.isVoidType()) {
252      add(IRBuilder::addReturn);
253    } else if (constructorTarget) {
254      // Return newly created instance
255      int instanceRegister = argRegisters.get(0);
256      int adjustedValue = prepareReturnValue(instanceRegister,
257          erasedReturnType, enforcedReturnType, methodToCall.holder);
258      add(builder -> builder.addReturn(
259          MoveType.fromDexType(erasedReturnType), adjustedValue));
260    } else {
261      MoveType implMoveType = MoveType.fromDexType(implReturnType);
262      int tempValue = nextRegister(implMoveType);
263      add(builder -> builder.addMoveResult(implMoveType, tempValue));
264      int adjustedValue = prepareReturnValue(tempValue,
265          erasedReturnType, enforcedReturnType, methodToCall.proto.returnType);
266      MoveType adjustedMoveType = MoveType.fromDexType(erasedReturnType);
267      add(builder -> builder.addReturn(adjustedMoveType, adjustedValue));
268    }
269  }
270
271  // Adds necessary casts and transformations to adjust the value
272  // returned by impl-method to expected return type of the method.
273  private int prepareReturnValue(int register,
274      DexType erasedType, DexType enforcedType, DexType actualType) {
275    // `actualType` must be adjusted to `enforcedType` first.
276    register = adjustType(register, actualType, enforcedType);
277
278    // `erasedType` and `enforcedType` may only differ when they both
279    // are class types and `erasedType` is a base type of `enforcedType`,
280    // so no transformation is actually needed.
281    assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, erasedType);
282    return register;
283  }
284
285  // Adds necessary casts and transformations to adjust parameter
286  // value to the expected type of method-impl argument.
287  //
288  // Note that the original parameter type (`erasedType`) may need to
289  // be converted to enforced parameter type (`enforcedType`), which,
290  // in its turn, may need to be adjusted to the parameter type of
291  // the impl-method (`expectedType`).
292  private int prepareParameterValue(int register,
293      DexType erasedType, DexType enforcedType, DexType expectedType) {
294    register = enforceParameterType(register, erasedType, enforcedType);
295    register = adjustType(register, enforcedType, expectedType);
296    return register;
297  }
298
299  private int adjustType(int register, DexType fromType, DexType toType) {
300    if (fromType == toType) {
301      return register;
302    }
303
304    boolean fromTypePrimitive = fromType.isPrimitiveType();
305    boolean toTypePrimitive = toType.isPrimitiveType();
306
307    // If both are primitive they must be convertible via primitive widening conversion.
308    if (fromTypePrimitive && toTypePrimitive) {
309      return addPrimitiveWideningConversion(register, fromType, toType);
310    }
311
312    // If the first one is a boxed primitive type and the second one is a primitive
313    // type, the value must be unboxed and converted to the resulting type via primitive
314    // widening conversion.
315    if (toTypePrimitive) {
316      DexType fromTypeAsPrimitive = getPrimitiveFromBoxed(fromType);
317      if (fromTypeAsPrimitive != null) {
318        int unboxedRegister = addPrimitiveUnboxing(register, fromTypeAsPrimitive, fromType);
319        return addPrimitiveWideningConversion(unboxedRegister, fromTypeAsPrimitive, toType);
320      }
321    }
322
323    // If the first one is a primitive type and the second one is a boxed
324    // type for this primitive type, just box the value.
325    if (fromTypePrimitive) {
326      DexType boxedFromType = getBoxedForPrimitiveType(fromType);
327      if (toType == boxedFromType ||
328          toType == factory().objectType ||
329          (boxedFromType != factory().booleanType &&
330              boxedFromType != factory().charType &&
331              toType == factory().boxedNumberType)) {
332        return addPrimitiveBoxing(register, fromType, boxedFromType);
333      }
334    }
335
336    if (fromType.isArrayType() && toType == factory().objectType) {
337      // If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
338      return register;
339    }
340
341    if (fromType.isClassType() && toType.isClassType()) {
342      // If `fromType` and `toType` are both reference types, `fromType` must
343      // be deriving from `toType`.
344      // NOTE: we don't check `toType` for being actually a supertype, since we
345      // might not have full classpath with inheritance information to do that.
346      return register;
347    }
348
349    throw new Unreachable("Unexpected type adjustment from "
350        + fromType.toSourceString() + " to " + toType);
351  }
352
353  private int addPrimitiveWideningConversion(int register, DexType fromType, DexType toType) {
354    assert fromType.isPrimitiveType() && toType.isPrimitiveType();
355    if (fromType == toType) {
356      return register;
357    }
358
359    NumericType from = NumericType.fromDexType(fromType);
360    NumericType to = NumericType.fromDexType(toType);
361
362    if (from != null && to != null) {
363      assert from != to;
364
365      switch (to) {
366        case SHORT: {
367          if (from != NumericType.BYTE) {
368            break; // Only BYTE can be converted to SHORT via widening conversion.
369          }
370          int result = nextRegister(MoveType.SINGLE);
371          add(builder -> builder.addConversion(to, NumericType.INT, result, register));
372          return result;
373        }
374
375        case INT:
376          if (from == NumericType.BYTE || from == NumericType.CHAR || from == NumericType.SHORT) {
377            return register; // No actual conversion is needed.
378          }
379          break;
380
381        case LONG: {
382          if (from == NumericType.FLOAT || from == NumericType.DOUBLE) {
383            break; // Not a widening conversion.
384          }
385          int result = nextRegister(MoveType.WIDE);
386          add(builder -> builder.addConversion(to, NumericType.INT, result, register));
387          return result;
388        }
389
390        case FLOAT: {
391          if (from == NumericType.DOUBLE) {
392            break; // Not a widening conversion.
393          }
394          int result = nextRegister(MoveType.SINGLE);
395          NumericType type = (from == NumericType.LONG) ? NumericType.LONG : NumericType.INT;
396          add(builder -> builder.addConversion(to, type, result, register));
397          return result;
398        }
399
400        case DOUBLE: {
401          int result = nextRegister(MoveType.WIDE);
402          NumericType type = (from == NumericType.FLOAT || from == NumericType.LONG)
403              ? from : NumericType.INT;
404          add(builder -> builder.addConversion(to, type, result, register));
405          return result;
406        }
407        default:
408          // exception is thrown below
409          break;
410      }
411    }
412
413    throw new Unreachable("Type " + fromType.toSourceString() + " cannot be " +
414        "converted to " + toType.toSourceString() + " via primitive widening conversion.");
415  }
416
417  private DexMethod getUnboxMethod(byte primitive, DexType boxType) {
418    DexItemFactory factory = factory();
419    DexProto proto;
420    switch (primitive) {
421      case 'Z':  // byte
422        proto = factory.createProto(factory.booleanType);
423        return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
424      case 'B':  // byte
425        proto = factory.createProto(factory.byteType);
426        return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
427      case 'S':  // short
428        proto = factory.createProto(factory.shortType);
429        return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
430      case 'C':  // char
431        proto = factory.createProto(factory.charType);
432        return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
433      case 'I':  // int
434        proto = factory.createProto(factory.intType);
435        return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
436      case 'J':  // long
437        proto = factory.createProto(factory.longType);
438        return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
439      case 'F':  // float
440        proto = factory.createProto(factory.floatType);
441        return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
442      case 'D':  // double
443        proto = factory.createProto(factory.doubleType);
444        return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
445      default:
446        throw new Unreachable("Invalid primitive type descriptor: " + primitive);
447    }
448  }
449
450  private int addPrimitiveUnboxing(int register, DexType primitiveType, DexType boxType) {
451    DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType);
452
453    List<MoveType> argMoveTypes = Collections.singletonList(MoveType.OBJECT);
454    List<Integer> argRegisters = Collections.singletonList(register);
455    add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
456        method, method.proto, argMoveTypes, argRegisters));
457
458    MoveType moveType = MoveType.fromDexType(primitiveType);
459    int result = nextRegister(moveType);
460    add(builder -> builder.addMoveResult(moveType, result));
461    return result;
462  }
463
464  private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) {
465    // Generate factory method fo boxing.
466    DexItemFactory factory = factory();
467    DexProto proto = factory.createProto(boxType, new DexType[]{primitiveType});
468    DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
469
470    MoveType moveType = MoveType.fromDexType(primitiveType);
471    List<MoveType> argMoveTypes = Collections.singletonList(moveType);
472    List<Integer> argRegisters = Collections.singletonList(register);
473    add(builder -> builder.addInvoke(Invoke.Type.STATIC,
474        method, method.proto, argMoveTypes, argRegisters));
475
476    int result = nextRegister(MoveType.OBJECT);
477    add(builder -> builder.addMoveResult(MoveType.OBJECT, result));
478    return result;
479  }
480}
481