|  | // 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.androidapi.AndroidApiReferenceLevelCache; | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.DexClassAndMember; | 
|  | import com.android.tools.r8.graph.LookupTarget; | 
|  | import com.android.tools.r8.graph.ProgramDefinition; | 
|  | import com.android.tools.r8.graph.ProgramField; | 
|  | import com.android.tools.r8.graph.ProgramMethod; | 
|  | import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry; | 
|  | import com.android.tools.r8.utils.AndroidApiLevel; | 
|  |  | 
|  | public class ApiModelAnalysis extends EnqueuerAnalysis { | 
|  |  | 
|  | private final AppView<?> appView; | 
|  | private final AndroidApiLevel minApiLevel; | 
|  | private final AndroidApiReferenceLevelCache referenceLevelCache; | 
|  |  | 
|  | public ApiModelAnalysis(AppView<?> appView, AndroidApiReferenceLevelCache referenceLevelCache) { | 
|  | this.appView = appView; | 
|  | this.minApiLevel = appView.options().minApiLevel; | 
|  | this.referenceLevelCache = referenceLevelCache; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void processNewlyLiveField(ProgramField field, ProgramDefinition context) { | 
|  | computeApiLevelForDefinition(field); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) { | 
|  | computeApiLevelForDefinition(method); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) { | 
|  | assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel); | 
|  | if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) { | 
|  | appView | 
|  | .options() | 
|  | .apiModelingOptions() | 
|  | .tracedMethodApiLevelCallback | 
|  | .accept(method.getMethodReference(), registry.getMaxApiReferenceLevel()); | 
|  | } | 
|  | computeApiLevelForDefinition(method); | 
|  | method.getDefinition().setApiLevelForCode(registry.getMaxApiReferenceLevel()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void notifyMarkMethodAsTargeted(ProgramMethod method) { | 
|  | computeApiLevelForDefinition(method); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void notifyMarkFieldAsReachable(ProgramField field) { | 
|  | computeApiLevelForDefinition(field); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) { | 
|  | target.accept( | 
|  | this::computeApiLevelForDefinition, | 
|  | lookupLambdaTarget -> { | 
|  | // The implementation method will be assigned an api level when visited. | 
|  | }); | 
|  | } | 
|  |  | 
|  | private void computeApiLevelForDefinition(DexClassAndMember<?, ?> member) { | 
|  | member | 
|  | .getDefinition() | 
|  | .setApiLevelForDefinition( | 
|  | member | 
|  | .getReference() | 
|  | .computeApiLevelForReferencedTypes(appView, referenceLevelCache::lookupMax)); | 
|  | } | 
|  | } |