aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/android/tools/r8/graph/GraphLense.java
blob: 3abe7c5bbcceda3c838fc60d2d962b35d972e60e (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 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.graph;

import java.util.IdentityHashMap;
import java.util.Map;

/**
 * A GraphLense implements a virtual view on top of the graph, used to delay global rewrites until
 * later IR processing stages.
 * <p>
 * Valid remappings are limited to the following operations:
 * <ul>
 * <li>Mapping a classes type to one of the super/subtypes.</li>
 * <li>Renaming private methods/fields.</li>
 * <li>Moving methods/fields to a super/subclass.</li>
 * <li>Replacing method/field references by the same method/field on a super/subtype</li>
 * </ul>
 * Note that the latter two have to take visibility into account.
 */
public abstract class GraphLense {

  public static class Builder {

    private Builder() {

    }

    private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
    private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
    private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();

    public void map(DexType from, DexType to) {
      typeMap.put(from, to);
    }

    public void map(DexMethod from, DexMethod to) {
      methodMap.put(from, to);
    }

    public void map(DexField from, DexField to) {
      fieldMap.put(from, to);
    }

    public GraphLense build(DexItemFactory dexItemFactory) {
      return build(new IdentityGraphLense(), dexItemFactory);
    }

    public GraphLense build(GraphLense previousLense, DexItemFactory dexItemFactory) {
      return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense, dexItemFactory);
    }

  }

  public static Builder builder() {
    return new Builder();
  }

  public abstract DexType lookupType(DexType type, DexEncodedMethod context);

  public abstract DexMethod lookupMethod(DexMethod method, DexEncodedMethod context);

  public abstract DexField lookupField(DexField field, DexEncodedMethod context);

  public abstract boolean isContextFree();

  public static GraphLense getIdentityLense() {
    return new IdentityGraphLense();
  }

  public final boolean isIdentityLense() {
    return this instanceof IdentityGraphLense;
  }

  private static class IdentityGraphLense extends GraphLense {

    @Override
    public DexType lookupType(DexType type, DexEncodedMethod context) {
      return type;
    }

    @Override
    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
      return method;
    }

    @Override
    public DexField lookupField(DexField field, DexEncodedMethod context) {
      return field;
    }

    @Override
    public boolean isContextFree() {
      return true;
    }
  }

  private static class NestedGraphLense extends GraphLense {

    private final GraphLense previousLense;
    private final DexItemFactory dexItemFactory;

    private final Map<DexType, DexType> typeMap;
    private final Map<DexType, DexType> arrayTypeCache = new IdentityHashMap<>();
    private final Map<DexMethod, DexMethod> methodMap;
    private final Map<DexField, DexField> fieldMap;

    private NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
        Map<DexField, DexField> fieldMap, GraphLense previousLense, DexItemFactory dexItemFactory) {
      this.typeMap = typeMap;
      this.methodMap = methodMap;
      this.fieldMap = fieldMap;
      this.previousLense = previousLense;
      this.dexItemFactory = dexItemFactory;
    }

    @Override
    public DexType lookupType(DexType type, DexEncodedMethod context) {
      if (type.isArrayType()) {
        synchronized(this) {
          // This block need to be synchronized due to arrayTypeCache.
          DexType result = arrayTypeCache.get(type);
          if (result == null) {
            DexType baseType = type.toBaseType(dexItemFactory);
            DexType newType = lookupType(baseType, context);
            if (baseType == newType) {
              result = type;
            } else {
              result = type.replaceBaseType(newType, dexItemFactory);
            }
            arrayTypeCache.put(type, result);
          }
          return result;
        }
      }
      DexType previous = previousLense.lookupType(type, context);
      return typeMap.getOrDefault(previous, previous);
    }

    @Override
    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
      DexMethod previous = previousLense.lookupMethod(method, context);
      return methodMap.getOrDefault(previous, previous);
    }

    @Override
    public DexField lookupField(DexField field, DexEncodedMethod context) {
      DexField previous = previousLense.lookupField(field, context);
      return fieldMap.getOrDefault(previous, previous);
    }

    @Override
    public boolean isContextFree() {
      return previousLense.isContextFree();
    }
  }
}