blob: 7c17e4cc2b3704549d7316542232c4a48f8e5f08 [file] [log] [blame]
// Copyright (c) 2021, 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.analysis;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
/**
* Workaround for situations where an interface in the framework has been changed into a class. When
* compiling against a recent android.jar, virtual invokes to such classes may resolve to interface
* methods on older API levels, which can lead to verification errors (see b/206891715).
*
* <p>To mitigate this issue, we pin methods that have virtual invokes to such classes. This ensures
* that we don't move these virtual invokes elsewhere (e.g., due to inlining or class merging),
* which would otherwise lead to verification errors in other classes.
*/
// TODO(b/206891715): This only mitigates the issue. The user may still need to manually outline
// virtual invokes to classes that was once an interface. To avoid this in general (including D8)
// the compiler should outline the problematic invokes.
public class InvokeVirtualToInterfaceVerifyErrorWorkaround implements EnqueuerInvokeAnalysis {
private final DexType androidHardwareCamera2CameraDeviceType;
private final Enqueuer enqueuer;
private final InternalOptions options;
public InvokeVirtualToInterfaceVerifyErrorWorkaround(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
this.androidHardwareCamera2CameraDeviceType =
appView.dexItemFactory().createType("Landroid/hardware/camera2/CameraDevice;");
this.enqueuer = enqueuer;
this.options = appView.options();
}
public static void register(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
if (!isNoop(appView)) {
enqueuer.registerInvokeAnalysis(
new InvokeVirtualToInterfaceVerifyErrorWorkaround(appView, enqueuer));
}
}
private static boolean isNoop(AppView<? extends AppInfoWithClassHierarchy> appView) {
return appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
@Override
public void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
if (isInterfaceInSomeApiLevel(invokedMethod.getHolderType())) {
enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
}
}
private boolean isInterfaceInSomeApiLevel(DexType type) {
// CameraDevice was added as a class in API 21 (L), but was defined as an interface in the
// framework before then.
return type == androidHardwareCamera2CameraDeviceType
&& (options.isGeneratingClassFiles()
|| options.getMinApiLevel().isLessThan(AndroidApiLevel.L));
}
@Override
public void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
// Intentionally empty.
}
@Override
public void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
// Intentionally empty.
}
@Override
public void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
// Intentionally empty.
}
@Override
public void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
// Intentionally empty.
}
}