| // Copyright (c) 2023, 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.profile.art; | |
| import static com.android.tools.r8.profile.art.ArtProfileCompletenessChecker.CompletenessExceptions.ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS; | |
| import com.android.tools.r8.graph.AppView; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.graph.DexReference; | |
| import com.android.tools.r8.graph.DexType; | |
| import com.android.tools.r8.graph.ProgramDefinition; | |
| import com.android.tools.r8.graph.ProgramMethod; | |
| import com.android.tools.r8.synthesis.SyntheticItems; | |
| import com.android.tools.r8.utils.StringUtils; | |
| import com.google.common.collect.Sets; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.Set; | |
| /** | |
| * Verifies that, if given an ART profile containing all program classes and methods in the input, | |
| * then the residual ART profile also contains all classes and methods in the output. | |
| * | |
| * <p>If this check fails, either: | |
| * | |
| * <ul> | |
| * <li>The current change added new synthetics to the program that need to be added to each | |
| * profile that contains the synthesizing context, or | |
| * <li>The existing rewriting of ART profiles and inclusion of synthetics in incomplete. | |
| * </ul> | |
| * | |
| * <p>In the latter case, create a tracking bug and suppress the assertion failure by setting {@link | |
| * ArtProfileOptions#setEnableCompletenessCheckForTesting(boolean)} to false. | |
| */ | |
| public class ArtProfileCompletenessChecker { | |
| public enum CompletenessExceptions { | |
| ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS | |
| } | |
| public static boolean verify( | |
| AppView<?> appView, CompletenessExceptions... completenessExceptions) { | |
| if (appView.options().getArtProfileOptions().isCompletenessCheckForTestingEnabled()) { | |
| ArtProfile completeArtProfile = appView.getArtProfileCollection().asNonEmpty().getLast(); | |
| assert verifyProfileIsComplete( | |
| appView, completeArtProfile, Sets.newHashSet(completenessExceptions)); | |
| } | |
| return true; | |
| } | |
| private static boolean verifyProfileIsComplete( | |
| AppView<?> appView, | |
| ArtProfile artProfile, | |
| Set<CompletenessExceptions> completenessExceptions) { | |
| assert !appView.getSyntheticItems().hasPendingSyntheticClasses(); | |
| List<DexReference> missing = new ArrayList<>(); | |
| for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) { | |
| if (appView.horizontallyMergedClasses().hasBeenMergedIntoDifferentType(clazz.getType()) | |
| || (appView.hasVerticallyMergedClasses() | |
| && appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(clazz.getType())) | |
| || appView.unboxedEnums().isUnboxedEnum(clazz)) { | |
| continue; | |
| } | |
| if (!artProfile.containsClassRule(clazz.getType())) { | |
| recordMissingDefinition(appView, clazz, completenessExceptions, missing); | |
| } | |
| for (ProgramMethod method : clazz.programMethods()) { | |
| if (!artProfile.containsMethodRule(method.getReference())) { | |
| recordMissingDefinition(appView, method, completenessExceptions, missing); | |
| } | |
| } | |
| } | |
| if (!missing.isEmpty()) { | |
| String message = | |
| StringUtils.join(System.lineSeparator(), missing, DexReference::toSmaliString); | |
| assert false : message; | |
| } | |
| return true; | |
| } | |
| private static void recordMissingDefinition( | |
| AppView<?> appView, | |
| ProgramDefinition definition, | |
| Set<CompletenessExceptions> completenessExceptions, | |
| List<DexReference> missing) { | |
| if (completenessExceptions.contains(ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS)) { | |
| DexType contextType = definition.getContextType(); | |
| SyntheticItems syntheticItems = appView.getSyntheticItems(); | |
| if (syntheticItems.isSynthetic(contextType)) { | |
| if (syntheticItems.isSyntheticOfKind( | |
| contextType, naming -> naming.ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD) | |
| || syntheticItems.isSyntheticOfKind( | |
| contextType, naming -> naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS) | |
| || syntheticItems.isSyntheticOfKind( | |
| contextType, naming -> naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS)) { | |
| return; | |
| } | |
| } | |
| } | |
| missing.add(definition.getReference()); | |
| } | |
| } |