blob: 53bb117df7697ebf8454a6ea5d432f99cdd749e8 [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.dex;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.ConstStringJumbo;
import com.android.tools.r8.code.Goto32;
import com.android.tools.r8.code.IfEq;
import com.android.tools.r8.code.IfEqz;
import com.android.tools.r8.code.IfNe;
import com.android.tools.r8.code.IfNez;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class JumboStringProcessing extends TestBase {
@Test
public void branching() {
DexItemFactory factory = new DexItemFactory();
DexString string = factory.createString("turn into jumbo");
factory.sort(NamingLens.getIdentityLens());
Instruction[] instructions = buildInstructions(string, false);
DexCode code = jumboStringProcess(factory, string, instructions);
Instruction[] rewrittenInstructions = code.instructions;
assert rewrittenInstructions[1] instanceof IfEq;
IfEq condition = (IfEq) rewrittenInstructions[1];
assert condition.getOffset() + condition.CCCC == rewrittenInstructions[3].getOffset();
assert rewrittenInstructions[2] instanceof Goto32;
Goto32 jump = (Goto32) rewrittenInstructions[2];
Instruction lastInstruction = rewrittenInstructions[rewrittenInstructions.length - 1];
assert jump.getOffset() + jump.AAAAAAAA == lastInstruction.getOffset();
}
@Test
public void branching2() {
DexItemFactory factory = new DexItemFactory();
DexString string = factory.createString("turn into jumbo");
factory.sort(NamingLens.getIdentityLens());
Instruction[] instructions = buildInstructions(string, true);
DexCode code = jumboStringProcess(factory, string, instructions);
Instruction[] rewrittenInstructions = code.instructions;
assert rewrittenInstructions[1] instanceof IfEqz;
IfEqz condition = (IfEqz) rewrittenInstructions[1];
assert condition.getOffset() + condition.BBBB == rewrittenInstructions[3].getOffset();
assert rewrittenInstructions[2] instanceof Goto32;
Goto32 jump = (Goto32) rewrittenInstructions[2];
Instruction lastInstruction = rewrittenInstructions[rewrittenInstructions.length - 1];
assert jump.getOffset() + jump.AAAAAAAA == lastInstruction.getOffset();
}
private Instruction[] buildInstructions(DexString string, boolean zeroCondition) {
List<Instruction> instructions = new ArrayList<>();
int offset = 0;
Instruction instr = new Const4(0, 0);
instr.setOffset(offset);
instructions.add(instr);
offset += instr.getSize();
int lastInstructionOffset = 15000 * 2 + 2 + offset;
if (zeroCondition) {
instr = new IfNez(0, lastInstructionOffset - offset);
} else {
instr = new IfNe(0, 0, lastInstructionOffset - offset);
}
instr.setOffset(offset);
instructions.add(instr);
offset += instr.getSize();
for (int i = 0; i < 15000; i++) {
instr = new ConstString(0, string);
instr.setOffset(offset);
instructions.add(instr);
offset += instr.getSize();
}
instr = new ReturnVoid();
instr.setOffset(offset);
instructions.add(instr);
assert instr.getOffset() == lastInstructionOffset;
return instructions.toArray(Instruction.EMPTY_ARRAY);
}
private int countJumboStrings(Instruction[] instructions) {
int count = 0;
for (Instruction instruction : instructions) {
count += instruction instanceof ConstStringJumbo ? 1 : 0;
}
return count;
}
private int countSimpleNops(Instruction[] instructions) {
int count = 0;
for (Instruction instruction : instructions) {
count += instruction.isSimpleNop() ? 1 : 0;
}
return count;
}
@Test
public void regress78072750() throws Exception {
// This dex file have the baksmali output from the failing class from b/78072750, with all
// const-string/jumbo replaced with const-string. Also one of the nops before the first
// payload has been removed to make it valid dex file (correct alignment of the payload
// instruction).
Path originalDexFile =
Paths.get(ToolHelper.SMALI_BUILD_DIR, "regression/78072750/78072750.dex");
AndroidApp application = AndroidApp.builder()
.addDexProgramData(Files.toByteArray(originalDexFile.toFile()), Origin.unknown())
.build();
CodeInspector inspector = new CodeInspector(application);
DexEncodedMethod method = getMethod(
inspector,
"android.databinding.DataBinderMapperImpl",
"android.databinding.ViewDataBinding",
"getDataBinder",
ImmutableList.of("android.databinding.DataBindingComponent", "android.view.View", "int"));
Instruction[] instructions = method.getCode().asDexCode().instructions;
assertEquals(0, countJumboStrings(instructions));
assertEquals(1, countSimpleNops(instructions));
DexItemFactory factory = inspector.getFactory();
DexString string = factory.createString("view must have a tag");
factory.sort(NamingLens.getIdentityLens());
DexCode code = jumboStringProcess(factory, string, instructions);
Instruction[] rewrittenInstructions = code.instructions;
assertEquals(289, countJumboStrings(rewrittenInstructions));
assertEquals(0, countSimpleNops(rewrittenInstructions));
}
private DexCode jumboStringProcess(
DexItemFactory factory, DexString string, Instruction[] instructions) {
DexCode code = new DexCode(
1,
0,
0,
instructions,
new Try[0],
null,
null);
MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
DexEncodedMethod method =
new DexEncodedMethod(null, flags, null, ParameterAnnotationsList.empty(), code);
return new JumboStringRewriter(method, string, factory).rewrite();
}
}