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 package com.android.tools.r8.graph; 5 6 import java.util.IdentityHashMap; 7 import 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 */ 22 public abstract class GraphLense { 23 24 public static class Builder { 25 Builder()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 map(DexType from, DexType to)34 public void map(DexType from, DexType to) { 35 typeMap.put(from, to); 36 } 37 map(DexMethod from, DexMethod to)38 public void map(DexMethod from, DexMethod to) { 39 methodMap.put(from, to); 40 } 41 map(DexField from, DexField to)42 public void map(DexField from, DexField to) { 43 fieldMap.put(from, to); 44 } 45 build(DexItemFactory dexItemFactory)46 public GraphLense build(DexItemFactory dexItemFactory) { 47 return build(new IdentityGraphLense(), dexItemFactory); 48 } 49 build(GraphLense previousLense, DexItemFactory dexItemFactory)50 public GraphLense build(GraphLense previousLense, DexItemFactory dexItemFactory) { 51 return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense, dexItemFactory); 52 } 53 54 } 55 builder()56 public static Builder builder() { 57 return new Builder(); 58 } 59 lookupType(DexType type, DexEncodedMethod context)60 public abstract DexType lookupType(DexType type, DexEncodedMethod context); 61 lookupMethod(DexMethod method, DexEncodedMethod context)62 public abstract DexMethod lookupMethod(DexMethod method, DexEncodedMethod context); 63 lookupField(DexField field, DexEncodedMethod context)64 public abstract DexField lookupField(DexField field, DexEncodedMethod context); 65 isContextFree()66 public abstract boolean isContextFree(); 67 getIdentityLense()68 public static GraphLense getIdentityLense() { 69 return new IdentityGraphLense(); 70 } 71 isIdentityLense()72 public final boolean isIdentityLense() { 73 return this instanceof IdentityGraphLense; 74 } 75 76 private static class IdentityGraphLense extends GraphLense { 77 78 @Override lookupType(DexType type, DexEncodedMethod context)79 public DexType lookupType(DexType type, DexEncodedMethod context) { 80 return type; 81 } 82 83 @Override lookupMethod(DexMethod method, DexEncodedMethod context)84 public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) { 85 return method; 86 } 87 88 @Override lookupField(DexField field, DexEncodedMethod context)89 public DexField lookupField(DexField field, DexEncodedMethod context) { 90 return field; 91 } 92 93 @Override isContextFree()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 NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap, Map<DexField, DexField> fieldMap, GraphLense previousLense, DexItemFactory dexItemFactory)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 lookupType(DexType type, DexEncodedMethod context)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 lookupMethod(DexMethod method, DexEncodedMethod context)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 lookupField(DexField field, DexEncodedMethod context)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 isContextFree()151 public boolean isContextFree() { 152 return previousLense.isContextFree(); 153 } 154 } 155 } 156