blob: 1243e98f8e44eeefba8525aded380b6bf8f8a45c [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.DexClass;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
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;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
/**
* In Dalvik it is a verification error to read and use a field of type Missing[].
*
* <p>Example:
*
* <pre>
* Consumer<?>[] consumer = this.field;
* acceptConsumer(consumer); // acceptConsumer(Consumer[])
* </pre>
*
* <p>To avoid that the compiler moves such code into other contexts (e.g., as a result of inlining
* or class merging), and thereby causes new classes to no longer verify on Dalvik, we soft-pin
* methods that reads such fields.
*/
public class GetArrayOfMissingTypeVerifyErrorWorkaround implements EnqueuerFieldAccessAnalysis {
private final DexItemFactory dexItemFactory;
private final Enqueuer enqueuer;
private final Set<DexType> knownToBePresentOnDalvik;
public GetArrayOfMissingTypeVerifyErrorWorkaround(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
this.dexItemFactory = appView.dexItemFactory();
this.enqueuer = enqueuer;
this.knownToBePresentOnDalvik =
ImmutableSet.<DexType>builder()
.add(dexItemFactory.boxedBooleanType)
.add(dexItemFactory.boxedByteType)
.add(dexItemFactory.boxedCharType)
.add(dexItemFactory.boxedDoubleType)
.add(dexItemFactory.boxedFloatType)
.add(dexItemFactory.boxedIntType)
.add(dexItemFactory.boxedLongType)
.add(dexItemFactory.boxedShortType)
.add(dexItemFactory.classType)
.add(dexItemFactory.objectType)
.add(dexItemFactory.enumType)
.add(dexItemFactory.stringType)
.build();
}
public static void register(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
if (!isNoop(appView)) {
enqueuer.registerFieldAccessAnalysis(
new GetArrayOfMissingTypeVerifyErrorWorkaround(appView, enqueuer));
}
}
private static boolean isNoop(AppView<? extends AppInfoWithClassHierarchy> appView) {
InternalOptions options = appView.options();
return options.isGeneratingDex()
&& options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
@Override
public void traceInstanceFieldRead(
DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
if (isUnsafeToUseFieldOnDalvik(field, context)) {
enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
}
}
@Override
public void traceStaticFieldRead(
DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
if (isUnsafeToUseFieldOnDalvik(field, context)) {
enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
}
}
private boolean isUnsafeToUseFieldOnDalvik(DexField field, ProgramMethod context) {
DexType fieldType = field.getType();
if (!fieldType.isArrayType()) {
return false;
}
DexType baseType = fieldType.toBaseType(dexItemFactory);
if (!baseType.isClassType()) {
return false;
}
if (knownToBePresentOnDalvik.contains(baseType)) {
return false;
}
// TODO(b/206891715): Use the API database to determine if the given type is introduced in API
// level L or later.
DexClass baseClass = enqueuer.definitionFor(baseType, context);
return baseClass != null && baseClass.isLibraryClass();
}
@Override
public void traceInstanceFieldWrite(
DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
// Intentionally empty.
}
@Override
public void traceStaticFieldWrite(
DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
// Intentionally empty.
}
}