blob: 28075e6d04b779fd90241688b04172ee4cce8013 [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.utils.codeinspector.analysis;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.Sets;
import java.util.Set;
import java.util.function.Function;
public class ProtoApplicationStats {
private static final String EXTENDABLE_MESSAGE_TYPE =
"com.google.protobuf.GeneratedMessageLite$ExtendableMessage";
private static final String EXTENSION_REGISTRY_LITE_TYPE =
"com.google.protobuf.ExtensionRegistryLite";
private static final String GENERATED_EXTENSION_TYPE =
"com.google.protobuf.GeneratedMessageLite$GeneratedExtension";
private static final String GENERATED_MESSAGE_LITE_TYPE =
"com.google.protobuf.GeneratedMessageLite";
private static final String GENERATED_MESSAGE_LITE_BUILDER_TYPE =
GENERATED_MESSAGE_LITE_TYPE + "$Builder";
private static final String GENERATED_MESSAGE_LITE_EXTENDABLE_BUILDER_TYPE =
GENERATED_MESSAGE_LITE_TYPE + "$ExtendableBuilder";
abstract static class Stats {
static <T> String progress(T actual, T baseline, T original, Function<T, Set<?>> fn) {
StringBuilder builder = new StringBuilder();
if (original != null) {
builder.append(fn.apply(original).size()).append(" -> ");
}
Set<?> actualSet = fn.apply(actual);
builder.append(actualSet.size());
if (baseline != null) {
Set<?> baselineSet = fn.apply(baseline);
builder
.append(" (unfulfilled potential: ")
.append(Sets.difference(actualSet, baselineSet).size())
.append(", improvement over baseline: ")
.append(Sets.difference(baselineSet, actualSet).size())
.append(" )");
}
return builder.toString();
}
}
class EnumStats extends Stats {
final Set<DexType> enums = Sets.newIdentityHashSet();
String getStats(EnumStats baseline, EnumStats original) {
return StringUtils.lines(
"Enum stats:", " # enums: " + progress(this, baseline, original, x -> x.enums));
}
}
class ProtoBuilderStats extends Stats {
final Set<DexType> builders = Sets.newIdentityHashSet();
String getStats(ProtoBuilderStats baseline, ProtoBuilderStats original) {
return StringUtils.lines(
"Proto builder stats:",
" # builders: " + progress(this, baseline, original, x -> x.builders));
}
}
class ProtoMessageStats extends Stats {
final boolean extendable;
ProtoMessageStats(boolean extendable) {
this.extendable = extendable;
}
final Set<DexType> messages = Sets.newIdentityHashSet();
final Set<DexField> bitFields = Sets.newIdentityHashSet();
final Set<DexField> nonBitFields = Sets.newIdentityHashSet();
String getStats(ProtoMessageStats baseline, ProtoMessageStats original) {
return StringUtils.lines(
extendable ? "Extendable proto message stats" : "Proto message stats:",
" # messages: " + progress(this, baseline, original, x -> x.messages),
" # bit fields: " + progress(this, baseline, original, x -> x.bitFields),
" # non-bit fields: " + progress(this, baseline, original, x -> x.nonBitFields));
}
}
public class GeneratedExtensionRegistryStats extends Stats {
final Set<DexMethod> findLiteExtensionByNumberMethods = Sets.newIdentityHashSet();
final Set<DexField> retainedExtensionFields = Sets.newIdentityHashSet();
final Set<DexField> spuriouslyRetainedExtensionFields = Sets.newIdentityHashSet();
public Set<DexField> getSpuriouslyRetainedExtensionFields() {
return spuriouslyRetainedExtensionFields;
}
String getStats(
GeneratedExtensionRegistryStats baseline, GeneratedExtensionRegistryStats original) {
return StringUtils.lines(
"Generated extension registry stats:",
" # findLiteExtensionByNumber() methods: "
+ progress(this, baseline, original, x -> x.findLiteExtensionByNumberMethods),
" # retained extensions: "
+ progress(this, baseline, original, x -> x.retainedExtensionFields),
" # spuriously retained extension fields: " + spuriouslyRetainedExtensionFields.size());
}
}
private final DexItemFactory dexItemFactory;
private final CodeInspector inspector;
private final ProtoApplicationStats original;
private final EnumStats enumStats = new EnumStats();
private final ProtoMessageStats extendableProtoMessageStats = new ProtoMessageStats(true);
private final GeneratedExtensionRegistryStats generatedExtensionRegistryStats =
new GeneratedExtensionRegistryStats();
private final ProtoBuilderStats protoBuilderStats = new ProtoBuilderStats();
private final ProtoMessageStats protoMessageStats = new ProtoMessageStats(false);
public ProtoApplicationStats(DexItemFactory dexItemFactory, CodeInspector inspector) {
this(dexItemFactory, inspector, null);
}
public ProtoApplicationStats(
DexItemFactory dexItemFactory, CodeInspector inspector, ProtoApplicationStats original) {
this.dexItemFactory = dexItemFactory;
this.inspector = inspector;
this.original = original;
computeStats();
}
private void computeStats() {
for (FoundClassSubject classSubject : inspector.allClasses()) {
DexType originalType = classSubject.getOriginalDexType(dexItemFactory);
if (classSubject.getDexClass().isEnum()) {
enumStats.enums.add(originalType);
}
ClassSubject superClassSubject = classSubject.getSuperClass();
if (!superClassSubject.isPresent()) {
continue;
}
ProtoMessageStats messageStats = null;
switch (superClassSubject.getOriginalName()) {
case GENERATED_MESSAGE_LITE_TYPE:
messageStats = protoMessageStats;
break;
case EXTENDABLE_MESSAGE_TYPE:
messageStats = extendableProtoMessageStats;
break;
case GENERATED_MESSAGE_LITE_BUILDER_TYPE:
case GENERATED_MESSAGE_LITE_EXTENDABLE_BUILDER_TYPE:
protoBuilderStats.builders.add(
dexItemFactory.createType(classSubject.getOriginalDescriptor()));
break;
case EXTENSION_REGISTRY_LITE_TYPE:
for (FoundMethodSubject methodSubject : classSubject.allMethods()) {
String originalMethodName = methodSubject.getOriginalName(false);
if (originalMethodName.startsWith("findLiteExtensionByNumber")) {
generatedExtensionRegistryStats.findLiteExtensionByNumberMethods.add(
methodSubject.getOriginalDexMethod(dexItemFactory));
for (InstructionSubject instruction :
methodSubject.instructions(InstructionSubject::isStaticGet)) {
DexField field = instruction.getField();
FoundClassSubject typeClassSubject =
inspector.clazz(field.type.toSourceString()).asFoundClassSubject();
if (!typeClassSubject.getOriginalName().equals(GENERATED_EXTENSION_TYPE)) {
continue;
}
FoundClassSubject extensionClassSubject =
inspector.clazz(field.holder.toSourceString()).asFoundClassSubject();
FoundFieldSubject extensionFieldSubject =
extensionClassSubject
.uniqueFieldWithFinalName(field.name.toSourceString())
.asFoundFieldSubject();
generatedExtensionRegistryStats.retainedExtensionFields.add(
extensionFieldSubject.getOriginalDexField(dexItemFactory));
}
}
}
break;
}
if (messageStats != null) {
messageStats.messages.add(originalType);
for (FoundFieldSubject fieldSubject : classSubject.allInstanceFields()) {
String originalFieldName = fieldSubject.getOriginalName(false);
if (originalFieldName.startsWith("bitField")) {
messageStats.bitFields.add(fieldSubject.getOriginalDexField(dexItemFactory));
} else {
messageStats.nonBitFields.add(fieldSubject.getOriginalDexField(dexItemFactory));
}
}
}
}
if (original != null) {
for (DexField extensionField :
original.generatedExtensionRegistryStats.retainedExtensionFields) {
if (generatedExtensionRegistryStats.retainedExtensionFields.contains(extensionField)) {
continue;
}
ClassSubject classSubject = inspector.clazz(extensionField.holder.toSourceString());
if (!classSubject.isPresent()) {
continue;
}
FieldSubject fieldSubject =
classSubject.uniqueFieldWithName(extensionField.name.toSourceString());
if (fieldSubject.isPresent()) {
generatedExtensionRegistryStats.spuriouslyRetainedExtensionFields.add(extensionField);
}
}
}
}
public GeneratedExtensionRegistryStats getGeneratedExtensionRegistryStats() {
return generatedExtensionRegistryStats;
}
public String getStats() {
return StringUtils.lines(
enumStats.getStats(null, original.enumStats),
protoMessageStats.getStats(null, original.protoMessageStats),
extendableProtoMessageStats.getStats(null, original.extendableProtoMessageStats),
protoBuilderStats.getStats(null, original.protoBuilderStats),
generatedExtensionRegistryStats.getStats(null, original.generatedExtensionRegistryStats));
}
public String getStats(ProtoApplicationStats baseline) {
return StringUtils.lines(
enumStats.getStats(baseline.enumStats, original.enumStats),
protoMessageStats.getStats(baseline.protoMessageStats, original.protoMessageStats),
extendableProtoMessageStats.getStats(
baseline.extendableProtoMessageStats, original.extendableProtoMessageStats),
protoBuilderStats.getStats(baseline.protoBuilderStats, original.protoBuilderStats),
generatedExtensionRegistryStats.getStats(
baseline.generatedExtensionRegistryStats, original.generatedExtensionRegistryStats));
}
}