| // Copyright (c) 2019, 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.DexProgramClass; | 
 | import com.android.tools.r8.graph.DexType; | 
 | import com.android.tools.r8.graph.ProgramMethod; | 
 | import com.android.tools.r8.ir.analysis.type.ClassTypeElement; | 
 | import com.android.tools.r8.shaking.Enqueuer; | 
 | import java.util.IdentityHashMap; | 
 | import java.util.Map; | 
 |  | 
 | public class InitializedClassesInInstanceMethodsAnalysis extends EnqueuerAnalysis { | 
 |  | 
 |   // A simple structure that stores the result of the analysis. | 
 |   public static class InitializedClassesInInstanceMethods { | 
 |  | 
 |     private final AppView<? extends AppInfoWithClassHierarchy> appView; | 
 |     private final Map<DexType, DexType> mapping; | 
 |  | 
 |     private InitializedClassesInInstanceMethods( | 
 |         AppView<? extends AppInfoWithClassHierarchy> appView, Map<DexType, DexType> mapping) { | 
 |       this.appView = appView; | 
 |       this.mapping = mapping; | 
 |     } | 
 |  | 
 |     public boolean isClassDefinitelyLoadedInInstanceMethod( | 
 |         DexProgramClass subject, ProgramMethod context) { | 
 |       assert !context.getDefinition().isStatic(); | 
 |       // If `subject` is kept, then it is instantiated by reflection, which means that the analysis | 
 |       // has not seen all allocation sites. In that case, we conservatively return false. | 
 |       AppInfoWithClassHierarchy appInfo = appView.appInfo(); | 
 |       if (appInfo.hasLiveness() && appInfo.withLiveness().isPinned(subject.type)) { | 
 |         return false; | 
 |       } | 
 |  | 
 |       // Check that `subject` is guaranteed to be initialized in all instance methods of `context`. | 
 |       DexType guaranteedToBeInitializedInContext = | 
 |           mapping.getOrDefault(context.getHolderType(), appView.dexItemFactory().objectType); | 
 |       if (!appInfo.isSubtype(guaranteedToBeInitializedInContext, subject.type)) { | 
 |         return false; | 
 |       } | 
 |  | 
 |       // Also check that `subject` is not an interface, since interfaces are not initialized | 
 |       // transitively. | 
 |       return !subject.isInterface(); | 
 |     } | 
 |   } | 
 |  | 
 |   private final AppView<? extends AppInfoWithClassHierarchy> appView; | 
 |  | 
 |   // If the mapping contains an entry `X -> Y`, then the type Y is guaranteed to be initialized in | 
 |   // all instance methods of X. | 
 |   private final Map<DexType, DexType> mapping = new IdentityHashMap<>(); | 
 |  | 
 |   public InitializedClassesInInstanceMethodsAnalysis( | 
 |       AppView<? extends AppInfoWithClassHierarchy> appView) { | 
 |     this.appView = appView; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) { | 
 |     DexType key = clazz.type; | 
 |     DexType objectType = appView.dexItemFactory().objectType; | 
 |     if (context == null) { | 
 |       // Record that we don't know anything about the set of classes that are guaranteed to be | 
 |       // initialized in the instance methods of `clazz`. | 
 |       mapping.put(key, objectType); | 
 |       return; | 
 |     } | 
 |  | 
 |     // Record that the enclosing class is guaranteed to be initialized at the allocation site. | 
 |     AppInfoWithClassHierarchy appInfo = appView.appInfo(); | 
 |     DexType guaranteedToBeInitialized = context.getHolderType(); | 
 |     DexType existingGuaranteedToBeInitialized = | 
 |         mapping.getOrDefault(key, guaranteedToBeInitialized); | 
 |     mapping.put( | 
 |         key, | 
 |         ClassTypeElement.computeLeastUpperBoundOfClasses( | 
 |             appInfo, guaranteedToBeInitialized, existingGuaranteedToBeInitialized)); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public void done(Enqueuer enqueuer) { | 
 |     appView.setInitializedClassesInInstanceMethods( | 
 |         new InitializedClassesInInstanceMethods(appView, mapping)); | 
 |   } | 
 | } |