blob: cc74d21a933712344559d9b9c8e5a3ab21ed168a [file] [log] [blame]
// 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));
}
}