blob: b589aa4a1d8973771b3ce66b899d9534f72a509a [file] [log] [blame]
// Copyright (c) 2017, 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.naming;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import java.util.Map;
import java.util.function.Function;
class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
FieldNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
super(appInfo, rootSet, options);
}
@Override
Function<DexType, ?> getKeyTransform() {
if (overloadAggressively) {
// Use the type as the key, hence reuse names per type.
return a -> a;
} else {
// Always use the same key, hence do not reuse names per type.
return a -> Void.class;
}
}
Map<DexField, DexString> computeRenaming(Timing timing) {
// Reserve names in all classes first. We do this in subtyping order so we do not
// shadow a reserved field in subclasses. While there is no concept of virtual field
// dispatch in Java, field resolution still traverses the super type chain and external
// code might use a subtype to reference the field.
timing.begin("reserve-classes");
reserveNamesInSubtypes(appInfo.dexItemFactory.objectType, globalState);
timing.end();
// Next, reserve field names in interfaces. These should only be static.
timing.begin("reserve-interfaces");
DexType.forAllInterfaces(appInfo.dexItemFactory,
iface -> reserveNamesInSubtypes(iface, globalState));
timing.end();
// Rename the definitions.
timing.begin("rename-definitions");
renameFieldsInSubtypes(appInfo.dexItemFactory.objectType);
DexType.forAllInterfaces(appInfo.dexItemFactory, this::renameFieldsInSubtypes);
timing.end();
// Rename the references that are not rebound to definitions for some reasons.
timing.begin("rename-references");
renameNonReboundReferences();
timing.end();
return renaming;
}
private void reserveNamesInSubtypes(DexType type, NamingState<DexType, ?> state) {
DexClass holder = appInfo.definitionFor(type);
if (holder == null) {
return;
}
NamingState<DexType, ?> newState = computeStateIfAbsent(type, t -> state.createChild());
holder.forEachField(field -> reserveFieldName(field, newState, holder.isLibraryClass()));
type.forAllExtendsSubtypes(subtype -> reserveNamesInSubtypes(subtype, newState));
}
private void reserveFieldName(
DexEncodedField encodedField,
NamingState<DexType, ?> state,
boolean isLibrary) {
if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
DexField field = encodedField.field;
state.reserveName(field.name, field.type);
}
}
private void renameFieldsInSubtypes(DexType type) {
DexClass clazz = appInfo.definitionFor(type);
if (clazz == null) {
return;
}
NamingState<DexType, ?> state = getState(clazz.type);
assert state != null;
clazz.forEachField(field -> renameField(field, state));
type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
}
private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
DexField field = encodedField.field;
if (!state.isReserved(field.name, field.type)) {
renaming.put(
field,
state.assignNewNameFor(field.name, field.type, useUniqueMemberNames));
}
}
private void renameNonReboundReferences() {
// TODO(b/123068484): Collect non-rebound references instead of visiting all references.
Sets.union(
Sets.union(appInfo.staticFieldReads.keySet(), appInfo.staticFieldWrites.keySet()),
Sets.union(appInfo.instanceFieldReads.keySet(), appInfo.instanceFieldWrites.keySet()))
.forEach(this::renameNonReboundReference);
}
private void renameNonReboundReference(DexField field) {
// Already renamed
if (renaming.containsKey(field)) {
return;
}
DexEncodedField definition = appInfo.definitionFor(field);
if (definition != null) {
assert definition.field == field;
return;
}
// Now, `field` is reference. Find its definition and check if it's renamed.
DexType holderType = field.getHolder();
DexClass holder = appInfo.definitionFor(holderType);
// We don't care pruned types or library classes.
if (holder == null || holder.isLibraryClass()) {
return;
}
definition = appInfo.resolveFieldOn(holderType, field);
if (definition == null) {
// The program is already broken in the sense that it has an unresolvable field reference.
// Leave it as-is.
return;
}
assert definition.field != field;
assert definition.field.getHolder() != holderType;
// If the definition is renamed,
if (renaming.containsKey(definition.field)) {
// Assign the same, renamed name as the definition to the reference.
renaming.put(field, renaming.get(definition.field));
}
}
}