aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
blob: 1cf64fd2b9520a38935db0ef6f8c8bcb005c3f5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

package com.android.tools.r8.ir.desugar;

import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexAccessFlags;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.MoveType;
import com.android.tools.r8.ir.conversion.IRBuilder;
import java.util.ArrayList;
import java.util.List;

// Source code representing synthesized accessor method.
final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {

  AccessorMethodSourceCode(LambdaClass lambda) {
    super(lambda, lambda.target.callTarget, null /* no receiver for static method */);
    // We should never need an accessor for interface methods since
    // they are supposed to be public.
    assert !descriptor().implHandle.type.isInvokeInterface();
    assert checkSignatures();
  }

  private boolean checkSignatures() {
    DexMethodHandle implHandle = descriptor().implHandle;
    assert implHandle != null;

    DexType[] accessorParams = proto.parameters.values;
    DexMethod implMethod = implHandle.asMethod();
    DexProto implProto = implMethod.proto;
    DexType[] implParams = implProto.parameters.values;

    int index = 0;
    if (implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect()) {
      assert accessorParams[index] == descriptor().getImplReceiverType();
      index++;
    }

    for (DexType implParam : implParams) {
      assert accessorParams[index] == implParam;
      index++;
    }
    assert index == accessorParams.length;

    assert delegatingToConstructor()
        ? this.proto.returnType == implMethod.holder
        : this.proto.returnType == implProto.returnType;
    return true;
  }

  private boolean isPrivateMethod() {
    // We should be able to find targets for all private impl-methods, so
    // we can rely on knowing accessibility flags for them.
    DexAccessFlags flags = descriptor().getAccessibility();
    return flags != null && flags.isPrivate();
  }

  // Are we delegating to a constructor?
  private boolean delegatingToConstructor() {
    return descriptor().implHandle.type.isInvokeConstructor();
  }

  private Invoke.Type inferInvokeType() {
    switch (descriptor().implHandle.type) {
      case INVOKE_INSTANCE:
        return Invoke.Type.VIRTUAL;
      case INVOKE_STATIC:
        return Invoke.Type.STATIC;
      case INVOKE_DIRECT:
      case INVOKE_CONSTRUCTOR:
        return Invoke.Type.DIRECT;
      case INVOKE_INTERFACE:
        throw new Unreachable("Accessor for an interface method?");
      default:
        throw new Unreachable();
    }
  }

  @Override
  protected void prepareInstructions() {
    DexMethod implMethod = descriptor().implHandle.asMethod();
    DexType[] accessorParams = proto.parameters.values;

    // Prepare call arguments.
    List<MoveType> argMoveTypes = new ArrayList<>();
    List<Integer> argRegisters = new ArrayList<>();

    // If we are delegating to a constructor, we need to create the instance
    // first. This instance will be the first argument to the call.
    if (delegatingToConstructor()) {
      int instance = nextRegister(MoveType.OBJECT);
      add(builder -> builder.addNewInstance(instance, implMethod.holder));
      argMoveTypes.add(MoveType.OBJECT);
      argRegisters.add(instance);
    }

    for (int i = 0; i < accessorParams.length; i++) {
      DexType param = accessorParams[i];
      argMoveTypes.add(MoveType.fromDexType(param));
      argRegisters.add(getParamRegister(i));
    }

    // Method call to the original impl-method.
    add(builder -> builder.addInvoke(inferInvokeType(),
        implMethod, implMethod.proto, argMoveTypes, argRegisters));

    // Does the method have return value?
    if (proto.returnType == factory().voidType) {
      add(IRBuilder::addReturn);
    } else if (delegatingToConstructor()) {
      // Return newly created instance
      add(builder -> builder.addReturn(MoveType.OBJECT, argRegisters.get(0)));
    } else {
      MoveType moveType = MoveType.fromDexType(proto.returnType);
      int tempValue = nextRegister(moveType);
      add(builder -> builder.addMoveResult(moveType, tempValue));
      add(builder -> builder.addReturn(moveType, tempValue));
    }
  }
}