blob: 7651e51a5529c08b50fcc8d5b0862c2afb393865 [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.ir.desugar;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DesugaredLibraryEmulatedInterfaceDuplicator {
final AppView<?> appView;
final Map<DexType, DexType> emulatedInterfaces;
public DesugaredLibraryEmulatedInterfaceDuplicator(AppView<?> appView) {
this.appView = appView;
emulatedInterfaces =
appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
}
public void duplicateEmulatedInterfaces() {
// All classes implementing an emulated interface now implements the interface and the
// emulated one, as well as hidden overrides, for correct emulated dispatch.
// We not that duplicated interfaces won't feature the correct type parameters in the
// class signature since such signature is expected to be unused.
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (clazz.type == appView.dexItemFactory().objectType) {
continue;
}
if (emulatedInterfaces.containsKey(clazz.type)) {
transformEmulatedInterfaces(clazz);
} else {
duplicateEmulatedInterfaces(clazz);
}
}
}
private void transformEmulatedInterfaces(DexProgramClass clazz) {
List<ClassTypeSignature> newInterfaces = new ArrayList<>();
GenericSignature.ClassSignature classSignature = clazz.getClassSignature(appView);
for (int i = 0; i < clazz.interfaces.size(); i++) {
DexType itf = clazz.interfaces.values[i];
assert emulatedInterfaces.containsKey(itf);
List<FieldTypeSignature> typeArguments;
if (classSignature.hasNoSignature()) {
typeArguments = Collections.emptyList();
} else {
ClassTypeSignature classTypeSignature = classSignature.superInterfaceSignatures().get(i);
assert itf == classTypeSignature.type();
typeArguments = classTypeSignature.typeArguments();
}
newInterfaces.add(new ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
}
clazz.replaceInterfaces(newInterfaces, appView);
}
private void duplicateEmulatedInterfaces(DexProgramClass clazz) {
List<DexType> extraInterfaces = new ArrayList<>();
LinkedList<DexClass> workList = new LinkedList<>();
Set<DexType> processed = Sets.newIdentityHashSet();
workList.add(clazz);
while (!workList.isEmpty()) {
DexClass dexClass = workList.removeFirst();
if (processed.contains(dexClass.type)) {
continue;
}
processed.add(dexClass.type);
if (dexClass.superType != appView.dexItemFactory().objectType) {
processSuperType(clazz.superType, extraInterfaces, workList);
}
for (DexType itf : dexClass.interfaces.values) {
processSuperType(itf, extraInterfaces, workList);
}
}
extraInterfaces = removeDuplicates(extraInterfaces);
List<ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
for (DexType extraInterface : extraInterfaces) {
extraInterfaceSignatures.add(new ClassTypeSignature(extraInterface));
}
clazz.addExtraInterfaces(extraInterfaceSignatures, appView);
}
private List<DexType> removeDuplicates(List<DexType> extraInterfaces) {
if (extraInterfaces.size() <= 1) {
return extraInterfaces;
}
// TODO(b/161399032): It would be nice to remove duplicate based on inheritance, i.e.,
// if there is ConcurrentMap<K,V> and Map<K,V>, Map<K,V> can be removed.
return new ArrayList<>(new HashSet<>(extraInterfaces));
}
void processSuperType(
DexType superType, List<DexType> extraInterfaces, LinkedList<DexClass> workList) {
if (emulatedInterfaces.containsKey(superType)) {
extraInterfaces.add(emulatedInterfaces.get(superType));
} else {
DexClass superClass = appView.definitionFor(superType);
if (shouldProcessSuperclass(superClass)) {
workList.add(superClass);
}
}
}
private boolean shouldProcessSuperclass(DexClass superclazz) {
if (appView.options().isDesugaredLibraryCompilation()) {
return false;
}
// TODO(b/161399032): Pay-as-you-go design: stop duplication on library boundaries.
return superclazz != null && superclazz.isLibraryClass();
}
}