blob: 48a01f81fa297aa1ba3b6ce87a78a6e3fbcd48f3 [file] [log] [blame]
// Copyright (c) 2020, 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.optimize;
import com.android.tools.r8.graph.AbstractAccessContexts.ConcreteAccessContexts;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class MemberRebindingIdentityLensFactory {
/**
* In order to construct an instance of {@link MemberRebindingIdentityLens} we need a mapping from
* non-rebound field and method references to their definitions.
*
* <p>If shrinking or minification is enabled, we retrieve these from {@link AppInfoWithLiveness}.
* Otherwise we apply the {@link NonReboundMemberReferencesRegistry} below to all code objects to
* compute the mapping.
*/
public static MemberRebindingIdentityLens create(
AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService)
throws ExecutionException {
TestingOptions testingOptions = appView.options().testing;
FieldAccessInfoCollection<?> fieldAccessInfoCollection =
appView.appInfo().hasLiveness()
&& testingOptions.alwaysUseExistingFieldAccessInfoCollectionInMemberRebinding
? appView.appInfo().withLiveness().getFieldAccessInfoCollection()
: createFieldAccessInfoCollectionForMemberRebinding(appView, executorService);
return create(fieldAccessInfoCollection, appView.graphLens());
}
public static MemberRebindingIdentityLens create(
FieldAccessInfoCollection<?> fieldAccessInfoCollection, GraphLens previousLens) {
MemberRebindingIdentityLens.Builder builder = MemberRebindingIdentityLens.builder();
fieldAccessInfoCollection.forEach(builder::recordNonReboundFieldAccesses);
return builder.build(previousLens);
}
/**
* Applies {@link NonReboundMemberReferencesRegistry} to all code objects to construct a mapping
* from non-rebound field references to their definition.
*/
private static FieldAccessInfoCollection<?> createFieldAccessInfoCollectionForMemberRebinding(
AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService)
throws ExecutionException {
NonReboundMemberReferencesRegistry registry = new NonReboundMemberReferencesRegistry(appView);
ThreadUtils.processItems(appView.appInfo()::forEachMethod, registry::accept, executorService);
return registry.getFieldAccessInfoCollection();
}
private static class NonReboundMemberReferencesRegistry extends UseRegistry {
private final AppInfoWithClassHierarchy appInfo;
private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
new FieldAccessInfoCollectionImpl(new ConcurrentHashMap<>());
private final Set<DexField> seenFieldReferences = Sets.newConcurrentHashSet();
FieldAccessInfoCollection<?> getFieldAccessInfoCollection() {
return fieldAccessInfoCollection;
}
public NonReboundMemberReferencesRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView) {
super(appView.dexItemFactory());
this.appInfo = appView.appInfo();
}
@Override
public void registerInstanceFieldRead(DexField field) {
registerFieldAccess(field);
}
@Override
public void registerInstanceFieldWrite(DexField field) {
registerFieldAccess(field);
}
@Override
public void registerStaticFieldRead(DexField field) {
registerFieldAccess(field);
}
@Override
public void registerStaticFieldWrite(DexField field) {
registerFieldAccess(field);
}
private void registerFieldAccess(DexField field) {
if (!seenFieldReferences.add(field)) {
return;
}
SuccessfulFieldResolutionResult resolutionResult =
appInfo.resolveField(field).asSuccessfulResolution();
if (resolutionResult == null) {
return;
}
DexField reboundReference = resolutionResult.getResolvedField().toReference();
if (field == reboundReference) {
// For the purpose of member rebinding, we don't care about already rebound references.
return;
}
FieldAccessInfoImpl fieldAccessInfo =
fieldAccessInfoCollection.computeIfAbsent(reboundReference, FieldAccessInfoImpl::new);
synchronized (fieldAccessInfo) {
// Record the fact that there is a non-rebound access to the given field. We don't
// distinguish between non-rebound reads and writes, so we just record it as a read.
ConcreteAccessContexts accessContexts =
fieldAccessInfo.getReadsWithContexts().isConcrete()
? fieldAccessInfo.getReadsWithContexts().asConcrete()
: new ConcreteAccessContexts();
// For the purpose of member rebinding, we don't care about the access contexts, so we
// simply use the empty set.
accessContexts.getAccessesWithContexts().put(field, ProgramMethodSet.empty());
}
}
@Override
public void registerInvokeVirtual(DexMethod method) {
// Intentionally empty.
}
@Override
public void registerInvokeDirect(DexMethod method) {
// Intentionally empty.
}
@Override
public void registerInvokeStatic(DexMethod method) {
// Intentionally empty.
}
@Override
public void registerInvokeInterface(DexMethod method) {
// Intentionally empty.
}
@Override
public void registerInvokeSuper(DexMethod method) {
// Intentionally empty.
}
@Override
public void registerInitClass(DexType type) {
// Intentionally empty.
}
@Override
public void registerNewInstance(DexType type) {
// Intentionally empty.
}
@Override
public void registerTypeReference(DexType type) {
// Intentionally empty.
}
@Override
public void registerInstanceOf(DexType type) {
// Intentionally empty.
}
}
}