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.
4package com.android.tools.r8.graph;
5
6import java.util.IdentityHashMap;
7import java.util.Map;
8
9/**
10 * A GraphLense implements a virtual view on top of the graph, used to delay global rewrites until
11 * later IR processing stages.
12 * <p>
13 * Valid remappings are limited to the following operations:
14 * <ul>
15 * <li>Mapping a classes type to one of the super/subtypes.</li>
16 * <li>Renaming private methods/fields.</li>
17 * <li>Moving methods/fields to a super/subclass.</li>
18 * <li>Replacing method/field references by the same method/field on a super/subtype</li>
19 * </ul>
20 * Note that the latter two have to take visibility into account.
21 */
22public abstract class GraphLense {
23
24  public static class Builder {
25
26    private Builder() {
27
28    }
29
30    private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
31    private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
32    private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
33
34    public void map(DexType from, DexType to) {
35      typeMap.put(from, to);
36    }
37
38    public void map(DexMethod from, DexMethod to) {
39      methodMap.put(from, to);
40    }
41
42    public void map(DexField from, DexField to) {
43      fieldMap.put(from, to);
44    }
45
46    public GraphLense build(DexItemFactory dexItemFactory) {
47      return build(new IdentityGraphLense(), dexItemFactory);
48    }
49
50    public GraphLense build(GraphLense previousLense, DexItemFactory dexItemFactory) {
51      return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense, dexItemFactory);
52    }
53
54  }
55
56  public static Builder builder() {
57    return new Builder();
58  }
59
60  public abstract DexType lookupType(DexType type, DexEncodedMethod context);
61
62  public abstract DexMethod lookupMethod(DexMethod method, DexEncodedMethod context);
63
64  public abstract DexField lookupField(DexField field, DexEncodedMethod context);
65
66  public abstract boolean isContextFree();
67
68  public static GraphLense getIdentityLense() {
69    return new IdentityGraphLense();
70  }
71
72  public final boolean isIdentityLense() {
73    return this instanceof IdentityGraphLense;
74  }
75
76  private static class IdentityGraphLense extends GraphLense {
77
78    @Override
79    public DexType lookupType(DexType type, DexEncodedMethod context) {
80      return type;
81    }
82
83    @Override
84    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
85      return method;
86    }
87
88    @Override
89    public DexField lookupField(DexField field, DexEncodedMethod context) {
90      return field;
91    }
92
93    @Override
94    public boolean isContextFree() {
95      return true;
96    }
97  }
98
99  private static class NestedGraphLense extends GraphLense {
100
101    private final GraphLense previousLense;
102    private final DexItemFactory dexItemFactory;
103
104    private final Map<DexType, DexType> typeMap;
105    private final Map<DexType, DexType> arrayTypeCache = new IdentityHashMap<>();
106    private final Map<DexMethod, DexMethod> methodMap;
107    private final Map<DexField, DexField> fieldMap;
108
109    private NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
110        Map<DexField, DexField> fieldMap, GraphLense previousLense, DexItemFactory dexItemFactory) {
111      this.typeMap = typeMap;
112      this.methodMap = methodMap;
113      this.fieldMap = fieldMap;
114      this.previousLense = previousLense;
115      this.dexItemFactory = dexItemFactory;
116    }
117
118    @Override
119    public DexType lookupType(DexType type, DexEncodedMethod context) {
120      if (type.isArrayType()) {
121        DexType result = arrayTypeCache.get(type);
122        if (result == null) {
123          DexType baseType = type.toBaseType(dexItemFactory);
124          DexType newType = lookupType(baseType, context);
125          if (baseType == newType) {
126            result = type;
127          } else {
128            result = type.replaceBaseType(newType, dexItemFactory);
129          }
130          arrayTypeCache.put(type, result);
131        }
132        return result;
133      }
134      DexType previous = previousLense.lookupType(type, context);
135      return typeMap.getOrDefault(previous, previous);
136    }
137
138    @Override
139    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
140      DexMethod previous = previousLense.lookupMethod(method, context);
141      return methodMap.getOrDefault(previous, previous);
142    }
143
144    @Override
145    public DexField lookupField(DexField field, DexEncodedMethod context) {
146      DexField previous = previousLense.lookupField(field, context);
147      return fieldMap.getOrDefault(previous, previous);
148    }
149
150    @Override
151    public boolean isContextFree() {
152      return previousLense.isContextFree();
153    }
154  }
155}
156