blob: 7ecd5271424fce0f44628917c4f942f22be17ae9 [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.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
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 AppInfoWithSubtyping> appView;
private final Map<DexType, DexType> mapping;
private InitializedClassesInInstanceMethods(
AppView<? extends AppInfoWithSubtyping> appView, Map<DexType, DexType> mapping) {
this.appView = appView;
this.mapping = mapping;
}
public boolean isClassDefinitelyLoadedInInstanceMethodsOn(DexType subject, DexType context) {
// 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.
AppInfoWithSubtyping appInfo = appView.appInfo();
if (appInfo.hasLiveness() && appInfo.withLiveness().isPinned(subject)) {
return false;
}
// Check that `subject` is guaranteed to be initialized in all instance methods of `context`.
DexType guaranteedToBeInitializedInContext =
mapping.getOrDefault(context, appView.dexItemFactory().objectType);
if (!appInfo.isSubtype(guaranteedToBeInitializedInContext, subject)) {
return false;
}
// Also check that `subject` is not an interface, since interfaces are not initialized
// transitively.
DexClass clazz = appView.definitionFor(subject);
return clazz != null && !clazz.isInterface();
}
}
private final AppView<? extends AppInfoWithSubtyping> 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 AppInfoWithSubtyping> appView) {
this.appView = appView;
}
@Override
public void processNewlyInstantiatedClass(DexProgramClass clazz, DexEncodedMethod 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.
AppInfoWithSubtyping appInfo = appView.appInfo();
DexType guaranteedToBeInitialized = context.holder();
DexType existingGuaranteedToBeInitialized =
mapping.getOrDefault(key, guaranteedToBeInitialized);
mapping.put(
key,
ClassTypeElement.computeLeastUpperBoundOfClasses(
appInfo, guaranteedToBeInitialized, existingGuaranteedToBeInitialized));
}
@Override
public void done() {
appView.setInitializedClassesInInstanceMethods(
new InitializedClassesInInstanceMethods(appView, mapping));
}
}