blob: e670925049328f7f981e2778edee13e556b74085 [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 static com.android.tools.r8.ir.optimize.ReflectionOptimizer.computeClassName;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfDexItemBasedConstString;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.DexItemBasedConstString;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexItemBasedValueString;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardClassFilter;
import java.util.List;
import java.util.Map;
/**
* Replaces all instances of DexItemBasedConstString by ConstString, and all instances of
* DexItemBasedValueString by DexValueString.
*/
class IdentifierMinifier {
private final AppInfoWithLiveness appInfo;
private final ProguardClassFilter adaptClassStrings;
private final NamingLens lens;
IdentifierMinifier(
AppInfoWithLiveness appInfo, ProguardClassFilter adaptClassStrings, NamingLens lens) {
this.appInfo = appInfo;
this.adaptClassStrings = adaptClassStrings;
this.lens = lens;
}
void run() {
if (!adaptClassStrings.isEmpty()) {
adaptClassStrings();
}
replaceDexItemBasedConstString();
}
private void adaptClassStrings() {
for (DexProgramClass clazz : appInfo.classes()) {
if (adaptClassStrings.matches(clazz.type)) {
for (DexEncodedField field : clazz.staticFields()) {
adaptClassStringsInStaticField(field);
}
clazz.forEachMethod(this::adaptClassStringsInMethod);
}
}
}
private void adaptClassStringsInStaticField(DexEncodedField encodedField) {
assert encodedField.accessFlags.isStatic();
DexValue staticValue = encodedField.getStaticValue();
if (staticValue instanceof DexValueString) {
DexString original = ((DexValueString) staticValue).getValue();
encodedField.setStaticValue(new DexValueString(getRenamedStringLiteral(original)));
}
}
private void adaptClassStringsInMethod(DexEncodedMethod encodedMethod) {
// Abstract methods do not have code_item.
if (encodedMethod.shouldNotHaveCode()) {
return;
}
Code code = encodedMethod.getCode();
if (code == null) {
return;
}
if (code.isDexCode()) {
for (Instruction instruction : code.asDexCode().instructions) {
if (instruction.isConstString()) {
ConstString cnst = instruction.asConstString();
cnst.BBBB = getRenamedStringLiteral(cnst.getString());
}
}
} else {
assert code.isCfCode();
for (CfInstruction instruction : code.asCfCode().getInstructions()) {
if (instruction.isConstString()) {
CfConstString cnst = instruction.asConstString();
cnst.setString(getRenamedStringLiteral(cnst.getString()));
}
}
}
}
private DexString getRenamedStringLiteral(DexString originalLiteral) {
String originalString = originalLiteral.toString();
Map<String, DexType> renamedYetMatchedTypes =
lens.getRenamedItems(
DexType.class,
type -> type.toSourceString().equals(originalString),
DexType::toSourceString);
DexType type = renamedYetMatchedTypes.get(originalString);
if (type != null) {
DexString renamed = lens.lookupDescriptor(type);
// Create a new DexString only when the corresponding string literal will be replaced.
if (renamed != originalLiteral) {
return appInfo.dexItemFactory.createString(descriptorToJavaType(renamed.toString()));
}
}
return originalLiteral;
}
private void replaceDexItemBasedConstString() {
for (DexProgramClass clazz : appInfo.classes()) {
// Some const strings could be moved to field's static value (from <clinit>).
for (DexEncodedField field : clazz.staticFields()) {
replaceDexItemBasedConstStringInStaticField(field);
}
clazz.forEachMethod(this::replaceDexItemBasedConstStringInMethod);
}
}
private void replaceDexItemBasedConstStringInStaticField(DexEncodedField encodedField) {
assert encodedField.accessFlags.isStatic();
DexValue staticValue = encodedField.getStaticValue();
if (staticValue instanceof DexItemBasedValueString) {
DexItemBasedValueString dexItemBasedValueString = (DexItemBasedValueString) staticValue;
DexReference original = dexItemBasedValueString.getValue();
DexString replacement =
dexItemBasedValueString.getClassNameComputationInfo().needsToComputeClassName()
? appInfo.dexItemFactory.createString(
computeClassName(
lens.lookupDescriptor(original.asDexType()),
appInfo.definitionFor(original.asDexType()),
dexItemBasedValueString.getClassNameComputationInfo()))
: lens.lookupName(original, appInfo.dexItemFactory);
encodedField.setStaticValue(new DexValueString(replacement));
}
}
private void replaceDexItemBasedConstStringInMethod(DexEncodedMethod encodedMethod) {
if (!encodedMethod.getOptimizationInfo().useIdentifierNameString()) {
return;
}
// Abstract methods do not have code_item.
if (encodedMethod.shouldNotHaveCode()) {
return;
}
Code code = encodedMethod.getCode();
if (code == null) {
return;
}
if (code.isDexCode()) {
Instruction[] instructions = code.asDexCode().instructions;
for (int i = 0; i < instructions.length; ++i) {
Instruction instruction = instructions[i];
if (instruction instanceof DexItemBasedConstString) {
DexItemBasedConstString cnst = instruction.asDexItemBasedConstString();
DexString replacement =
cnst.getClassNameComputationInfo().needsToComputeClassName()
? appInfo.dexItemFactory.createString(
computeClassName(
lens.lookupDescriptor(cnst.getItem().asDexType()),
appInfo.definitionFor(cnst.getItem().asDexType()),
cnst.getClassNameComputationInfo()))
: lens.lookupName(cnst.getItem(), appInfo.dexItemFactory);
ConstString constString = new ConstString(cnst.AA, replacement);
constString.setOffset(instruction.getOffset());
instructions[i] = constString;
}
}
} else {
assert code.isCfCode();
List<CfInstruction> instructions = code.asCfCode().instructions;
for (int i = 0; i < instructions.size(); ++i) {
CfInstruction instruction = instructions.get(i);
if (instruction.isDexItemBasedConstString()) {
CfDexItemBasedConstString cnst = instruction.asDexItemBasedConstString();
DexString replacement =
cnst.getClassNameComputationInfo().needsToComputeClassName()
? appInfo.dexItemFactory.createString(
computeClassName(
lens.lookupDescriptor(cnst.getItem().asDexType()),
appInfo.definitionFor(cnst.getItem().asDexType()),
cnst.getClassNameComputationInfo()))
: lens.lookupName(cnst.getItem(), appInfo.dexItemFactory);
instructions.set(i, new CfConstString(replacement));
}
}
}
}
}