Reintroduce "Move invocation type mapping to GraphLense"
Commit 9ce04928aee256c977f708cee7506e6907bcc2fe caused a test that had an invoke-custom instruction in a static method to fail, and was therefore reverted in commit 4191da58ce88884de9174caed7af01f07f6cc728.
This CL applies a small patch to 9ce04928aee256c977f708cee7506e6907bcc2fe and reintroduces the change.
Change-Id: I26bbf8a7f64b7c66fad768cd522fa145398e8c60
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 2c12a90..7d17f11 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -20,11 +20,35 @@
* <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>
+ * <li>Moved methods might require changed invocation type at the call site</li>
* </ul>
* Note that the latter two have to take visibility into account.
*/
public abstract class GraphLense {
+ /**
+ * Result of a method lookup in a GraphLense.
+ *
+ * This provide the new target and the invoke type to use.
+ */
+ public static class GraphLenseLookupResult {
+ private final DexMethod method;
+ private final Type type;
+
+ public GraphLenseLookupResult(DexMethod method, Type type) {
+ this.method = method;
+ this.type = type;
+ }
+
+ public DexMethod getMethod() {
+ return method;
+ }
+
+ public Type getType() {
+ return type;
+ }
+ }
+
public static class Builder {
protected Builder() {
@@ -68,10 +92,11 @@
// This overload can be used when the graph lense is known to be context insensitive.
public DexMethod lookupMethod(DexMethod method) {
assert isContextFreeForMethod(method);
- return lookupMethod(method, null, null);
+ return lookupMethod(method, null, null).getMethod();
}
- public abstract DexMethod lookupMethod(DexMethod method, DexEncodedMethod context, Type type);
+ public abstract GraphLenseLookupResult lookupMethod(
+ DexMethod method, DexEncodedMethod context, Type type);
// Context sensitive graph lenses should override this method.
public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) {
@@ -126,8 +151,9 @@
}
@Override
- public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context, Type type) {
- return method;
+ public GraphLenseLookupResult lookupMethod(
+ DexMethod method, DexEncodedMethod context, Type type) {
+ return new GraphLenseLookupResult(method, type);
}
@Override
@@ -141,19 +167,29 @@
}
}
+ /**
+ * GraphLense implementation with a parent lense using a simple mapping for type, method and
+ * field mapping.
+ *
+ * Subclasses can override the lookup methods.
+ *
+ * For method mapping where invocation type can change just override
+ * {@link #mapInvocationType(DexMethod, DexMethod, DexEncodedMethod, Type)} if
+ * the default name mapping applies, and only invocation type might need to change.
+ */
public static class NestedGraphLense extends GraphLense {
- private final GraphLense previousLense;
+ protected final GraphLense previousLense;
protected final DexItemFactory dexItemFactory;
- private final Map<DexType, DexType> typeMap;
+ protected 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;
+ protected final Map<DexMethod, DexMethod> methodMap;
+ protected final Map<DexField, DexField> fieldMap;
public NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap, GraphLense previousLense, DexItemFactory dexItemFactory) {
- this.typeMap = typeMap;
+ this.typeMap = typeMap.isEmpty() ? null : typeMap;
this.methodMap = methodMap;
this.fieldMap = fieldMap;
this.previousLense = previousLense;
@@ -180,13 +216,58 @@
}
}
DexType previous = previousLense.lookupType(type);
- return typeMap.getOrDefault(previous, previous);
+ return typeMap != null ? typeMap.getOrDefault(previous, previous) : previous;
}
@Override
- public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context, Type type) {
- DexMethod previous = previousLense.lookupMethod(method, context, type);
- return methodMap.getOrDefault(previous, previous);
+ public GraphLenseLookupResult lookupMethod(
+ DexMethod method, DexEncodedMethod context, Type type) {
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
+ DexMethod newMethod = methodMap.get(previous.getMethod());
+ if (newMethod == null) {
+ return previous;
+ }
+ // TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
+ // that only subclasses which are known to need it actually do it?
+ return new GraphLenseLookupResult(
+ newMethod, mapInvocationType(newMethod, method, context, type));
+ }
+
+ /**
+ * Default invocation type mapping.
+ *
+ * This is an identity mapping. If a subclass need invocation type mapping either override
+ * this method or {@link #lookupMethod(DexMethod, DexEncodedMethod, Type)}
+ */
+ protected Type mapInvocationType(
+ DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+ return type;
+ }
+
+ /**
+ * Standard mapping between interface and virtual invoke type.
+ *
+ * Handle methods moved from interface to class or class to interface.
+ */
+ final protected Type mapVirtualInterfaceInvocationTypes(
+ AppInfo appInfo, DexMethod newMethod, DexMethod originalMethod,
+ DexEncodedMethod context, Type type) {
+ if (type == Type.VIRTUAL || type == Type.INTERFACE) {
+ // Get the invoke type of the actual definition.
+ DexClass newTargetClass = appInfo.definitionFor(newMethod.holder);
+ if (newTargetClass == null) {
+ return type;
+ }
+ DexClass originalTargetClass = appInfo.definitionFor(originalMethod.holder);
+ if (originalTargetClass != null
+ && (originalTargetClass.isInterface() ^ (type == Type.INTERFACE))) {
+ // The invoke was wrong to start with, so we keep it wrong. This is to ensure we get
+ // the IncompatibleClassChangeError the original invoke would have triggered.
+ return newTargetClass.accessFlags.isInterface() ? Type.VIRTUAL : Type.INTERFACE;
+ }
+ return newTargetClass.accessFlags.isInterface() ? Type.INTERFACE : Type.VIRTUAL;
+ }
+ return type;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 21b8dc4..85506ad 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -363,7 +363,7 @@
private void processInvoke(Type type, DexMethod method) {
DexEncodedMethod source = caller.method;
- method = graphLense.lookupMethod(method, source, type);
+ method = graphLense.lookupMethod(method, source, type).getMethod();
DexEncodedMethod definition = appInfo.lookup(type, method, source.method.holder);
if (definition != null) {
assert !source.accessFlags.isBridge() || definition != caller.method;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 735fffa..1134686 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -141,7 +141,7 @@
this.memberValuePropagation =
options.enableValuePropagation ?
new MemberValuePropagation(appInfo.withLiveness()) : null;
- this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping(), options);
+ this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping());
if (appInfo.hasLiveness()) {
// When disabling the pruner here, also disable the ProtoLiteExtension in R8.java.
this.protoLiteRewriter =
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 4c0be09..e653ff0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstClass;
@@ -35,7 +36,6 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.InternalOptions;
import java.util.List;
import java.util.ListIterator;
import java.util.stream.Collectors;
@@ -44,13 +44,10 @@
private final GraphLense graphLense;
private final AppInfoWithSubtyping appInfo;
- private final InternalOptions options;
- public LensCodeRewriter(
- GraphLense graphLense, AppInfoWithSubtyping appInfo, InternalOptions options) {
+ public LensCodeRewriter(GraphLense graphLense, AppInfoWithSubtyping appInfo) {
this.graphLense = graphLense;
this.appInfo = appInfo;
- this.options = options;
}
private Value makeOutValue(Instruction insn, IRCode code) {
@@ -110,8 +107,10 @@
if (!invokedHolder.isClassType()) {
continue;
}
- DexMethod actualTarget = graphLense.lookupMethod(invokedMethod, method, invoke.getType());
- Invoke.Type invokeType = getInvokeType(invoke, actualTarget, invokedMethod, method);
+ GraphLenseLookupResult lenseLookup =
+ graphLense.lookupMethod(invokedMethod, method, invoke.getType());
+ DexMethod actualTarget = lenseLookup.getMethod();
+ Invoke.Type invokeType = lenseLookup.getType();
if (actualTarget != invokedMethod || invoke.getType() != invokeType) {
Invoke newInvoke = Invoke.create(invokeType, actualTarget, null,
invoke.outValue(), invoke.inValues());
@@ -230,16 +229,17 @@
DexEncodedMethod method, DexMethodHandle methodHandle) {
if (methodHandle.isMethodHandle()) {
DexMethod invokedMethod = methodHandle.asMethod();
- DexMethod actualTarget =
+ GraphLenseLookupResult lenseLookup =
graphLense.lookupMethod(invokedMethod, method, methodHandle.type.toInvokeType());
+ DexMethod actualTarget = lenseLookup.getMethod();
if (actualTarget != invokedMethod) {
- DexClass clazz = appInfo.definitionFor(actualTarget.holder);
MethodHandleType newType = methodHandle.type;
- if (clazz != null
- && (newType.isInvokeInterface() || newType.isInvokeInstance())) {
- newType = clazz.accessFlags.isInterface()
- ? MethodHandleType.INVOKE_INTERFACE
- : MethodHandleType.INVOKE_INSTANCE;
+ DexClass clazz = appInfo.definitionFor(actualTarget.holder);
+ if (clazz != null && (newType.isInvokeInterface() || newType.isInvokeInstance())) {
+ newType =
+ lenseLookup.getType() == Type.INTERFACE
+ ? MethodHandleType.INVOKE_INTERFACE
+ : MethodHandleType.INVOKE_INSTANCE;
}
return new DexMethodHandle(newType, actualTarget);
}
@@ -252,55 +252,4 @@
}
return methodHandle;
}
-
- private Type getInvokeType(
- InvokeMethod invoke,
- DexMethod actualTarget,
- DexMethod originalTarget,
- DexEncodedMethod invocationContext) {
- // We might move methods from interfaces to classes and vice versa. So we have to support
- // fixing the invoke kind, yet only if it was correct to start with.
- if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
- // Get the invoke type of the actual definition.
- DexClass newTargetClass = appInfo.definitionFor(actualTarget.holder);
- if (newTargetClass == null) {
- return invoke.getType();
- }
- DexClass originalTargetClass = appInfo.definitionFor(originalTarget.holder);
- if (originalTargetClass != null
- && (originalTargetClass.isInterface() ^ (invoke.getType() == Type.INTERFACE))) {
- // The invoke was wrong to start with, so we keep it wrong. This is to ensure we get
- // the IncompatibleClassChangeError the original invoke would have triggered.
- return newTargetClass.accessFlags.isInterface() ? Type.VIRTUAL : Type.INTERFACE;
- }
- return newTargetClass.accessFlags.isInterface() ? Type.INTERFACE : Type.VIRTUAL;
- }
- if (options.enableClassMerging && invoke.isInvokeSuper()) {
- if (actualTarget.getHolder() == invocationContext.method.getHolder()) {
- DexClass targetClass = appInfo.definitionFor(actualTarget.holder);
- if (targetClass == null) {
- return invoke.getType();
- }
-
- // If the super class A of the enclosing class B (i.e., invocationContext.method.holder)
- // has been merged into B during vertical class merging, and this invoke-super instruction
- // was resolving to a method in A, then the target method has been changed to a direct
- // method and moved into B, so that we need to use an invoke-direct instruction instead of
- // invoke-super.
- //
- // At this point, we have an invoke-super instruction where the static target is the
- // enclosing class. However, such an instruction could occur even if a subclass has never
- // been merged into the enclosing class. Therefore, to determine if vertical class merging
- // has been applied, we look if there is a direct method with the right signature, and only
- // return Type.DIRECT in that case.
- DexEncodedMethod method = targetClass.lookupDirectMethod(actualTarget);
- if (method != null) {
- // The target method has been moved from the super class into the sub class during class
- // merging such that we now need to use an invoke-direct instruction.
- return Type.DIRECT;
- }
- }
- }
- return invoke.getType();
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 029b169..25cd969 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -271,7 +271,7 @@
Origin origin = appInfo.originFor(target.method.holder);
IRCode code = target.buildInliningIR(appInfo, options, generator, callerPosition, origin);
if (!target.isProcessed()) {
- new LensCodeRewriter(graphLense, appInfo, options).rewrite(code, target);
+ new LensCodeRewriter(graphLense, appInfo).rewrite(code, target);
}
return code;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
index 56089f4..492bcd0 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -46,14 +46,14 @@
assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
if (kind == InvokeKind.STATIC) {
assert method.accessFlags.isStatic();
- DexMethod actualTarget = lense.lookupMethod(target, method, Type.STATIC);
+ DexMethod actualTarget = lense.lookupMethod(target, method, Type.STATIC).getMethod();
DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(actualTarget);
if (targetMethod != null) {
addForwarding(method, targetMethod);
}
} else if (kind == InvokeKind.VIRTUAL) {
// TODO(herhut): Add support for bridges with multiple targets.
- DexMethod actualTarget = lense.lookupMethod(target, method, Type.VIRTUAL);
+ DexMethod actualTarget = lense.lookupMethod(target, method, Type.VIRTUAL).getMethod();
DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(actualTarget);
if (targetMethod != null) {
addForwarding(method, targetMethod);
@@ -93,15 +93,16 @@
}
@Override
- public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context, Type type) {
- DexMethod previous = previousLense.lookupMethod(method, context, type);
- DexMethod bridge = bridgeTargetToBridgeMap.get(previous);
+ public GraphLenseLookupResult lookupMethod(
+ DexMethod method, DexEncodedMethod context, Type type) {
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
+ DexMethod bridge = bridgeTargetToBridgeMap.get(previous.getMethod());
// Do not forward calls from a bridge method to itself while the bridge method is still
// a bridge.
if (bridge == null || (context.accessFlags.isBridge() && bridge == context.method)) {
return previous;
}
- return bridge;
+ return new GraphLenseLookupResult(bridge, type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index fc408e3..da7a787 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -26,12 +26,13 @@
private final AppInfoWithLiveness appInfo;
private final GraphLense lense;
- private final GraphLense.Builder builder = GraphLense.builder();
+ private final MemberRebindingLense.Builder builder;
public MemberRebindingAnalysis(AppInfoWithLiveness appInfo, GraphLense lense) {
assert lense.isContextFreeForMethods();
this.appInfo = appInfo;
this.lense = lense;
+ this.builder = MemberRebindingLense.builder(appInfo);
}
private DexMethod validTargetFor(DexMethod target, DexMethod original) {
@@ -250,6 +251,6 @@
mergeFieldAccessContexts(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
appInfo::resolveFieldOn, DexClass::lookupField);
- return builder.build(appInfo.dexItemFactory, lense);
+ return builder.build(lense);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
new file mode 100644
index 0000000..4ee0cee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2018, 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.optimize;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class MemberRebindingLense extends NestedGraphLense {
+ public static class Builder extends NestedGraphLense.Builder {
+ private final AppInfo appInfo;
+
+ protected Builder(AppInfo appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public GraphLense build(GraphLense previousLense) {
+ assert typeMap.isEmpty();
+ if (methodMap.isEmpty() && fieldMap.isEmpty()) {
+ return previousLense;
+ }
+ return new MemberRebindingLense(appInfo, methodMap, fieldMap, previousLense);
+ }
+ }
+
+ private final AppInfo appInfo;
+
+ public MemberRebindingLense(
+ AppInfo appInfo,
+ Map<DexMethod, DexMethod> methodMap,
+ Map<DexField, DexField> fieldMap,
+ GraphLense previousLense) {
+ super(ImmutableMap.of(), methodMap, fieldMap, previousLense, appInfo.dexItemFactory);
+ this.appInfo = appInfo;
+ }
+
+ public static Builder builder(AppInfo appInfo) {
+ return new Builder(appInfo);
+ }
+
+
+ @Override
+ protected Type mapInvocationType(
+ DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+ return super.mapVirtualInterfaceInvocationTypes(
+ appInfo, newMethod, originalMethod, context, type);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index b11e33d..f0519a1 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -321,7 +321,7 @@
// The resulting graph lense that should be used after class merging.
VerticalClassMergerGraphLense.Builder renamedMembersLense =
- new VerticalClassMergerGraphLense.Builder();
+ VerticalClassMergerGraphLense.builder(appInfo);
Iterator<DexProgramClass> classIterator = classes.iterator();
@@ -490,7 +490,7 @@
private final DexClass source;
private final DexClass target;
private final VerticalClassMergerGraphLense.Builder deferredRenamings =
- new VerticalClassMergerGraphLense.Builder();
+ VerticalClassMergerGraphLense.builder(appInfo);
private boolean abortMerge = false;
private ClassMerger(DexClass source, DexClass target) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 942a857..99fb63a 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -40,47 +42,57 @@
// invocation will hit the same implementation as the original super.m() call.
//
// For the invocation "invoke-virtual A.m()" in B.m2, this graph lense will return the method B.m.
-public class VerticalClassMergerGraphLense extends GraphLense {
- private final GraphLense previousLense;
+public class VerticalClassMergerGraphLense extends NestedGraphLense {
+ private final AppInfo appInfo;
- private final Map<DexField, DexField> fieldMap;
- private final Map<DexMethod, DexMethod> methodMap;
private final Set<DexMethod> mergedMethods;
private final Map<DexType, Map<DexMethod, DexMethod>> contextualVirtualToDirectMethodMaps;
public VerticalClassMergerGraphLense(
+ AppInfo appInfo,
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
Set<DexMethod> mergedMethods,
Map<DexType, Map<DexMethod, DexMethod>> contextualVirtualToDirectMethodMaps,
GraphLense previousLense) {
- this.previousLense = previousLense;
- this.fieldMap = fieldMap;
- this.methodMap = methodMap;
+ super(ImmutableMap.of(), methodMap, fieldMap, previousLense, appInfo.dexItemFactory);
+ this.appInfo = appInfo;
this.mergedMethods = mergedMethods;
this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
}
- @Override
- public DexType lookupType(DexType type) {
- return previousLense.lookupType(type);
+ public static Builder builder(AppInfo appInfo) {
+ return new Builder(appInfo);
}
@Override
- public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context, Type type) {
+ public GraphLenseLookupResult lookupMethod(
+ DexMethod method, DexEncodedMethod context, Type type) {
assert isContextFreeForMethod(method) || (context != null && type != null);
- DexMethod previous = previousLense.lookupMethod(method, context, type);
- if (type == Type.SUPER && !mergedMethods.contains(context.method)) {
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
+ if (previous.getType() == Type.SUPER && !mergedMethods.contains(context.method)) {
Map<DexMethod, DexMethod> virtualToDirectMethodMap =
contextualVirtualToDirectMethodMaps.get(context.method.holder);
if (virtualToDirectMethodMap != null) {
- DexMethod directMethod = virtualToDirectMethodMap.get(previous);
+ DexMethod directMethod = virtualToDirectMethodMap.get(previous.getMethod());
if (directMethod != null) {
- return directMethod;
+ // If the super class A of the enclosing class B (i.e., context.method.holder)
+ // has been merged into B during vertical class merging, and this invoke-super instruction
+ // was resolving to a method in A, then the target method has been changed to a direct
+ // method and moved into B, so that we need to use an invoke-direct instruction instead of
+ // invoke-super.
+ return new GraphLenseLookupResult(directMethod, Type.DIRECT);
}
}
}
- return methodMap.getOrDefault(previous, previous);
+ return super.lookupMethod(previous.getMethod(), context, previous.getType());
+ }
+
+ @Override
+ protected Type mapInvocationType(
+ DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+ return super.mapVirtualInterfaceInvocationTypes(
+ appInfo, newMethod, originalMethod, context, type);
}
@Override
@@ -100,12 +112,6 @@
}
@Override
- public DexField lookupField(DexField field) {
- DexField previous = previousLense.lookupField(field);
- return fieldMap.getOrDefault(previous, previous);
- }
-
- @Override
public boolean isContextFreeForMethods() {
return contextualVirtualToDirectMethodMaps.isEmpty() && previousLense.isContextFreeForMethods();
}
@@ -126,6 +132,7 @@
}
public static class Builder {
+ private final AppInfo appInfo;
private final ImmutableMap.Builder<DexField, DexField> fieldMapBuilder = ImmutableMap.builder();
private final ImmutableMap.Builder<DexMethod, DexMethod> methodMapBuilder =
@@ -134,7 +141,9 @@
private final Map<DexType, Map<DexMethod, DexMethod>> contextualVirtualToDirectMethodMaps =
new HashMap<>();
- public Builder() {}
+ private Builder(AppInfo appInfo) {
+ this.appInfo = appInfo;
+ }
public GraphLense build(GraphLense previousLense) {
Map<DexField, DexField> fieldMap = fieldMapBuilder.build();
@@ -145,6 +154,7 @@
return previousLense;
}
return new VerticalClassMergerGraphLense(
+ appInfo,
fieldMap,
methodMap,
mergedMethodsBuilder.build(),