Merge commit 'cd8160b889e496347c224bb0065884a6953f45ad' into dev-release
diff --git a/scripts/add-android-jar.sh b/scripts/add-android-jar.sh index 3eb0d21..4a0f57c 100755 --- a/scripts/add-android-jar.sh +++ b/scripts/add-android-jar.sh
@@ -8,6 +8,8 @@ set -x echo "Update this script manually before using" +echo "If updating API database also update API_LEVEL in " \ + "AndroidApiHashingDatabaseBuilderGeneratorTest" exit -1 # Download Platform SDK in @SDK_HOME @@ -21,21 +23,46 @@ THIRD_PARTY_ANDROID_JAR=third_party/android_jar THIRD_PARTY_ANDROID_JAR_LIB=$THIRD_PARTY_ANDROID_JAR/lib-v$SDK_VERSION -rm -rf $THIRD_PARTY_ANDROID_JAR_LIB -rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz -rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.sha1 +UPDATE_ANDROID_JAR="no" +if [[ "$UPDATE_ANDROID_JAR" == "yes" ]]; then + rm -rf $THIRD_PARTY_ANDROID_JAR_LIB + rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz + rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.sha1 -mkdir -p $THIRD_PARTY_ANDROID_JAR_LIB/optional -cp $SDK_DIR/android.jar $THIRD_PARTY_ANDROID_JAR_LIB/android.jar -cp $SDK_DIR/optional/*.jar $THIRD_PARTY_ANDROID_JAR_LIB/optional -cp $SDK_DIR/optional/optional.json $THIRD_PARTY_ANDROID_JAR_LIB/optional -cp $THIRD_PARTY_ANDROID_JAR/lib-v31/README.google $THIRD_PARTY_ANDROID_JAR_LIB -vi $THIRD_PARTY_ANDROID_JAR_LIB/README.google + mkdir -p $THIRD_PARTY_ANDROID_JAR_LIB/optional + cp $SDK_DIR/android.jar $THIRD_PARTY_ANDROID_JAR_LIB/android.jar + cp $SDK_DIR/optional/*.jar $THIRD_PARTY_ANDROID_JAR_LIB/optional + cp $SDK_DIR/optional/optional.json $THIRD_PARTY_ANDROID_JAR_LIB/optional + cp $THIRD_PARTY_ANDROID_JAR/lib-v31/README.google $THIRD_PARTY_ANDROID_JAR_LIB + vi $THIRD_PARTY_ANDROID_JAR_LIB/README.google -(cd $THIRD_PARTY_ANDROID_JAR \ - && upload_to_google_storage.py -a --bucket r8-deps lib-v$SDK_VERSION) -rm -rf $THIRD_PARTY_ANDROID_JAR_LIB -rm ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz -git add ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz.sha1 + (cd $THIRD_PARTY_ANDROID_JAR \ + && upload_to_google_storage.py -a --bucket r8-deps lib-v$SDK_VERSION) + rm -rf $THIRD_PARTY_ANDROID_JAR_LIB + rm ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz + git add ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz.sha1 +fi -echo "Update build.gradle with this new cloud dependency, and verify with tools/gradle.py downloadDeps" \ No newline at end of file +UPDATE_API_DATABASE="no" +if [[ "$UPDATE_API_DATABASE" == "yes" ]]; then + rm -rf $THIRD_PARTY_ANDROID_JAR/api-versions + rm -f $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz + rm -f $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz.sha1 + mkdir -p $THIRD_PARTY_ANDROID_JAR/api-versions + cp $SDK_DIR/data/api-versions.xml $THIRD_PARTY_ANDROID_JAR/api-versions + (cd $THIRD_PARTY_ANDROID_JAR \ + && upload_to_google_storage.py -a --bucket r8-deps api-versions) + tools/gradle.py r8NoManifestWithoutDeps testJar repackageTestDeps + java -cp build/libs/r8_no_manifest_without_deps.jar:build/libs/deps_all.jar:build/libs/r8tests.jar:build/libs/test_deps_all.jar \ + com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGeneratorTest + + rm -rf $THIRD_PARTY_ANDROID_JAR/api-versions + rm -f $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz + git add $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz.sha1 + git add src/main/resources/api_database/api_database_ambiguous.txt + git add src/main/resources/api_database/api_database_api_level.ser + git add src/main/resources/api_database/api_database_hash_lookup.ser +fi + +echo "Update build.gradle with this new cloud dependency, " \ + "and verify with tools/gradle.py downloadDeps"
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 242064b..1e6d847 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -486,7 +486,6 @@ assert !internal.enableClassStaticizer; assert !internal.enableEnumValueOptimization; assert !internal.outline.enabled; - assert !internal.enableValuePropagation; assert !internal.enableTreeShakingOfLibraryMethodOverrides; // TODO(b/187675788): Enable class merging for synthetics in D8.
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index c0fb797..745d94d 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -182,7 +182,6 @@ assert !internal.enableClassStaticizer; assert !internal.enableEnumValueOptimization; assert !internal.outline.enabled; - assert !internal.enableValuePropagation; assert !internal.enableTreeShakingOfLibraryMethodOverrides; HorizontalClassMergerOptions horizontalClassMergerOptions =
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index d68ff6b..a0ac288 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -979,9 +979,10 @@ } if (code.isCfCode()) { assert verifyOriginalMethodInPosition(code.asCfCode(), originalMethod); - } else { - assert code.isDexCode(); + } else if (code.isDexCode()) { assert verifyOriginalMethodInDebugInfo(code.asDexCode(), originalMethod); + } else { + assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode(); } } })); @@ -994,7 +995,7 @@ continue; } CfPosition position = instruction.asPosition(); - assert position.getPosition().getOutermostCaller().method == originalMethod; + assert position.getPosition().getOutermostCaller().getMethod() == originalMethod; } return true; }
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java index ab9165b..2f90085 100644 --- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java +++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -26,6 +26,8 @@ import com.android.tools.r8.cf.code.CfIf; import com.android.tools.r8.cf.code.CfIfCmp; import com.android.tools.r8.cf.code.CfIinc; +import com.android.tools.r8.cf.code.CfInstanceFieldRead; +import com.android.tools.r8.cf.code.CfInstanceFieldWrite; import com.android.tools.r8.cf.code.CfInstanceOf; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; @@ -44,11 +46,14 @@ import com.android.tools.r8.cf.code.CfReturn; import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfStackInstruction; +import com.android.tools.r8.cf.code.CfStaticFieldRead; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.cf.code.CfSwitch; import com.android.tools.r8.cf.code.CfThrow; import com.android.tools.r8.cf.code.CfTryCatch; import com.android.tools.r8.errors.Unimplemented; +import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; @@ -75,7 +80,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import org.objectweb.asm.Opcodes; /** Rudimentary printer to print the source representation for creating CfCode object. */ public class CfCodePrinter extends CfPrinter { @@ -531,24 +535,28 @@ } @Override - public void print(CfFieldInstruction insn) { - printNewInstruction( - "CfFieldInstruction", fieldInstructionOpcode(insn), dexField(insn.getField())); + public void print(CfInstanceFieldRead insn) { + printNewInstruction("CfInstanceFieldRead", dexField(insn.getField())); } - private String fieldInstructionOpcode(CfFieldInstruction insn) { - switch (insn.getOpcode()) { - case Opcodes.GETSTATIC: - return asmOpcodesType() + ".GETSTATIC"; - case Opcodes.PUTSTATIC: - return asmOpcodesType() + ".PUTSTATIC"; - case Opcodes.GETFIELD: - return asmOpcodesType() + ".GETFIELD"; - case Opcodes.PUTFIELD: - return asmOpcodesType() + ".PUTFIELD"; - default: - throw new Unimplemented(); - } + @Override + public void print(CfInstanceFieldWrite insn) { + printNewInstruction("CfInstanceFieldWrite", dexField(insn.getField())); + } + + @Override + public void print(CfStaticFieldRead insn) { + printNewInstruction("CfStaticFieldRead", dexField(insn.getField())); + } + + @Override + public void print(CfStaticFieldWrite insn) { + printNewInstruction("CfStaticFieldWrite", dexField(insn.getField())); + } + + @Override + public void print(CfFieldInstruction insn) { + throw new Unreachable(); } @Override
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java index 98808c7..d0a3c23 100644 --- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java +++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -25,6 +25,8 @@ import com.android.tools.r8.cf.code.CfIfCmp; import com.android.tools.r8.cf.code.CfIinc; import com.android.tools.r8.cf.code.CfInitClass; +import com.android.tools.r8.cf.code.CfInstanceFieldRead; +import com.android.tools.r8.cf.code.CfInstanceFieldWrite; import com.android.tools.r8.cf.code.CfInstanceOf; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; @@ -46,6 +48,8 @@ import com.android.tools.r8.cf.code.CfReturn; import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfStackInstruction; +import com.android.tools.r8.cf.code.CfStaticFieldRead; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.cf.code.CfSwitch; import com.android.tools.r8.cf.code.CfSwitch.Kind; @@ -472,6 +476,22 @@ appendClass(insn.getType()); } + public void print(CfInstanceFieldRead insn) { + print(insn.asFieldInstruction()); + } + + public void print(CfInstanceFieldWrite insn) { + print(insn.asFieldInstruction()); + } + + public void print(CfStaticFieldRead insn) { + print(insn.asFieldInstruction()); + } + + public void print(CfStaticFieldWrite insn) { + print(insn.asFieldInstruction()); + } + public void print(CfFieldInstruction insn) { indent(); switch (insn.getOpcode()) { @@ -555,8 +575,8 @@ public void print(CfPosition instruction) { Position position = instruction.getPosition(); indent(); - builder.append(".line ").append(position.line); - if (position.file != null || position.callerPosition != null) { + builder.append(".line ").append(position.getLine()); + if (position.hasCallerPosition() || position.hasFile()) { appendComment(position.toString()); } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java index a2f4e80..2585329 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -10,14 +10,12 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; -import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.InitClassLens; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.CfState.Slot; @@ -28,11 +26,10 @@ import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; -import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -public class CfFieldInstruction extends CfInstruction { +public abstract class CfFieldInstruction extends CfInstruction { private final int opcode; private final DexField field; @@ -53,6 +50,21 @@ assert field.type == declaringField.type; } + public static CfFieldInstruction create(int opcode, DexField field, DexField declaringField) { + switch (opcode) { + case Opcodes.GETSTATIC: + return new CfStaticFieldRead(field, declaringField); + case Opcodes.PUTSTATIC: + return new CfStaticFieldWrite(field, declaringField); + case Opcodes.GETFIELD: + return new CfInstanceFieldRead(field, declaringField); + case Opcodes.PUTFIELD: + return new CfInstanceFieldWrite(field, declaringField); + default: + throw new Unreachable("Unexpected opcode " + opcode); + } + } + public DexField getField() { return field; } @@ -110,27 +122,6 @@ } @Override - void internalRegisterUse( - UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { - switch (opcode) { - case Opcodes.GETFIELD: - registry.registerInstanceFieldRead(field); - break; - case Opcodes.PUTFIELD: - registry.registerInstanceFieldWrite(field); - break; - case Opcodes.GETSTATIC: - registry.registerStaticFieldRead(field); - break; - case Opcodes.PUTSTATIC: - registry.registerStaticFieldWrite(field); - break; - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } - - @Override public boolean canThrow() { return true; }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java new file mode 100644 index 0000000..145bc94 --- /dev/null +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
@@ -0,0 +1,29 @@ +// Copyright (c) 2021, 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.cf.code; + +import com.android.tools.r8.code.CfOrDexInstanceFieldRead; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.UseRegistry; +import java.util.ListIterator; +import org.objectweb.asm.Opcodes; + +public class CfInstanceFieldRead extends CfFieldInstruction implements CfOrDexInstanceFieldRead { + + public CfInstanceFieldRead(DexField field) { + this(field, field); + } + + public CfInstanceFieldRead(DexField field, DexField declaringField) { + super(Opcodes.GETFIELD, field, declaringField); + } + + @Override + void internalRegisterUse( + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + registry.registerInstanceFieldReadInstruction(this); + } +}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java new file mode 100644 index 0000000..6d01f49 --- /dev/null +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
@@ -0,0 +1,28 @@ +// Copyright (c) 2021, 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.cf.code; + +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.UseRegistry; +import java.util.ListIterator; +import org.objectweb.asm.Opcodes; + +public class CfInstanceFieldWrite extends CfFieldInstruction { + + public CfInstanceFieldWrite(DexField field) { + this(field, field); + } + + public CfInstanceFieldWrite(DexField field, DexField declaringField) { + super(Opcodes.PUTFIELD, field, declaringField); + } + + @Override + void internalRegisterUse( + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + registry.registerInstanceFieldWrite(getField()); + } +}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java index 742a2be..4427f41 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.code.CfOrDexInstruction; +import com.android.tools.r8.code.Instruction; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -110,6 +111,11 @@ return true; } + @Override + public Instruction asDexInstruction() { + return null; + } + public CfRecordFieldValues asRecordFieldValues() { return null; }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java index 45077e2..0112d3f 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -28,8 +28,6 @@ private final CfLabel label; private final Position position; - - public CfPosition(CfLabel label, Position position) { this.label = label; this.position = position; @@ -47,7 +45,7 @@ this, (CfPosition) other, spec -> - spec.withInt(p -> p.position.line) + spec.withInt(p -> p.position.getLine()) .withCustomItem(p -> p.label, helper.labelAcceptor())); } @@ -61,7 +59,7 @@ NamingLens namingLens, LensCodeRewriterUtils rewriter, MethodVisitor visitor) { - visitor.visitLineNumber(position.line, label.getLabel()); + visitor.visitLineNumber(position.getLine(), label.getLabel()); } @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java new file mode 100644 index 0000000..66fc48e --- /dev/null +++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java
@@ -0,0 +1,29 @@ +// Copyright (c) 2021, 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.cf.code; + +import com.android.tools.r8.code.CfOrDexStaticFieldRead; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.UseRegistry; +import java.util.ListIterator; +import org.objectweb.asm.Opcodes; + +public class CfStaticFieldRead extends CfFieldInstruction implements CfOrDexStaticFieldRead { + + public CfStaticFieldRead(DexField field) { + super(Opcodes.GETSTATIC, field); + } + + public CfStaticFieldRead(DexField field, DexField declaringField) { + super(Opcodes.GETSTATIC, field, declaringField); + } + + @Override + void internalRegisterUse( + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + registry.registerStaticFieldReadInstruction(this); + } +}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java new file mode 100644 index 0000000..2cd0f26 --- /dev/null +++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
@@ -0,0 +1,28 @@ +// Copyright (c) 2021, 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.cf.code; + +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.UseRegistry; +import java.util.ListIterator; +import org.objectweb.asm.Opcodes; + +public class CfStaticFieldWrite extends CfFieldInstruction { + + public CfStaticFieldWrite(DexField field) { + super(Opcodes.PUTSTATIC, field); + } + + public CfStaticFieldWrite(DexField field, DexField declaringField) { + super(Opcodes.PUTSTATIC, field, declaringField); + } + + @Override + void internalRegisterUse( + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + registry.registerStaticFieldWrite(getField()); + } +}
diff --git a/src/main/java/com/android/tools/r8/code/CfOrDexInstanceFieldRead.java b/src/main/java/com/android/tools/r8/code/CfOrDexInstanceFieldRead.java new file mode 100644 index 0000000..3d560fc --- /dev/null +++ b/src/main/java/com/android/tools/r8/code/CfOrDexInstanceFieldRead.java
@@ -0,0 +1,12 @@ +// Copyright (c) 2021, 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.code; + +import com.android.tools.r8.graph.DexField; + +public interface CfOrDexInstanceFieldRead extends CfOrDexInstruction { + + DexField getField(); +}
diff --git a/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java b/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java index e85c75c..e8ac111 100644 --- a/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java +++ b/src/main/java/com/android/tools/r8/code/CfOrDexInstruction.java
@@ -11,4 +11,6 @@ CfInstruction asCfInstruction(); boolean isCfInstruction(); + + Instruction asDexInstruction(); }
diff --git a/src/main/java/com/android/tools/r8/code/CfOrDexStaticFieldRead.java b/src/main/java/com/android/tools/r8/code/CfOrDexStaticFieldRead.java new file mode 100644 index 0000000..121023a --- /dev/null +++ b/src/main/java/com/android/tools/r8/code/CfOrDexStaticFieldRead.java
@@ -0,0 +1,12 @@ +// Copyright (c) 2021, 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.code; + +import com.android.tools.r8.graph.DexField; + +public interface CfOrDexStaticFieldRead extends CfOrDexInstruction { + + DexField getField(); +}
diff --git a/src/main/java/com/android/tools/r8/code/Iget.java b/src/main/java/com/android/tools/r8/code/Iget.java index 71c11c3..ee8e7a0 100644 --- a/src/main/java/com/android/tools/r8/code/Iget.java +++ b/src/main/java/com/android/tools/r8/code/Iget.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class Iget extends IgetOrIput { +public class Iget extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x52; public static final String NAME = "Iget"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/IgetBoolean.java b/src/main/java/com/android/tools/r8/code/IgetBoolean.java index 36c00b6..3212878 100644 --- a/src/main/java/com/android/tools/r8/code/IgetBoolean.java +++ b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class IgetBoolean extends IgetOrIput { +public class IgetBoolean extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x55; public static final String NAME = "IgetBoolean"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/IgetByte.java b/src/main/java/com/android/tools/r8/code/IgetByte.java index 8d57bb5..a876d70 100644 --- a/src/main/java/com/android/tools/r8/code/IgetByte.java +++ b/src/main/java/com/android/tools/r8/code/IgetByte.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class IgetByte extends IgetOrIput { +public class IgetByte extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x56; public static final String NAME = "IgetByte"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/IgetChar.java b/src/main/java/com/android/tools/r8/code/IgetChar.java index 2ba0e26..efa458d 100644 --- a/src/main/java/com/android/tools/r8/code/IgetChar.java +++ b/src/main/java/com/android/tools/r8/code/IgetChar.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class IgetChar extends IgetOrIput { +public class IgetChar extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x57; public static final String NAME = "IgetChar"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/IgetObject.java b/src/main/java/com/android/tools/r8/code/IgetObject.java index c685229..93540d6 100644 --- a/src/main/java/com/android/tools/r8/code/IgetObject.java +++ b/src/main/java/com/android/tools/r8/code/IgetObject.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class IgetObject extends IgetOrIput { +public class IgetObject extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x54; public static final String NAME = "IgetObject"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/IgetShort.java b/src/main/java/com/android/tools/r8/code/IgetShort.java index 77667ba..23bd792 100644 --- a/src/main/java/com/android/tools/r8/code/IgetShort.java +++ b/src/main/java/com/android/tools/r8/code/IgetShort.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class IgetShort extends IgetOrIput { +public class IgetShort extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x58; public static final String NAME = "IgetShort"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/IgetWide.java b/src/main/java/com/android/tools/r8/code/IgetWide.java index a71b95a..12ad976 100644 --- a/src/main/java/com/android/tools/r8/code/IgetWide.java +++ b/src/main/java/com/android/tools/r8/code/IgetWide.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class IgetWide extends IgetOrIput { +public class IgetWide extends IgetOrIput implements CfOrDexInstanceFieldRead { public static final int OPCODE = 0x53; public static final String NAME = "IgetWide"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerInstanceFieldRead(getField()); + registry.registerInstanceFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java index 03d7a3d..8671b68 100644 --- a/src/main/java/com/android/tools/r8/code/Instruction.java +++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -133,6 +133,10 @@ write32BitValue(item.getOffset(mapping), dest); } + public boolean hasOffset() { + return offset >= 0; + } + public int getOffset() { return offset; } @@ -151,6 +155,11 @@ return false; } + @Override + public Instruction asDexInstruction() { + return this; + } + public CheckCast asCheckCast() { return null; }
diff --git a/src/main/java/com/android/tools/r8/code/Sget.java b/src/main/java/com/android/tools/r8/code/Sget.java index c2d93df..2bd4653 100644 --- a/src/main/java/com/android/tools/r8/code/Sget.java +++ b/src/main/java/com/android/tools/r8/code/Sget.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class Sget extends SgetOrSput { +public class Sget extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x60; public static final String NAME = "Sget"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetBoolean.java b/src/main/java/com/android/tools/r8/code/SgetBoolean.java index 3d79934..fb51776 100644 --- a/src/main/java/com/android/tools/r8/code/SgetBoolean.java +++ b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class SgetBoolean extends SgetOrSput { +public class SgetBoolean extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x63; public static final String NAME = "SgetBoolean"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetByte.java b/src/main/java/com/android/tools/r8/code/SgetByte.java index 08f2b97..ee693ac 100644 --- a/src/main/java/com/android/tools/r8/code/SgetByte.java +++ b/src/main/java/com/android/tools/r8/code/SgetByte.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class SgetByte extends SgetOrSput { +public class SgetByte extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x64; public static final String NAME = "SgetByte"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetChar.java b/src/main/java/com/android/tools/r8/code/SgetChar.java index 5ce7f8c..5f61f90 100644 --- a/src/main/java/com/android/tools/r8/code/SgetChar.java +++ b/src/main/java/com/android/tools/r8/code/SgetChar.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class SgetChar extends SgetOrSput { +public class SgetChar extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x65; public static final String NAME = "SgetChar"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetObject.java b/src/main/java/com/android/tools/r8/code/SgetObject.java index e4ae240..8e5018e 100644 --- a/src/main/java/com/android/tools/r8/code/SgetObject.java +++ b/src/main/java/com/android/tools/r8/code/SgetObject.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class SgetObject extends SgetOrSput { +public class SgetObject extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x62; public static final String NAME = "SgetObject"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetShort.java b/src/main/java/com/android/tools/r8/code/SgetShort.java index 62e2de7..99fc4ee 100644 --- a/src/main/java/com/android/tools/r8/code/SgetShort.java +++ b/src/main/java/com/android/tools/r8/code/SgetShort.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class SgetShort extends SgetOrSput { +public class SgetShort extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x66; public static final String NAME = "SgetShort"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetWide.java b/src/main/java/com/android/tools/r8/code/SgetWide.java index 962d0c1..75a4343 100644 --- a/src/main/java/com/android/tools/r8/code/SgetWide.java +++ b/src/main/java/com/android/tools/r8/code/SgetWide.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; -public class SgetWide extends SgetOrSput { +public class SgetWide extends SgetOrSput implements CfOrDexStaticFieldRead { public static final int OPCODE = 0x61; public static final String NAME = "SgetWide"; @@ -39,7 +39,7 @@ @Override public void registerUse(UseRegistry<?> registry) { - registry.registerStaticFieldRead(getField()); + registry.registerStaticFieldReadInstruction(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java index 24b42d3..b313b75 100644 --- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java +++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -7,12 +7,17 @@ import com.android.tools.r8.CompatProguardCommandBuilder; import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.MapIdProvider; import com.android.tools.r8.OutputMode; import com.android.tools.r8.R8; +import com.android.tools.r8.SourceFileProvider; import com.android.tools.r8.Version; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.origin.CommandLineOrigin; import com.android.tools.r8.utils.ExceptionUtils; +import com.android.tools.r8.utils.MapIdTemplateProvider; +import com.android.tools.r8.utils.SourceFileTemplateProvider; import com.google.common.collect.ImmutableList; import java.nio.file.Paths; import java.util.List; @@ -39,6 +44,8 @@ public final boolean includeDataResources; public final boolean multiDex; public final String mainDexList; + public final MapIdProvider mapIdProvider; + public final SourceFileProvider sourceFileProvider; public final List<String> proguardConfig; public boolean printHelpAndExit; @@ -54,6 +61,8 @@ boolean forceProguardCompatibility, boolean includeDataResources, String mainDexList, + MapIdProvider mapIdProvider, + SourceFileProvider sourceFileProvider, boolean printHelpAndExit, boolean disableVerticalClassMerging) { this.output = output; @@ -64,11 +73,14 @@ this.multiDex = multiDex; this.mainDexList = mainDexList; this.proguardConfig = proguardConfig; + this.mapIdProvider = mapIdProvider; + this.sourceFileProvider = sourceFileProvider; this.printHelpAndExit = printHelpAndExit; this.disableVerticalClassMerging = disableVerticalClassMerging; } public static CompatProguardOptions parse(String[] args) { + DiagnosticsHandler handler = new DiagnosticsHandler() {}; String output = null; CompilationMode mode = null; int minApi = 1; @@ -77,6 +89,8 @@ boolean multiDex = false; String mainDexList = null; boolean printHelpAndExit = false; + MapIdProvider mapIdProvider = null; + SourceFileProvider sourceFileProvider = null; // Flags to disable experimental features. boolean disableVerticalClassMerging = false; // These flags are currently ignored. @@ -116,6 +130,10 @@ mainDexList = args[++i]; } else if (arg.startsWith("--main-dex-list=")) { mainDexList = arg.substring("--main-dex-list=".length()); + } else if (arg.equals("--map-id-template")) { + mapIdProvider = MapIdTemplateProvider.create(args[++i], handler); + } else if (arg.equals("--source-file-template")) { + sourceFileProvider = SourceFileTemplateProvider.create(args[++i], handler); } else if (arg.equals("--no-vertical-class-merging")) { disableVerticalClassMerging = true; } else if (arg.equals("--minimal-main-dex")) { @@ -153,6 +171,8 @@ forceProguardCompatibility, includeDataResources, mainDexList, + mapIdProvider, + sourceFileProvider, printHelpAndExit, disableVerticalClassMerging); } @@ -199,7 +219,9 @@ builder .setOutput(Paths.get(options.output), OutputMode.DexIndexed, options.includeDataResources) .addProguardConfiguration(options.proguardConfig, CommandLineOrigin.INSTANCE) - .setMinApiLevel(options.minApi); + .setMinApiLevel(options.minApi) + .setMapIdProvider(options.mapIdProvider) + .setSourceFileProvider(options.sourceFileProvider); if (options.mode != null) { builder.setMode(options.mode); }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java index dab9181..10e78f3 100644 --- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java +++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -15,7 +15,6 @@ import com.android.tools.r8.ProgramConsumer; import com.android.tools.r8.ResourceException; import com.android.tools.r8.SourceFileEnvironment; -import com.android.tools.r8.code.Instruction; import com.android.tools.r8.dex.FileWriter.ByteBufferResult; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer; @@ -25,8 +24,6 @@ import com.android.tools.r8.graph.DexAnnotationDirectory; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexApplication; -import com.android.tools.r8.graph.DexCallSite; -import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.graph.DexDebugInfo; import com.android.tools.r8.graph.DexEncodedArray; import com.android.tools.r8.graph.DexEncodedField; @@ -36,6 +33,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.DexValue; +import com.android.tools.r8.graph.DexWritableCode; import com.android.tools.r8.graph.EnclosingMethodAttribute; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.InitClassLens; @@ -127,7 +125,7 @@ } @Override - public boolean add(DexCode dexCode) { + public boolean add(DexEncodedMethod method, DexWritableCode dexCode) { return true; } @@ -642,18 +640,9 @@ } private void setCallSiteContexts(DexProgramClass clazz) { - for (DexEncodedMethod method : clazz.methods()) { - if (method.hasCode()) { - DexCode code = method.getCode().asDexCode(); - assert code != null; - for (Instruction instruction : code.instructions) { - DexCallSite callSite = instruction.getCallSite(); - if (callSite != null) { - callSite.setContext(method.getReference(), instruction.getOffset()); - } - } - } - } + clazz.forEachProgramMethodMatching( + DexEncodedMethod::hasCode, + method -> method.getDefinition().getCode().asDexWritableCode().setCallSiteContexts(method)); } /** @@ -683,20 +672,23 @@ // for all code objects and write the processed results into that map. // TODO(b/181636450): Reconsider the code mapping setup now that synthetics are never duplicated // in outputs. - Map<DexEncodedMethod, DexCode> codeMapping = new IdentityHashMap<>(); + Map<DexEncodedMethod, DexWritableCode> codeMapping = new IdentityHashMap<>(); for (DexProgramClass clazz : classes) { - clazz.forEachMethod( + clazz.forEachProgramMethodMatching( + DexEncodedMethod::hasCode, method -> { - DexCode code = - method.rewriteCodeWithJumboStrings( + DexWritableCode code = method.getDefinition().getCode().asDexWritableCode(); + DexWritableCode rewrittenCode = + code.rewriteCodeWithJumboStrings( + method, mapping, application.dexItemFactory, options.testing.forceJumboStringProcessing); - codeMapping.put(method, code); + codeMapping.put(method.getDefinition(), rewrittenCode); // The mapping now has ownership of the methods code object. This ensures freeing of // code resources once the map entry is cleared and also ensures that we don't end up // using the incorrect code pointer again later! - method.removeCode(); + method.getDefinition().unsetCode(); }); } return MethodToCodeObjectMapping.fromMapBacking(codeMapping);
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java index 0e1edc9..44affb4 100644 --- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java +++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -33,11 +33,11 @@ return new DesugaredLibraryCodeToKeep(namingLens, options); } - abstract void recordMethod(DexMethod method); + public abstract void recordMethod(DexMethod method); - abstract void recordField(DexField field); + public abstract void recordField(DexField field); - abstract void recordClass(DexType type); + public abstract void recordClass(DexType type); abstract void recordClassAllAccesses(DexType type); @@ -82,7 +82,7 @@ } @Override - void recordMethod(DexMethod method) { + public void recordMethod(DexMethod method) { DexType baseType = method.holder.toBaseType(options.dexItemFactory()); if (shouldKeep(baseType)) { keepClass(baseType); @@ -101,7 +101,7 @@ } @Override - void recordField(DexField field) { + public void recordField(DexField field) { DexType baseType = field.holder.toBaseType(options.dexItemFactory()); if (shouldKeep(baseType)) { keepClass(baseType); @@ -115,7 +115,7 @@ } @Override - void recordClass(DexType type) { + public void recordClass(DexType type) { if (shouldKeep(type)) { keepClass(type); } @@ -218,13 +218,13 @@ public static class NopCodeToKeep extends CodeToKeep { @Override - void recordMethod(DexMethod method) {} + public void recordMethod(DexMethod method) {} @Override - void recordField(DexField field) {} + public void recordField(DexField field) {} @Override - void recordClass(DexType type) {} + public void recordClass(DexType type) {} @Override void recordClassAllAccesses(DexType type) {}
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java index b193e73..08f8cad 100644 --- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java +++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -4,11 +4,8 @@ package com.android.tools.r8.dex; import com.android.tools.r8.ByteBufferProvider; -import com.android.tools.r8.code.Instruction; import com.android.tools.r8.errors.CompilationError; -import com.android.tools.r8.graph.DexCode; -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexWritableCode; import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.EncodedValueUtils; @@ -95,37 +92,17 @@ } public void putInstructions( - DexCode code, + DexWritableCode code, ProgramMethod context, ObjectToOffsetMapping mapping, CodeToKeep desugaredLibraryCodeToKeep) { - int size = 0; - Instruction[] instructions = code.instructions; - for (Instruction instruction : instructions) { - size += instruction.getSize(); - } + int size = code.codeSizeInBytes(); ensureSpaceFor(size * Short.BYTES); assert byteBuffer.position() % 2 == 0; ShortBuffer shortBuffer = byteBuffer.asShortBuffer(); - for (int i = 0; i < instructions.length; i++) { - Instruction insn = instructions[i]; - DexMethod method = insn.getMethod(); - DexField field = insn.getField(); - if (field != null) { - assert method == null; - desugaredLibraryCodeToKeep.recordField(field); - } else if (method != null) { - desugaredLibraryCodeToKeep.recordMethod(method); - } else if (insn.isConstClass()) { - desugaredLibraryCodeToKeep.recordClass(insn.asConstClass().getType()); - } else if (insn.isInstanceOf()) { - desugaredLibraryCodeToKeep.recordClass(insn.asInstanceOf().getType()); - } else if (insn.isCheckCast()) { - desugaredLibraryCodeToKeep.recordClass(insn.asCheckCast().getType()); - } - insn.write( - shortBuffer, context, mapping.getGraphLens(), mapping, mapping.getLensCodeRewriter()); - } + code.writeDex( + shortBuffer, context, mapping.getGraphLens(), mapping.getLensCodeRewriter(), mapping); + code.writeKeepRulesForDesugaredLibrary(desugaredLibraryCodeToKeep); byteBuffer.position(byteBuffer.position() + shortBuffer.position() * Short.BYTES); }
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java index dc93c37..079d0d6 100644 --- a/src/main/java/com/android/tools/r8/dex/FileWriter.java +++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -6,7 +6,6 @@ import static com.android.tools.r8.utils.LebUtils.sizeAsUleb128; import com.android.tools.r8.ByteBufferProvider; -import com.android.tools.r8.code.Instruction; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.errors.DefaultInterfaceMethodDiagnostic; import com.android.tools.r8.errors.InvokeCustomDiagnostic; @@ -20,7 +19,6 @@ import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.graph.DexCode.Try; import com.android.tools.r8.graph.DexCode.TryHandler; import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair; @@ -43,6 +41,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.DexValue; +import com.android.tools.r8.graph.DexWritableCode; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.IndexedDexItem; import com.android.tools.r8.graph.ObjectToOffsetMapping; @@ -341,7 +340,7 @@ for (DexProgramClass clazz : mapping.getClasses()) { clazz.forEachProgramMethod( method -> { - DexCode code = codeMapping.getCode(method.getDefinition()); + DexWritableCode code = codeMapping.getCode(method.getDefinition()); assert code != null || method.getDefinition().shouldNotHaveCode(); if (code != null) { ProgramDexCode programCode = new ProgramDexCode(code, method); @@ -409,18 +408,15 @@ return size; } - private int sizeOfCodeItem(DexCode code) { + private int sizeOfCodeItem(DexWritableCode code) { int result = 16; - int insnSize = 0; - for (Instruction insn : code.instructions) { - insnSize += insn.getSize(); - } + int insnSize = code.codeSizeInBytes(); result += insnSize * 2; - result += code.tries.length * 8; - if (code.handlers.length > 0) { + result += code.getTries().length * 8; + if (code.getHandlers().length > 0) { result = alignSize(4, result); - result += LebUtils.sizeAsUleb128(code.handlers.length); - for (TryHandler handler : code.handlers) { + result += LebUtils.sizeAsUleb128(code.getHandlers().length); + for (TryHandler handler : code.getHandlers()) { boolean hasCatchAll = handler.catchAllAddr != TryHandler.NO_HANDLER; result += LebUtils .sizeAsSleb128(hasCatchAll ? -handler.pairs.length : handler.pairs.length); @@ -501,13 +497,13 @@ writeCodeItem(code.getCode(), code.getMethod()); } - private void writeCodeItem(DexCode code, ProgramMethod method) { - mixedSectionOffsets.setOffsetFor(code, dest.align(4)); + private void writeCodeItem(DexWritableCode code, ProgramMethod method) { + mixedSectionOffsets.setOffsetFor(method.getDefinition(), code, dest.align(4)); // Fixed size header information. - dest.putShort((short) code.registerSize); - dest.putShort((short) code.incomingRegisterSize); - dest.putShort((short) code.outgoingRegisterSize); - dest.putShort((short) code.tries.length); + dest.putShort((short) code.getRegisterSize(method)); + dest.putShort((short) code.getIncomingRegisterSize(method)); + dest.putShort((short) code.getOutgoingRegisterSize()); + dest.putShort((short) code.getTries().length); dest.putInt(mixedSectionOffsets.getOffsetFor(code.getDebugInfoForWriting())); // Jump over the size. int insnSizeOffset = dest.position(); @@ -519,16 +515,16 @@ dest.rewind(insnSize + 4); dest.putInt(insnSize / 2); dest.forward(insnSize); - if (code.tries.length > 0) { + if (code.getTries().length > 0) { // The tries need to be 4 byte aligned. int beginOfTriesOffset = dest.align(4); // First write the handlers, so that we know their mixedSectionOffsets. - dest.forward(code.tries.length * 8); + dest.forward(code.getTries().length * 8); int beginOfHandlersOffset = dest.position(); - dest.putUleb128(code.handlers.length); - short[] offsets = new short[code.handlers.length]; + dest.putUleb128(code.getHandlers().length); + short[] offsets = new short[code.getHandlers().length]; int i = 0; - for (TryHandler handler : code.handlers) { + for (TryHandler handler : code.getHandlers()) { offsets[i++] = (short) (dest.position() - beginOfHandlersOffset); boolean hasCatchAll = handler.catchAllAddr != TryHandler.NO_HANDLER; dest.putSleb128(hasCatchAll ? -handler.pairs.length : handler.pairs.length); @@ -544,7 +540,7 @@ int endOfCodeOffset = dest.position(); // Now write the tries. dest.moveTo(beginOfTriesOffset); - for (Try aTry : code.tries) { + for (Try aTry : code.getTries()) { dest.putInt(aTry.startAddress); dest.putShort((short) aTry.instructionCount); dest.putShort(offsets[aTry.handlerIndex]); @@ -665,13 +661,13 @@ dest.putUleb128(nextOffset - currentOffset); currentOffset = nextOffset; dest.putUleb128(method.accessFlags.getAsDexAccessFlags()); - DexCode code = codeMapping.getCode(method); + DexWritableCode code = codeMapping.getCode(method); desugaredLibraryCodeToKeep.recordMethod(method.getReference()); if (code == null) { assert method.shouldNotHaveCode(); dest.putUleb128(0); } else { - dest.putUleb128(mixedSectionOffsets.getOffsetFor(code)); + dest.putUleb128(mixedSectionOffsets.getOffsetFor(method, code)); // Writing the methods starts to take up memory so we are going to flush the // code objects since they are no longer necessary after this. codeMapping.clearCode(method); @@ -867,7 +863,7 @@ dest.putInt((int) adler.getValue()); } - private int alignSize(int bytes, int value) { + private static int alignSize(int bytes, int value) { int mask = bytes - 1; return (value + mask) & ~mask; } @@ -1083,7 +1079,7 @@ private final MethodToCodeObjectMapping codeMapping; - private final Reference2IntMap<DexCode> codes = createReference2IntMap(); + private final Reference2IntMap<DexEncodedMethod> codes = createReference2IntMap(); private final Object2IntMap<DexDebugInfo> debugInfos = createObject2IntMap(); private final Object2IntMap<DexTypeList> typeLists = createObject2IntMap(); private final Reference2IntMap<DexString> stringData = createReference2IntMap(); @@ -1159,8 +1155,8 @@ } @Override - public boolean add(DexCode code) { - return add(codes, code); + public boolean add(DexEncodedMethod method, DexWritableCode code) { + return add(codes, method); } @Override @@ -1201,7 +1197,7 @@ return add(stringData, string); } - public Collection<DexCode> getCodes() { + public Collection<DexEncodedMethod> getCodes() { return codes.keySet(); } @@ -1312,8 +1308,8 @@ return lookup(annotationSetRefList, annotationSetRefLists); } - public int getOffsetFor(DexCode code) { - return lookup(code, codes); + public int getOffsetFor(DexEncodedMethod method, DexWritableCode code) { + return lookup(method, codes); } private <T> void setOffsetFor(T item, int offset, Object2IntMap<T> map) { @@ -1330,8 +1326,8 @@ setOffsetFor(debugInfo, offset, debugInfos); } - void setOffsetFor(DexCode code, int offset) { - setOffsetFor(code, offset, codes); + void setOffsetFor(DexEncodedMethod method, DexWritableCode code, int offset) { + setOffsetFor(method, offset, codes); } void setOffsetFor(DexTypeList typeList, int offset) {
diff --git a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java index 5541398..dfdb07a 100644 --- a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java +++ b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
@@ -4,24 +4,27 @@ package com.android.tools.r8.dex; import com.android.tools.r8.graph.Code; -import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexWritableCode; import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public abstract class MethodToCodeObjectMapping { - public abstract DexCode getCode(DexEncodedMethod method); + public abstract DexWritableCode getCode(DexEncodedMethod method); public abstract void clearCode(DexEncodedMethod method); - public abstract boolean verifyCodeObjects(Collection<DexCode> codes); + public abstract boolean verifyCodeObjects(Collection<DexEncodedMethod> codes); public static MethodToCodeObjectMapping fromMethodBacking() { return MethodBacking.INSTANCE; } - public static MethodToCodeObjectMapping fromMapBacking(Map<DexEncodedMethod, DexCode> map) { + public static MethodToCodeObjectMapping fromMapBacking( + Map<DexEncodedMethod, DexWritableCode> map) { return new MapBacking(map); } @@ -30,33 +33,33 @@ private static final MethodBacking INSTANCE = new MethodBacking(); @Override - public DexCode getCode(DexEncodedMethod method) { + public DexWritableCode getCode(DexEncodedMethod method) { Code code = method.getCode(); - assert code == null || code.isDexCode(); - return code == null ? null : code.asDexCode(); + assert code == null || code.isDexWritableCode(); + return code == null ? null : code.asDexWritableCode(); } @Override public void clearCode(DexEncodedMethod method) { - method.removeCode(); + method.unsetCode(); } @Override - public boolean verifyCodeObjects(Collection<DexCode> codes) { + public boolean verifyCodeObjects(Collection<DexEncodedMethod> codes) { return true; } } private static class MapBacking extends MethodToCodeObjectMapping { - private final Map<DexEncodedMethod, DexCode> codes; + private final Map<DexEncodedMethod, DexWritableCode> codes; - public MapBacking(Map<DexEncodedMethod, DexCode> codes) { + public MapBacking(Map<DexEncodedMethod, DexWritableCode> codes) { this.codes = codes; } @Override - public DexCode getCode(DexEncodedMethod method) { + public DexWritableCode getCode(DexEncodedMethod method) { return codes.get(method); } @@ -67,7 +70,10 @@ } @Override - public boolean verifyCodeObjects(Collection<DexCode> codes) { + public boolean verifyCodeObjects(Collection<DexEncodedMethod> methods) { + // TODO(b/204056443): Convert to a Set<DexWritableCode> when DexCode#hashCode() works. + List<DexWritableCode> codes = + methods.stream().map(this::getCode).collect(Collectors.toList()); assert this.codes.values().containsAll(codes); return true; }
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java index 2cedf5c..0ca695e 100644 --- a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java +++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -6,13 +6,13 @@ import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexAnnotationDirectory; import com.android.tools.r8.graph.DexAnnotationSet; -import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.graph.DexDebugInfo; import com.android.tools.r8.graph.DexEncodedArray; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItem; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexTypeList; +import com.android.tools.r8.graph.DexWritableCode; import com.android.tools.r8.graph.ParameterAnnotationsList; /** @@ -66,11 +66,11 @@ /** * Adds the given code item to the collection. * - * Does not add any dependencies. + * <p>Does not add any dependencies. * * @return true if the item was not added before */ - public abstract boolean add(DexCode dexCode); + public abstract boolean add(DexEncodedMethod method, DexWritableCode dexCode); /** * Adds the given debug info to the collection.
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java index 4d3d50a..cfb13ae 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -514,6 +514,10 @@ return options().testing; } + public boolean hasRootSet() { + return rootSet != null; + } + public RootSet rootSet() { return rootSet; } @@ -711,7 +715,10 @@ setProguardCompatibilityActions( getProguardCompatibilityActions().withoutPrunedItems(prunedItems)); } - if (mainDexRootSet != null) { + if (hasRootSet()) { + rootSet.pruneItems(prunedItems); + } + if (hasMainDexRootSet()) { setMainDexRootSet(mainDexRootSet.withoutPrunedItems(prunedItems)); } }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java index c043d4a..a48fb5c 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -28,6 +28,7 @@ import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; @@ -61,7 +62,7 @@ import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; -public class CfCode extends Code implements StructuralItem<CfCode> { +public class CfCode extends Code implements CfWritableCode, StructuralItem<CfCode> { public enum StackMapStatus { NOT_VERIFIED, @@ -181,6 +182,11 @@ } @Override + public CfWritableCodeKind getCfWritableCodeKind() { + return CfWritableCodeKind.DEFAULT; + } + + @Override public StructuralMapping<CfCode> getStructuralMapping() { throw new Unreachable(); } @@ -260,11 +266,21 @@ } @Override + public boolean isCfWritableCode() { + return true; + } + + @Override public CfCode asCfCode() { return this; } @Override + public CfWritableCode asCfWritableCode() { + return this; + } + + @Override public void acceptHashing(HashingVisitor visitor) { // Rather than hash the entire content, hash the sizes and each instruction "type" which // should provide a fast yet reasonably distinct key. @@ -315,7 +331,8 @@ return true; } - public void write( + @Override + public void writeCf( ProgramMethod method, CfVersion classFileVersion, AppView<?> appView, @@ -763,7 +780,7 @@ @Override public Code getCodeAsInlining(DexMethod caller, DexMethod callee) { - Position callerPosition = Position.synthetic(0, caller, null); + Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build(); List<CfInstruction> newInstructions = new ArrayList<>(instructions.size() + 2); CfLabel firstLabel; if (instructions.get(0).isLabel()) {
diff --git a/src/main/java/com/android/tools/r8/graph/CfWritableCode.java b/src/main/java/com/android/tools/r8/graph/CfWritableCode.java new file mode 100644 index 0000000..939695f --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
@@ -0,0 +1,60 @@ +// Copyright (c) 2021, 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.graph; + +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.HashingVisitor; +import org.objectweb.asm.MethodVisitor; + +public interface CfWritableCode { + + enum CfWritableCodeKind { + DEFAULT, + DEFAULT_INSTANCE_INITIALIZER, + THROW_NULL + } + + default int acceptCompareTo(CfWritableCode code, CompareToVisitor visitor) { + CfWritableCodeKind kind = getCfWritableCodeKind(); + CfWritableCodeKind otherKind = code.getCfWritableCodeKind(); + if (kind != otherKind) { + return kind.compareTo(otherKind); + } + switch (kind) { + case DEFAULT: + return asCfCode().acceptCompareTo(code.asCfCode(), visitor); + case DEFAULT_INSTANCE_INITIALIZER: + return 0; + case THROW_NULL: + return 0; + default: + throw new Unreachable(); + } + } + + void acceptHashing(HashingVisitor visitor); + + CfWritableCodeKind getCfWritableCodeKind(); + + default boolean isCfCode() { + return false; + } + + default CfCode asCfCode() { + return null; + } + + void writeCf( + ProgramMethod method, + CfVersion classFileVersion, + AppView<?> appView, + NamingLens namingLens, + LensCodeRewriterUtils rewriter, + MethodVisitor visitor); +}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java index 205422c..6e41fa0 100644 --- a/src/main/java/com/android/tools/r8/graph/Code.java +++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -3,12 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import com.android.tools.r8.code.CfOrDexInstruction; import com.android.tools.r8.dex.MixedSectionCollection; import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; -import com.android.tools.r8.ir.optimize.OutlinerImpl.OutlineCode; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; @@ -30,6 +31,10 @@ + getClass().getCanonicalName()); } + public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) { + return null; + } + public abstract void registerCodeReferences(ProgramMethod method, UseRegistry registry); public abstract void registerCodeReferencesForDesugaring( @@ -53,10 +58,26 @@ return false; } + public boolean isCfWritableCode() { + return false; + } + + public boolean isDefaultInstanceInitializerCode() { + return false; + } + + public DefaultInstanceInitializerCode asDefaultInstanceInitializerCode() { + return null; + } + public boolean isDexCode() { return false; } + public boolean isDexWritableCode() { + return false; + } + public boolean isHorizontalClassMergingCode() { return false; } @@ -65,6 +86,17 @@ return false; } + public boolean isSharedCodeObject() { + return false; + } + + public boolean isThrowNullCode() { + return false; + } + + public ThrowNullCode asThrowNullCode() { + return null; + } /** Estimate the number of IR instructions emitted by buildIR(). */ public int estimatedSizeForInlining() { @@ -82,6 +114,10 @@ throw new Unreachable(getClass().getCanonicalName() + ".asCfCode()"); } + public CfWritableCode asCfWritableCode() { + throw new Unreachable(getClass().getCanonicalName() + ".asCfWritableCode()"); + } + public LazyCfCode asLazyCfCode() { throw new Unreachable(getClass().getCanonicalName() + ".asLazyCfCode()"); } @@ -90,8 +126,8 @@ throw new Unreachable(getClass().getCanonicalName() + ".asDexCode()"); } - public OutlineCode asOutlineCode() { - throw new Unreachable(getClass().getCanonicalName() + ".asOutlineCode()"); + public DexWritableCode asDexWritableCode() { + throw new Unreachable(getClass().getCanonicalName() + ".asDexWritableCode()"); } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java new file mode 100644 index 0000000..f336d89 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -0,0 +1,404 @@ +// Copyright (c) 2021, 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.graph; + +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.cf.code.CfInvoke; +import com.android.tools.r8.cf.code.CfLoad; +import com.android.tools.r8.cf.code.CfReturnVoid; +import com.android.tools.r8.code.InvokeDirect; +import com.android.tools.r8.code.ReturnVoid; +import com.android.tools.r8.dex.CodeToKeep; +import com.android.tools.r8.dex.IndexedItemCollection; +import com.android.tools.r8.dex.MixedSectionCollection; +import com.android.tools.r8.graph.DexCode.Try; +import com.android.tools.r8.graph.DexCode.TryHandler; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.NumberGenerator; +import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; +import com.android.tools.r8.ir.code.ValueType; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.IteratorUtils; +import com.android.tools.r8.utils.structural.HashingVisitor; +import com.google.common.collect.ImmutableList; +import java.nio.ShortBuffer; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A piece of code on the form: + * + * <pre> + * aload_0 + * invoke-special LSuperClass;-><init>()V + * return + * </pre> + * + * <p>Note that (i) {@code SuperClass} may be different from {@link java.lang.Object} and (ii) the + * method holding this code object may have a non-empty proto. + */ +public class DefaultInstanceInitializerCode extends Code + implements CfWritableCode, DexWritableCode { + + private static final DefaultInstanceInitializerCode INSTANCE = + new DefaultInstanceInitializerCode(); + + private DefaultInstanceInitializerCode() {} + + public static DefaultInstanceInitializerCode get() { + return INSTANCE; + } + + public static boolean canonicalizeCodeIfPossible(AppView<?> appView, ProgramMethod method) { + if (hasDefaultInstanceInitializerCode(method, appView)) { + method.getDefinition().setCode(get(), appView); + return true; + } + return false; + } + + public static void uncanonicalizeCode(AppView<?> appView, ProgramMethod method) { + uncanonicalizeCode(appView, method, method.getHolder().getSuperType()); + } + + public static void uncanonicalizeCode( + AppView<?> appView, ProgramMethod method, DexType superType) { + DexEncodedMethod definition = method.getDefinition(); + assert definition.getCode().isDefaultInstanceInitializerCode(); + definition.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView); + } + + private static boolean hasDefaultInstanceInitializerCode( + ProgramMethod method, AppView<?> appView) { + if (!method.getDefinition().isInstanceInitializer()) { + return false; + } + Code code = method.getDefinition().getCode(); + if (!code.isCfCode()) { + return false; + } + CfCode cfCode = code.asCfCode(); + if (!method.getDefinition().isInstanceInitializer() + || !cfCode.getLocalVariables().isEmpty() + || !cfCode.getTryCatchRanges().isEmpty()) { + return false; + } + if (cfCode.getInstructions().size() > 6) { + // Default instance initializers typically have the following instruction sequence: + // [CfLabel, CfPosition, CfLoad, CfInvoke, CfReturnVoid, CfLabel]. + return false; + } + DexItemFactory dexItemFactory = appView.dexItemFactory(); + Iterator<CfInstruction> instructionIterator = cfCode.getInstructions().iterator(); + // Allow skipping CfPosition instructions in instance initializers that only call Object.<init>. + Predicate<CfInstruction> instructionOfInterest = + method.getHolder().getSuperType() == dexItemFactory.objectType + ? instruction -> !instruction.isLabel() && !instruction.isPosition() + : instruction -> !instruction.isLabel(); + CfLoad load = IteratorUtils.nextUntil(instructionIterator, instructionOfInterest).asLoad(); + if (load == null || load.getLocalIndex() != 0) { + return false; + } + CfInvoke invoke = instructionIterator.next().asInvoke(); + if (invoke == null + || !invoke.isInvokeConstructor(dexItemFactory) + || invoke.getMethod() != getParentConstructor(method, dexItemFactory)) { + return false; + } + return instructionIterator.next().isReturnVoid(); + } + + @Override + public void acceptHashing(HashingVisitor visitor) { + visitor.visitInt(getCfWritableCodeKind().hashCode()); + } + + @Override + public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { + DefaultInstanceInitializerSourceCode source = new DefaultInstanceInitializerSourceCode(method); + return IRBuilder.create(method, appView, source, origin).build(method); + } + + @Override + public IRCode buildInliningIR( + ProgramMethod context, + ProgramMethod method, + AppView<?> appView, + GraphLens codeLens, + NumberGenerator valueNumberGenerator, + Position callerPosition, + Origin origin, + RewrittenPrototypeDescription protoChanges) { + DefaultInstanceInitializerSourceCode source = + new DefaultInstanceInitializerSourceCode(method, callerPosition); + return IRBuilder.createForInlining( + method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges) + .build(context); + } + + @Override + public int codeSizeInBytes() { + return InvokeDirect.SIZE + ReturnVoid.SIZE; + } + + @Override + public void collectIndexedItems( + IndexedItemCollection indexedItems, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils rewriter) { + getParentConstructor(context, rewriter.dexItemFactory()).collectIndexedItems(indexedItems); + } + + @Override + public void collectMixedSectionItems(MixedSectionCollection mixedItems) { + // Intentionally empty. + } + + @Override + protected int computeHashCode() { + return System.identityHashCode(this); + } + + @Override + protected boolean computeEquals(Object other) { + return this == other; + } + + @Override + public int estimatedDexCodeSizeUpperBoundInBytes() { + return codeSizeInBytes(); + } + + @Override + public CfWritableCodeKind getCfWritableCodeKind() { + return CfWritableCodeKind.DEFAULT_INSTANCE_INITIALIZER; + } + + @Override + public DexWritableCodeKind getDexWritableCodeKind() { + return DexWritableCodeKind.DEFAULT_INSTANCE_INITIALIZER; + } + + @Override + public DexDebugInfoForWriting getDebugInfoForWriting() { + return null; + } + + @Override + public TryHandler[] getHandlers() { + return new TryHandler[0]; + } + + @Override + public DexString getHighestSortingString() { + return null; + } + + @Override + public int getIncomingRegisterSize(ProgramMethod method) { + return getMaxLocals(method); + } + + static DexMethod getParentConstructor(DexClassAndMethod method, DexItemFactory dexItemFactory) { + return dexItemFactory.createInstanceInitializer(method.getHolder().getSuperType()); + } + + private int getMaxLocals(ProgramMethod method) { + int maxLocals = method.getAccessFlags().isStatic() ? 0 : 1; + for (DexType parameter : method.getParameters()) { + maxLocals += parameter.getRequiredRegisters(); + } + return maxLocals; + } + + private int getMaxStack() { + return 1; + } + + @Override + public int getOutgoingRegisterSize() { + return 1; + } + + @Override + public int getRegisterSize(ProgramMethod method) { + return getIncomingRegisterSize(method); + } + + @Override + public Try[] getTries() { + return new Try[0]; + } + + @Override + public boolean isCfWritableCode() { + return true; + } + + @Override + public CfWritableCode asCfWritableCode() { + return this; + } + + @Override + public boolean isDexWritableCode() { + return true; + } + + @Override + public DexWritableCode asDexWritableCode() { + return this; + } + + @Override + public boolean isEmptyVoidMethod() { + return false; + } + + @Override + public boolean isDefaultInstanceInitializerCode() { + return true; + } + + @Override + public DefaultInstanceInitializerCode asDefaultInstanceInitializerCode() { + return this; + } + + @Override + public boolean isSharedCodeObject() { + return true; + } + + @Override + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry<?> registry) { + registry.registerInvokeDirect(getParentConstructor(method, registry.dexItemFactory())); + } + + @Override + public DexWritableCode rewriteCodeWithJumboStrings( + ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) { + // Intentionally empty. This piece of code does not have any const-string instructions. + return this; + } + + @Override + public void setCallSiteContexts(ProgramMethod method) { + // Intentionally empty. This piece of code does not have any call sites. + } + + public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory) { + return toCfCode(method, dexItemFactory, method.getHolder().getSuperType()); + } + + public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory, DexType supertype) { + List<CfInstruction> instructions = + Arrays.asList( + new CfLoad(ValueType.OBJECT, 0), + new CfInvoke( + Opcodes.INVOKESPECIAL, dexItemFactory.createInstanceInitializer(supertype), false), + new CfReturnVoid()); + return new CfCode(method.getHolderType(), getMaxStack(), getMaxLocals(method), instructions); + } + + @Override + public void writeCf( + ProgramMethod method, + CfVersion classFileVersion, + AppView<?> appView, + NamingLens namingLens, + LensCodeRewriterUtils rewriter, + MethodVisitor visitor) { + visitor.visitVarInsn(Opcodes.ALOAD, 0); + visitor.visitMethodInsn( + Opcodes.INVOKESPECIAL, + namingLens.lookupInternalName(method.getHolder().getSuperType()), + "<init>", + "()V", + false); + visitor.visitInsn(Opcodes.RETURN); + visitor.visitEnd(); + visitor.visitMaxs(getMaxStack(), getMaxLocals(method)); + } + + @Override + public void writeDex( + ShortBuffer shortBuffer, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils lensCodeRewriter, + ObjectToOffsetMapping mapping) { + new InvokeDirect(1, getParentConstructor(context, mapping.dexItemFactory()), 0, 0, 0, 0, 0) + .write(shortBuffer, context, graphLens, mapping, lensCodeRewriter); + new ReturnVoid().write(shortBuffer, context, graphLens, mapping, lensCodeRewriter); + } + + @Override + public void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep) { + // Intentionally empty. + } + + @Override + public String toString() { + return "DefaultInstanceInitializerCode"; + } + + @Override + public String toString(DexEncodedMethod method, ClassNameMapper naming) { + return toString(); + } + + static class DefaultInstanceInitializerSourceCode extends SyntheticStraightLineSourceCode { + + DefaultInstanceInitializerSourceCode(ProgramMethod method) { + this(method, null); + } + + DefaultInstanceInitializerSourceCode(ProgramMethod method, Position callerPosition) { + super( + getInstructionBuilders(), + SyntheticPosition.builder() + .setLine(0) + .setMethod(method.getReference()) + .setCallerPosition(callerPosition) + .build()); + } + + private static List<Consumer<IRBuilder>> getInstructionBuilders() { + return ImmutableList.of( + builder -> + builder.add( + com.android.tools.r8.ir.code.InvokeDirect.builder() + .setMethod( + getParentConstructor( + builder.getProgramMethod(), builder.dexItemFactory())) + .setSingleArgument(builder.getReceiverValue()) + .build()), + IRBuilder::addReturn); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java index 8409ee1..8c16e46 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -3,18 +3,25 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import com.android.tools.r8.code.CfOrDexInstruction; import com.android.tools.r8.code.Instruction; import com.android.tools.r8.code.ReturnVoid; import com.android.tools.r8.code.SwitchPayload; +import com.android.tools.r8.dex.CodeToKeep; +import com.android.tools.r8.dex.Constants; import com.android.tools.r8.dex.IndexedItemCollection; +import com.android.tools.r8.dex.JumboStringRewriter; import com.android.tools.r8.dex.MixedSectionCollection; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair; import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame; import com.android.tools.r8.graph.DexDebugEvent.StartLocal; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.conversion.DexSourceCode; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; @@ -23,11 +30,13 @@ import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.structural.Equatable; import com.android.tools.r8.utils.structural.HashCodeVisitor; +import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralItem; import com.android.tools.r8.utils.structural.StructuralMapping; import com.android.tools.r8.utils.structural.StructuralSpecification; import com.google.common.base.Strings; import it.unimi.dsi.fastutil.ints.Int2IntMap; +import java.nio.ShortBuffer; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -37,7 +46,7 @@ import java.util.Set; // DexCode corresponds to code item in dalvik/dex-format.html -public class DexCode extends Code implements StructuralItem<DexCode> { +public class DexCode extends Code implements DexWritableCode, StructuralItem<DexCode> { public static final String FAKE_THIS_PREFIX = "_"; public static final String FAKE_THIS_SUFFIX = "this"; @@ -53,6 +62,8 @@ private DexDebugInfo debugInfo; private DexDebugInfoForWriting debugInfoForWriting; + private final BytecodeMetadata<Instruction> metadata; + private static void specify(StructuralSpecification<DexCode, ?> spec) { spec.withInt(c -> c.registerSize) .withInt(c -> c.incomingRegisterSize) @@ -71,6 +82,26 @@ Try[] tries, TryHandler[] handlers, DexDebugInfo debugInfo) { + this( + registerSize, + insSize, + outsSize, + instructions, + tries, + handlers, + debugInfo, + BytecodeMetadata.empty()); + } + + public DexCode( + int registerSize, + int insSize, + int outsSize, + Instruction[] instructions, + Try[] tries, + TryHandler[] handlers, + DexDebugInfo debugInfo, + BytecodeMetadata<Instruction> metadata) { this.incomingRegisterSize = insSize; this.registerSize = registerSize; this.outgoingRegisterSize = outsSize; @@ -78,6 +109,7 @@ this.tries = tries; this.handlers = handlers; this.debugInfo = debugInfo; + this.metadata = metadata; assert tries != null; assert handlers != null; assert instructions != null; @@ -90,10 +122,54 @@ } @Override + public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) { + return getMetadata(instruction.asDexInstruction()); + } + + public BytecodeInstructionMetadata getMetadata(Instruction instruction) { + return metadata.getMetadata(instruction); + } + + @Override + public DexWritableCodeKind getDexWritableCodeKind() { + return DexWritableCodeKind.DEFAULT; + } + + @Override public StructuralMapping<DexCode> getStructuralMapping() { return DexCode::specify; } + @Override + public DexWritableCode rewriteCodeWithJumboStrings( + ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) { + DexString firstJumboString = null; + if (force) { + firstJumboString = mapping.getFirstString(); + } else { + assert highestSortingString != null + || Arrays.stream(instructions).noneMatch(Instruction::isConstString); + assert Arrays.stream(instructions).noneMatch(Instruction::isDexItemBasedConstString); + if (highestSortingString != null + && mapping.getOffsetFor(highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) { + firstJumboString = mapping.getFirstJumboString(); + } + } + return firstJumboString != null + ? new JumboStringRewriter(method.getDefinition(), firstJumboString, factory).rewrite() + : this; + } + + @Override + public void setCallSiteContexts(ProgramMethod method) { + for (Instruction instruction : instructions) { + DexCallSite callSite = instruction.getCallSite(); + if (callSite != null) { + callSite.setContext(method.getReference(), instruction.getOffset()); + } + } + } + public DexCode withoutThisParameter() { // Note that we assume the original code has a register associated with 'this' // argument of the (former) instance method. We also assume (but do not check) @@ -117,6 +193,16 @@ } @Override + public boolean isDexWritableCode() { + return true; + } + + @Override + public DexWritableCode asDexWritableCode() { + return this; + } + + @Override public int estimatedSizeForInlining() { return codeSizeInBytes(); } @@ -180,7 +266,7 @@ } private DexDebugInfo debugInfoAsInlining(DexMethod caller, DexMethod callee) { - Position callerPosition = Position.synthetic(0, caller, null); + Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build(); if (debugInfo == null) { // If the method has no debug info we generate a preamble position to denote the inlining. // This is consistent with the building IR for inlining which will always ensure the method @@ -239,6 +325,11 @@ } @Override + public void acceptHashing(HashingVisitor visitor) { + visitor.visit(this, getStructuralMapping()); + } + + @Override public int computeHashCode() { return incomingRegisterSize * 2 + registerSize * 3 @@ -451,6 +542,7 @@ return builder.toString(); } + @Override public void collectIndexedItems( IndexedItemCollection indexedItems, ProgramMethod context, @@ -474,6 +566,7 @@ } } + @Override public DexDebugInfoForWriting getDebugInfoForWriting() { if (debugInfo == null) { return null; @@ -485,6 +578,36 @@ return debugInfoForWriting; } + @Override + public TryHandler[] getHandlers() { + return handlers; + } + + @Override + public DexString getHighestSortingString() { + return highestSortingString; + } + + @Override + public Try[] getTries() { + return tries; + } + + @Override + public int getRegisterSize(ProgramMethod method) { + return registerSize; + } + + @Override + public int getIncomingRegisterSize(ProgramMethod method) { + return incomingRegisterSize; + } + + @Override + public int getOutgoingRegisterSize() { + return outgoingRegisterSize; + } + private void updateHighestSortingString(DexString candidate) { assert candidate != null; if (highestSortingString == null || highestSortingString.compareTo(candidate) < 0) { @@ -497,17 +620,59 @@ } @Override - void collectMixedSectionItems(MixedSectionCollection mixedItems) { - if (mixedItems.add(this)) { - if (debugInfo != null) { - getDebugInfoForWriting().collectMixedSectionItems(mixedItems); + public void collectMixedSectionItems(MixedSectionCollection mixedItems) { + if (debugInfo != null) { + getDebugInfoForWriting().collectMixedSectionItems(mixedItems); + } + } + + @Override + public int codeSizeInBytes() { + Instruction last = instructions[instructions.length - 1]; + assert last.hasOffset(); + int result = last.getOffset() + last.getSize(); + assert result == computeCodeSizeInBytes(); + return result; + } + + private int computeCodeSizeInBytes() { + int size = 0; + for (Instruction insn : instructions) { + size += insn.getSize(); + } + return size; + } + + @Override + public void writeKeepRulesForDesugaredLibrary(CodeToKeep desugaredLibraryCodeToKeep) { + for (Instruction instruction : instructions) { + DexMethod method = instruction.getMethod(); + DexField field = instruction.getField(); + if (field != null) { + assert method == null; + desugaredLibraryCodeToKeep.recordField(field); + } else if (method != null) { + desugaredLibraryCodeToKeep.recordMethod(method); + } else if (instruction.isConstClass()) { + desugaredLibraryCodeToKeep.recordClass(instruction.asConstClass().getType()); + } else if (instruction.isInstanceOf()) { + desugaredLibraryCodeToKeep.recordClass(instruction.asInstanceOf().getType()); + } else if (instruction.isCheckCast()) { + desugaredLibraryCodeToKeep.recordClass(instruction.asCheckCast().getType()); } } } - public int codeSizeInBytes() { - Instruction last = instructions[instructions.length - 1]; - return last.getOffset() + last.getSize(); + @Override + public void writeDex( + ShortBuffer shortBuffer, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils lensCodeRewriter, + ObjectToOffsetMapping mapping) { + for (Instruction instruction : instructions) { + instruction.write(shortBuffer, context, graphLens, mapping, lensCodeRewriter); + } } public static class Try extends DexItem implements StructuralItem<Try> {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java index 8eb786a..2547996 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -63,8 +63,8 @@ builder.append(":").append(method.name); Position caller = callerPosition; while (caller != null) { - builder.append(";").append(caller.line).append(":").append(caller.method.name); - caller = caller.callerPosition; + builder.append(";").append(caller.getLine()).append(":").append(caller.getMethod().name); + caller = caller.getCallerPosition(); } } if (prologueEnd) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java index 09bc3b7..ce338d6 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineCallerFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineFrame; import com.android.tools.r8.ir.code.ValueType; import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap; @@ -113,6 +115,16 @@ } @Override + public void visit(SetOutlineFrame setOutlineFrame) { + positionState.visit(setOutlineFrame); + } + + @Override + public void visit(SetOutlineCallerFrame setOutlineCallerFrame) { + positionState.visit(setOutlineCallerFrame); + } + + @Override public void visit(DexDebugEvent.Default defaultEvent) { positionState.visit(defaultEvent); entryEventReceived(true);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java index dfd4f1c..bb93147 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.errors.InternalCompilerError; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.utils.Int2StructuralItemArrayMap; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralItem; @@ -21,6 +22,8 @@ // Compare ID(s) for virtual debug events. private static final int DBG_SET_INLINE_FRAME_COMPARE_ID = Constants.DBG_LAST_SPECIAL + 1; + private static final int DBG_SET_OUTLINE_FRAME_COMPARE_ID = Constants.DBG_LAST_SPECIAL + 2; + private static final int DBG_SET_OUTLINE_CALLER_COMPARE_ID = Constants.DBG_LAST_SPECIAL + 3; public static final DexDebugEvent[] EMPTY_ARRAY = {}; @@ -84,10 +87,22 @@ return false; } + public boolean isSetOutlineFrame() { + return false; + } + + public boolean isSetOutlineCallerFrame() { + return false; + } + public SetInlineFrame asSetInlineFrame() { return null; } + public SetOutlineCallerFrame asSetOutlineCallerFrame() { + return null; + } + public static class AdvancePC extends DexDebugEvent { public final int delta; @@ -579,7 +594,118 @@ public boolean hasOuterPosition(DexMethod method) { return (caller == null && callee == method) - || (caller != null && caller.getOutermostCaller().method == method); + || (caller != null && caller.getOutermostCaller().getMethod() == method); + } + } + + public static class SetOutlineFrame extends DexDebugEvent { + + @Override + public String toString() { + return "SET_OUTLINE_FRAME"; + } + + @Override + public int hashCode() { + return 7; + } + + @Override + int getCompareToId() { + return DBG_SET_OUTLINE_FRAME_COMPARE_ID; + } + + @Override + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return 0; + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + // Intentionally empty: no content besides the compare-id + } + + @Override + public void writeOn( + DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) { + // Will not be written + } + + @Override + public void accept(DexDebugEventVisitor visitor) { + visitor.visit(this); + } + } + + public static class SetOutlineCallerFrame extends DexDebugEvent { + + private final DexMethod outlineCallee; + private final Int2StructuralItemArrayMap<Position> outlinePositions; + + private static void specify(StructuralSpecification<SetOutlineCallerFrame, ?> spec) { + spec.withItem(e -> e.outlineCallee).withNullableItem(e -> e.outlinePositions); + } + + SetOutlineCallerFrame( + DexMethod outlineCallee, Int2StructuralItemArrayMap<Position> outlinePositions) { + assert outlineCallee != null; + assert !outlinePositions.isEmpty(); + this.outlineCallee = outlineCallee; + this.outlinePositions = outlinePositions; + } + + public DexMethod getOutlineCallee() { + return outlineCallee; + } + + public Int2StructuralItemArrayMap<Position> getOutlinePositions() { + return outlinePositions; + } + + @Override + public void writeOn( + DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) { + // CallerPosition will not be written. + } + + @Override + public void accept(DexDebugEventVisitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return String.format("SET_OUTLINE_CALLER_FRAME %s %s", outlineCallee, outlinePositions); + } + + @Override + public int hashCode() { + return Objects.hash(outlineCallee, outlinePositions); + } + + @Override + int getCompareToId() { + return DBG_SET_OUTLINE_CALLER_COMPARE_ID; + } + + @Override + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visit(this, (SetOutlineCallerFrame) other, SetOutlineCallerFrame::specify); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visit(this, SetOutlineCallerFrame::specify); + } + + @Override + public boolean isSetOutlineCallerFrame() { + return true; + } + + @Override + public SetOutlineCallerFrame asSetOutlineCallerFrame() { + return this; } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java index 7a8a3f6..f8bfbeb 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SourcePosition; import com.android.tools.r8.utils.InternalOptions; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry; @@ -183,17 +184,17 @@ assert !position.equals(emittedPosition); if (startLine == NO_LINE_INFO) { assert emittedPosition.isNone(); - if (position.synthetic && position.callerPosition == null) { + if (position.isSyntheticPosition() && !position.hasCallerPosition()) { // Ignore synthetic positions prior to any actual position. // We do need to preserve synthetic position establishing the stack frame for inlined // methods. return; } - startLine = position.line; + startLine = position.getLine(); emittedPosition = - Position.builder() - .setLine(position.line) - .setMethod(position.getOutermostCaller().method) + SourcePosition.builder() + .setLine(position.getLine()) + .setMethod(position.getOutermostCaller().getMethod()) .build(); } assert emittedPc != pc; @@ -235,21 +236,30 @@ assert previousPc >= 0; int pcDelta = nextPc - previousPc; assert !previousPosition.isNone() || nextPosition.isNone(); - assert nextPosition.isNone() || nextPosition.line >= 0; - int lineDelta = nextPosition.isNone() ? 0 : nextPosition.line - previousPosition.line; + assert nextPosition.isNone() || nextPosition.getLine() >= 0; + int lineDelta = nextPosition.isNone() ? 0 : nextPosition.getLine() - previousPosition.getLine(); assert pcDelta >= 0; - if (nextPosition.file != previousPosition.file) { - events.add(factory.createSetFile(nextPosition.file)); + if (nextPosition.getFile() != previousPosition.getFile()) { + events.add(factory.createSetFile(nextPosition.getFile())); } // The LineNumberOptimizer maps new positions based on the outer most caller with // callerPosition == null. - assert null != nextPosition.callerPosition - || null != previousPosition.callerPosition - || nextPosition.method == previousPosition.method + assert nextPosition.hasCallerPosition() + || previousPosition.hasCallerPosition() + || nextPosition.getMethod() == previousPosition.getMethod() || optimizingLineNumbers; - if (nextPosition.callerPosition != previousPosition.callerPosition - || nextPosition.method != previousPosition.method) { - events.add(factory.createSetInlineFrame(nextPosition.method, nextPosition.callerPosition)); + if (nextPosition.getCallerPosition() != previousPosition.getCallerPosition() + || nextPosition.getMethod() != previousPosition.getMethod()) { + events.add( + factory.createSetInlineFrame(nextPosition.getMethod(), nextPosition.getCallerPosition())); + } + if (nextPosition.isOutline()) { + events.add(factory.createSetOutlineFrame()); + } + if (nextPosition.getOutlineCallee() != null) { + events.add( + factory.createSetOutlineCallerFrame( + nextPosition.getOutlineCallee(), nextPosition.getOutlinePositions())); } if (lineDelta < Constants.DBG_LINE_BASE || lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java index 464c93d..165858b 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java
@@ -11,6 +11,8 @@ import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin; import com.android.tools.r8.graph.DexDebugEvent.SetFile; import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineCallerFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineFrame; import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd; import com.android.tools.r8.graph.DexDebugEvent.StartLocal; @@ -21,6 +23,10 @@ void visit(SetInlineFrame setInlineFrame); + void visit(SetOutlineFrame setOutlineFrame); + + void visit(SetOutlineCallerFrame setOutlineCallerFrame); + void visit(Default defaultEvent); void visit(SetFile setFile);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java index b56ad04..9c5ffd6 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
@@ -12,9 +12,12 @@ import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin; import com.android.tools.r8.graph.DexDebugEvent.SetFile; import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineCallerFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineFrame; import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd; import com.android.tools.r8.graph.DexDebugEvent.StartLocal; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.utils.Int2StructuralItemArrayMap; /** * State machine to process and accumulate position-related DexDebugEvents. Clients should retrieve @@ -27,6 +30,9 @@ private DexString currentFile = null; private DexMethod currentMethod; private Position currentCallerPosition = null; + private boolean isOutline; + private DexMethod outlineCallee; + private Int2StructuralItemArrayMap<Position> outlineCallerPositions; public DexDebugPositionState(int startLine, DexMethod method) { currentLine = startLine; @@ -51,6 +57,17 @@ } @Override + public void visit(SetOutlineFrame setOutlineFrame) { + isOutline = true; + } + + @Override + public void visit(SetOutlineCallerFrame setOutlineCallerFrame) { + outlineCallee = setOutlineCallerFrame.getOutlineCallee(); + outlineCallerPositions = setOutlineCallerFrame.getOutlinePositions(); + } + + @Override public void visit(Default defaultEvent) { assert defaultEvent.getPCDelta() >= 0; currentPc += defaultEvent.getPCDelta(); @@ -106,4 +123,22 @@ public Position getCurrentCallerPosition() { return currentCallerPosition; } + + public boolean isOutline() { + return isOutline; + } + + public DexMethod getOutlineCallee() { + return outlineCallee; + } + + public Int2StructuralItemArrayMap<Position> getOutlineCallerPositions() { + return outlineCallerPositions; + } + + public void resetOutlineInformation() { + isOutline = false; + outlineCallee = null; + outlineCallerPositions = null; + } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index 209e51c..d3be3c2 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -40,8 +40,6 @@ import com.android.tools.r8.code.Return; import com.android.tools.r8.code.Throw; import com.android.tools.r8.code.XorIntLit8; -import com.android.tools.r8.dex.Constants; -import com.android.tools.r8.dex.JumboStringRewriter; import com.android.tools.r8.dex.MethodToCodeObjectMapping; import com.android.tools.r8.dex.MixedSectionCollection; import com.android.tools.r8.errors.InternalCompilerError; @@ -50,6 +48,7 @@ import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection; import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.NumericType; @@ -305,21 +304,22 @@ } private static int compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) { - if (code1.isCfCode() && code2.isCfCode()) { - return code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor); + if (code1.isCfWritableCode() && code2.isCfWritableCode()) { + return code1.asCfWritableCode().acceptCompareTo(code2.asCfWritableCode(), visitor); } - if (code1.isDexCode() && code2.isDexCode()) { - return code1.asDexCode().acceptCompareTo(code2.asDexCode(), visitor); + if (code1.isDexWritableCode() && code2.isDexWritableCode()) { + return code1.asDexWritableCode().acceptCompareTo(code2.asDexWritableCode(), visitor); } throw new Unreachable( "Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2); } private static void hashCodeObject(Code code, HashingVisitor visitor) { - if (code.isCfCode()) { - code.asCfCode().acceptHashing(visitor); + if (code.isCfWritableCode()) { + code.asCfWritableCode().acceptHashing(visitor); } else { - code.asDexCode().acceptHashing(visitor); + assert code.isDexWritableCode(); + code.asDexWritableCode().acceptHashing(visitor); } } @@ -395,6 +395,11 @@ return null; } + public ProgramMethod asProgramMethod(DexProgramClass holder) { + assert getHolderType() == holder.getType(); + return new ProgramMethod(holder, this); + } + public ProgramMethod asProgramMethod(DexDefinitionSupplier definitions) { assert getReference().holder.isClassType(); DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference())); @@ -727,9 +732,13 @@ code = newCode; } - public void setCode(IRCode ir, RegisterAllocator registerAllocator, AppView<?> appView) { + public void setCode( + IRCode ir, + BytecodeMetadataProvider bytecodeMetadataProvider, + RegisterAllocator registerAllocator, + AppView<?> appView) { checkIfObsolete(); - DexBuilder builder = new DexBuilder(ir, registerAllocator); + DexBuilder builder = new DexBuilder(ir, bytecodeMetadataProvider, registerAllocator); setCode(builder.build(), appView); } @@ -771,8 +780,8 @@ public void collectMixedSectionItemsWithCodeMapping( MixedSectionCollection mixedItems, MethodToCodeObjectMapping mapping) { - DexCode code = mapping.getCode(this); - if (code != null) { + DexWritableCode code = mapping.getCode(this); + if (code != null && mixedItems.add(this, code)) { code.collectMixedSectionItems(mixedItems); } annotations().collectMixedSectionItems(mixedItems); @@ -792,11 +801,6 @@ return code; } - public void removeCode() { - checkIfObsolete(); - code = null; - } - public CfVersion getClassFileVersion() { checkIfObsolete(); assert classFileVersion != null; @@ -1293,34 +1297,6 @@ return method; } - /** Rewrites the code in this method to have JumboString bytecode if required by mapping. */ - public DexCode rewriteCodeWithJumboStrings( - ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) { - checkIfObsolete(); - assert code == null || code.isDexCode(); - if (code == null) { - return null; - } - DexCode code = this.code.asDexCode(); - DexString firstJumboString = null; - if (force) { - firstJumboString = mapping.getFirstString(); - } else { - assert code.highestSortingString != null - || Arrays.stream(code.instructions).noneMatch(Instruction::isConstString); - assert Arrays.stream(code.instructions).noneMatch(Instruction::isDexItemBasedConstString); - if (code.highestSortingString != null - && mapping.getOffsetFor(code.highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) { - firstJumboString = mapping.getFirstJumboString(); - } - } - if (firstJumboString != null) { - JumboStringRewriter rewriter = new JumboStringRewriter(this, firstJumboString, factory); - return rewriter.rewrite(); - } - return code; - } - public String codeToString() { checkIfObsolete(); return code == null ? "<no code>" : code.toString(this, null);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java index c1c3fb7..d72df8c 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -18,6 +18,8 @@ import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin; import com.android.tools.r8.graph.DexDebugEvent.SetFile; import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineCallerFrame; +import com.android.tools.r8.graph.DexDebugEvent.SetOutlineFrame; import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd; import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType; import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; @@ -34,6 +36,7 @@ import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.utils.ArrayUtils; import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.Int2StructuralItemArrayMap; import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.LRUCacheTable; import com.android.tools.r8.utils.ListUtils; @@ -92,6 +95,7 @@ private final SetEpilogueBegin setEpilogueBegin = new SetEpilogueBegin(); private final SetPrologueEnd setPrologueEnd = new SetPrologueEnd(); private final Map<DexString, SetFile> setFiles = new HashMap<>(); + private final SetOutlineFrame setOutlineFrame = new SetOutlineFrame(); private final Map<SetInlineFrame, SetInlineFrame> setInlineFrames = new HashMap<>(); // ReferenceTypeElement canonicalization. @@ -2768,6 +2772,15 @@ } } + public SetOutlineFrame createSetOutlineFrame() { + return setOutlineFrame; + } + + public SetOutlineCallerFrame createSetOutlineCallerFrame( + DexMethod outlineCallee, Int2StructuralItemArrayMap<Position> outlinePositions) { + return new SetOutlineCallerFrame(outlineCallee, outlinePositions); + } + public boolean isConstructor(DexMethod method) { return method.name == constructorMethodName; }
diff --git a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java new file mode 100644 index 0000000..664b4e8 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
@@ -0,0 +1,94 @@ +// Copyright (c) 2021, 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.graph; + +import com.android.tools.r8.dex.CodeToKeep; +import com.android.tools.r8.dex.IndexedItemCollection; +import com.android.tools.r8.dex.MixedSectionCollection; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.DexCode.Try; +import com.android.tools.r8.graph.DexCode.TryHandler; +import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.HashingVisitor; +import java.nio.ShortBuffer; + +public interface DexWritableCode { + + enum DexWritableCodeKind { + DEFAULT, + DEFAULT_INSTANCE_INITIALIZER, + THROW_NULL + } + + default int acceptCompareTo(DexWritableCode code, CompareToVisitor visitor) { + DexWritableCodeKind kind = getDexWritableCodeKind(); + DexWritableCodeKind otherKind = code.getDexWritableCodeKind(); + if (kind != otherKind) { + return kind.compareTo(otherKind); + } + switch (kind) { + case DEFAULT: + return asDexCode().acceptCompareTo(code.asDexCode(), visitor); + case DEFAULT_INSTANCE_INITIALIZER: + return 0; + case THROW_NULL: + return 0; + default: + throw new Unreachable(); + } + } + + void acceptHashing(HashingVisitor visitor); + + int codeSizeInBytes(); + + void collectIndexedItems( + IndexedItemCollection indexedItems, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils rewriter); + + void collectMixedSectionItems(MixedSectionCollection mixedItems); + + void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep); + + DexDebugInfoForWriting getDebugInfoForWriting(); + + DexWritableCodeKind getDexWritableCodeKind(); + + DexString getHighestSortingString(); + + TryHandler[] getHandlers(); + + Try[] getTries(); + + int getRegisterSize(ProgramMethod method); + + int getIncomingRegisterSize(ProgramMethod method); + + int getOutgoingRegisterSize(); + + default boolean isDexCode() { + return false; + } + + default DexCode asDexCode() { + return null; + } + + /** Rewrites the code to have JumboString bytecode if required by mapping. */ + DexWritableCode rewriteCodeWithJumboStrings( + ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force); + + void setCallSiteContexts(ProgramMethod method); + + void writeDex( + ShortBuffer shortBuffer, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils lensCodeRewriter, + ObjectToOffsetMapping mapping); +}
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java index f222dbf..9589d1e 100644 --- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java +++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -168,6 +168,8 @@ // If code is (lazy) CF code, then use the CF code object rather than the lazy wrapper. if (code.isCfCode()) { code = code.asCfCode(); + } else if (code.isSharedCodeObject()) { + continue; } DexEncodedMethod otherMethod = codeOwners.put(code, method); assert otherMethod == null;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java index f0675ba..9236d1f 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLens.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -284,6 +284,10 @@ public abstract DexField getOriginalFieldSignature(DexField field); + public final DexMember<?, ?> getOriginalMemberSignature(DexMember<?, ?> member) { + return member.apply(this::getOriginalFieldSignature, this::getOriginalMethodSignature); + } + public final DexMethod getOriginalMethodSignature(DexMethod method) { return getOriginalMethodSignature(method, null); } @@ -572,7 +576,7 @@ } public <R extends DexReference, T> Map<R, T> rewriteReferenceKeys( - Map<R, T> map, Function<List<T>, T> merge) { + Map<R, T> map, BiFunction<R, List<T>, T> merge) { Map<R, T> result = new IdentityHashMap<>(); Map<R, List<T>> needsMerge = new IdentityHashMap<>(); map.forEach( @@ -593,7 +597,7 @@ }); needsMerge.forEach( (rewrittenReference, unmergedValues) -> { - T mergedValue = merge.apply(unmergedValues); + T mergedValue = merge.apply(rewrittenReference, unmergedValues); if (mergedValue != null) { result.put(rewrittenReference, mergedValue); }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java index 0aaa24e..2403804 100644 --- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java +++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -57,6 +57,7 @@ import com.android.tools.r8.ir.code.Monitor; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SourcePosition; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; @@ -120,6 +121,11 @@ } @Override + public boolean isCfWritableCode() { + return true; + } + + @Override public LazyCfCode asLazyCfCode() { return this; } @@ -133,6 +139,11 @@ return code; } + @Override + public CfWritableCode asCfWritableCode() { + return asCfCode(); + } + private void internalParseCode() { ReparseContext context = this.context; JarApplicationReader application = this.application; @@ -781,7 +792,7 @@ factory.createField(createTypeFromInternalType(owner), factory.createType(desc), name); // TODO(mathiasr): Don't require CfFieldInstruction::declaringField. It is needed for proper // renaming in the backend, but it is not available here in the frontend. - instructions.add(new CfFieldInstruction(opcode, field, field)); + instructions.add(CfFieldInstruction.create(opcode, field, field)); } @Override @@ -1007,7 +1018,7 @@ if (debugParsingOptions.lineInfo) { instructions.add( new CfPosition( - getLabel(start), Position.builder().setLine(line).setMethod(method).build())); + getLabel(start), SourcePosition.builder().setLine(line).setMethod(method).build())); } }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java index c1ff02b..287ef0f 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -84,6 +84,10 @@ return null; } + public DexClass getResolvedHolder() { + return null; + } + public DexEncodedMethod getResolvedMethod() { return null; }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java index 459c008..aa5e4c8 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -19,8 +19,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -144,6 +146,25 @@ return builder(true, null).rewrittenWithLens(this, definitions, lens).build(definitions); } + public ObjectAllocationInfoCollectionImpl withoutPrunedItems(PrunedItems prunedItems) { + if (prunedItems.hasRemovedMethods()) { + Iterator<Entry<DexProgramClass, Set<DexEncodedMethod>>> iterator = + classesWithAllocationSiteTracking.entrySet().iterator(); + while (iterator.hasNext()) { + Entry<DexProgramClass, Set<DexEncodedMethod>> entry = iterator.next(); + Set<DexEncodedMethod> allocationSites = entry.getValue(); + allocationSites.removeIf( + allocationSite -> + prunedItems.getRemovedMethods().contains(allocationSite.getReference())); + if (allocationSites.isEmpty()) { + classesWithoutAllocationSiteTracking.add(entry.getKey()); + iterator.remove(); + } + } + } + return this; + } + public void forEachInstantiatedSubType( DexType type, Consumer<DexProgramClass> onClass,
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java index 196bc31..04b3eb0 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -29,6 +29,7 @@ private final static int NOT_FOUND = -1; private final static int NOT_SET = -2; + private final AppView<?> appView; private final GraphLens graphLens; private final NamingLens namingLens; private final InitClassLens initClassLens; @@ -74,6 +75,7 @@ assert callSites != null; assert methodHandles != null; assert initClassLens != null; + this.appView = appView; this.graphLens = graphLens; this.namingLens = namingLens; this.initClassLens = initClassLens; @@ -228,6 +230,10 @@ return map == null ? Collections.emptyList() : map.keySet(); } + public DexItemFactory dexItemFactory() { + return appView.dexItemFactory(); + } + public GraphLens getGraphLens() { return graphLens; }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDexCode.java b/src/main/java/com/android/tools/r8/graph/ProgramDexCode.java index 93e42d1..4e15eac 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramDexCode.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramDexCode.java
@@ -6,15 +6,15 @@ public class ProgramDexCode { - private final DexCode code; + private final DexWritableCode code; private final ProgramMethod method; - public ProgramDexCode(DexCode code, ProgramMethod method) { + public ProgramDexCode(DexWritableCode code, ProgramMethod method) { this.code = code; this.method = method; } - public DexCode getCode() { + public DexWritableCode getCode() { return code; }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java index 146e33f..844b45e 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -61,11 +61,10 @@ IndexedItemCollection indexedItems, GraphLens graphLens, LensCodeRewriterUtils rewriter) { DexEncodedMethod definition = getDefinition(); assert !definition.isObsolete(); - assert !definition.hasCode() || definition.getCode().isDexCode(); getReference().collectIndexedItems(indexedItems); - Code code = definition.getCode(); - if (code != null && code.isDexCode()) { - code.asDexCode().collectIndexedItems(indexedItems, this, graphLens, rewriter); + if (definition.hasCode()) { + Code code = definition.getCode(); + code.asDexWritableCode().collectIndexedItems(indexedItems, this, graphLens, rewriter); } definition.annotations().collectIndexedItems(indexedItems); definition.parameterAnnotationsList.collectIndexedItems(indexedItems); @@ -106,9 +105,8 @@ public void convertToThrowNullMethod(AppView<?> appView) { MethodAccessFlags accessFlags = getAccessFlags(); accessFlags.demoteFromAbstract(); - Code emptyThrowingCode = getDefinition().buildEmptyThrowingCode(appView.options()); getDefinition().setApiLevelForCode(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView)); - getDefinition().setCode(emptyThrowingCode, appView); + getDefinition().setCode(ThrowNullCode.get(), appView); getSimpleFeedback().markProcessed(getDefinition(), ConstraintWithTarget.ALWAYS); getSimpleFeedback().unsetOptimizationInfoForThrowNullMethod(this); }
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java index 4489421..4f901fd 100644 --- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java +++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -55,6 +55,10 @@ return removedMethods.contains(method) || removedClasses.contains(method.getHolderType()); } + public boolean isRemoved(DexReference reference) { + return reference.apply(this::isRemoved, this::isRemoved, this::isRemoved); + } + public boolean isRemoved(DexType type) { return removedClasses.contains(type); } @@ -107,7 +111,7 @@ private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet(); private Set<DexType> removedClasses = Sets.newIdentityHashSet(); private final Set<DexField> removedFields = Sets.newIdentityHashSet(); - private final Set<DexMethod> removedMethods = Sets.newIdentityHashSet(); + private Set<DexMethod> removedMethods = Sets.newIdentityHashSet(); public Builder setPrunedApp(DexApplication prunedApp) { this.prunedApp = prunedApp; @@ -146,6 +150,11 @@ return this; } + public Builder setRemovedMethods(Set<DexMethod> removedMethods) { + this.removedMethods = removedMethods; + return this; + } + public PrunedItems build() { return new PrunedItems( prunedApp,
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java new file mode 100644 index 0000000..eb7728c --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -0,0 +1,281 @@ +// Copyright (c) 2021, 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.graph; + +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.code.Const4; +import com.android.tools.r8.code.Throw; +import com.android.tools.r8.dex.CodeToKeep; +import com.android.tools.r8.dex.IndexedItemCollection; +import com.android.tools.r8.dex.MixedSectionCollection; +import com.android.tools.r8.graph.DexCode.Try; +import com.android.tools.r8.graph.DexCode.TryHandler; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.NumberGenerator; +import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.structural.HashingVisitor; +import com.google.common.collect.ImmutableList; +import java.nio.ShortBuffer; +import java.util.List; +import java.util.function.Consumer; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class ThrowNullCode extends Code implements CfWritableCode, DexWritableCode { + + private static final ThrowNullCode INSTANCE = new ThrowNullCode(); + + private ThrowNullCode() {} + + public static ThrowNullCode get() { + return INSTANCE; + } + + @Override + public void acceptHashing(HashingVisitor visitor) { + visitor.visitInt(getCfWritableCodeKind().hashCode()); + } + + @Override + public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { + ThrowNullSourceCode source = new ThrowNullSourceCode(method); + return IRBuilder.create(method, appView, source, origin).build(method); + } + + @Override + public IRCode buildInliningIR( + ProgramMethod context, + ProgramMethod method, + AppView<?> appView, + GraphLens codeLens, + NumberGenerator valueNumberGenerator, + Position callerPosition, + Origin origin, + RewrittenPrototypeDescription protoChanges) { + ThrowNullSourceCode source = new ThrowNullSourceCode(method, callerPosition); + return IRBuilder.createForInlining( + method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges) + .build(context); + } + + @Override + public int codeSizeInBytes() { + return Const4.SIZE + Throw.SIZE; + } + + @Override + public void collectIndexedItems( + IndexedItemCollection indexedItems, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils rewriter) { + // Intentionally empty. + } + + @Override + public void collectMixedSectionItems(MixedSectionCollection mixedItems) { + // Intentionally empty. + } + + @Override + protected int computeHashCode() { + return System.identityHashCode(this); + } + + @Override + protected boolean computeEquals(Object other) { + return this == other; + } + + @Override + public int estimatedDexCodeSizeUpperBoundInBytes() { + return codeSizeInBytes(); + } + + @Override + public CfWritableCodeKind getCfWritableCodeKind() { + return CfWritableCodeKind.THROW_NULL; + } + + @Override + public DexWritableCodeKind getDexWritableCodeKind() { + return DexWritableCodeKind.THROW_NULL; + } + + @Override + public DexDebugInfoForWriting getDebugInfoForWriting() { + return null; + } + + @Override + public TryHandler[] getHandlers() { + return new TryHandler[0]; + } + + @Override + public DexString getHighestSortingString() { + return null; + } + + @Override + public int getIncomingRegisterSize(ProgramMethod method) { + return getMaxLocals(method); + } + + private int getMaxLocals(ProgramMethod method) { + int maxLocals = method.getAccessFlags().isStatic() ? 0 : 1; + for (DexType parameter : method.getParameters()) { + maxLocals += parameter.getRequiredRegisters(); + } + return maxLocals; + } + + @Override + public int getOutgoingRegisterSize() { + return 0; + } + + @Override + public int getRegisterSize(ProgramMethod method) { + return Math.max(getIncomingRegisterSize(method), 1); + } + + @Override + public Try[] getTries() { + return new Try[0]; + } + + @Override + public boolean isCfWritableCode() { + return true; + } + + @Override + public CfWritableCode asCfWritableCode() { + return this; + } + + @Override + public boolean isDexWritableCode() { + return true; + } + + @Override + public DexWritableCode asDexWritableCode() { + return this; + } + + @Override + public boolean isEmptyVoidMethod() { + return false; + } + + @Override + public boolean isSharedCodeObject() { + return true; + } + + @Override + public boolean isThrowNullCode() { + return true; + } + + @Override + public ThrowNullCode asThrowNullCode() { + return this; + } + + @Override + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { + // Intentionally empty. + } + + @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { + // Intentionally empty. + } + + @Override + public DexWritableCode rewriteCodeWithJumboStrings( + ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) { + // Intentionally empty. This piece of code does not have any const-string instructions. + return this; + } + + @Override + public void setCallSiteContexts(ProgramMethod method) { + // Intentionally empty. This piece of code does not have any call sites. + } + + @Override + public void writeCf( + ProgramMethod method, + CfVersion classFileVersion, + AppView<?> appView, + NamingLens namingLens, + LensCodeRewriterUtils rewriter, + MethodVisitor visitor) { + int maxStack = 1; + visitor.visitInsn(Opcodes.ACONST_NULL); + visitor.visitInsn(Opcodes.ATHROW); + visitor.visitEnd(); + visitor.visitMaxs(maxStack, getMaxLocals(method)); + } + + @Override + public void writeDex( + ShortBuffer shortBuffer, + ProgramMethod context, + GraphLens graphLens, + LensCodeRewriterUtils lensCodeRewriter, + ObjectToOffsetMapping mapping) { + int register = 0; + new Const4(register, 0).write(shortBuffer, context, graphLens, mapping, lensCodeRewriter); + new Throw(register).write(shortBuffer, context, graphLens, mapping, lensCodeRewriter); + } + + @Override + public void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep) { + // Intentionally empty. + } + + @Override + public String toString() { + return "ThrowNullCode"; + } + + @Override + public String toString(DexEncodedMethod method, ClassNameMapper naming) { + return "ThrowNullCode"; + } + + static class ThrowNullSourceCode extends SyntheticStraightLineSourceCode { + + ThrowNullSourceCode(ProgramMethod method) { + this(method, null); + } + + ThrowNullSourceCode(ProgramMethod method, Position callerPosition) { + super( + getInstructionBuilders(), + SyntheticPosition.builder() + .setLine(0) + .setMethod(method.getReference()) + .setCallerPosition(callerPosition) + .build()); + } + + private static List<Consumer<IRBuilder>> getInstructionBuilders() { + return ImmutableList.of(builder -> builder.addNullConst(0), builder -> builder.addThrow(0)); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java index 4dd921c..8ac7c59 100644 --- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java +++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -3,7 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import com.android.tools.r8.code.CfOrDexInstanceFieldRead; import com.android.tools.r8.code.CfOrDexInstruction; +import com.android.tools.r8.code.CfOrDexStaticFieldRead; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.utils.TraversalContinuation; import java.util.ListIterator; @@ -84,6 +86,10 @@ public abstract void registerInstanceFieldRead(DexField field); + public void registerInstanceFieldReadInstruction(CfOrDexInstanceFieldRead instruction) { + registerInstanceFieldRead(instruction.getField()); + } + public void registerInstanceFieldReadFromMethodHandle(DexField field) { registerInstanceFieldRead(field); } @@ -108,6 +114,10 @@ public abstract void registerStaticFieldRead(DexField field); + public void registerStaticFieldReadInstruction(CfOrDexStaticFieldRead instruction) { + registerStaticFieldRead(instruction.getField()); + } + public void registerStaticFieldReadFromMethodHandle(DexField field) { registerStaticFieldRead(field); }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java index 37e08d2..45bff3f 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLoad; import com.android.tools.r8.cf.code.CfLogicalBinop; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexEncodedMethod; @@ -109,9 +110,9 @@ CfConstNumber.class, CfGoto.class, CfConstNumber.class, - CfFieldInstruction.class); + CfStaticFieldWrite.class); private static List<Class<?>> r8InstructionSequence = - ImmutableList.of(CfConstNumber.class, CfLogicalBinop.class, CfFieldInstruction.class); + ImmutableList.of(CfConstNumber.class, CfLogicalBinop.class, CfStaticFieldWrite.class); private static List<Class<?>> jacocoInstructionSequence = ImmutableList.of(CfLoad.class, CfConstNumber.class, CfConstNumber.class, CfArrayStore.class);
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java new file mode 100644 index 0000000..97839cf --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java
@@ -0,0 +1,56 @@ +// Copyright (c) 2021, 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.graph.bytecodemetadata; + +/** + * A piece of information that can be attached to instructions in {@link + * com.android.tools.r8.graph.CfCode} and {@link com.android.tools.r8.graph.DexCode}. + */ +public class BytecodeInstructionMetadata { + + /** + * Set for instance and static field read instructions which are only used to write the same + * field. + * + * <p>Used by {@link com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor} + * to skip such instructions in the "is-field-read" analysis. + */ + private final boolean isReadForWrite; + + BytecodeInstructionMetadata(boolean isReadForWrite) { + this.isReadForWrite = isReadForWrite; + } + + public static Builder builder() { + return new Builder(); + } + + public static BytecodeInstructionMetadata none() { + return null; + } + + public boolean isReadForWrite() { + return isReadForWrite; + } + + public static class Builder { + + private boolean isReadForWrite; + + private boolean isEmpty() { + return !isReadForWrite; + } + + public Builder setIsReadForWrite() { + isReadForWrite = true; + return this; + } + + public BytecodeInstructionMetadata build() { + assert !isEmpty(); + return new BytecodeInstructionMetadata(isReadForWrite); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java new file mode 100644 index 0000000..ad7dee4 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
@@ -0,0 +1,64 @@ +// Copyright (c) 2021, 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.graph.bytecodemetadata; + +import com.android.tools.r8.ir.code.Instruction; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Objects; + +/** + * A collection of information that pertains to the instructions in a piece of {@link + * com.android.tools.r8.graph.CfCode} or {@link com.android.tools.r8.graph.DexCode}. + */ +public class BytecodeMetadata<I> { + + private static final BytecodeMetadata<?> EMPTY = new BytecodeMetadata<>(Collections.emptyMap()); + + private final Map<I, BytecodeInstructionMetadata> backing; + + BytecodeMetadata(Map<I, BytecodeInstructionMetadata> backing) { + assert backing.values().stream().noneMatch(Objects::isNull); + this.backing = backing; + } + + public static <I> Builder<I> builder(BytecodeMetadataProvider bytecodeMetadataProvider) { + return new Builder<>(bytecodeMetadataProvider); + } + + @SuppressWarnings("unchecked") + public static <I> BytecodeMetadata<I> empty() { + return (BytecodeMetadata<I>) EMPTY; + } + + public BytecodeInstructionMetadata getMetadata(I bytecodeInstruction) { + return backing.get(bytecodeInstruction); + } + + public static class Builder<I> { + + private final BytecodeMetadataProvider bytecodeMetadataProvider; + + private final Map<I, BytecodeInstructionMetadata> backing = new IdentityHashMap<>(); + + Builder(BytecodeMetadataProvider bytecodeMetadataProvider) { + this.bytecodeMetadataProvider = bytecodeMetadataProvider; + } + + public Builder<I> setMetadata(Instruction irInstruction, I bytecodeInstruction) { + BytecodeInstructionMetadata instructionMetadata = + bytecodeMetadataProvider.getMetadata(irInstruction); + if (instructionMetadata != null) { + backing.put(bytecodeInstruction, instructionMetadata); + } + return this; + } + + public BytecodeMetadata<I> build() { + return backing.isEmpty() ? empty() : new BytecodeMetadata<>(backing); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadataProvider.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadataProvider.java new file mode 100644 index 0000000..02ba8d4 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadataProvider.java
@@ -0,0 +1,74 @@ +// Copyright (c) 2021, 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.graph.bytecodemetadata; + +import static com.android.tools.r8.utils.MapUtils.ignoreKey; + +import com.android.tools.r8.ir.code.Instruction; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Mapping from IR instructions to the metadata that should be attached to the resulting CF or DEX + * bytecode instruction that results from the given IR instruction. + */ +public class BytecodeMetadataProvider { + + private static final BytecodeMetadataProvider EMPTY = + new BytecodeMetadataProvider(Collections.emptyMap()); + + private final Map<Instruction, BytecodeInstructionMetadata> backing; + + BytecodeMetadataProvider(Map<Instruction, BytecodeInstructionMetadata> backing) { + this.backing = backing; + } + + public static Builder builder() { + return new Builder(); + } + + public static BytecodeMetadataProvider empty() { + return EMPTY; + } + + /** + * Returns the metadata for a given IR instruction that should be attached to the CF or DEX + * instruction when finalizing the IR to CF or DEX. + */ + public BytecodeInstructionMetadata getMetadata(Instruction instruction) { + return backing.get(instruction); + } + + public static class Builder { + + private final Map<Instruction, BytecodeInstructionMetadata.Builder> builders = + new IdentityHashMap<>(); + + /** + * Used to mutate the metadata that should be attached to the CF or DEX instruction that results + * from the given IR instruction. + */ + public Builder addMetadata( + Instruction instruction, Consumer<BytecodeInstructionMetadata.Builder> fn) { + assert !builders.containsKey(instruction); + BytecodeInstructionMetadata.Builder builder = + builders.computeIfAbsent(instruction, ignoreKey(BytecodeInstructionMetadata::builder)); + fn.accept(builder); + return this; + } + + public BytecodeMetadataProvider build() { + if (builders.isEmpty()) { + return empty(); + } + Map<Instruction, BytecodeInstructionMetadata> backing = + new IdentityHashMap<>(builders.size()); + builders.forEach((instruction, builder) -> backing.put(instruction, builder.build())); + return new BytecodeMetadataProvider(backing); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java index 2f9a4ee..e16a15f 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -117,7 +117,7 @@ appView.setHorizontallyMergedClasses(mergedClasses, mode); HorizontalClassMergerGraphLens horizontalClassMergerGraphLens = - createLens(mergedClasses, lensBuilder, syntheticArgumentClass); + createLens(mergedClasses, lensBuilder, mode, syntheticArgumentClass); assert verifyNoCyclesInInterfaceHierarchies(groups); @@ -235,8 +235,9 @@ private HorizontalClassMergerGraphLens createLens( HorizontallyMergedClasses mergedClasses, HorizontalClassMergerGraphLens.Builder lensBuilder, + Mode mode, SyntheticArgumentClass syntheticArgumentClass) { - return new TreeFixer(appView, mergedClasses, lensBuilder, syntheticArgumentClass) + return new TreeFixer(appView, mergedClasses, lensBuilder, mode, syntheticArgumentClass) .fixupTypeReferences(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java index 2b12d6d..03a044d 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -250,13 +250,7 @@ MutableBidirectionalManyToOneRepresentativeMap<R, R> newMemberSignatures, MutableBidirectionalManyToOneRepresentativeMap<R, R> pendingNewMemberSignatureUpdates) { newMemberSignatures.removeAll(pendingNewMemberSignatureUpdates.keySet()); - pendingNewMemberSignatureUpdates.forEachManyToOneMapping( - (keys, value, representative) -> { - newMemberSignatures.put(keys, value); - if (keys.size() > 1) { - newMemberSignatures.setRepresentative(value, representative); - } - }); + newMemberSignatures.putAll(pendingNewMemberSignatureUpdates); pendingNewMemberSignatureUpdates.clear(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java index e68cc71..2affeaf 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -9,7 +9,7 @@ import com.android.tools.r8.cf.code.CfConstNumber; import com.android.tools.r8.cf.code.CfConstString; import com.android.tools.r8.cf.code.CfDexItemBasedConstString; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfInstanceFieldWrite; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLabel; @@ -28,6 +28,7 @@ import com.android.tools.r8.ir.analysis.value.SingleConstValue; import com.android.tools.r8.ir.analysis.value.SingleDexItemBasedStringValue; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo; import com.android.tools.r8.utils.IntBox; @@ -110,8 +111,14 @@ ImmutableList.Builder<CfInstruction> instructionBuilder = ImmutableList.builder(); // Set position. - Position callerPosition = Position.synthetic(0, syntheticMethodReference, null); - Position calleePosition = Position.synthetic(0, originalMethodReference, callerPosition); + Position callerPosition = + SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build(); + Position calleePosition = + SyntheticPosition.builder() + .setLine(0) + .setMethod(originalMethodReference) + .setCallerPosition(callerPosition) + .build(); CfPosition position = new CfPosition(new CfLabel(), calleePosition); instructionBuilder.add(position); instructionBuilder.add(position.getLabel()); @@ -122,7 +129,7 @@ int classIdLocalIndex = maxLocals - 1; instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0)); instructionBuilder.add(new CfLoad(ValueType.INT, classIdLocalIndex)); - instructionBuilder.add(new CfFieldInstruction(Opcodes.PUTFIELD, group.getClassIdField())); + instructionBuilder.add(new CfInstanceFieldWrite(group.getClassIdField())); maxStack.set(2); } else { assert !hasClassId; @@ -180,7 +187,7 @@ int stackSizeForInitializationInfo = addCfInstructionsForInitializationInfo( instructionBuilder, initializationInfo, argumentToLocalIndex, field.getType()); - instructionBuilder.add(new CfFieldInstruction(Opcodes.PUTFIELD, field)); + instructionBuilder.add(new CfInstanceFieldWrite(field)); maxStack.setMax(stackSizeForInitializationInfo + 1); }); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java index 2ab6c46..3c97009 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -375,8 +375,8 @@ syntheticInitializerConverterBuilder.add( new ProgramMethod(group.getTarget(), newInstanceInitializer)); } else { - assert !appView.options().isGeneratingClassFiles() - || newInstanceInitializer.getCode().isCfCode(); + assert appView.options().isGeneratingDex() + || newInstanceInitializer.getCode().isCfWritableCode(); } } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java index b85eab3..7a74a2b 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DefaultInstanceInitializerCode; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; @@ -19,6 +20,7 @@ import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.EnclosingMethodAttribute; import com.android.tools.r8.graph.TreeFixerBase; +import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode; import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter; import com.android.tools.r8.shaking.AnnotationFixer; import com.android.tools.r8.utils.ArrayUtils; @@ -28,8 +30,10 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -42,9 +46,12 @@ private final AppView<? extends AppInfoWithClassHierarchy> appView; private final HorizontallyMergedClasses mergedClasses; + private final Mode mode; private final HorizontalClassMergerGraphLens.Builder lensBuilder; private final DexItemFactory dexItemFactory; private final SyntheticArgumentClass syntheticArgumentClass; + + private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>(); private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures = HashBiMap.create(); @@ -52,10 +59,12 @@ AppView<? extends AppInfoWithClassHierarchy> appView, HorizontallyMergedClasses mergedClasses, HorizontalClassMergerGraphLens.Builder lensBuilder, + Mode mode, SyntheticArgumentClass syntheticArgumentClass) { super(appView); this.appView = appView; this.mergedClasses = mergedClasses; + this.mode = mode; this.lensBuilder = lensBuilder; this.syntheticArgumentClass = syntheticArgumentClass; this.dexItemFactory = appView.dexItemFactory(); @@ -145,7 +154,11 @@ } private void fixupProgramClassSuperTypes(DexProgramClass clazz) { - clazz.superType = fixupType(clazz.superType); + DexType rewrittenSuperType = fixupType(clazz.getSuperType()); + if (rewrittenSuperType != clazz.getSuperType()) { + originalSuperTypes.put(clazz, clazz.getSuperType()); + clazz.superType = rewrittenSuperType; + } clazz.setInterfaces(fixupInterfaces(clazz, clazz.getInterfaces())); } @@ -166,7 +179,7 @@ method -> fixupVirtualMethod(remappedClassVirtualMethods, newMethodReferences, method)); clazz .getMethodCollection() - .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, method)); + .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, clazz, method)); Set<DexField> newFieldReferences = Sets.newIdentityHashSet(); DexEncodedField[] instanceFields = clazz.clearInstanceFields(); @@ -220,7 +233,7 @@ Set<DexMethodSignature> newDirectMethods = new LinkedHashSet<>(); iface .getMethodCollection() - .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, method)); + .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, iface, method)); iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod); assert !iface.hasInstanceFields(); @@ -263,7 +276,7 @@ } private DexEncodedMethod fixupDirectMethod( - Set<DexMethodSignature> newMethods, DexEncodedMethod method) { + Set<DexMethodSignature> newMethods, DexProgramClass clazz, DexEncodedMethod method) { DexMethod originalMethodReference = method.getReference(); // Fix all type references in the method prototype. @@ -299,6 +312,17 @@ boolean changed = newMethods.add(newMethodReference.getSignature()); assert changed; + // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens + // code rewriting. + if (mode.isInitial() + && method.hasCode() + && method.getCode().isDefaultInstanceInitializerCode() + && mergedClasses.hasBeenMergedOrIsMergeTarget(clazz.getSuperType())) { + DexType originalSuperType = originalSuperTypes.getOrDefault(clazz, clazz.getSuperType()); + DefaultInstanceInitializerCode.uncanonicalizeCode( + appView, method.asProgramMethod(clazz), originalSuperType); + } + return fixupProgramMethod(newMethodReference, method); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java index 8372926..085276f 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -32,6 +32,7 @@ import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; @@ -132,7 +133,8 @@ public CfCode build(DexMethod syntheticMethodReference) { // Building the instructions will adjust maxStack and maxLocals. Build it here before invoking // the CfCode constructor to ensure that the value passed in is the updated values. - Position callerPosition = Position.synthetic(0, syntheticMethodReference, null); + Position callerPosition = + SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build(); List<CfInstruction> instructions = buildInstructions(callerPosition); return new CfCode( syntheticMethodReference.getHolderType(), @@ -201,7 +203,8 @@ public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { assert !classInitializers.isEmpty(); - Position callerPosition = Position.synthetic(0, syntheticMethodReference, null); + Position callerPosition = + SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build(); IRMetadata metadata = new IRMetadata(); NumberGenerator blockNumberGenerator = new NumberGenerator(); NumberGenerator valueNumberGenerator = new NumberGenerator();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java index fce04fa..6e88f4d 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -34,19 +34,20 @@ } // For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed. - if (code == null || !code.isCfCode()) { + if (code == null) { return true; } - CfCode cfCode = code.asCfCode(); - - ConstraintWithTarget constraint = - cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method); - if (constraint == ConstraintWithTarget.NEVER) { + if (code.isCfCode()) { + CfCode cfCode = code.asCfCode(); + ConstraintWithTarget constraint = + cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method); + return constraint == ConstraintWithTarget.NEVER; + } else if (code.isDefaultInstanceInitializerCode()) { + return false; + } else { return true; } - - return false; } @Override
diff --git a/src/main/java/com/android/tools/r8/inspector/ClassInspector.java b/src/main/java/com/android/tools/r8/inspector/ClassInspector.java index b18dd26..6234592 100644 --- a/src/main/java/com/android/tools/r8/inspector/ClassInspector.java +++ b/src/main/java/com/android/tools/r8/inspector/ClassInspector.java
@@ -14,6 +14,9 @@ /** Get the class reference for the class of this inspector. */ ClassReference getClassReference(); + /** Get the source file attribute content if present, otherwise null. */ + String getSourceFile(); + /** Iterate all fields declared in the class/interface (unspecified order). */ void forEachField(Consumer<FieldInspector> inspection);
diff --git a/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java b/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java index c2ebc8e..9525bdc 100644 --- a/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java +++ b/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.inspector.internal; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexString; import com.android.tools.r8.inspector.ClassInspector; import com.android.tools.r8.inspector.FieldInspector; import com.android.tools.r8.inspector.MethodInspector; @@ -29,6 +30,12 @@ } @Override + public String getSourceFile() { + DexString sourceFile = clazz.getSourceFile(); + return sourceFile == null ? null : sourceFile.toString(); + } + + @Override public void forEachField(Consumer<FieldInspector> inspection) { clazz.forEachField(field -> inspection.accept(new FieldInspectorImpl(this, field))); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java index 2b5e70f..150f206 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.FieldResolutionResult; import com.android.tools.r8.graph.ProgramField; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.code.FieldInstruction; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -20,33 +21,34 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; +import com.google.common.annotations.VisibleForTesting; public class FieldAccessAnalysis { private final AppView<? extends AppInfoWithClassHierarchy> appView; private final FieldAssignmentTracker fieldAssignmentTracker; private final FieldBitAccessAnalysis fieldBitAccessAnalysis; + private final FieldReadForWriteAnalysis fieldReadForWriteAnalysis; public FieldAccessAnalysis(AppView<AppInfoWithLiveness> appView) { InternalOptions options = appView.options(); this.appView = appView; this.fieldBitAccessAnalysis = options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null; - this.fieldAssignmentTracker = - options.enableFieldAssignmentTracker ? new FieldAssignmentTracker(appView) : null; + this.fieldAssignmentTracker = new FieldAssignmentTracker(appView); + this.fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView); } + @VisibleForTesting public FieldAccessAnalysis( AppView<? extends AppInfoWithClassHierarchy> appView, FieldAssignmentTracker fieldAssignmentTracker, - FieldBitAccessAnalysis fieldBitAccessAnalysis) { + FieldBitAccessAnalysis fieldBitAccessAnalysis, + FieldReadForWriteAnalysis fieldReadForWriteAnalysis) { this.appView = appView; this.fieldAssignmentTracker = fieldAssignmentTracker; this.fieldBitAccessAnalysis = fieldBitAccessAnalysis; - } - - public static boolean enable(InternalOptions options) { - return options.enableFieldBitAccessAnalysis || options.enableFieldAssignmentTracker; + this.fieldReadForWriteAnalysis = fieldReadForWriteAnalysis; } public FieldAssignmentTracker fieldAssignmentTracker() { @@ -61,7 +63,10 @@ } public void recordFieldAccesses( - IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) { + IRCode code, + BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder, + OptimizationFeedback feedback, + MethodProcessor methodProcessor) { if (!methodProcessor.isPrimaryMethodProcessor()) { return; } @@ -87,6 +92,10 @@ fieldBitAccessAnalysis.recordFieldAccess( fieldInstruction, field.getDefinition(), feedback); } + if (fieldReadForWriteAnalysis != null) { + fieldReadForWriteAnalysis.recordFieldAccess( + fieldInstruction, field, bytecodeMetadataProviderBuilder); + } } } } else if (instruction.isNewInstance()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java new file mode 100644 index 0000000..3b4f8dc --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
@@ -0,0 +1,81 @@ +// Copyright (c) 2021, 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.analysis.fieldaccess; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.ProgramField; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata.Builder; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; +import com.android.tools.r8.ir.code.FieldGet; +import com.android.tools.r8.ir.code.FieldInstruction; +import com.android.tools.r8.ir.code.FieldPut; +import com.android.tools.r8.ir.code.Instruction; +import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.WorkList; + +public class FieldReadForWriteAnalysis { + + private final AppView<AppInfoWithLiveness> appView; + + FieldReadForWriteAnalysis(AppView<AppInfoWithLiveness> appView) { + this.appView = appView; + } + + public void recordFieldAccess( + FieldInstruction instruction, + ProgramField field, + BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder) { + if (instruction.isFieldPut()) { + return; + } + + FieldGet fieldGet = instruction.asFieldGet(); + if (isValueOnlyUsedToWriteField(fieldGet.outValue(), field)) { + bytecodeMetadataProviderBuilder.addMetadata(instruction, Builder::setIsReadForWrite); + } + } + + private boolean isValueOnlyUsedToWriteField(Value value, ProgramField field) { + WorkList<Instruction> users = WorkList.newIdentityWorkList(value.uniqueUsers()); + if (!enqueueUsersForAnalysis(value, users)) { + return false; + } + boolean foundWrite = false; + while (users.hasNext()) { + Instruction user = users.next(); + if (user.isArithmeticBinop() || user.isLogicalBinop() || user.isUnop()) { + if (enqueueUsersForAnalysis(user.outValue(), users)) { + // OK. + continue; + } + } else if (user.isFieldPut()) { + FieldPut fieldPut = user.asFieldPut(); + DexField writtenFieldReference = fieldPut.getField(); + if (writtenFieldReference.match(field.getReference()) + && fieldPut.isStaticPut() == field.getAccessFlags().isStatic()) { + ProgramField writtenField = + appView.appInfo().resolveField(writtenFieldReference).getProgramField(); + if (writtenField != null && writtenField.isStructurallyEqualTo(field)) { + // OK. + foundWrite = true; + continue; + } + } + } + return false; + } + return foundWrite; + } + + private boolean enqueueUsersForAnalysis(Value value, WorkList<Instruction> users) { + if (value.hasDebugUsers() || value.hasPhiUsers()) { + return false; + } + users.addIfNotSeen(value.uniqueUsers()); + return true; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java index 7e6d5d3..f392dec 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -4,6 +4,10 @@ package com.android.tools.r8.ir.analysis.fieldaccess; +import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback; + +import com.android.tools.r8.code.CfOrDexInstanceFieldRead; +import com.android.tools.r8.code.CfOrDexStaticFieldRead; import com.android.tools.r8.graph.AbstractAccessContexts; import com.android.tools.r8.graph.AbstractAccessContexts.ConcreteAccessContexts; import com.android.tools.r8.graph.AppView; @@ -19,6 +23,7 @@ import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; @@ -26,7 +31,6 @@ import com.android.tools.r8.ir.analysis.value.SingleValue; import com.android.tools.r8.ir.conversion.PostMethodProcessor; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed; -import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; @@ -95,7 +99,14 @@ constantFields.forEach(this::markFieldAsDead); readFields.keySet().forEach(this::markFieldAsDead); - writtenFields.keySet().forEach(this::markFieldAsDead); + writtenFields.keySet().forEach(this::markWriteOnlyFieldAsDead); + } + + private void markWriteOnlyFieldAsDead(DexEncodedField field) { + markFieldAsDead(field); + getSimpleFeedback() + .recordFieldHasAbstractValue( + field, appView, appView.abstractValueFactory().createNullValue()); } private void markFieldAsDead(DexEncodedField field) { @@ -104,7 +115,7 @@ if (appView.appInfo().isPinned(field)) { assert field.getType().isAlwaysNull(appView); } else { - OptimizationFeedbackSimple.getInstance().markFieldAsDead(field); + getSimpleFeedback().markFieldAsDead(field); } } @@ -294,7 +305,11 @@ super(appView, method); } - private void registerFieldAccess(DexField reference, boolean isStatic, boolean isWrite) { + private void registerFieldAccess( + DexField reference, + boolean isStatic, + boolean isWrite, + BytecodeInstructionMetadata metadata) { SuccessfulFieldResolutionResult resolutionResult = appView.appInfo().resolveField(reference).asSuccessfulResolution(); if (resolutionResult == null) { @@ -311,6 +326,13 @@ return; } + if (metadata != null && metadata.isReadForWrite()) { + // Ignore this read. If the field ends up only being written, then we will still reprocess + // the method with the read-for-write instruction, since the method contains a write that + // requires reprocessing. + return; + } + // Record access. if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(field)) { if (field.getAccessFlags().isStatic() == isStatic) { @@ -372,22 +394,36 @@ @Override public void registerInstanceFieldRead(DexField field) { - registerFieldAccess(field, false, false); + registerFieldAccess(field, false, false, BytecodeInstructionMetadata.none()); + } + + @Override + public void registerInstanceFieldReadInstruction(CfOrDexInstanceFieldRead instruction) { + BytecodeInstructionMetadata metadata = + getContext().getDefinition().getCode().getMetadata(instruction); + registerFieldAccess(instruction.getField(), false, false, metadata); } @Override public void registerInstanceFieldWrite(DexField field) { - registerFieldAccess(field, false, true); + registerFieldAccess(field, false, true, null); } @Override public void registerStaticFieldRead(DexField field) { - registerFieldAccess(field, true, false); + registerFieldAccess(field, true, false, BytecodeInstructionMetadata.none()); + } + + @Override + public void registerStaticFieldReadInstruction(CfOrDexStaticFieldRead instruction) { + BytecodeInstructionMetadata metadata = + getContext().getDefinition().getCode().getMetadata(instruction); + registerFieldAccess(instruction.getField(), true, false, metadata); } @Override public void registerStaticFieldWrite(DexField field) { - registerFieldAccess(field, true, true); + registerFieldAccess(field, true, true, null); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java index f0e63e9..6accb73 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
@@ -64,7 +64,7 @@ } // The optimization relies on shrinking and member value propagation to actually clear // the anonymous subclasses of EnumLiteMap. - if (!appView.options().isShrinking() || !appView.options().enableValuePropagation) { + if (!appView.options().isShrinking()) { return; } internalClearDeadEnumLiteMaps();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java index 01c9fe0..42a035f 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -32,7 +32,7 @@ import com.android.tools.r8.shaking.TreePrunerConfiguration; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Timing; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collections; @@ -166,8 +166,8 @@ IRConverter converter, ExecutorService executorService, Timing timing) throws ExecutionException { timing.begin("[Proto] Post optimize generated extension registry"); - SortedProgramMethodSet wave = - SortedProgramMethodSet.create(this::forEachMethodThatRequiresPostOptimization); + ProgramMethodSet wave = + ProgramMethodSet.create(this::forEachMethodThatRequiresPostOptimization); OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView); methodProcessor.forEachWaveWithExtension( (method, methodProcessingContext) ->
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java index 4e36bed..7596684 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -34,7 +34,7 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; import com.android.tools.r8.shaking.DependentMinimumKeepInfoCollection; import com.android.tools.r8.utils.Timing; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -114,7 +114,7 @@ IRConverter converter, ExecutorService executorService, Timing timing) throws ExecutionException { timing.begin("[Proto] Post optimize dynamic methods"); - SortedProgramMethodSet wave = SortedProgramMethodSet.create(this::forEachDynamicMethod); + ProgramMethodSet wave = ProgramMethodSet.create(this::forEachDynamicMethod); OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView); methodProcessor.forEachWaveWithExtension( (method, methodProcessingContext) ->
diff --git a/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java index ad16c77..85467b7 100644 --- a/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java +++ b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
@@ -5,6 +5,8 @@ package com.android.tools.r8.ir.code; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.ir.code.Position.SourcePosition; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; @@ -36,7 +38,7 @@ methodIsSynthesized ? callerPosition : getCanonical( - Position.builder() + SourcePosition.builder() .setLine(0) .setMethod(method) .setCallerPosition(callerPosition) @@ -44,7 +46,8 @@ } else { this.callerPosition = null; isCompilerSynthesizedInlinee = false; - preamblePosition = getCanonical(Position.synthetic(0, method, null)); + preamblePosition = + getCanonical(SyntheticPosition.builder().setLine(0).setMethod(method).build()); } } @@ -81,7 +84,11 @@ Position callerOfCaller = canonicalizeCallerPosition(caller.callerPosition); return getCanonical( caller.isNone() - ? Position.noneWithMethod(caller.method, callerOfCaller) + ? SourcePosition.builder() + .setMethod(caller.method) + .setCallerPosition(callerOfCaller) + .disableLineCheck() + .build() : caller.builderWithCopy().setCallerPosition(callerOfCaller).build()); } @@ -106,7 +113,11 @@ syntheticPosition = (min == Integer.MAX_VALUE) ? getPreamblePosition() - : Position.synthetic(min < max ? min - 1 : min, originalMethod, callerPosition); + : SyntheticPosition.builder() + .setLine(min < max ? min - 1 : min) + .setMethod(originalMethod) + .setCallerPosition(callerPosition) + .build(); } else { // If in release mode we explicitly associate a synthetic none position with monitor exit. // This is safe as the runtime must never throw at this position because the monitor cannot
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java index c347f87..d4d76da 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java +++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -42,7 +42,7 @@ @Override public void buildDex(DexBuilder builder) { - assert getPosition().isSome() && !getPosition().synthetic; + assert getPosition().isSome() && !getPosition().isSyntheticPosition(); builder.addDebugPosition(this); } @@ -84,7 +84,7 @@ @Override public void buildCf(CfBuilder builder) { - assert getPosition().isSome() && !getPosition().synthetic; + assert getPosition().isSome() && !getPosition().isSyntheticPosition(); // All redundant debug positions are removed. Remaining ones must force a pc advance. builder.add(new CfNop()); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldGet.java b/src/main/java/com/android/tools/r8/ir/code/FieldGet.java new file mode 100644 index 0000000..3f62f1f --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/code/FieldGet.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2021, 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.code; + +public interface FieldGet { + + Value outValue(); +}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java index e5c792f..f4a2cdc 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -595,7 +595,11 @@ for (Instruction instruction : instructions()) { if (instruction.outValue != null && instruction.outValue.getType().isClassType()) { ClassTypeElement classTypeLattice = instruction.outValue.getType().asClassType(); - assert !mergedClasses.hasBeenMergedIntoDifferentType(classTypeLattice.getClassType()); + assert !mergedClasses.hasBeenMergedIntoDifferentType(classTypeLattice.getClassType()) + : "Expected reference to " + + classTypeLattice.getClassType().getTypeName() + + " to be rewritten at instruction " + + instruction.toString(); assert !classTypeLattice .getInterfaces() .anyMatch(
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java index 090812b..347ca58 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -6,7 +6,7 @@ import com.android.tools.r8.cf.LoadStoreHelper; import com.android.tools.r8.cf.TypeVerificationHelper; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfInstanceFieldRead; import com.android.tools.r8.code.Iget; import com.android.tools.r8.code.IgetBoolean; import com.android.tools.r8.code.IgetByte; @@ -32,7 +32,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.Set; -public class InstanceGet extends FieldInstruction implements InstanceFieldInstruction { +public class InstanceGet extends FieldInstruction implements FieldGet, InstanceFieldInstruction { public InstanceGet(Value dest, Value object, DexField field) { super(field, dest, object); @@ -148,6 +148,16 @@ } @Override + public boolean isFieldGet() { + return true; + } + + @Override + public FieldGet asFieldGet() { + return this; + } + + @Override public boolean isInstanceFieldInstruction() { return true; } @@ -190,9 +200,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add( - new CfFieldInstruction( - org.objectweb.asm.Opcodes.GETFIELD, getField(), builder.resolveField(getField()))); + builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField()))); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java index 3dff277..2a5d60e 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -5,7 +5,7 @@ package com.android.tools.r8.ir.code; import com.android.tools.r8.cf.LoadStoreHelper; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfInstanceFieldWrite; import com.android.tools.r8.code.Iput; import com.android.tools.r8.code.IputBoolean; import com.android.tools.r8.code.IputByte; @@ -195,6 +195,16 @@ } @Override + public boolean isFieldPut() { + return true; + } + + @Override + public FieldPut asFieldPut() { + return this; + } + + @Override public boolean isInstanceFieldInstruction() { return true; } @@ -226,9 +236,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add( - new CfFieldInstruction( - org.objectweb.asm.Opcodes.PUTFIELD, getField(), builder.resolveField(getField()))); + builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField()))); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java index 090fb45..3409c01 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java +++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -956,12 +956,20 @@ return null; } - public final boolean isFieldGet() { - return isInstanceGet() || isStaticGet(); + public boolean isFieldGet() { + return false; } - public final boolean isFieldPut() { - return isInstancePut() || isStaticPut(); + public FieldGet asFieldGet() { + return null; + } + + public boolean isFieldPut() { + return false; + } + + public FieldPut asFieldPut() { + return null; } public boolean isInstancePut() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java index b5aa980..cb20fd4 100644 --- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -48,6 +48,10 @@ return seenBlocks.contains(basicBlock); } + public Set<BasicBlock> getSeenBlocks() { + return seenBlocks; + } + @Override public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) { currentBlockIterator.replaceCurrentInstruction(newInstruction, affectedValues); @@ -199,6 +203,7 @@ if (!isLinearEdge(target, candidate)) { break; } + seenBlocks.add(target); target = candidate; } currentBlock = target;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java index cb2ff68..cf718aa 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Position.java +++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -7,114 +7,125 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.utils.Int2StructuralItemArrayMap; +import com.android.tools.r8.utils.structural.Equatable; +import com.android.tools.r8.utils.structural.HashCodeVisitor; import com.android.tools.r8.utils.structural.StructuralItem; import com.android.tools.r8.utils.structural.StructuralMapping; import com.android.tools.r8.utils.structural.StructuralSpecification; -import com.google.common.annotations.VisibleForTesting; -import java.util.Objects; -public class Position implements StructuralItem<Position> { +public abstract class Position implements StructuralItem<Position> { - // A no-position marker. Not having a position means the position is implicitly defined by the - // context, e.g., the marker does not materialize anything concrete. - private static final Position NO_POSITION = new Position(-1, null, null, null, false, false); + // Compare ID(s) for positions. + private static final int SOURCE_POSITION_COMPARE_ID = 1; + private static final int SYNTHETIC_POSITION_COMPARE_ID = 2; + private static final int OUTLINE_POSITION_COMPARE_ID = 3; + private static final int OUTLINE_CALLER_POSITION_COMPARE_ID = 4; - // A synthetic marker position that should never materialize. - // This is used specifically to mark exceptional exit blocks from synchronized methods in release. - private static final Position NO_POSITION_SYNTHETIC = - new Position(-1, null, null, null, true, false); - - // Fake position to use for representing an actual position in testing code. - private static final Position TESTING_POSITION = new Position(0, null, null, null, true, false); - - public final int line; - public final DexString file; - public final boolean synthetic; + protected final int line; + protected final DexMethod method; // If there's no inlining, callerPosition is null. // // For an inlined instruction its Position contains the inlinee's line and method and // callerPosition is the position of the invoke instruction in the caller. + protected final Position callerPosition; - public final DexMethod method; - public final Position callerPosition; - public final boolean removeInnerFrameIfThrowingNpe; - - private static void specify(StructuralSpecification<Position, ?> spec) { - spec.withInt(p -> p.line) - .withNullableItem(p -> p.file) - .withBool(p -> p.synthetic) - .withNullableItem(p -> p.method) - .withNullableItem(p -> p.callerPosition); - } + private final boolean removeInnerFramesIfThrowingNpe; private Position( - int line, - DexString file, - DexMethod method, - Position callerPosition, - boolean synthetic, - boolean removeInnerFrameIfThrowingNpe) { + int line, DexMethod method, Position callerPosition, boolean removeInnerFramesIfThrowingNpe) { this.line = line; - this.file = file; - this.synthetic = synthetic; this.method = method; this.callerPosition = callerPosition; - this.removeInnerFrameIfThrowingNpe = removeInnerFrameIfThrowingNpe; - assert callerPosition == null || callerPosition.method != null; + this.removeInnerFramesIfThrowingNpe = removeInnerFramesIfThrowingNpe; } - public static Position synthetic(int line, DexMethod method, Position callerPosition) { - assert line >= 0; - assert method != null; - return new Position(line, null, method, callerPosition, true, false); + public boolean isSyntheticPosition() { + return false; } - public static Position none() { - return NO_POSITION; + public boolean isAdditionalMappingInfoPosition() { + return false; } - public static Position syntheticNone() { - return NO_POSITION_SYNTHETIC; + public boolean isRemoveInnerFramesIfThrowingNpe() { + return removeInnerFramesIfThrowingNpe; } - @VisibleForTesting - public static Position testingPosition() { - return TESTING_POSITION; + public boolean isOutline() { + return false; } - // This factory method is used by the Inliner to create Positions when the caller has no valid - // positions. Since the callee still may have valid positions we need a non-null Position to set - // it as the caller of the inlined Positions. - public static Position noneWithMethod(DexMethod method, Position callerPosition) { - assert method != null; - return new Position(-1, null, method, callerPosition, false, false); + public DexMethod getOutlineCallee() { + return null; } - public static Position getPositionForInlining( - AppView<?> appView, InvokeMethod invoke, ProgramMethod context) { - Position position = invoke.getPosition(); - if (position.method == null) { - assert position.isNone(); - position = Position.noneWithMethod(context.getReference(), null); - } - assert position.getOutermostCaller().method - == appView.graphLens().getOriginalMethodSignature(context.getReference()); - return position; + public Int2StructuralItemArrayMap<Position> getOutlinePositions() { + return null; } public boolean hasCallerPosition() { return callerPosition != null; } + public Position getCallerPosition() { + return callerPosition; + } + + public int getLine() { + return line; + } + + public DexMethod getMethod() { + return method; + } + + public static Position none() { + return SourcePosition.NO_POSITION; + } + + public boolean hasFile() { + return false; + } + + public DexString getFile() { + return null; + } + @Override public Position self() { return this; } + // Unique id to determine the ordering of positions + public abstract int getCompareToId(); + @Override - public StructuralMapping<Position> getStructuralMapping() { - return Position::specify; + public abstract StructuralMapping<Position> getStructuralMapping(); + + private static void specifyBasePosition(StructuralSpecification<Position, ?> spec) { + spec.withInt(Position::getCompareToId) + .withInt(Position::getLine) + .withNullableItem(Position::getMethod) + .withNullableItem(Position::getCallerPosition) + .withBool(Position::isRemoveInnerFramesIfThrowingNpe); + } + + public static Position syntheticNone() { + return SyntheticPosition.NO_POSITION_SYNTHETIC; + } + + public static Position getPositionForInlining( + AppView<?> appView, InvokeMethod invoke, ProgramMethod context) { + Position position = invoke.getPosition(); + if (position.method == null) { + assert position.isNone(); + position = SourcePosition.builder().setMethod(context.getReference()).build(); + } + assert position.getOutermostCaller().method + == appView.graphLens().getOriginalMethodSignature(context.getReference()); + return position; } public boolean isNone() { @@ -122,7 +133,7 @@ } public boolean isSyntheticNone() { - return this == NO_POSITION_SYNTHETIC; + return this == syntheticNone(); } public boolean isSome() { @@ -139,10 +150,6 @@ return lastPosition; } - public Position getCallerPosition() { - return callerPosition; - } - public Position withOutermostCallerPosition(Position newOutermostCallerPosition) { return builderWithCopy() .setCallerPosition( @@ -153,14 +160,13 @@ } @Override - public boolean equals(Object other) { - return other instanceof Position && compareTo((Position) other) == 0; + public final boolean equals(Object other) { + return Equatable.equalsImpl(this, other); } @Override - public int hashCode() { - return Objects.hash( - line, file, synthetic, method, callerPosition, removeInnerFrameIfThrowingNpe); + public final int hashCode() { + return HashCodeVisitor.run(this); } private String toString(boolean forceMethod) { @@ -168,8 +174,8 @@ return "--"; } StringBuilder builder = new StringBuilder(); - if (file != null) { - builder.append(file).append(":"); + if (hasFile()) { + builder.append(getFile()).append(":"); } builder.append("#").append(line); if (method != null && (forceMethod || callerPosition != null)) { @@ -190,64 +196,367 @@ return toString(false); } - public static Builder builder() { - return new Builder(); - } + public abstract PositionBuilder<?, ?> builderWithCopy(); - public Builder builderWithCopy() { - return new Builder() - .setLine(line) - .setFile(file) - .setMethod(method) - .setCallerPosition(callerPosition) - .setSynthetic(synthetic) - .setRemoveInnerFramesIfThrowingNpe(removeInnerFrameIfThrowingNpe); - } + public abstract static class PositionBuilder< + P extends Position, B extends PositionBuilder<P, B>> { - public static class Builder { + protected int line = -1; + protected DexMethod method; + protected Position callerPosition; + protected boolean removeInnerFramesIfThrowingNpe; - public int line; - public DexString file; - public boolean synthetic; - public DexMethod method; - public Position callerPosition; - public boolean removeInnerFrameIfThrowingNpe; + protected boolean noCheckOfPosition; + protected boolean noCheckOfMethod; - public Builder setLine(int line) { + abstract B self(); + + public B setLine(int line) { this.line = line; - return this; + return self(); } - public Builder setFile(DexString file) { - this.file = file; - return this; + public boolean hasLine() { + return line > -1; } - public Builder setSynthetic(boolean synthetic) { - this.synthetic = synthetic; - return this; - } - - public Builder setMethod(DexMethod method) { + public B setMethod(DexMethod method) { this.method = method; - return this; + return self(); } - public Builder setCallerPosition(Position callerPosition) { + public B setCallerPosition(Position callerPosition) { this.callerPosition = callerPosition; - return this; + return self(); } - public Builder setRemoveInnerFramesIfThrowingNpe(boolean removeInnerFramesIfThrowingNpe) { - this.removeInnerFrameIfThrowingNpe = removeInnerFramesIfThrowingNpe; - return this; + public B setRemoveInnerFramesIfThrowingNpe(boolean removeInnerFramesIfThrowingNpe) { + this.removeInnerFramesIfThrowingNpe = removeInnerFramesIfThrowingNpe; + return self(); } - public Position build() { - assert line >= 0; - assert method != null; - return new Position( - line, file, method, callerPosition, synthetic, removeInnerFrameIfThrowingNpe); + public B disableLineCheck() { + noCheckOfPosition = true; + return self(); + } + + public B disableMethodCheck() { + noCheckOfMethod = true; + return self(); + } + + public abstract P build(); + } + + public static class SourcePosition extends Position { + + // A no-position marker. Not having a position means the position is implicitly defined by the + // context, e.g., the marker does not materialize anything concrete. + private static final SourcePosition NO_POSITION = + new SourcePosition(-1, null, null, false, null); + + public final DexString file; + + private static void specify(StructuralSpecification<Position, ?> spec) { + spec.withSpec(Position::specifyBasePosition).withNullableItem(Position::getFile); + } + + private SourcePosition( + int line, + DexMethod method, + Position callerPosition, + boolean removeInnerFramesIfThrowingNpe, + DexString file) { + super(line, method, callerPosition, removeInnerFramesIfThrowingNpe); + this.file = file; + assert callerPosition == null || callerPosition.method != null; + } + + @Override + public boolean hasFile() { + return file != null; + } + + @Override + public DexString getFile() { + return file; + } + + @Override + public int getCompareToId() { + return SOURCE_POSITION_COMPARE_ID; + } + + @Override + public PositionBuilder<?, ?> builderWithCopy() { + return builder() + .setLine(line) + .setFile(file) + .setMethod(method) + .setCallerPosition(callerPosition); + } + + @Override + public StructuralMapping<Position> getStructuralMapping() { + return SourcePosition::specify; + } + + public static SourcePositionBuilder builder() { + return new SourcePositionBuilder(); + } + + public static class SourcePositionBuilder + extends PositionBuilder<SourcePosition, SourcePositionBuilder> { + + private DexString file; + + @Override + SourcePositionBuilder self() { + return this; + } + + public SourcePositionBuilder setFile(DexString file) { + this.file = file; + return this; + } + + @Override + public SourcePosition build() { + assert noCheckOfPosition || line >= 0; + assert noCheckOfMethod || method != null; + return new SourcePosition( + line, method, callerPosition, removeInnerFramesIfThrowingNpe, file); + } + } + } + + public static class SyntheticPosition extends Position { + + // A synthetic marker position that should never materialize. + // This is used specifically to mark exceptional exit blocks from synchronized methods in + // release. + private static final Position NO_POSITION_SYNTHETIC = + new SyntheticPosition(-1, null, null, false); + + private SyntheticPosition( + int line, + DexMethod method, + Position callerPosition, + boolean removeInnerFramesIfThrowingNpe) { + super(line, method, callerPosition, removeInnerFramesIfThrowingNpe); + } + + @Override + public boolean isSyntheticPosition() { + return true; + } + + @Override + public int getCompareToId() { + return SYNTHETIC_POSITION_COMPARE_ID; + } + + @Override + public PositionBuilder<?, ?> builderWithCopy() { + return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition); + } + + @Override + public StructuralMapping<Position> getStructuralMapping() { + return Position::specifyBasePosition; + } + + public static SyntheticPositionBuilder builder() { + return new SyntheticPositionBuilder(); + } + + public static class SyntheticPositionBuilder + extends PositionBuilder<SyntheticPosition, SyntheticPositionBuilder> { + + private SyntheticPositionBuilder() {} + + @Override + SyntheticPositionBuilder self() { + return this; + } + + @Override + public SyntheticPosition build() { + assert noCheckOfPosition || line >= 0; + assert noCheckOfMethod || method != null; + return new SyntheticPosition(line, method, callerPosition, removeInnerFramesIfThrowingNpe); + } + } + } + + public static class OutlinePosition extends Position { + + private OutlinePosition( + int line, + DexMethod method, + Position callerPosition, + boolean removeInnerFramesIfThrowingNpe) { + super(line, method, callerPosition, removeInnerFramesIfThrowingNpe); + } + + @Override + public boolean isOutline() { + return true; + } + + @Override + public int getCompareToId() { + return OUTLINE_POSITION_COMPARE_ID; + } + + @Override + public PositionBuilder<?, ?> builderWithCopy() { + return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition); + } + + @Override + public StructuralMapping<Position> getStructuralMapping() { + return Position::specifyBasePosition; + } + + public static OutlinePositionBuilder builder() { + return new OutlinePositionBuilder(); + } + + public static class OutlinePositionBuilder + extends PositionBuilder<OutlinePosition, OutlinePositionBuilder> { + + private OutlinePositionBuilder() {} + + @Override + OutlinePositionBuilder self() { + return this; + } + + @Override + public OutlinePosition build() { + return new OutlinePosition(line, method, callerPosition, removeInnerFramesIfThrowingNpe); + } + } + } + + public static class OutlineCallerPosition extends Position { + + private final Int2StructuralItemArrayMap<Position> outlinePositions; + private final DexMethod outlineCallee; + private final boolean isOutline; + + public static void specify(StructuralSpecification<Position, ?> spec) { + spec.withSpec(Position::specifyBasePosition) + .withBool(Position::isOutline) + .withItem(Position::getOutlineCallee) + .withItem(Position::getOutlinePositions); + } + + private OutlineCallerPosition( + int line, + DexMethod method, + Position callerPosition, + boolean removeInnerFramesIfThrowingNpe, + Int2StructuralItemArrayMap<Position> outlinePositions, + DexMethod outlineCallee, + boolean isOutline) { + super(line, method, callerPosition, removeInnerFramesIfThrowingNpe); + this.outlinePositions = outlinePositions; + this.outlineCallee = outlineCallee; + this.isOutline = isOutline; + } + + @Override + public boolean isNone() { + return false; + } + + @Override + public int getCompareToId() { + return OUTLINE_CALLER_POSITION_COMPARE_ID; + } + + @Override + public PositionBuilder<?, ?> builderWithCopy() { + OutlineCallerPositionBuilder outlineCallerPositionBuilder = + builder() + .setLine(line) + .setMethod(method) + .setCallerPosition(callerPosition) + .setOutlineCallee(outlineCallee) + .setIsOutline(isOutline); + outlinePositions.forEach(outlineCallerPositionBuilder::addOutlinePosition); + return outlineCallerPositionBuilder; + } + + @Override + public boolean isOutline() { + return isOutline; + } + + @Override + public DexMethod getOutlineCallee() { + return outlineCallee; + } + + @Override + public Int2StructuralItemArrayMap<Position> getOutlinePositions() { + return outlinePositions; + } + + @Override + public StructuralMapping<Position> getStructuralMapping() { + return OutlineCallerPosition::specify; + } + + public static OutlineCallerPositionBuilder builder() { + return new OutlineCallerPositionBuilder(); + } + + public static class OutlineCallerPositionBuilder + extends PositionBuilder<OutlineCallerPosition, OutlineCallerPositionBuilder> { + + private final Int2StructuralItemArrayMap.Builder<Position> outlinePositionsBuilder = + Int2StructuralItemArrayMap.builder(); + private DexMethod outlineCallee; + private boolean isOutline; + + private OutlineCallerPositionBuilder() {} + + @Override + OutlineCallerPositionBuilder self() { + return this; + } + + public OutlineCallerPositionBuilder setOutlineCallee(DexMethod outlineCallee) { + this.outlineCallee = outlineCallee; + return this; + } + + public OutlineCallerPositionBuilder addOutlinePosition(int line, Position callerPosition) { + outlinePositionsBuilder.put(line, callerPosition); + return this; + } + + public OutlineCallerPositionBuilder setIsOutline(boolean isOutline) { + this.isOutline = isOutline; + return this; + } + + @Override + public OutlineCallerPosition build() { + assert noCheckOfPosition || line >= 0; + assert noCheckOfMethod || method != null; + return new OutlineCallerPosition( + line, + method, + callerPosition, + removeInnerFramesIfThrowingNpe, + outlinePositionsBuilder.build(), + outlineCallee, + isOutline); + } } } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java index b4caedd..c7e076c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -5,7 +5,7 @@ import com.android.tools.r8.cf.LoadStoreHelper; import com.android.tools.r8.cf.TypeVerificationHelper; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfStaticFieldRead; import com.android.tools.r8.code.Sget; import com.android.tools.r8.code.SgetBoolean; import com.android.tools.r8.code.SgetByte; @@ -32,7 +32,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.Set; -public class StaticGet extends FieldInstruction implements StaticFieldInstruction { +public class StaticGet extends FieldInstruction implements FieldGet, StaticFieldInstruction { public StaticGet(Value dest, DexField field) { super(field, dest, (Value) null); @@ -183,6 +183,16 @@ } @Override + public boolean isFieldGet() { + return true; + } + + @Override + public FieldGet asFieldGet() { + return this; + } + + @Override public boolean isStaticFieldInstruction() { return true; } @@ -204,9 +214,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add( - new CfFieldInstruction( - org.objectweb.asm.Opcodes.GETSTATIC, getField(), builder.resolveField(getField()))); + builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField()))); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java index 92eea52..8cbeb6d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.ir.code; import com.android.tools.r8.cf.LoadStoreHelper; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.code.Sput; import com.android.tools.r8.code.SputBoolean; import com.android.tools.r8.code.SputByte; @@ -185,6 +185,16 @@ } @Override + public boolean isFieldPut() { + return true; + } + + @Override + public FieldPut asFieldPut() { + return this; + } + + @Override public boolean isStaticFieldInstruction() { return true; } @@ -206,9 +216,7 @@ @Override public void buildCf(CfBuilder builder) { - builder.add( - new CfFieldInstruction( - org.objectweb.asm.Opcodes.PUTSTATIC, getField(), builder.resolveField(getField()))); + builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField()))); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java index 66d7fa4..6ed9ef6 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.ir.conversion.CallGraphBuilderBase.CycleEliminator.CycleEliminationResult; import com.android.tools.r8.ir.conversion.CallSiteInformation.CallGraphBasedCallSiteInformation; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.Iterator; import java.util.Set; @@ -276,16 +276,16 @@ return nodes.isEmpty(); } - public SortedProgramMethodSet extractLeaves() { + public ProgramMethodSet extractLeaves() { return extractNodes(Node::isLeaf, Node::cleanCallersAndReadersForRemoval); } - public SortedProgramMethodSet extractRoots() { + public ProgramMethodSet extractRoots() { return extractNodes(Node::isRoot, Node::cleanCalleesAndWritersForRemoval); } - private SortedProgramMethodSet extractNodes(Predicate<Node> predicate, Consumer<Node> clean) { - SortedProgramMethodSet result = SortedProgramMethodSet.create(); + private ProgramMethodSet extractNodes(Predicate<Node> predicate, Consumer<Node> clean) { + ProgramMethodSet result = ProgramMethodSet.create(); Set<Node> removed = Sets.newIdentityHashSet(); Iterator<Node> nodeIterator = nodes.iterator(); while (nodeIterator.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java index 0085b70..b358828 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -519,7 +519,9 @@ && position != currentPosition // Ignore synthetic positions prior to any actual position, except when inlined // (that is, callerPosition != null). - && !(currentPosition.isNone() && position.synthetic && position.callerPosition == null) + && !(currentPosition.isNone() + && position.isSyntheticPosition() + && !position.hasCallerPosition()) && (appView.options().debug || instruction.instructionTypeCanThrow()); if (!didLocalsChange && !didPositionChange) { return;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java index 5abeb6e..1a39011 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -904,7 +904,7 @@ position .builderWithCopy() .setCallerPosition( - canonicalPositions.canonicalizeCallerPosition(position.callerPosition)) + canonicalPositions.canonicalizeCallerPosition(position.getCallerPosition())) .build()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java index 3bca6db..c4ac3ff 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -42,6 +42,8 @@ import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair; import com.android.tools.r8.graph.DexDebugEventBuilder; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.Argument; import com.android.tools.r8.ir.code.BasicBlock; @@ -84,6 +86,9 @@ // The IR representation of the code to build. private final IRCode ir; + // Extra information that should be attached to the bytecode instructions. + private final BytecodeMetadata.Builder<Instruction> bytecodeMetadataBuilder; + // The register allocator providing register assignments for the code to build. private final RegisterAllocator registerAllocator; @@ -120,13 +125,21 @@ BasicBlock nextBlock; - public DexBuilder(IRCode ir, RegisterAllocator registerAllocator) { - this(ir, registerAllocator, registerAllocator.options()); + public DexBuilder( + IRCode ir, + BytecodeMetadataProvider bytecodeMetadataProvider, + RegisterAllocator registerAllocator) { + this(ir, bytecodeMetadataProvider, registerAllocator, registerAllocator.options()); assert ir != null; } - private DexBuilder(IRCode ir, RegisterAllocator registerAllocator, InternalOptions options) { + private DexBuilder( + IRCode ir, + BytecodeMetadataProvider bytecodeMetadataProvider, + RegisterAllocator registerAllocator, + InternalOptions options) { this.ir = ir; + this.bytecodeMetadataBuilder = BytecodeMetadata.builder(bytecodeMetadataProvider); this.registerAllocator = registerAllocator; this.options = options; if (isBuildingForComparison()) { @@ -138,7 +151,8 @@ com.android.tools.r8.ir.code.Instruction a, com.android.tools.r8.ir.code.Instruction b, RegisterAllocator allocator) { - DexBuilder builder = new DexBuilder(null, allocator, allocator.options()); + DexBuilder builder = + new DexBuilder(null, BytecodeMetadataProvider.empty(), allocator, allocator.options()); Info infoA = buildInfoForComparison(a, builder); Info infoB = buildInfoForComparison(b, builder); return infoA.identicalInstructions(infoB, builder); @@ -314,7 +328,8 @@ dexInstructions.toArray(Instruction.EMPTY_ARRAY), tryInfo.tries, tryInfo.handlers, - debugEventBuilder.build()); + debugEventBuilder.build(), + bytecodeMetadataBuilder.build()); return code; } @@ -606,6 +621,7 @@ public void add(com.android.tools.r8.ir.code.Instruction instr, Instruction dex) { assert !instr.isGoto(); add(instr, new FixedSizeInfo(instr, dex)); + bytecodeMetadataBuilder.setMetadata(instr, dex); } public void add(com.android.tools.r8.ir.code.Instruction ir, Instruction... dex) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java index 4181373..f2c1d8d 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -43,6 +43,7 @@ import com.android.tools.r8.ir.code.CanonicalPositions; import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SourcePosition; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -255,10 +256,10 @@ private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) { // If this instruction has already been inlined then this.method must be the outermost caller. assert entry.callerPosition == null - || entry.callerPosition.getOutermostCaller().method == originalMethod; + || entry.callerPosition.getOutermostCaller().getMethod() == originalMethod; return canonicalPositions.getCanonical( - Position.builder() + SourcePosition.builder() .setLine(entry.line) .setFile(entry.sourceFile) .setMethod(entry.method)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java index 40c7bb9..b6fcd8c 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -829,7 +829,9 @@ } else { current = position; } - } else if (position.isSome() && !position.synthetic && !position.equals(current)) { + } else if (position.isSome() + && !position.isSyntheticPosition() + && !position.equals(current)) { DebugPosition positionChange = new DebugPosition(); positionChange.setPosition(position); it.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java index 07ba0ce..316fdb9 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -24,6 +24,8 @@ import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.analysis.TypeChecker; import com.android.tools.r8.ir.analysis.VerifyTypesHelper; import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation; @@ -110,16 +112,16 @@ import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import com.google.common.base.Suppliers; +import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -171,6 +173,7 @@ private DexString highestSortingString; private List<Action> onWaveDoneActions = null; + private final Set<DexMethod> prunedMethodsInWave = Sets.newIdentityHashSet(); private final List<DexString> neverMergePrefixes; // Use AtomicBoolean to satisfy TSAN checking (see b/153714743). @@ -264,8 +267,7 @@ this.classStaticizer = options.enableClassStaticizer ? new ClassStaticizer(appViewWithLiveness, this) : null; this.dynamicTypeOptimization = new DynamicTypeOptimization(appViewWithLiveness); - this.fieldAccessAnalysis = - FieldAccessAnalysis.enable(options) ? new FieldAccessAnalysis(appViewWithLiveness) : null; + this.fieldAccessAnalysis = new FieldAccessAnalysis(appViewWithLiveness); this.libraryMethodOverrideAnalysis = options.enableTreeShakingOfLibraryMethodOverrides ? new LibraryMethodOverrideAnalysis(appViewWithLiveness) @@ -274,8 +276,7 @@ this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness, enumUnboxer); this.inliner = new Inliner(appViewWithLiveness, this, lensCodeRewriter); this.outliner = Outliner.create(appViewWithLiveness); - this.memberValuePropagation = - options.enableValuePropagation ? new MemberValuePropagation(appViewWithLiveness) : null; + this.memberValuePropagation = new MemberValuePropagation(appViewWithLiveness); this.methodOptimizationInfoCollector = new MethodOptimizationInfoCollector(appViewWithLiveness, this); if (options.isMinifying()) { @@ -326,6 +327,10 @@ this(AppView.createForD8(appInfo), timing, printer); } + public Inliner getInliner() { + return inliner; + } + private void synthesizeBridgesForNestBasedAccessesOnClasspath( D8MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException { @@ -562,7 +567,10 @@ } } - private boolean needsIRConversion() { + private boolean needsIRConversion(ProgramMethod method) { + if (method.getDefinition().getCode().isThrowNullCode()) { + return false; + } if (appView.enableWholeProgramOptimizations()) { return true; } @@ -710,7 +718,7 @@ appView.withArgumentPropagator( argumentPropagator -> argumentPropagator.tearDownCodeScanner( - postMethodProcessorBuilder, executorService, timing)); + this, postMethodProcessorBuilder, executorService, timing)); appView.withCallSiteOptimizationInfoPropagator( callSiteOptimizationInfoPropagator -> callSiteOptimizationInfoPropagator.enqueueMethodsForReprocessing( @@ -844,12 +852,11 @@ onWaveDoneActions = Collections.synchronizedList(new ArrayList<>()); } - private void waveDone(ProgramMethodSet wave) { + private void waveDone(ProgramMethodSet wave, ExecutorService executorService) + throws ExecutionException { delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness()); delayedOptimizationFeedback.updateVisibleOptimizationInfo(); - if (options.enableFieldAssignmentTracker) { - fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback); - } + fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback); appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria); if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) { appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues(); @@ -858,6 +865,15 @@ assert delayedOptimizationFeedback.noUpdatesLeft(); onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute); onWaveDoneActions = null; + if (!prunedMethodsInWave.isEmpty()) { + appView.pruneItems( + PrunedItems.builder() + .setRemovedMethods(prunedMethodsInWave) + .setPrunedApp(appView.appInfo().app()) + .build(), + executorService); + prunedMethodsInWave.clear(); + } } public void addWaveDoneAction(com.android.tools.r8.utils.Action action) { @@ -886,30 +902,6 @@ }); } - private void forEachSelectedOutliningMethod( - ProgramMethodSet methodsSelectedForOutlining, - Consumer<IRCode> consumer, - ExecutorService executorService) - throws ExecutionException { - assert !options.skipIR; - ThreadUtils.processItems( - methodsSelectedForOutlining, - method -> { - IRCode code = method.buildIR(appView); - assert code != null; - assert !method.getDefinition().getCode().isOutlineCode(); - // Instead of repeating all the optimizations of rewriteCode(), only run the - // optimizations needed for outlining: rewriteMoveResult() to remove out-values on - // StringBuilder/StringBuffer method invocations, and removeDeadCode() to remove - // unused out-values. - codeRewriter.rewriteMoveResult(code); - deadCodeRemover.run(code, Timing.empty()); - CodeRewriter.removeAssumeInstructions(appView, code); - consumer.accept(code); - }, - executorService); - } - private void processSynthesizedServiceLoaderMethods( List<ProgramMethod> serviceLoadMethods, ExecutorService executorService) throws ExecutionException { @@ -952,7 +944,7 @@ RegisterAllocator registerAllocator = performRegisterAllocation( code, method, DefaultMethodConversionOptions.getInstance(), timing); - method.setCode(code, registerAllocator, appView); + method.setCode(code, BytecodeMetadataProvider.empty(), registerAllocator, appView); if (Log.ENABLED) { Log.debug(getClass(), "Resulting dex code for %s:\n%s", method.toSourceString(), logCode(options, method)); @@ -963,7 +955,7 @@ List<ProgramMethod> programMethods, ExecutorService executorService) throws ExecutionException { // Process the generated class, but don't apply any outlining. - SortedProgramMethodSet methods = SortedProgramMethodSet.create(programMethods::forEach); + ProgramMethodSet methods = ProgramMethodSet.create(programMethods::forEach); processMethodsConcurrently(methods, executorService); } @@ -979,8 +971,8 @@ } } - public void processMethodsConcurrently( - SortedProgramMethodSet wave, ExecutorService executorService) throws ExecutionException { + public void processMethodsConcurrently(ProgramMethodSet wave, ExecutorService executorService) + throws ExecutionException { if (!wave.isEmpty()) { OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView); methodProcessor.forEachWaveWithExtension( @@ -1089,7 +1081,7 @@ options.testing.hookInIrConversion.run(); } - if (!needsIRConversion() || options.skipIR) { + if (!needsIRConversion(method) || options.skipIR) { feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER); return Timing.empty(); } @@ -1193,7 +1185,7 @@ assert code.verifyTypes(appView); assert code.isConsistentSSA(); - if (appView.isCfByteCodePassThrough(method)) { + if (shouldPassThrough(context)) { // If the code is pass trough, do not finalize by overwriting the existing code. assert appView.enableWholeProgramOptimizations(); timing.begin("Collect optimization info"); @@ -1204,8 +1196,10 @@ feedback, methodProcessor, conversionOptions, + BytecodeMetadataProvider.builder(), timing); timing.end(); + markProcessed(code, feedback); return timing; } @@ -1438,7 +1432,7 @@ previous = printMethod(code, "IR after interface method rewriting (SSA)", previous); - // TODO(b/140766440): an ideal solution would be puttting CodeOptimization for this into + // TODO(b/140766440): an ideal solution would be putting CodeOptimization for this into // the list for primary processing only. outliner.collectOutlineSites(code, timing); @@ -1495,6 +1489,8 @@ deadCodeRemover.run(code, timing); + BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder = + BytecodeMetadataProvider.builder(); if (appView.enableWholeProgramOptimizations()) { timing.begin("Collect optimization info"); collectOptimizationInfo( @@ -1504,6 +1500,7 @@ feedback, methodProcessor, conversionOptions, + bytecodeMetadataProviderBuilder, timing); timing.end(); } @@ -1531,11 +1528,20 @@ printMethod(code, "Optimized IR (SSA)", previous); timing.begin("Finalize IR"); - finalizeIR(code, feedback, conversionOptions, timing); + finalizeIR(code, feedback, conversionOptions, bytecodeMetadataProviderBuilder.build(), timing); timing.end(); return timing; } + private boolean shouldPassThrough(ProgramMethod method) { + if (appView.isCfByteCodePassThrough(method.getDefinition())) { + return true; + } + Code code = method.getDefinition().getCode(); + assert !code.isThrowNullCode(); + return code.isDefaultInstanceInitializerCode(); + } + // Compute optimization info summary for the current method unless it is pinned // (in that case we should not be making any assumptions about the behavior of the method). public void collectOptimizationInfo( @@ -1545,6 +1551,7 @@ OptimizationFeedback feedback, MethodProcessor methodProcessor, MutableMethodConversionOptions conversionOptions, + BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder, Timing timing) { appView.withArgumentPropagator( argumentPropagator -> argumentPropagator.scan(method, code, methodProcessor, timing)); @@ -1561,7 +1568,8 @@ if (fieldAccessAnalysis != null) { timing.begin("Analyze field accesses"); - fieldAccessAnalysis.recordFieldAccesses(code, feedback, methodProcessor); + fieldAccessAnalysis.recordFieldAccesses( + code, bytecodeMetadataProviderBuilder, feedback, methodProcessor); if (classInitializerDefaultsResult != null) { fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult); } @@ -1617,20 +1625,26 @@ stringSwitchRemover.run(code); } deadCodeRemover.run(code, timing); - finalizeIR(code, feedback, DefaultMethodConversionOptions.getInstance(), timing); + finalizeIR( + code, + feedback, + DefaultMethodConversionOptions.getInstance(), + BytecodeMetadataProvider.empty(), + timing); } public void finalizeIR( IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions, + BytecodeMetadataProvider bytecodeMetadataProvider, Timing timing) { code.traceBlocks(); if (options.isGeneratingClassFiles()) { finalizeToCf(code, feedback, conversionOptions); } else { assert options.isGeneratingDex(); - finalizeToDex(code, feedback, conversionOptions, timing); + finalizeToDex(code, feedback, conversionOptions, bytecodeMetadataProvider, timing); } } @@ -1648,6 +1662,7 @@ IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions, + BytecodeMetadataProvider bytecodeMetadataProvider, Timing timing) { DexEncodedMethod method = code.method(); // Workaround massive dex2oat memory use for self-recursive methods. @@ -1658,7 +1673,7 @@ RegisterAllocator registerAllocator = performRegisterAllocation(code, method, conversionOptions, timing); timing.begin("Build DEX code"); - method.setCode(code, registerAllocator, appView); + method.setCode(code, bytecodeMetadataProvider, registerAllocator, appView); timing.end(); updateHighestSortingStrings(method); if (Log.ENABLED) { @@ -1697,7 +1712,9 @@ } private synchronized void updateHighestSortingStrings(DexEncodedMethod method) { - DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString; + Code code = method.getCode(); + assert code.isDexWritableCode(); + DexString highestSortingReferencedString = code.asDexWritableCode().getHighestSortingString(); if (highestSortingReferencedString != null) { if (highestSortingString == null || highestSortingReferencedString.compareTo(highestSortingString) > 0) { @@ -1961,9 +1978,13 @@ appView.withArgumentPropagator(argumentPropagator -> argumentPropagator.onMethodPruned(method)); enumUnboxer.onMethodPruned(method); outliner.onMethodPruned(method); + if (classStaticizer != null) { + classStaticizer.onMethodPruned(method); + } if (inliner != null) { inliner.onMethodPruned(method); } + prunedMethodsInWave.add(method.getReference()); } /** @@ -1977,6 +1998,9 @@ argumentPropagator -> argumentPropagator.onMethodCodePruned(method)); enumUnboxer.onMethodCodePruned(method); outliner.onMethodCodePruned(method); + if (classStaticizer != null) { + classStaticizer.onMethodCodePruned(method); + } if (inliner != null) { inliner.onMethodCodePruned(method); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java index dd5b328..c411187 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -61,6 +61,10 @@ this.rewrittenCallSiteCache = null; } + public DexItemFactory dexItemFactory() { + return definitions.dexItemFactory(); + } + public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) { if (rewrittenCallSiteCache == null) { return rewriteCallSiteInternal(callSite, context);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java index d0f2413..f0c766d 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java
@@ -4,12 +4,12 @@ package com.android.tools.r8.ir.conversion; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; public abstract class MethodProcessorWithWave extends MethodProcessor { - protected SortedProgramMethodSet wave; - protected SortedProgramMethodSet waveExtension = SortedProgramMethodSet.createConcurrent(); + protected ProgramMethodSet wave; + protected ProgramMethodSet waveExtension = ProgramMethodSet.createConcurrent(); @Override public CallSiteInformation getCallSiteInformation() { @@ -28,10 +28,10 @@ protected void prepareForWaveExtensionProcessing() { if (waveExtension.isEmpty()) { - wave = SortedProgramMethodSet.empty(); + wave = ProgramMethodSet.empty(); } else { wave = waveExtension; - waveExtension = SortedProgramMethodSet.createConcurrent(); + waveExtension = ProgramMethodSet.createConcurrent(); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java index 1ca91a7..65a3287 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.ThreadUtils; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -20,7 +20,7 @@ private final ProcessorContext processorContext; - private OneTimeMethodProcessor(ProcessorContext processorContext, SortedProgramMethodSet wave) { + private OneTimeMethodProcessor(ProcessorContext processorContext, ProgramMethodSet wave) { this.processorContext = processorContext; this.wave = wave; } @@ -30,21 +30,21 @@ } public static OneTimeMethodProcessor create(ProgramMethod methodToProcess, AppView<?> appView) { - return create(SortedProgramMethodSet.create(methodToProcess), appView); + return create(ProgramMethodSet.create(methodToProcess), appView); } public static OneTimeMethodProcessor create( ProgramMethod methodToProcess, ProcessorContext processorContext) { - return create(SortedProgramMethodSet.create(methodToProcess), processorContext); + return create(ProgramMethodSet.create(methodToProcess), processorContext); } public static OneTimeMethodProcessor create( - SortedProgramMethodSet methodsToProcess, AppView<?> appView) { + ProgramMethodSet methodsToProcess, AppView<?> appView) { return create(methodsToProcess, appView.createProcessorContext()); } public static OneTimeMethodProcessor create( - SortedProgramMethodSet methodsToProcess, ProcessorContext processorContext) { + ProgramMethodSet methodsToProcess, ProcessorContext processorContext) { return new OneTimeMethodProcessor(processorContext, methodsToProcess); } @@ -85,7 +85,7 @@ public static class Builder { - private final SortedProgramMethodSet methodsToProcess = SortedProgramMethodSet.create(); + private final ProgramMethodSet methodsToProcess = ProgramMethodSet.create(); private final ProcessorContext processorContext; Builder(ProcessorContext processorContext) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java index 6435f37..c0f6e90 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -23,7 +23,6 @@ import com.android.tools.r8.utils.Timing.TimingMerger; import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; @@ -34,7 +33,7 @@ public class PostMethodProcessor extends MethodProcessorWithWave { private final ProcessorContext processorContext; - private final Deque<SortedProgramMethodSet> waves; + private final Deque<ProgramMethodSet> waves; private final ProgramMethodSet processed = ProgramMethodSet.create(); private PostMethodProcessor( @@ -116,11 +115,11 @@ } } - private Deque<SortedProgramMethodSet> createWaves(CallGraph callGraph) { - Deque<SortedProgramMethodSet> waves = new ArrayDeque<>(); + private Deque<ProgramMethodSet> createWaves(CallGraph callGraph) { + Deque<ProgramMethodSet> waves = new ArrayDeque<>(); int waveCount = 1; while (!callGraph.isEmpty()) { - SortedProgramMethodSet wave = callGraph.extractLeaves(); + ProgramMethodSet wave = callGraph.extractLeaves(); waves.addLast(wave); if (Log.ENABLED && Log.isLoggingEnabledFor(PostMethodProcessor.class)) { Log.info(getClass(), "Wave #%d: %d", waveCount++, wave.size());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java index bea221a..be8f65e 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -16,14 +16,12 @@ import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.Timing.TimingMerger; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.function.Consumer; /** * A {@link MethodProcessor} that processes methods in the whole program in a bottom-up manner, @@ -36,9 +34,15 @@ void notifyWaveStart(ProgramMethodSet wave); } + interface WaveDoneAction { + + void notifyWaveDone(ProgramMethodSet wave, ExecutorService executorService) + throws ExecutionException; + } + private final AppView<?> appView; private final CallSiteInformation callSiteInformation; - private final Deque<SortedProgramMethodSet> waves; + private final Deque<ProgramMethodSet> waves; private ProcessorContext processorContext; @@ -80,13 +84,13 @@ return callSiteInformation; } - private Deque<SortedProgramMethodSet> createWaves(AppView<?> appView, CallGraph callGraph) { + private Deque<ProgramMethodSet> createWaves(AppView<?> appView, CallGraph callGraph) { InternalOptions options = appView.options(); - Deque<SortedProgramMethodSet> waves = new ArrayDeque<>(); + Deque<ProgramMethodSet> waves = new ArrayDeque<>(); Set<Node> nodes = callGraph.nodes; int waveCount = 1; while (!nodes.isEmpty()) { - SortedProgramMethodSet wave = callGraph.extractLeaves(); + ProgramMethodSet wave = callGraph.extractLeaves(); waves.addLast(wave); if (Log.ENABLED && Log.isLoggingEnabledFor(PrimaryMethodProcessor.class)) { Log.info(getClass(), "Wave #%d: %d", waveCount++, wave.size()); @@ -110,7 +114,7 @@ <E extends Exception> void forEachMethod( MethodAction<E> consumer, WaveStartAction waveStartAction, - Consumer<ProgramMethodSet> waveDone, + WaveDoneAction waveDoneAction, Timing timing, ExecutorService executorService) throws ExecutionException { @@ -133,7 +137,7 @@ }, executorService); merger.add(timings); - waveDone.accept(wave); + waveDoneAction.notifyWaveDone(wave, executorService); prepareForWaveExtensionProcessing(); } while (!wave.isEmpty()); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SyntheticStraightLineSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SyntheticStraightLineSourceCode.java new file mode 100644 index 0000000..b505cf0 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/conversion/SyntheticStraightLineSourceCode.java
@@ -0,0 +1,142 @@ +// Copyright (c) 2021, 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.conversion; + +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.DebugLocalInfo; +import com.android.tools.r8.ir.code.CatchHandlers; +import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.utils.ConsumerUtils; +import java.util.List; +import java.util.function.Consumer; + +public abstract class SyntheticStraightLineSourceCode implements SourceCode { + + private final List<Consumer<IRBuilder>> instructionBuilders; + private final Position position; + + protected SyntheticStraightLineSourceCode( + List<Consumer<IRBuilder>> instructionBuilders, Position position) { + this.instructionBuilders = instructionBuilders; + this.position = position; + } + + @Override + public int instructionCount() { + return instructionBuilders.size(); + } + + @Override + public int instructionIndex(int instructionOffset) { + return instructionOffset; + } + + @Override + public int instructionOffset(int instructionIndex) { + return instructionIndex; + } + + @Override + public void buildPrelude(IRBuilder builder) { + int firstArgumentRegister = 0; + builder.buildArgumentsWithRewrittenPrototypeChanges( + firstArgumentRegister, builder.getMethod(), ConsumerUtils.emptyBiConsumer()); + } + + @Override + public void buildInstruction( + IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) { + instructionBuilders.get(instructionIndex).accept(builder); + } + + @Override + public void buildPostlude(IRBuilder builder) { + // Intentionally empty. + } + + @Override + public void clear() { + // Intentionally empty. + } + + @Override + public Position getCanonicalDebugPositionAtOffset(int offset) { + return null; + } + + @Override + public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) { + return null; + } + + @Override + public Position getCurrentPosition() { + return position; + } + + @Override + public DebugLocalInfo getIncomingLocal(int register) { + return null; + } + + @Override + public DebugLocalInfo getIncomingLocalAtBlock(int register, int blockOffset) { + return null; + } + + @Override + public DebugLocalInfo getOutgoingLocal(int register) { + return null; + } + + @Override + public void setUp() { + // Intentionally empty. + } + + @Override + public int traceInstruction(int instructionIndex, IRBuilder builder) { + // This instruction does not close the block. + return -1; + } + + @Override + public boolean verifyCurrentInstructionCanThrow() { + return true; + } + + @Override + public boolean verifyRegister(int register) { + return true; + } + + @Override + public void buildBlockTransfer( + IRBuilder builder, int predecessorOffset, int successorOffset, boolean isExceptional) { + throw new Unreachable(); + } + + @Override + public int getMoveExceptionRegister(int instructionIndex) { + throw new Unreachable(); + } + + @Override + public void resolveAndBuildNewArrayFilledData( + int arrayRef, int payloadOffset, IRBuilder builder) { + throw new Unreachable(); + } + + @Override + public void resolveAndBuildSwitch( + int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) { + throw new Unreachable(); + } + + @Override + public boolean verifyLocalInScope(DebugLocalInfo local) { + throw new Unreachable(); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java index fd7a8b9..b0abf2a 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -4,12 +4,12 @@ package com.android.tools.r8.ir.desugar; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfNew; import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfStackInstruction; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.graph.CfCode; import com.google.common.collect.ImmutableList; import org.objectweb.asm.Opcodes; @@ -29,7 +29,7 @@ new CfNew(lambda.type), new CfStackInstruction(Opcode.Dup), new CfInvoke(Opcodes.INVOKESPECIAL, lambda.constructor, false), - new CfFieldInstruction(Opcodes.PUTSTATIC, lambda.lambdaField, lambda.lambdaField), + new CfStaticFieldWrite(lambda.lambdaField, lambda.lambdaField), new CfReturnVoid()), ImmutableList.of(), ImmutableList.of());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java index 65ead77..7ccba56 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.ir.desugar; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfInstanceFieldWrite; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLoad; @@ -42,7 +42,7 @@ ValueType type = ValueType.fromDexType(field.type); instructions.add(new CfLoad(ValueType.OBJECT, 0)); instructions.add(new CfLoad(type, maxLocals)); - instructions.add(new CfFieldInstruction(Opcodes.PUTFIELD, field, field)); + instructions.add(new CfInstanceFieldWrite(field)); maxLocals += type.requiredRegisters(); maxStack += type.requiredRegisters(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java index acfee7b..eb5a2eb 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -5,7 +5,7 @@ package com.android.tools.r8.ir.desugar; import com.android.tools.r8.cf.code.CfCheckCast; -import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfInstanceFieldRead; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLoad; @@ -233,7 +233,7 @@ DexField field = lambda.getCaptureField(i); ValueType valueType = ValueType.fromDexType(field.type); instructions.add(new CfLoad(ValueType.OBJECT, 0)); - instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, field, field)); + instructions.add(new CfInstanceFieldRead(field)); maxStack += valueType.requiredRegisters(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java index 3d7ae96..3f9e078 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -7,9 +7,7 @@ import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.THROW_ICCE; import static com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass.Behaviour.THROW_NSME; import static com.android.tools.r8.utils.AndroidApiLevel.minApiLevelIfEnabledOrUnknown; -import static org.objectweb.asm.Opcodes.GETSTATIC; import static org.objectweb.asm.Opcodes.INVOKESTATIC; -import static org.objectweb.asm.Opcodes.PUTSTATIC; import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfConstClass; @@ -17,7 +15,6 @@ import com.android.tools.r8.cf.code.CfConstNull; import com.android.tools.r8.cf.code.CfConstNumber; import com.android.tools.r8.cf.code.CfConstString; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfFrame; import com.android.tools.r8.cf.code.CfFrame.FrameType; import com.android.tools.r8.cf.code.CfGoto; @@ -30,6 +27,8 @@ import com.android.tools.r8.cf.code.CfReturn; import com.android.tools.r8.cf.code.CfStackInstruction; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStaticFieldRead; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.cf.code.CfThrow; import com.android.tools.r8.cf.code.CfTryCatch; @@ -242,7 +241,7 @@ CfLabel tryCatchTarget = new CfLabel(); CfLabel tryCatchEndFinally = new CfLabel(); - instructions.add(new CfFieldInstruction(GETSTATIC, initializedValueField)); + instructions.add(new CfStaticFieldRead(initializedValueField)); instructions.add(new CfIf(If.Type.NE, ValueType.INT, initializedTrue)); instructions.add(new CfConstClass(builder.getType())); @@ -251,13 +250,13 @@ instructions.add(new CfMonitor(Monitor.Type.ENTER)); instructions.add(tryCatchStart); - instructions.add(new CfFieldInstruction(GETSTATIC, initializedValueField)); + instructions.add(new CfStaticFieldRead(initializedValueField)); instructions.add(new CfIf(If.Type.NE, ValueType.INT, initializedTrueSecond)); invokeBootstrapMethod(instructions); - instructions.add(new CfFieldInstruction(PUTSTATIC, constantValueField)); + instructions.add(new CfStaticFieldWrite(constantValueField)); instructions.add(new CfConstNumber(1, ValueType.INT)); - instructions.add(new CfFieldInstruction(PUTSTATIC, initializedValueField)); + instructions.add(new CfStaticFieldWrite(initializedValueField)); instructions.add(initializedTrueSecond); instructions.add( @@ -287,7 +286,7 @@ instructions.add(initializedTrue); instructions.add(new CfFrame(ImmutableInt2ReferenceSortedMap.empty(), ImmutableDeque.of())); - instructions.add(new CfFieldInstruction(GETSTATIC, constantValueField)); + instructions.add(new CfStaticFieldRead(constantValueField)); instructions.add(new CfReturn(ValueType.OBJECT)); List<CfTryCatch> tryCatchRanges =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java index 6b76836..7226037 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -6,11 +6,11 @@ import com.android.tools.r8.cf.CfVersion; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfInitClass; import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfStackInstruction; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStaticFieldRead; import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -44,7 +44,6 @@ import java.util.Map; import java.util.Set; import java.util.function.Predicate; -import org.objectweb.asm.Opcodes; public class InterfaceDesugaringSyntheticHelper { @@ -493,10 +492,7 @@ isWide ? 2 : 1, 0, ImmutableList.of( - new CfFieldInstruction( - Opcodes.GETSTATIC, - clinitField.getReference(), - clinitField.getReference()), + new CfStaticFieldRead(clinitField.getReference(), clinitField.getReference()), isWide ? new CfStackInstruction(Opcode.Pop2) : new CfStackInstruction(Opcode.Pop),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java index 7513257..c89f90c 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -267,7 +267,7 @@ assert InvalidCode.isInvalidCode(defaultMethod.getCode()); assert !InvalidCode.isInvalidCode(companionMethod.getCode()); defaultMethod.accessFlags.setAbstract(); - defaultMethod.removeCode(); + defaultMethod.unsetCode(); graphLensBuilder.recordCodeMovedToCompanionClass( defaultMethod.getReference(), companionMethod.getReference()); });
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java index 206c276..4c45086 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.ir.desugar.lambda; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfInvokeDynamic; @@ -12,6 +11,7 @@ import com.android.tools.r8.cf.code.CfNew; import com.android.tools.r8.cf.code.CfStackInstruction; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStaticFieldRead; import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; import com.android.tools.r8.graph.AppView; @@ -123,8 +123,7 @@ if (lambdaClass.isStateless()) { return ImmutableList.of( - new CfFieldInstruction( - Opcodes.GETSTATIC, lambdaClass.lambdaField, lambdaClass.lambdaField)); + new CfStaticFieldRead(lambdaClass.lambdaField, lambdaClass.lambdaField)); } DexTypeList captureTypes = lambdaClass.descriptor.captures;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java index cdc2c6e..bcd27e6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -79,6 +79,7 @@ import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.StaticGet; import com.android.tools.r8.ir.code.Switch; import com.android.tools.r8.ir.code.Throw; @@ -3673,7 +3674,8 @@ InstructionListIterator iterator = block.listIterator(code); // Attach some synthetic position to all inserted code. - Position position = Position.synthetic(1, method.getReference(), null); + Position position = + SyntheticPosition.builder().setLine(1).setMethod(method.getReference()).build(); iterator.setInsertionPosition(position); // Split arguments into their own block.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index 32d2228..1e69a6b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -98,6 +98,8 @@ // pruned when the wave ends. private final Map<DexProgramClass, ProgramMethodSet> singleCallerInlinedMethodsInWave = new ConcurrentHashMap<>(); + private final Set<DexMethod> singleCallerInlinedPrunedMethodsForTesting = + Sets.newIdentityHashSet(); private final AvailableApiExceptions availableApiExceptions; @@ -1202,10 +1204,8 @@ Set<BasicBlock> inlineeBlocks = SetUtils.newIdentityHashSet(inlinee.blocks); // Run member value propagation on the inlinee blocks. - if (appView.options().enableValuePropagation) { - rewindBlockIteratorToFirstInlineeBlock(blockIterator, block); - applyMemberValuePropagationToInlinee(code, blockIterator, block, inlineeBlocks); - } + rewindBlockIteratorToFirstInlineeBlock(blockIterator, block); + applyMemberValuePropagationToInlinee(code, blockIterator, block, inlineeBlocks); // Add non-null IRs only to the inlinee blocks. insertAssumeInstructions(code, blockIterator, block, inlineeBlocks, timing); @@ -1268,10 +1268,13 @@ (clazz, singleCallerInlinedMethodsForClass) -> { // Convert and remove virtual single caller inlined methods to abstract or throw null. singleCallerInlinedMethodsForClass.removeIf( - singleCallerInlinedMethod -> { - if (singleCallerInlinedMethod.getDefinition().belongsToVirtualPool() || true) { - singleCallerInlinedMethod.convertToAbstractOrThrowNullMethod(appView); - converter.onMethodCodePruned(singleCallerInlinedMethod); + method -> { + // TODO(b/203188583): Enable pruning of methods with generic signatures. For this to + // work we need to pass in a seed to GenericSignatureContextBuilder.create in R8. + if (method.getDefinition().belongsToVirtualPool() + || method.getDefinition().getGenericSignature().hasSignature()) { + method.convertToAbstractOrThrowNullMethod(appView); + converter.onMethodCodePruned(method); return true; } return false; @@ -1284,7 +1287,10 @@ .removeMethods( singleCallerInlinedMethodsForClass.toDefinitionSet( SetUtils::newIdentityHashSet)); - singleCallerInlinedMethodsForClass.forEach(converter::onMethodPruned); + for (ProgramMethod method : singleCallerInlinedMethodsForClass) { + converter.onMethodPruned(method); + singleCallerInlinedPrunedMethodsForTesting.add(method.getReference()); + } } }); singleCallerInlinedMethodsInWave.clear(); @@ -1302,4 +1308,9 @@ } return true; } + + public boolean verifyIsPrunedDueToSingleCallerInlining(DexMethod method) { + assert singleCallerInlinedPrunedMethodsForTesting.contains(method); + return true; + } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java index 31801c6..921bc7f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -43,10 +43,14 @@ import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.InvokeStatic; +import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator; import com.android.tools.r8.ir.code.Mul; import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.OutlineCallerPosition; +import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder; +import com.android.tools.r8.ir.code.Position.OutlinePosition; import com.android.tools.r8.ir.code.Rem; import com.android.tools.r8.ir.code.Sub; import com.android.tools.r8.ir.code.Value; @@ -73,7 +77,9 @@ import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.ProgramMethodSet; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -83,6 +89,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; @@ -761,9 +768,8 @@ abstract private class OutlineSpotter { final ProgramMethod method; - final BasicBlock block; - // instructionArrayCache is block.getInstructions() copied to an ArrayList. - private List<Instruction> instructionArrayCache = null; + final IRCode irCode; + final List<Instruction> currentCandidateInstructions; int start; int index; @@ -777,32 +783,17 @@ int returnValueUniqueUsersLeft; int pendingNewInstanceIndex = -1; - OutlineSpotter(ProgramMethod method, BasicBlock block) { + OutlineSpotter( + ProgramMethod method, IRCode irCode, List<Instruction> currentCandidateInstructions) { this.method = method; - this.block = block; + this.irCode = irCode; + this.currentCandidateInstructions = currentCandidateInstructions; reset(0); } - protected List<Instruction> getInstructionArray() { - if (instructionArrayCache == null) { - instructionArrayCache = new ArrayList<>(block.getInstructions()); - } - return instructionArrayCache; - } - - // Call this before modifying block.getInstructions(). - protected void invalidateInstructionArray() { - instructionArrayCache = null; - } - protected void process() { - List<Instruction> instructions; - for (;;) { - instructions = getInstructionArray(); // ProcessInstruction may have invalidated it. - if (index >= instructions.size()) { - break; - } - processInstruction(instructions.get(index)); + while (index < currentCandidateInstructions.size()) { + processInstruction(currentCandidateInstructions.get(index)); } } @@ -941,10 +932,9 @@ assert index > 0; int offset = 0; Instruction previous; - List<Instruction> instructions = getInstructionArray(); do { offset++; - previous = instructions.get(index - offset); + previous = currentCandidateInstructions.get(index - offset); } while (previous.isConstInstruction()); if (!previous.isNewInstance() || invoke != previous.asNewInstance().getUniqueConstructorInvoke(dexItemFactory)) { @@ -1134,8 +1124,7 @@ protected abstract void handle(int start, int end, Outline outline); private void candidate(int start, int index) { - List<Instruction> instructions = getInstructionArray(); - assert !instructions.get(start).isConstInstruction(); + assert !currentCandidateInstructions.get(start).isConstInstruction(); if (pendingNewInstanceIndex != -1) { if (pendingNewInstanceIndex == start) { @@ -1148,7 +1137,7 @@ // Back out of any const instructions ending this candidate. int end = index; - while (instructions.get(end - 1).isConstInstruction()) { + while (currentCandidateInstructions.get(end - 1).isConstInstruction()) { end--; } @@ -1159,7 +1148,8 @@ } Outline outline = - new Outline(instructions, argumentTypes, argumentsMap, returnType, start, end); + new Outline( + currentCandidateInstructions, argumentTypes, argumentsMap, returnType, start, end); handle(start, end, outline); // Start a new candidate search from the next instruction after this outline. @@ -1189,8 +1179,11 @@ private final List<Outline> outlinesForMethod; OutlineMethodIdentifier( - ProgramMethod method, BasicBlock block, List<Outline> outlinesForMethod) { - super(method, block); + ProgramMethod method, + IRCode irCode, + List<Instruction> currentCandidateInstructions, + List<Outline> outlinesForMethod) { + super(method, irCode, currentCandidateInstructions); this.outlinesForMethod = outlinesForMethod; } @@ -1202,8 +1195,9 @@ private class OutlineSiteIdentifier extends OutlineSpotter { - OutlineSiteIdentifier(ProgramMethod method, BasicBlock block) { - super(method, block); + OutlineSiteIdentifier( + ProgramMethod method, IRCode irCode, List<Instruction> currentCandidateInstructions) { + super(method, irCode, currentCandidateInstructions); } @Override @@ -1218,45 +1212,53 @@ private class OutlineRewriter extends OutlineSpotter { private final IRCode code; - private final ListIterator<BasicBlock> blocksIterator; - private final List<Integer> toRemove; + private final Set<Instruction> toRemove; + private final Set<Instruction> invokesToOutlineMethods; int argumentsMapIndex; OutlineRewriter( IRCode code, - ListIterator<BasicBlock> blocksIterator, - BasicBlock block, - List<Integer> toRemove) { - super(code.context(), block); + List<Instruction> currentCandidateInstructions, + Set<Instruction> toRemove, + Set<Instruction> invokesToOutlineMethods) { + super(code.context(), code, currentCandidateInstructions); this.code = code; - this.blocksIterator = blocksIterator; this.toRemove = toRemove; + this.invokesToOutlineMethods = invokesToOutlineMethods; } @Override protected void handle(int start, int end, Outline outline) { - DexMethod m = generatedOutlines.get(outline); - if (m != null) { + DexMethod outlineMethod = generatedOutlines.get(outline); + if (outlineMethod != null) { assert removeMethodFromOutlineList(outline); List<Value> in = new ArrayList<>(); Value returnValue = null; argumentsMapIndex = 0; + OutlineCallerPositionBuilder positionBuilder = + OutlineCallerPosition.builder() + .setMethod(appView.graphLens().getOriginalMethodSignature(method.getReference())) + .setOutlineCallee(outlineMethod) + // We set the line number to 0 here and rely on the LineNumberOptimizer to + // set a new disjoint line. + .setLine(0); + Instruction lastInstruction = null; Position position = Position.none(); { // Scope for 'instructions'. - List<Instruction> instructions = getInstructionArray(); + int outlinePositionIndex = 0; for (int i = start; i < end; i++) { - Instruction current = instructions.get(i); + Instruction current = currentCandidateInstructions.get(i); if (current.isConstInstruction()) { // Leave any const instructions. continue; } - if (position.isNone()) { - position = current.getPosition(); + if (current.getPosition() != null) { + positionBuilder.addOutlinePosition(outlinePositionIndex++, current.getPosition()); } + // Prepare to remove the instruction. List<Value> inValues = orderedInValues(current, returnValue); - for (int j = 0; j < inValues.size(); j++) { - Value value = inValues.get(j); + for (Value value : inValues) { value.removeUser(current); int argumentIndex = outline.argumentMap.get(argumentsMapIndex++); if (argumentIndex >= in.size()) { @@ -1270,32 +1272,25 @@ // The invoke of the outline method will be placed at the last instruction index, // so don't mark that for removal. if (i < end - 1) { - toRemove.add(i); + toRemove.add(current); } + lastInstruction = current; } } - assert m.proto.shorty.toString().length() - 1 == in.size(); + assert lastInstruction != null; + assert outlineMethod.proto.shorty.toString().length() - 1 == in.size(); if (returnValue != null && !returnValue.isUsed()) { returnValue = null; } - Invoke outlineInvoke = new InvokeStatic(m, returnValue, in); - outlineInvoke.setBlock(block); - outlineInvoke.setPosition(position); - if (position.isNone() && code.doAllThrowingInstructionsHavePositions()) { - // We have introduced a static invoke, but non of the outlines instructions could throw - // and none had a position. The code no longer has the previous property. - code.setAllThrowingInstructionsHavePositions(false); - } - InstructionListIterator endIterator = block.listIterator(code, end - 1); - Instruction instructionBeforeEnd = endIterator.next(); - invalidateInstructionArray(); // Because we're about to modify the original linked list. - instructionBeforeEnd.clearBlock(); + Invoke outlineInvoke = new InvokeStatic(outlineMethod, returnValue, in); + outlineInvoke.setBlock(lastInstruction.getBlock()); + outlineInvoke.setPosition(positionBuilder.build()); + InstructionListIterator endIterator = + lastInstruction.getBlock().listIterator(code, lastInstruction); + Instruction instructionBeforeEnd = endIterator.previous(); + assert instructionBeforeEnd == lastInstruction; endIterator.set(outlineInvoke); // Replaces instructionBeforeEnd. - if (block.hasCatchHandlers()) { - // If the inserted invoke is inserted in a block with handlers, split the block after - // the inserted invoke. - endIterator.split(code, blocksIterator); - } + invokesToOutlineMethods.add(outlineInvoke); } } @@ -1419,20 +1414,71 @@ timing.begin("Collect outlines"); List<Outline> outlinesForMethod = new ArrayList<>(); - for (BasicBlock block : code.blocks) { - new OutlineMethodIdentifier(context, block, outlinesForMethod).process(); - } + getInstructions( + appView, + code, + instructions -> + new OutlineMethodIdentifier(context, code, instructions, outlinesForMethod).process()); outlineCollection.set(appView, context, outlinesForMethod); timing.end(); } + public static void getInstructions( + AppView<?> appView, IRCode code, Consumer<List<Instruction>> consumer) { + int maxNumberOfInstructionsToBeConsidered = + appView.options().outline.maxNumberOfInstructionsToBeConsidered; + int minSize = appView.options().outline.minSize; + Set<BasicBlock> seenBlocks = Sets.newIdentityHashSet(); + for (BasicBlock block : code.blocks) { + if (seenBlocks.add(block)) { + ImmutableList.Builder<Instruction> builder = ImmutableList.builder(); + LinearFlowInstructionListIterator instructionIterator = + new LinearFlowInstructionListIterator(code, block); + // Maintaining the last seen block ensure that we always consider all instructions in a + // block before adding it to the seen set. + BasicBlock lastSeenBlock = block; + int counter = 0; + boolean sawLinearFlowWithCatchHandlers = false; + while (instructionIterator.hasNext()) { + Instruction instruction = instructionIterator.next(); + // Disregard linear flow when there are catch handlers + if (instruction.getBlock() != block + && (block.hasCatchHandlers() || instruction.getBlock().hasCatchHandlers())) { + lastSeenBlock = instruction.getBlock(); + sawLinearFlowWithCatchHandlers = true; + break; + } + builder.add(instruction); + counter++; + if (counter > maxNumberOfInstructionsToBeConsidered + && instruction.getBlock() != lastSeenBlock) { + // Ensure we only break on whole blocks. + break; + } + lastSeenBlock = instruction.getBlock(); + } + seenBlocks.addAll(instructionIterator.getSeenBlocks()); + if (sawLinearFlowWithCatchHandlers) { + assert lastSeenBlock != block; + // Remove the last seen block since we just visited the first instruction in that block + // and terminated without adding it. + seenBlocks.remove(lastSeenBlock); + } + if (counter >= minSize) { + consumer.accept(builder.build()); + } + } + } + } + public void identifyOutlineSites(IRCode code) { ProgramMethod context = code.context(); assert !context.getDefinition().getCode().isOutlineCode(); assert !ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView); - for (BasicBlock block : code.blocks) { - new OutlineSiteIdentifier(context, block).process(); - } + getInstructions( + appView, + code, + instructions -> new OutlineSiteIdentifier(context, code, instructions).process()); } public ProgramMethodSet selectMethodsForOutlining() { @@ -1512,13 +1558,33 @@ } public void applyOutliningCandidate(IRCode code) { - assert !code.method().getCode().isOutlineCode(); - ListIterator<BasicBlock> blocksIterator = code.listIterator(); - while (blocksIterator.hasNext()) { - BasicBlock block = blocksIterator.next(); - List<Integer> toRemove = new ArrayList<>(); - new OutlineRewriter(code, blocksIterator, block, toRemove).process(); - block.removeInstructions(toRemove); + assert !code.context().getDefinition().getCode().isOutlineCode(); + Set<Instruction> toRemove = Sets.newIdentityHashSet(); + Set<Instruction> invokesToOutlineMethods = Sets.newIdentityHashSet(); + getInstructions( + appView, + code, + instructions -> + new OutlineRewriter(code, instructions, toRemove, invokesToOutlineMethods).process()); + if (!toRemove.isEmpty()) { + assert !invokesToOutlineMethods.isEmpty(); + // Scan over the entire code to remove outline instructions. + ListIterator<BasicBlock> blocksIterator = code.listIterator(); + while (blocksIterator.hasNext()) { + BasicBlock block = blocksIterator.next(); + InstructionListIterator instructionListIterator = block.listIterator(code); + instructionListIterator.forEachRemaining( + instruction -> { + if (toRemove.contains(instruction)) { + instructionListIterator.removeInstructionIgnoreOutValue(); + } else if (invokesToOutlineMethods.contains(instruction) + && block.hasCatchHandlers()) { + // If the inserted invoke is inserted in a block with handlers, split the block + // after the inserted invoke. + instructionListIterator.split(code, blocksIterator); + } + }); + } } } @@ -1531,13 +1597,14 @@ private class OutlineSourceCode implements SourceCode { - final private Outline outline; - private final Position position; + private final Outline outline; + private final DexMethod method; + private int position; private int argumentMapIndex = 0; OutlineSourceCode(Outline outline, DexMethod method) { this.outline = outline; - this.position = Position.synthetic(0, method, null); + this.method = method; } @Override @@ -1623,6 +1690,7 @@ } return; } + position = instructionIndex; // Build IR from the template. argumentMapIndex = outline @@ -1660,7 +1728,7 @@ @Override public Position getCurrentPosition() { - return position; + return OutlinePosition.builder().setLine(position).setMethod(method).build(); } @Override @@ -1705,11 +1773,6 @@ } @Override - public OutlineCode asOutlineCode() { - return this; - } - - @Override public boolean isEmptyVoidMethod() { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java index 70b07a6..f51dc69 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -72,15 +72,22 @@ if (current.isInvokeMethodWithReceiver()) { InvokeMethodWithReceiver methodWithReceiver = current.asInvokeMethodWithReceiver(); + Value receiver = methodWithReceiver.getReceiver().getAliasedValue(); + if (!receiver.getType().isClassType() + || !appView + .appInfo() + .isSubtype(receiver.getType().asClassType().getClassType(), factory.enumType)) { + continue; + } + DexMethod invokedMethod = methodWithReceiver.getInvokedMethod(); - boolean isOrdinalInvoke = invokedMethod == factory.enumMembers.ordinalMethod; - boolean isNameInvoke = invokedMethod == factory.enumMembers.nameMethod; - boolean isToStringInvoke = invokedMethod == factory.enumMembers.toString; + boolean isOrdinalInvoke = invokedMethod.match(factory.enumMembers.ordinalMethod); + boolean isNameInvoke = invokedMethod.match(factory.enumMembers.nameMethod); + boolean isToStringInvoke = invokedMethod.match(factory.enumMembers.toString); if (!isOrdinalInvoke && !isNameInvoke && !isToStringInvoke) { continue; } - Value receiver = methodWithReceiver.getReceiver().getAliasedValue(); if (receiver.isPhi()) { continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java index 10ea774..90d49ac 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.cf.CfVersion; import com.android.tools.r8.cf.code.CfArrayStore; import com.android.tools.r8.cf.code.CfConstNumber; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLoad; @@ -18,6 +17,8 @@ import com.android.tools.r8.cf.code.CfReturnVoid; import com.android.tools.r8.cf.code.CfStackInstruction; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStaticFieldRead; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -267,7 +268,7 @@ instructions.add(new CfConstNumber(i + 1, ValueType.INT)); instructions.add(new CfArrayStore(MemberType.INT)); } - instructions.add(new CfFieldInstruction(Opcodes.PUTSTATIC, valuesField.getReference())); + instructions.add(new CfStaticFieldWrite(valuesField.getReference())); instructions.add(new CfReturnVoid()); int maxStack = 4; @@ -317,7 +318,7 @@ new CfNewArray(dexItemFactory.intArrayType), new CfStore(ValueType.OBJECT, resultLocalSlot), // System.arraycopy(SharedUtilityClass.$VALUES, 0, result, 0, size); - new CfFieldInstruction(Opcodes.GETSTATIC, valuesField.getReference()), + new CfStaticFieldRead(valuesField.getReference()), new CfConstNumber(0, ValueType.INT), new CfLoad(ValueType.OBJECT, resultLocalSlot), new CfConstNumber(0, ValueType.INT),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java index 748253f..30b0da1 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -105,6 +105,8 @@ final Map<CandidateInfo, LongLivedProgramMethodSetBuilder<?>> referencedFrom = new ConcurrentHashMap<>(); + private final Set<DexMethod> prunedMethods = Sets.newIdentityHashSet(); + // The map storing all the potential candidates for staticizing. final ConcurrentHashMap<DexType, CandidateInfo> candidates = new ConcurrentHashMap<>(); @@ -114,6 +116,14 @@ this.converter = converter; } + public void onMethodPruned(ProgramMethod method) { + onMethodCodePruned(method); + } + + public void onMethodCodePruned(ProgramMethod method) { + prunedMethods.add(method.getReference()); + } + public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) { collectCandidates(); this.graphLensForOptimizationPass = graphLensForPrimaryOptimizationPass; @@ -129,8 +139,11 @@ .values() .forEach( referencedFromBuilder -> - referencedFromBuilder.rewrittenWithLens(graphLensForSecondaryOptimizationPass)); + referencedFromBuilder + .removeAll(prunedMethods) + .rewrittenWithLens(graphLensForSecondaryOptimizationPass)); this.graphLensForOptimizationPass = graphLensForSecondaryOptimizationPass; + prunedMethods.clear(); } // Before doing any usage-based analysis we collect a set of classes that can be
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java index c73132f..8ab0859 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -20,6 +20,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -49,7 +50,6 @@ import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.collect.Streams; @@ -74,7 +74,7 @@ private final ClassStaticizer classStaticizer; private final IRConverter converter; - private final SortedProgramMethodSet methodsToReprocess = SortedProgramMethodSet.create(); + private final ProgramMethodSet methodsToReprocess = ProgramMethodSet.create(); // Optimization order matters, hence a collection that preserves orderings. private final Map<DexEncodedMethod, ImmutableList.Builder<BiConsumer<IRCode, MethodProcessor>>> @@ -237,9 +237,6 @@ referencedFrom = referencedFromBuilder .rewrittenWithLens(appView) - .removeIf( - appView, - method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()) .build(appView); materializedReferencedFromCollections.put(info, referencedFrom); } else { @@ -414,6 +411,7 @@ feedback, methodProcessor, new MutableMethodConversionOptions(methodProcessor), + BytecodeMetadataProvider.builder(), Timing.empty()); }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java index 7395cbf..6777d57 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -7,10 +7,11 @@ import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfConstNull; import com.android.tools.r8.cf.code.CfConstString; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfFrame; import com.android.tools.r8.cf.code.CfFrame.FrameType; import com.android.tools.r8.cf.code.CfIf; +import com.android.tools.r8.cf.code.CfInstanceFieldRead; +import com.android.tools.r8.cf.code.CfInstanceFieldWrite; import com.android.tools.r8.cf.code.CfInstanceOf; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; @@ -80,7 +81,7 @@ // use vivifiedTypes. instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); - instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, wrapperField, wrapperField)); + instructions.add(new CfInstanceFieldRead(wrapperField)); int index = 1; int stackIndex = 1; DexType[] newParameters = forwardMethod.proto.parameters.values.clone(); @@ -250,7 +251,7 @@ @Override void generatePushReceiver(List<CfInstruction> instructions) { instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); - instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, wrapperField, wrapperField)); + instructions.add(new CfInstanceFieldRead(wrapperField)); } @Override @@ -299,8 +300,7 @@ instructions.add(new CfIf(If.Type.EQ, ValueType.INT, unwrapDest)); instructions.add(new CfLoad(ValueType.fromDexType(argType), 0)); instructions.add(new CfCheckCast(reverseWrapperField.holder)); - instructions.add( - new CfFieldInstruction(Opcodes.GETFIELD, reverseWrapperField, reverseWrapperField)); + instructions.add(new CfInstanceFieldRead(reverseWrapperField)); instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type))); instructions.add(unwrapDest); instructions.add(new CfFrame(locals, ImmutableDeque.of())); @@ -410,7 +410,7 @@ false)); instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.type), 1)); - instructions.add(new CfFieldInstruction(Opcodes.PUTFIELD, wrapperField, wrapperField)); + instructions.add(new CfInstanceFieldWrite(wrapperField)); instructions.add(new CfReturnVoid()); return standardCfCodeFromInstructions(instructions); }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java index cb5de3e..5ef065a 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.cf.code.CfConstNull; import com.android.tools.r8.cf.code.CfConstNumber; import com.android.tools.r8.cf.code.CfConstString; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfFrame; import com.android.tools.r8.cf.code.CfFrame.FrameType; import com.android.tools.r8.cf.code.CfIf; @@ -20,6 +19,8 @@ import com.android.tools.r8.cf.code.CfReturn; import com.android.tools.r8.cf.code.CfStackInstruction; import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStaticFieldRead; +import com.android.tools.r8.cf.code.CfStaticFieldWrite; import com.android.tools.r8.cf.code.CfThrow; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; @@ -241,17 +242,17 @@ // return VALUES$com$x$MyEnum; List<CfInstruction> instructions = new ArrayList<>(); CfLabel nullDest = new CfLabel(); - instructions.add(new CfFieldInstruction(Opcodes.GETSTATIC, utilityField, utilityField)); + instructions.add(new CfStaticFieldRead(utilityField, utilityField)); instructions.add(new CfIf(If.Type.NE, ValueType.OBJECT, nullDest)); instructions.add((new CfConstNumber(numEnumInstances, ValueType.INT))); assert initializationMethod.getArity() == 1; instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, initializationMethod, false)); - instructions.add(new CfFieldInstruction(Opcodes.PUTSTATIC, utilityField, utilityField)); + instructions.add(new CfStaticFieldWrite(utilityField, utilityField)); instructions.add(nullDest); instructions.add( new CfFrame( ImmutableInt2ReferenceSortedMap.<FrameType>builder().build(), ImmutableDeque.of())); - instructions.add(new CfFieldInstruction(Opcodes.GETSTATIC, utilityField, utilityField)); + instructions.add(new CfStaticFieldRead(utilityField, utilityField)); instructions.add(new CfReturn(ValueType.OBJECT)); return standardCfCodeFromInstructions(instructions); }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java index d6794c6..f549990 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java
@@ -103,7 +103,7 @@ // Get or set the field. int opcode = Opcodes.GETSTATIC + BooleanUtils.intValue(isSetter()) + (isInstanceField.ordinal() << 1); - instructions.add(new CfFieldInstruction(opcode, field, field)); + instructions.add(CfFieldInstruction.create(opcode, field, field)); // Return. if (isSetter()) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java index 46124b9..be34a74 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/RecordCfCodeProvider.java
@@ -7,10 +7,10 @@ import com.android.tools.r8.cf.code.CfArrayStore; import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfConstNumber; -import com.android.tools.r8.cf.code.CfFieldInstruction; import com.android.tools.r8.cf.code.CfFrame; import com.android.tools.r8.cf.code.CfFrame.FrameType; import com.android.tools.r8.cf.code.CfIfCmp; +import com.android.tools.r8.cf.code.CfInstanceFieldRead; import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLabel; @@ -118,7 +118,7 @@ private void loadFieldAsObject(List<CfInstruction> instructions, DexField field) { DexItemFactory factory = appView.dexItemFactory(); instructions.add(new CfLoad(ValueType.OBJECT, 0)); - instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, field, field)); + instructions.add(new CfInstanceFieldRead(field)); if (field.type.isPrimitiveType()) { factory.primitiveToBoxed.forEach( (primitiveType, boxedType) -> {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java index ae06b60..c81383c 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.DexSourceCode; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -64,7 +65,12 @@ this.paramRegisters[i] = nextRegister(ValueType.fromDexType(params[i])); } - position = Position.synthetic(0, originalMethod, callerPosition); + position = + SyntheticPosition.builder() + .setLine(0) + .setMethod(originalMethod) + .setCallerPosition(callerPosition) + .build(); } protected final void add(Consumer<IRBuilder> constructor) {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java index 7b8da09..b10c36a 100644 --- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java +++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -15,7 +15,7 @@ import com.android.tools.r8.errors.ConstantPoolOverflowDiagnostic; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexAnnotationElement; import com.android.tools.r8.graph.DexAnnotationSet; @@ -591,8 +591,10 @@ NamingLens namingLens, LensCodeRewriterUtils rewriter, MethodVisitor visitor) { - CfCode code = method.getDefinition().getCode().asCfCode(); - code.write(method, classFileVersion, appView, namingLens, rewriter, visitor); + Code code = method.getDefinition().getCode(); + assert code.isCfWritableCode(); + code.asCfWritableCode() + .writeCf(method, classFileVersion, appView, namingLens, rewriter, visitor); } public static String printCf(byte[] result) {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java index a22a766..2e21467 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -156,8 +156,7 @@ instructions[i] = constString; } } - } else { - assert code.isCfCode(); + } else if (code.isCfCode()) { List<CfInstruction> instructions = code.asCfCode().getInstructions(); List<CfInstruction> newInstructions = ListUtils.mapOrElse( @@ -176,6 +175,8 @@ }, instructions); code.asCfCode().setInstructions(newInstructions); + } else { + assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode(); } } }
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java index 391f2c7..ea756b5 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -375,9 +375,9 @@ + " Thus, not all identifier strings flowing to that " + kind + " are renamed, which can cause resolution failures at runtime."; StringDiagnostic diagnostic = - instruction.getPosition().line >= 1 + instruction.getPosition().getLine() >= 1 ? new StringDiagnostic( - message, origin, new TextPosition(0L, instruction.getPosition().line, 1)) + message, origin, new TextPosition(0L, instruction.getPosition().getLine(), 1)) : new StringDiagnostic(message, origin); appView.options().reporter.warning(diagnostic); }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java index d451d82..7f0beee 100644 --- a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java +++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.naming.MapVersion; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntSortedMap; import java.util.function.Consumer; @@ -31,7 +32,15 @@ @Override public String serialize() { - throw new CompilationError("Should not yet serialize this"); + JsonObject result = new JsonObject(); + result.add(MAPPING_ID_KEY, new JsonPrimitive(ID)); + JsonObject mappedPositions = new JsonObject(); + positions.forEach( + (obfuscatedPosition, originalPosition) -> { + mappedPositions.add(obfuscatedPosition + "", new JsonPrimitive(originalPosition)); + }); + result.add(POSITIONS_KEY, mappedPositions); + return result.toString(); } @Override
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java index 697c5b6..20df16d 100644 --- a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java +++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
@@ -4,8 +4,9 @@ package com.android.tools.r8.naming.mappinginformation; -import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.naming.MapVersion; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import java.util.function.Consumer; public class OutlineMappingInformation extends MappingInformation { @@ -20,7 +21,9 @@ @Override public String serialize() { - throw new CompilationError("Should not yet serialize this"); + JsonObject object = new JsonObject(); + object.add(MAPPING_ID_KEY, new JsonPrimitive(ID)); + return object.toString(); } @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index 23e0e7a..bc1b7e3 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,9 +3,11 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.optimize; +import com.android.tools.r8.androidapi.AndroidApiLevelCompute; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndField; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; @@ -14,19 +16,23 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.FieldAccessInfoCollection; import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult; -import com.android.tools.r8.graph.GraphLens; +import com.android.tools.r8.graph.LibraryMethod; import com.android.tools.r8.graph.MethodAccessInfoCollection; +import com.android.tools.r8.graph.MethodResolutionResult; +import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BiForEachable; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.TriConsumer; import com.android.tools.r8.utils.collections.ProgramMethodSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.IdentityHashMap; @@ -40,52 +46,120 @@ public class MemberRebindingAnalysis { + private final AndroidApiLevelCompute androidApiLevelCompute; private final AppView<AppInfoWithLiveness> appView; - private final GraphLens lens; private final InternalOptions options; private final MemberRebindingLens.Builder lensBuilder; public MemberRebindingAnalysis(AppView<AppInfoWithLiveness> appView) { assert appView.graphLens().isContextFreeForMethods(); + this.androidApiLevelCompute = AndroidApiLevelCompute.create(appView); this.appView = appView; - this.lens = appView.graphLens(); this.options = appView.options(); this.lensBuilder = MemberRebindingLens.builder(appView); } - private DexMethod validTargetFor(DexMethod target, DexMethod original) { - DexClass clazz = appView.definitionFor(target.getHolderType()); - assert clazz != null; - if (clazz.isProgramClass()) { - return target; + private DexMethod validMemberRebindingTargetForNonProgramMethod( + DexClassAndMethod resolvedMethod, + SingleResolutionResult resolutionResult, + ProgramMethodSet contexts, + Type invokeType, + DexMethod original) { + assert !resolvedMethod.isProgramMethod(); + + if (invokeType.isDirect()) { + return original; } - DexType newHolder; - if (clazz.isInterface()) { - newHolder = - firstLibraryClassForInterfaceTarget( - appView, target, original.holder, DexClass::lookupMethod); - } else { - newHolder = firstLibraryClass(appView, target.getHolderType(), original.getHolderType()); + + LibraryMethod eligibleLibraryMethod = null; + SingleResolutionResult currentResolutionResult = resolutionResult; + while (currentResolutionResult != null) { + DexClassAndMethod currentResolvedMethod = currentResolutionResult.getResolutionPair(); + if (canRebindDirectlyToLibraryMethod( + currentResolvedMethod, currentResolutionResult, contexts, invokeType)) { + eligibleLibraryMethod = currentResolvedMethod.asLibraryMethod(); + } + if (appView.appInfo().isAssumeMethod(currentResolvedMethod)) { + break; + } + DexClass currentResolvedHolder = currentResolvedMethod.getHolder(); + if (resolvedMethod.getDefinition().belongsToVirtualPool() + && !currentResolvedHolder.isInterface() + && currentResolvedHolder.getSuperType() != null) { + currentResolutionResult = + appView + .appInfo() + .resolveMethodOnClass(original, currentResolvedHolder.getSuperType()) + .asSingleResolution(); + } else { + break; + } } - return newHolder == null - ? original - : appView.dexItemFactory().createMethod(newHolder, original.proto, original.name); + if (eligibleLibraryMethod != null) { + return eligibleLibraryMethod.getReference(); + } + + DexType newHolder = + resolvedMethod.getHolder().isInterface() + ? firstLibraryClassForInterfaceTarget( + appView, + resolvedMethod.getReference(), + original.getHolderType(), + DexClass::lookupMethod) + : firstLibraryClass(appView, original.getHolderType()); + return newHolder != null ? original.withHolder(newHolder, appView.dexItemFactory()) : original; + } + + private boolean canRebindDirectlyToLibraryMethod( + DexClassAndMethod resolvedMethod, + SingleResolutionResult resolutionResult, + ProgramMethodSet contexts, + Type invokeType) { + // TODO(b/194422791): It could potentially be that `original.holder` is not a subtype of + // `original.holder` on all API levels, in which case it is not OK to rebind to the resolved + // method. + return resolvedMethod.isLibraryMethod() + && isAccessibleInAllContexts(resolvedMethod, resolutionResult, contexts) + && !isInvokeSuperToInterfaceMethod(resolvedMethod, invokeType) + && isPresentSinceMinApi(resolvedMethod.asLibraryMethod()); + } + + private boolean isAccessibleInAllContexts( + DexClassAndMethod resolvedMethod, + SingleResolutionResult resolutionResult, + ProgramMethodSet contexts) { + if (resolvedMethod.getHolder().isPublic() && resolvedMethod.getDefinition().isPublic()) { + return true; + } + return Iterables.all( + contexts, + context -> resolutionResult.isAccessibleFrom(context, appView.appInfo()).isTrue()); + } + + private boolean isInvokeSuperToInterfaceMethod(DexClassAndMethod method, Type invokeType) { + return method.getHolder().isInterface() && invokeType.isSuper(); + } + + private boolean isPresentSinceMinApi(LibraryMethod method) { + AndroidApiLevel apiLevel = + androidApiLevelCompute.computeApiLevelForLibraryReference(method.getReference()); + return apiLevel.isLessThanOrEqualTo(options.minApiLevel); } public static DexField validMemberRebindingTargetFor( DexDefinitionSupplier definitions, DexClassAndField field, DexField original) { - DexClass clazz = field.getHolder(); if (field.isProgramField()) { return field.getReference(); } + DexClass fieldHolder = field.getHolder(); DexType newHolder = - clazz.isInterface() + fieldHolder.isInterface() ? firstLibraryClassForInterfaceTarget( definitions, field.getReference(), original.getHolderType(), DexClass::lookupField) - : firstLibraryClass(definitions, field.getHolderType(), original.getHolderType()); + : firstLibraryClass(definitions, original.getHolderType()); return newHolder != null - ? field.getReference().withHolder(newHolder, definitions.dexItemFactory()) + ? original.withHolder(newHolder, definitions.dexItemFactory()) : original; } @@ -111,7 +185,7 @@ return clazz.isNotProgramClass() ? current : matchingSuper; } } - for (DexType iface : clazz.interfaces.values) { + for (DexType iface : clazz.getInterfaces()) { DexType matchingIface = firstLibraryClassForInterfaceTarget(definitions, target, iface, lookup); if (matchingIface != null) { @@ -122,9 +196,7 @@ return null; } - private static DexType firstLibraryClass( - DexDefinitionSupplier definitions, DexType top, DexType bottom) { - assert definitions.definitionFor(top).isNotProgramClass(); + private static DexType firstLibraryClass(DexDefinitionSupplier definitions, DexType bottom) { DexClass searchClass = definitions.definitionFor(bottom); while (searchClass.isProgramClass()) { searchClass = definitions.definitionFor(searchClass.superType); @@ -132,43 +204,42 @@ return searchClass.type; } - private DexEncodedMethod classLookup(DexMethod method) { - return appView.appInfo().resolveMethodOnClass(method, method.holder).getSingleTarget(); + private MethodResolutionResult resolveMethodOnClass(DexMethod method) { + return appView.appInfo().resolveMethodOnClass(method, method.holder); } - private DexEncodedMethod interfaceLookup(DexMethod method) { - return appView.appInfo().resolveMethodOnInterface(method.holder, method).getSingleTarget(); + private MethodResolutionResult resolveMethodOnInterface(DexMethod method) { + return appView.appInfo().resolveMethodOnInterface(method.holder, method); } - private DexEncodedMethod anyLookup(DexMethod method) { - return appView.appInfo().unsafeResolveMethodDueToDexFormat(method).getSingleTarget(); + private MethodResolutionResult resolveMethod(DexMethod method) { + return appView.appInfo().unsafeResolveMethodDueToDexFormat(method); } private void computeMethodRebinding(MethodAccessInfoCollection methodAccessInfoCollection) { // Virtual invokes are on classes, so use class resolution. computeMethodRebinding( - methodAccessInfoCollection::forEachVirtualInvoke, this::classLookup, Type.VIRTUAL); + methodAccessInfoCollection::forEachVirtualInvoke, this::resolveMethodOnClass, Type.VIRTUAL); // Interface invokes are always on interfaces, so use interface resolution. computeMethodRebinding( - methodAccessInfoCollection::forEachInterfaceInvoke, this::interfaceLookup, Type.INTERFACE); + methodAccessInfoCollection::forEachInterfaceInvoke, + this::resolveMethodOnInterface, + Type.INTERFACE); // Super invokes can be on both kinds, decide using the holder class. computeMethodRebinding( - methodAccessInfoCollection::forEachSuperInvoke, this::anyLookup, Type.SUPER); - // Direct invokes (private/constructor) can also be on both kinds. - computeMethodRebinding( - methodAccessInfoCollection::forEachDirectInvoke, this::anyLookup, Type.DIRECT); + methodAccessInfoCollection::forEachSuperInvoke, this::resolveMethod, Type.SUPER); // Likewise static invokes. computeMethodRebinding( - methodAccessInfoCollection::forEachStaticInvoke, this::anyLookup, Type.STATIC); + methodAccessInfoCollection::forEachStaticInvoke, this::resolveMethod, Type.STATIC); } private void computeMethodRebinding( BiForEachable<DexMethod, ProgramMethodSet> methodsWithContexts, - Function<DexMethod, DexEncodedMethod> lookupTarget, + Function<DexMethod, MethodResolutionResult> resolver, Type invokeType) { - - Map<DexProgramClass, List<Pair<DexMethod, DexEncodedMethod>>> bridges = new IdentityHashMap<>(); - TriConsumer<DexProgramClass, DexMethod, DexEncodedMethod> addBridge = + Map<DexProgramClass, List<Pair<DexMethod, DexClassAndMethod>>> bridges = + new IdentityHashMap<>(); + TriConsumer<DexProgramClass, DexMethod, DexClassAndMethod> addBridge = (bridgeHolder, method, target) -> bridges .computeIfAbsent(bridgeHolder, k -> new ArrayList<>()) @@ -176,48 +247,58 @@ methodsWithContexts.forEach( (method, contexts) -> { - // We can safely ignore array types, as the corresponding methods are defined in a - // library. - if (!method.holder.isClassType()) { + MethodResolutionResult resolutionResult = resolver.apply(method); + if (!resolutionResult.isSingleResolution()) { return; } - DexClass originalClass = appView.definitionFor(method.holder); - if (originalClass == null || originalClass.isNotProgramClass()) { - return; - } - DexEncodedMethod target = lookupTarget.apply(method); + // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow // searching in library for methods, but this should be done on classpath instead. - if (target == null || target.getReference() == method) { + DexClassAndMethod resolvedMethod = resolutionResult.getResolutionPair(); + if (resolvedMethod.getReference() == method) { return; } - DexClass targetClass = appView.definitionFor(target.getHolderType()); - DexMethod targetMethod = target.getReference(); - if (originalClass.isProgramClass()) { + + DexClass initialResolutionHolder = resolutionResult.getInitialResolutionHolder(); + DexMethod bridgeMethod = null; + if (initialResolutionHolder.isProgramClass()) { // In Java bytecode, it is only possible to target interface methods that are in one of // the immediate super-interfaces via a super-invocation (see // IndirectSuperInterfaceTest). // To avoid introducing an IncompatibleClassChangeError at runtime we therefore insert a // bridge method when we are about to rebind to an interface method that is not the // original target. - if (needsBridgeForInterfaceMethod(originalClass, targetClass, invokeType)) { - targetMethod = + if (needsBridgeForInterfaceMethod( + initialResolutionHolder, resolvedMethod, invokeType)) { + bridgeMethod = insertBridgeForInterfaceMethod( - method, target, originalClass.asProgramClass(), targetClass, addBridge); - } - - // If the target class is not public but the targeted method is, we might run into - // visibility problems when rebinding. - final DexEncodedMethod finalTarget = target; - if (contexts.stream() - .anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) { - targetMethod = - insertBridgeForVisibilityIfNeeded( - method, target, originalClass, targetClass, addBridge); + method, resolvedMethod, initialResolutionHolder.asProgramClass(), addBridge); + } else { + // If the target class is not public but the targeted method is, we might run into + // visibility problems when rebinding. + if (contexts.stream() + .anyMatch(context -> mayNeedBridgeForVisibility(context, resolvedMethod))) { + bridgeMethod = + insertBridgeForVisibilityIfNeeded( + method, resolvedMethod, initialResolutionHolder, addBridge); + } } } - lensBuilder.map( - method, lens.lookupMethod(validTargetFor(targetMethod, method)), invokeType); + if (bridgeMethod != null) { + lensBuilder.map(method, bridgeMethod, invokeType); + } else if (resolvedMethod.isProgramMethod()) { + lensBuilder.map(method, resolvedMethod.getReference(), invokeType); + } else { + lensBuilder.map( + method, + validMemberRebindingTargetForNonProgramMethod( + resolvedMethod, + resolutionResult.asSingleResolution(), + contexts, + invokeType, + method), + invokeType); + } }); bridges.forEach( @@ -225,35 +306,34 @@ // Sorting the list of bridges within a class maintains a deterministic order of entries // in the method collection. targets.sort((p1, p2) -> p1.getFirst().compareTo(p2.getFirst())); - for (Pair<DexMethod, DexEncodedMethod> pair : targets) { + for (Pair<DexMethod, DexClassAndMethod> pair : targets) { DexMethod method = pair.getFirst(); - DexEncodedMethod target = pair.getSecond(); + DexClassAndMethod target = pair.getSecond(); DexMethod bridgeMethod = method.withHolder(bridgeHolder.getType(), appView.dexItemFactory()); if (bridgeHolder.getMethodCollection().getMethod(bridgeMethod) == null) { DexEncodedMethod bridgeMethodDefinition = - target.toForwardingMethod(bridgeHolder, appView); + target.getDefinition().toForwardingMethod(bridgeHolder, appView); bridgeHolder.addMethod(bridgeMethodDefinition); } - assert lookupTarget.apply(method).getReference() == bridgeMethod; + assert resolver.apply(method).getResolvedMethod().getReference() == bridgeMethod; } }); } private boolean needsBridgeForInterfaceMethod( - DexClass originalClass, DexClass targetClass, Type invokeType) { + DexClass originalClass, DexClassAndMethod method, Type invokeType) { return options.isGeneratingClassFiles() && invokeType == Type.SUPER - && targetClass != originalClass - && targetClass.accessFlags.isInterface(); + && method.getHolder() != originalClass + && method.getHolder().isInterface(); } private DexMethod insertBridgeForInterfaceMethod( DexMethod method, - DexEncodedMethod target, + DexClassAndMethod target, DexProgramClass originalClass, - DexClass targetClass, - TriConsumer<DexProgramClass, DexMethod, DexEncodedMethod> bridges) { + TriConsumer<DexProgramClass, DexMethod, DexClassAndMethod> bridges) { // If `targetClass` is a class, then insert the bridge method on the upper-most super class that // implements the interface. Otherwise, if it is an interface, then insert the bridge method // directly on the interface (because that interface must be the immediate super type, assuming @@ -263,9 +343,9 @@ // invoke-super instructions that hit indirect interface methods such that they always target // a method in an immediate super-interface, since this works on Art but not on the JVM. DexProgramClass bridgeHolder = - findHolderForInterfaceMethodBridge(originalClass, targetClass.type); + findHolderForInterfaceMethodBridge(originalClass, target.getHolderType()); assert bridgeHolder != null; - assert bridgeHolder != targetClass; + assert bridgeHolder != target.getHolder(); bridges.accept(bridgeHolder, method, target); return target.getReference().withHolder(bridgeHolder.getType(), appView.dexItemFactory()); } @@ -283,16 +363,18 @@ return findHolderForInterfaceMethodBridge(superClass.asProgramClass(), iface); } - private boolean mayNeedBridgeForVisibility(ProgramMethod context, DexEncodedMethod method) { + private boolean mayNeedBridgeForVisibility(ProgramMethod context, DexClassAndMethod method) { DexType holderType = method.getHolderType(); DexClass holder = appView.definitionFor(holderType); if (holder == null) { return false; } ConstraintWithTarget classVisibility = - ConstraintWithTarget.deriveConstraint(context, holderType, holder.accessFlags, appView); + ConstraintWithTarget.deriveConstraint( + context, holderType, holder.getAccessFlags(), appView); ConstraintWithTarget methodVisibility = - ConstraintWithTarget.deriveConstraint(context, holderType, method.accessFlags, appView); + ConstraintWithTarget.deriveConstraint( + context, holderType, method.getAccessFlags(), appView); // We may need bridge for visibility if the target class is not visible while the target method // is visible from the calling context. return classVisibility == ConstraintWithTarget.NEVER @@ -301,19 +383,18 @@ private DexMethod insertBridgeForVisibilityIfNeeded( DexMethod method, - DexEncodedMethod target, + DexClassAndMethod target, DexClass originalClass, - DexClass targetClass, - TriConsumer<DexProgramClass, DexMethod, DexEncodedMethod> bridges) { + TriConsumer<DexProgramClass, DexMethod, DexClassAndMethod> bridges) { // If the original class is public and this method is public, it might have been called // from anywhere, so we need a bridge. Likewise, if the original is in a different // package, we might need a bridge, too. String packageDescriptor = originalClass.accessFlags.isPublic() ? null : method.holder.getPackageDescriptor(); if (packageDescriptor == null - || !packageDescriptor.equals(targetClass.type.getPackageDescriptor())) { + || !packageDescriptor.equals(target.getHolderType().getPackageDescriptor())) { DexProgramClass bridgeHolder = - findHolderForVisibilityBridge(originalClass, targetClass, packageDescriptor); + findHolderForVisibilityBridge(originalClass, target.getHolder(), packageDescriptor); assert bridgeHolder != null; bridges.accept(bridgeHolder, method, target); return target.getReference().withHolder(bridgeHolder.getType(), appView.dexItemFactory());
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java index 158a8de..45f4984 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -130,6 +130,7 @@ } public void tearDownCodeScanner( + IRConverter converter, PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService, Timing timing) @@ -152,6 +153,7 @@ Map<Set<DexProgramClass>, DexMethodSignatureSet> interfaceDispatchOutsideProgram = new IdentityHashMap<>(); populateParameterOptimizationInfo( + converter, immediateSubtypingInfo, stronglyConnectedProgramComponents, (stronglyConnectedProgramComponent, signature) -> { @@ -189,6 +191,7 @@ * optimization info. */ private void populateParameterOptimizationInfo( + IRConverter converter, ImmediateProgramSubtypingInfo immediateSubtypingInfo, List<Set<DexProgramClass>> stronglyConnectedProgramComponents, BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram, @@ -209,7 +212,7 @@ reprocessingCriteriaCollection, stronglyConnectedProgramComponents, interfaceDispatchOutsideProgram) - .populateOptimizationInfo(executorService, timing); + .populateOptimizationInfo(converter, executorService, timing); reprocessingCriteriaCollection = null; timing.end(); }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java index 4d19a71..96e08b6 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -305,7 +305,7 @@ // If we already don't know anything about the parameters for the given type bounds, then don't // compute a method state. if (existingMethodStateForBounds.isUnknown()) { - return MethodState.unknown(); + return MethodState.bottom(); } ConcreteMonomorphicMethodStateOrUnknown methodStateForBounds =
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java index 5470ed5..e050de8 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.DynamicType; import com.android.tools.r8.ir.analysis.type.Nullability; +import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo; import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; @@ -82,7 +83,8 @@ * Computes an over-approximation of each parameter's value and type and stores the result in * {@link com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo}. */ - void populateOptimizationInfo(ExecutorService executorService, Timing timing) + void populateOptimizationInfo( + IRConverter converter, ExecutorService executorService, Timing timing) throws ExecutionException { // TODO(b/190154391): Propagate argument information to handle virtual dispatch. // TODO(b/190154391): To deal with arguments that are themselves passed as arguments to invoke @@ -98,7 +100,7 @@ // Solve the parameter flow constraints. timing.begin("Solve flow constraints"); - new InParameterFlowPropagator(appView, methodStates).run(executorService); + new InParameterFlowPropagator(appView, converter, methodStates).run(executorService); timing.end(); // The information stored on each method is now sound, and can be used as optimization info.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java index 9b583c7..9b10015 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState; import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState; import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState; @@ -42,11 +43,15 @@ public class InParameterFlowPropagator { final AppView<AppInfoWithLiveness> appView; + final IRConverter converter; final MethodStateCollectionByReference methodStates; public InParameterFlowPropagator( - AppView<AppInfoWithLiveness> appView, MethodStateCollectionByReference methodStates) { + AppView<AppInfoWithLiveness> appView, + IRConverter converter, + MethodStateCollectionByReference methodStates) { this.appView = appView; + this.converter = converter; this.methodStates = methodStates; } @@ -206,6 +211,16 @@ ParameterNode node = getOrCreateParameterNode(method, parameterIndex, methodState); for (MethodParameter inParameter : concreteParameterState.getInParameters()) { ProgramMethod enclosingMethod = getEnclosingMethod(inParameter); + if (enclosingMethod == null) { + // This is a parameter of a single caller inlined method. Since this method has been + // pruned, the call from inside the method no longer exists, and we can therefore safely + // skip it. + assert converter + .getInliner() + .verifyIsPrunedDueToSingleCallerInlining(inParameter.getMethod()); + continue; + } + MethodState enclosingMethodState = getMethodState(enclosingMethod); if (enclosingMethodState.isBottom()) { // The current method is called from a dead method; no need to propagate any information
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index b0f3ea9..61c9077 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -355,7 +355,7 @@ pruneMethods(previous.liveMethods, prunedItems, executorService, futures), previous.fieldAccessInfoCollection, previous.methodAccessInfoCollection, - previous.objectAllocationInfoCollection, + previous.objectAllocationInfoCollection.withoutPrunedItems(prunedItems), previous.callSites, extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()), previous.mayHaveSideEffects, @@ -439,6 +439,7 @@ private static <T> Set<T> pruneItems( Set<T> items, Set<T> removedItems, ExecutorService executorService, List<Future<?>> futures) { if (!removedItems.isEmpty()) { + futures.add( ThreadUtils.processAsynchronously( () -> { @@ -796,6 +797,10 @@ return neverInlineDueToSingleCaller.contains(method.getReference()); } + public boolean isAssumeMethod(DexClassAndMethod method) { + return isAssumeNoSideEffectsMethod(method) || isAssumeValuesMethod(method); + } + public boolean isAssumeNoSideEffectsMethod(DexMethod method) { return noSideEffects.containsKey(method); } @@ -804,6 +809,14 @@ return isAssumeNoSideEffectsMethod(method.getReference()); } + public boolean isAssumeValuesMethod(DexMethod method) { + return assumedValues.containsKey(method); + } + + public boolean isAssumeValuesMethod(DexClassAndMethod method) { + return isAssumeValuesMethod(method.getReference()); + } + public boolean isWhyAreYouNotInliningMethod(DexMethod method) { return whyAreYouNotInlining.contains(method); } @@ -1089,7 +1102,7 @@ public boolean mayPropagateValueFor(DexField field) { assert checkIfObsolete(); - if (!options().enableValuePropagation || neverPropagateValue.contains(field)) { + if (neverPropagateValue.contains(field)) { return false; } if (isPinned(field) && !field.getType().isAlwaysNull(this)) { @@ -1100,7 +1113,7 @@ public boolean mayPropagateValueFor(DexMethod method) { assert checkIfObsolete(); - if (!options().enableValuePropagation || neverPropagateValue.contains(method)) { + if (neverPropagateValue.contains(method)) { return false; } if (!method.getReturnType().isAlwaysNull(this) @@ -1271,10 +1284,14 @@ lens.rewriteCallSites(callSites, definitionSupplier), keepInfo.rewrite(definitionSupplier, lens, application.options), // Take any rule in case of collisions. - lens.rewriteReferenceKeys(mayHaveSideEffects, ListUtils::first), - // Drop assume rules in case of collisions. - lens.rewriteReferenceKeys(noSideEffects, rules -> null), - lens.rewriteReferenceKeys(assumedValues, rules -> null), + lens.rewriteReferenceKeys(mayHaveSideEffects, (reference, rules) -> ListUtils.first(rules)), + // Take the assume rule from the representative in case of collisions. + lens.rewriteReferenceKeys( + noSideEffects, + (reference, rules) -> noSideEffects.get(lens.getOriginalMemberSignature(reference))), + lens.rewriteReferenceKeys( + assumedValues, + (reference, rules) -> assumedValues.get(lens.getOriginalMemberSignature(reference))), lens.rewriteReferences(alwaysInline), lens.rewriteReferences(neverInline), lens.rewriteReferences(neverInlineDueToSingleCaller),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index fb814c6..fc81e6c 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -129,7 +129,6 @@ import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult; import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle; import com.android.tools.r8.utils.Action; -import com.android.tools.r8.utils.BooleanBox; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.OptionalBool; @@ -3726,11 +3725,11 @@ : missingClassesBuilder.assertNoMissingClasses(appView), SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType), Enqueuer.toDescriptorSet(targetedMethods.getItems()), - Collections.unmodifiableSet(failedMethodResolutionTargets), - Collections.unmodifiableSet(failedFieldResolutionTargets), - Collections.unmodifiableSet(bootstrapMethods), - Collections.unmodifiableSet(methodsTargetedByInvokeDynamic), - Collections.unmodifiableSet(virtualMethodsTargetedByInvokeDirect), + failedMethodResolutionTargets, + failedFieldResolutionTargets, + bootstrapMethods, + methodsTargetedByInvokeDynamic, + virtualMethodsTargetedByInvokeDirect, toDescriptorSet(liveMethods.getItems()), // Filter out library fields and pinned fields, because these are read by default. fieldAccessInfoCollection, @@ -3772,16 +3771,15 @@ if (methods.isEmpty() || interfaceProcessor == null) { return methods; } - BooleanBox changed = new BooleanBox(false); - ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder(); + Set<DexMethod> companionMethods = Sets.newIdentityHashSet(); interfaceProcessor.forEachMethodToMove( (method, companion) -> { if (methods.contains(method)) { - changed.set(true); - builder.add(companion); + companionMethods.add(companion); } }); - return changed.isTrue() ? builder.addAll(methods).build() : methods; + methods.addAll(companionMethods); + return methods; } private boolean verifyReferences(DexApplication app) { @@ -3850,11 +3848,11 @@ private static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> Set<R> toDescriptorSet(Set<D> set) { - ImmutableSet.Builder<R> builder = new ImmutableSet.Builder<>(); + Set<R> result = Sets.newIdentityHashSet(); for (D item : set) { - builder.add(item.getReference()); + result.add(item.getReference()); } - return builder.build(); + return result; } private static Object2BooleanMap<DexMember<?, ?>> joinIdentifierNameStrings( @@ -4625,7 +4623,7 @@ continue; } - DexProgramClass clazz = asProgramClassOrNull(definitionFor(type, method)); + DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(type, method); if (clazz != null && clazz.isInterface()) { KeepReason reason = KeepReason.reflectiveUseIn(method); markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
diff --git a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java index ec1594a..8062beb 100644 --- a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java +++ b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
@@ -17,6 +17,7 @@ import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.shaking.KeepInfo.Joiner; import com.android.tools.r8.utils.MapUtils; import java.util.Collections; @@ -131,6 +132,10 @@ }); } + public void pruneItems(PrunedItems prunedItems) { + minimumKeepInfo.keySet().removeIf(prunedItems::isRemoved); + } + public KeepClassInfo.Joiner remove(DexType clazz) { return (KeepClassInfo.Joiner) minimumKeepInfo.remove(clazz); }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java index b105a80..03cff10 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1740,6 +1740,17 @@ }); } + public void pruneItems(PrunedItems prunedItems) { + MinimumKeepInfoCollection unconditionalMinimumKeepInfo = + getDependentMinimumKeepInfo().getUnconditionalMinimumKeepInfoOrDefault(null); + if (unconditionalMinimumKeepInfo != null) { + unconditionalMinimumKeepInfo.pruneItems(prunedItems); + if (unconditionalMinimumKeepInfo.isEmpty()) { + getDependentMinimumKeepInfo().remove(UnconditionalKeepInfoEvent.get()); + } + } + } + void shouldNotBeMinified(ProgramDefinition definition) { getDependentMinimumKeepInfo() .getOrCreateUnconditionalMinimumKeepInfoFor(definition.getReference())
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java index f837095..db5dcdc 100644 --- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java +++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.shaking; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DefaultInstanceInitializerCode; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMember; @@ -296,15 +297,16 @@ firstUnreachableIndex(methods, method -> appInfo.isLiveMethod(method.getReference())); // Return the original array if all methods are used. if (firstUnreachable == -1) { + for (DexEncodedMethod method : methods) { + canonicalizeCode(method.asProgramMethod(clazz)); + } return null; } ArrayList<DexEncodedMethod> reachableMethods = new ArrayList<>(methods.size()); - for (int i = 0; i < firstUnreachable; i++) { - reachableMethods.add(methods.get(i)); - } - for (int i = firstUnreachable; i < methods.size(); i++) { + for (int i = 0; i < methods.size(); i++) { DexEncodedMethod method = methods.get(i); if (appInfo.isLiveMethod(method.getReference())) { + canonicalizeCode(method.asProgramMethod(clazz)); reachableMethods.add(method); } else if (options.configurationDebugging) { // Keep the method but rewrite its body, if it has one. @@ -339,6 +341,12 @@ : reachableMethods.toArray(DexEncodedMethod.EMPTY_ARRAY); } + private void canonicalizeCode(ProgramMethod method) { + if (method.getDefinition().hasCode()) { + DefaultInstanceInitializerCode.canonicalizeCodeIfPossible(appView, method); + } + } + private DexEncodedField[] reachableFields(List<DexEncodedField> fields) { AppInfoWithLiveness appInfo = appView.appInfo(); Predicate<DexEncodedField> isReachableOrReferencedField =
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index e157ff8..6d90250 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.Code; +import com.android.tools.r8.graph.DefaultInstanceInitializerCode; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndMethod; @@ -1012,8 +1013,13 @@ DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod); if (shadowedBy != null) { if (virtualMethod.isAbstract()) { - // Remove abstract/interface methods that are shadowed. - deferredRenamings.map(virtualMethod.getReference(), shadowedBy.getReference()); + // Remove abstract/interface methods that are shadowed. The identity mapping below is + // needed to ensure we correctly fixup the mapping in case the signature refers to + // merged classes. + deferredRenamings + .map(virtualMethod.getReference(), shadowedBy.getReference()) + .map(shadowedBy.getReference(), shadowedBy.getReference()) + .recordMerge(virtualMethod.getReference(), shadowedBy.getReference()); // The override now corresponds to the method in the parent, so unset its synthetic flag // if the method in the parent is not synthetic. @@ -1119,6 +1125,12 @@ // Rewrite generic signatures before we merge fields. rewriteGenericSignatures(target, source, directMethods.values(), virtualMethods.values()); + // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens + // code rewriting. + target.forEachProgramInstanceInitializerMatching( + method -> method.getCode().isDefaultInstanceInitializerCode(), + method -> DefaultInstanceInitializerCode.uncanonicalizeCode(appView, method)); + // Step 2: Merge fields Set<DexString> existingFieldNames = new HashSet<>(); for (DexEncodedField field : target.fields()) { @@ -1891,6 +1903,8 @@ return AbortReason.MAIN_DEX_ROOT_OUTSIDE_REFERENCE; } return null; + } else if (code.isDefaultInstanceInitializerCode()) { + return null; } // For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed. }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java index ea9f971..21af6a8 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -16,9 +16,10 @@ import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.utils.IterableUtils; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap; import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -74,7 +75,7 @@ Set<DexMethod> mergedMethods, Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>> contextualVirtualToDirectMethodMaps, - BidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures, + BidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> newMethodSignatures, Map<DexMethod, DexMethod> originalMethodSignaturesForBridges) { super(appView, fieldMap, methodMap, mergedClasses.getForwardMap(), newMethodSignatures); this.appView = appView; @@ -164,8 +165,8 @@ private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>> contextualVirtualToDirectMethodMaps = new IdentityHashMap<>(); - private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures = - new BidirectionalOneToOneHashMap<>(); + private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> + newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap(); private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges = new IdentityHashMap<>(); @@ -208,12 +209,17 @@ context); } } - builder.newMethodSignatures.forEach( - (originalMethodSignature, renamedMethodSignature) -> - newBuilder.recordMove( - originalMethodSignature, - builder.getMethodSignatureAfterClassMerging( - renamedMethodSignature, mergedClasses))); + builder.newMethodSignatures.forEachManyToOneMapping( + (originalMethodSignatures, renamedMethodSignature, representative) -> { + DexMethod methodSignatureAfterClassMerging = + builder.getMethodSignatureAfterClassMerging(renamedMethodSignature, mergedClasses); + newBuilder.newMethodSignatures.put( + originalMethodSignatures, methodSignatureAfterClassMerging); + if (originalMethodSignatures.size() > 1) { + newBuilder.newMethodSignatures.setRepresentative( + methodSignatureAfterClassMerging, representative); + } + }); for (Map.Entry<DexMethod, DexMethod> entry : builder.originalMethodSignaturesForBridges.entrySet()) { newBuilder.recordCreationOfBridgeMethod( @@ -317,6 +323,12 @@ return this; } + public void recordMerge(DexMethod from, DexMethod to) { + newMethodSignatures.put(from, to); + newMethodSignatures.put(to, to); + newMethodSignatures.setRepresentative(to, to); + } + public void recordMove(DexMethod from, DexMethod to) { newMethodSignatures.put(from, to); } @@ -336,7 +348,38 @@ fieldMap.putAll(builder.fieldMap); methodMap.putAll(builder.methodMap); mergedMethodsBuilder.addAll(builder.mergedMethodsBuilder.build()); - newMethodSignatures.putAll(builder.newMethodSignatures); + builder.newMethodSignatures.forEachManyToOneMapping( + (keys, value, representative) -> { + boolean isRemapping = + Iterables.any(keys, key -> newMethodSignatures.containsValue(key) && key != value); + if (isRemapping) { + // If I and J are merged into A and both I.m() and J.m() exists, then we may map J.m() + // to I.m() as a result of merging J into A, and then subsequently merge I.m() to + // A.m() as a result of merging I into A. + assert keys.size() == 1; + DexMethod key = keys.iterator().next(); + + // When merging J.m() to I.m() we create the mappings {I.m(), J.m()} -> I.m(). + DexMethod originalRepresentative = newMethodSignatures.getRepresentativeKey(key); + Set<DexMethod> originalKeys = newMethodSignatures.removeValue(key); + assert originalKeys.contains(key); + + // Now that I.m() is merged to A.m(), we modify the existing mappings into + // {I.m(), J.m()} -> A.m(). + newMethodSignatures.put(originalKeys, value); + newMethodSignatures.setRepresentative(value, originalRepresentative); + } else { + if (newMethodSignatures.containsValue(value) + && !newMethodSignatures.hasExplicitRepresentativeKey(value)) { + newMethodSignatures.setRepresentative( + value, newMethodSignatures.getRepresentativeKey(value)); + } + newMethodSignatures.put(keys, value); + if (keys.size() > 1 && !newMethodSignatures.hasExplicitRepresentativeKey(value)) { + newMethodSignatures.setRepresentative(value, representative); + } + } + }); originalMethodSignaturesForBridges.putAll(builder.originalMethodSignaturesForBridges); for (DexType context : builder.contextualVirtualToDirectMethodMaps.keySet()) { Map<DexMethod, GraphLensLookupResultProvider> current =
diff --git a/src/main/java/com/android/tools/r8/utils/Int2StructuralItemArrayMap.java b/src/main/java/com/android/tools/r8/utils/Int2StructuralItemArrayMap.java new file mode 100644 index 0000000..9ed1e4f --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/Int2StructuralItemArrayMap.java
@@ -0,0 +1,97 @@ +// Copyright (c) 2021, 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; + +import com.android.tools.r8.utils.structural.StructuralItem; +import com.android.tools.r8.utils.structural.StructuralMapping; +import com.android.tools.r8.utils.structural.StructuralSpecification; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Ints; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.BiConsumer; + +public class Int2StructuralItemArrayMap<T extends StructuralItem<T>> + implements StructuralItem<Int2StructuralItemArrayMap<T>> { + + private final int[] keys; + private final List<T> values; + + private Int2StructuralItemArrayMap(int[] keys, List<T> values) { + this.keys = keys; + this.values = values; + assert keys.length == values.size(); + } + + @Override + public Int2StructuralItemArrayMap<T> self() { + return this; + } + + @Override + public StructuralMapping<Int2StructuralItemArrayMap<T>> getStructuralMapping() { + return Int2StructuralItemArrayMap::specify; + } + + private static <T extends StructuralItem<T>> void specify( + StructuralSpecification<Int2StructuralItemArrayMap<T>, ?> spec) { + spec.withIntArray(p -> p.keys).withItemCollection(p -> p.values); + } + + public T lookup(int key) { + for (int i = 0; i < keys.length; i++) { + if (keys[i] == key) { + return values.get(i); + } + } + return null; + } + + public void forEach(BiConsumer<Integer, T> visitor) { + for (int i = 0; i < keys.length; i++) { + visitor.accept(keys[i], values.get(i)); + } + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object other) { + return other instanceof Int2StructuralItemArrayMap + && compareTo((Int2StructuralItemArrayMap<T>) other) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(keys), values); + } + + public static <T extends StructuralItem<T>> Builder<T> builder() { + return new Builder<T>(); + } + + public boolean isEmpty() { + return keys.length == 0; + } + + public static class Builder<T extends StructuralItem<T>> { + + private final List<Integer> keys = new ArrayList<>(); + private final ImmutableList.Builder<T> values = ImmutableList.builder(); + + private Builder() {} + + public Builder<T> put(int key, T value) { + keys.add(key); + values.add(value); + return this; + } + + public Int2StructuralItemArrayMap<T> build() { + return new Int2StructuralItemArrayMap<T>(Ints.toArray(keys), values.build()); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 7da1e8e..759381b 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -70,7 +70,7 @@ import com.android.tools.r8.utils.IROrdering.IdentityIROrdering; import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering; import com.android.tools.r8.utils.collections.DexClassAndMethodSet; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.android.tools.r8.utils.structural.Ordered; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence.Wrapper; @@ -239,7 +239,6 @@ enableEnumUnboxing = false; outline.enabled = false; enableEnumValueOptimization = false; - enableValuePropagation = false; enableSideEffectAnalysis = false; enableTreeShakingOfLibraryMethodOverrides = false; enableInitializedClassesAnalysis = false; @@ -267,7 +266,6 @@ public boolean libraryInterfacesMayHaveStaticInitialization = false; // Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations. - public boolean enableFieldAssignmentTracker = true; public boolean enableFieldBitAccessAnalysis = System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null; public boolean enableVerticalClassMerging = true; @@ -317,7 +315,6 @@ public final OutlineOptions outline = new OutlineOptions(); public boolean enableInitializedClassesInInstanceMethodsAnalysis = true; public boolean enableRedundantFieldLoadElimination = true; - public boolean enableValuePropagation = true; // Currently disabled, see b/146957343. public boolean enableUninstantiatedTypeOptimizationForInterfaces = false; // TODO(b/138917494): Disable until we have numbers on potential performance penalties. @@ -1207,6 +1204,7 @@ public int minSize = 3; public int maxSize = 99; public int threshold = 20; + public int maxNumberOfInstructionsToBeConsidered = 100; } public static class KotlinOptimizationOptions { @@ -1606,7 +1604,7 @@ public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer = ConsumerUtils.emptyBiConsumer(); - public Consumer<Deque<SortedProgramMethodSet>> waveModifier = waves -> {}; + public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {}; /** * If this flag is enabled, we will also compute the set of possible targets for invoke-
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java index 2a4db34..16af327 100644 --- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java +++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -39,12 +39,18 @@ import com.android.tools.r8.graph.DexValue.DexValueString; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.OutlineCallerPosition; +import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder; +import com.android.tools.r8.ir.code.Position.OutlinePosition; +import com.android.tools.r8.ir.code.Position.PositionBuilder; +import com.android.tools.r8.ir.code.Position.SourcePosition; import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser; import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Result; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.ClassNaming; import com.android.tools.r8.naming.ClassNaming.Builder; import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange; +import com.android.tools.r8.naming.MapVersion; import com.android.tools.r8.naming.MemberNaming; import com.android.tools.r8.naming.MemberNaming.FieldSignature; import com.android.tools.r8.naming.MemberNaming.MethodSignature; @@ -53,6 +59,8 @@ import com.android.tools.r8.naming.mappinginformation.CompilerSynthesizedMappingInformation; import com.android.tools.r8.naming.mappinginformation.FileNameInformation; import com.android.tools.r8.naming.mappinginformation.MappingInformation; +import com.android.tools.r8.naming.mappinginformation.OutlineCallsiteMappingInformation; +import com.android.tools.r8.naming.mappinginformation.OutlineMappingInformation; import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation; import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RemoveInnerFramesAction; import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.ThrowsCondition; @@ -61,6 +69,10 @@ import com.android.tools.r8.shaking.KeepInfoCollection; import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; import com.google.common.base.Suppliers; +import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; +import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntSortedMap; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; @@ -84,6 +96,8 @@ @Override public Pair<Position, Position> createRemappedPosition(Position position) { + // If we create outline calls we have to map them. + assert position.getOutlineCallee() == null; return new Pair<>(position, position); } } @@ -103,12 +117,12 @@ @Override public Pair<Position, Position> createRemappedPosition(Position position) { - assert position.method != null; - if (previousMethod == position.method) { + assert position.getMethod() != null; + if (previousMethod == position.getMethod()) { assert previousSourceLine >= 0; - if (position.line > previousSourceLine - && position.line - previousSourceLine <= maxLineDelta) { - nextOptimizedLineNumber += (position.line - previousSourceLine) - 1; + if (position.getLine() > previousSourceLine + && position.getLine() - previousSourceLine <= maxLineDelta) { + nextOptimizedLineNumber += (position.getLine() - previousSourceLine) - 1; } } @@ -118,8 +132,8 @@ .setLine(nextOptimizedLineNumber++) .setCallerPosition(null) .build(); - previousSourceLine = position.line; - previousMethod = position.method; + previousSourceLine = position.getLine(); + previousMethod = position.getMethod(); return new Pair<>(position, newPosition); } } @@ -149,8 +163,8 @@ @Override public Pair<Position, Position> createRemappedPosition(Position position) { assert currentMethod != null; - int line = position.line; - Result parsedData = getAndParseSourceDebugExtension(position.method.holder); + int line = position.getLine(); + Result parsedData = getAndParseSourceDebugExtension(position.getMethod().holder); if (parsedData == null) { return baseRemapper.createRemappedPosition(position); } @@ -183,7 +197,7 @@ factory.createString(methodName), factory.createString(returnTypeDescriptor), argumentDexStringDescriptors); - if (!inlinee.equals(position.method)) { + if (!inlinee.equals(position.getMethod())) { // We have an inline from a different method than the current position. Entry<Integer, KotlinSourceDebugExtensionParser.Position> calleePosition = parsedData.lookupCalleePosition(line); @@ -196,7 +210,7 @@ .build(); } return baseRemapper.createRemappedPosition( - Position.builder() + SourcePosition.builder() .setLine(originalInlineeLine) .setMethod(inlinee) .setCallerPosition(position) @@ -256,9 +270,8 @@ private void emitPositionEvents(int currentPc, Position currentPosition) { if (previousPosition == null) { - startLine = currentPosition.line; - previousPosition = - Position.builder().setLine(startLine).setFile(null).setMethod(method).build(); + startLine = currentPosition.getLine(); + previousPosition = SourcePosition.builder().setLine(startLine).setMethod(method).build(); } DexDebugEventBuilder.emitAdvancementEvents( previousPc, @@ -280,17 +293,34 @@ // We will be remapping positional debug events and collect them as MappedPositions. private static class MappedPosition { + private final DexMethod method; private final int originalLine; private final Position caller; private final int obfuscatedLine; + private final boolean isOutline; + private final DexMethod outlineCallee; + private final Int2StructuralItemArrayMap<Position> outlinePositions; private MappedPosition( - DexMethod method, int originalLine, Position caller, int obfuscatedLine) { + DexMethod method, + int originalLine, + Position caller, + int obfuscatedLine, + boolean isOutline, + DexMethod outlineCallee, + Int2StructuralItemArrayMap<Position> outlinePositions) { this.method = method; this.originalLine = originalLine; this.caller = caller; this.obfuscatedLine = obfuscatedLine; + this.isOutline = isOutline; + this.outlineCallee = outlineCallee; + this.outlinePositions = outlinePositions; + } + + public boolean isOutlineCaller() { + return outlineCallee != null; } } @@ -300,6 +330,9 @@ // We create it here to ensure it is only reading class files once. CfLineToMethodMapper cfLineToMethodMapper = new CfLineToMethodMapper(inputApp); ClassNameMapper.Builder classNameMapperBuilder = ClassNameMapper.builder(); + + Map<DexMethod, OutlineFixupBuilder> outlinesToFix = new IdentityHashMap<>(); + // Collect which files contain which classes that need to have their line numbers optimized. for (DexProgramClass clazz : application.classes()) { boolean isSyntheticClass = appView.getSyntheticItems().isSyntheticClass(clazz); @@ -330,9 +363,9 @@ } } - if (isSyntheticClass - && CompilerSynthesizedMappingInformation.isSupported( - appView.options().getMapFileVersion())) { + MapVersion mapFileVersion = appView.options().getMapFileVersion(); + + if (isSyntheticClass && CompilerSynthesizedMappingInformation.isSupported(mapFileVersion)) { onDemandClassNamingBuilder .get() .addMappingInformation( @@ -382,9 +415,11 @@ kotlinRemapper.currentMethod = method; List<MappedPosition> mappedPositions; Code code = method.getCode(); + boolean canUseDexPc = + appView.options().canUseDexPcAsDebugInformation() && methods.size() == 1; if (code != null) { if (code.isDexCode() && doesContainPositions(code.asDexCode())) { - if (appView.options().canUseDexPcAsDebugInformation() && methods.size() == 1) { + if (canUseDexPc) { mappedPositions = optimizeDexCodePositionsForPc(method, appView, kotlinRemapper); } else { mappedPositions = @@ -412,8 +447,7 @@ List<MappingInformation> methodMappingInfo = new ArrayList<>(); if (method.isD8R8Synthesized() - && CompilerSynthesizedMappingInformation.isSupported( - appView.options().getMapFileVersion())) { + && CompilerSynthesizedMappingInformation.isSupported(mapFileVersion)) { methodMappingInfo.add(CompilerSynthesizedMappingInformation.builder().build()); } @@ -446,6 +480,18 @@ signatures.computeIfAbsent( m, key -> MethodSignature.fromDexMethod(m, m.holder != clazz.getType())); + // Check if mapped position is an outline + if (mappedPositions.get(0).isOutline + && OutlineMappingInformation.isSupported(mapFileVersion)) { + outlinesToFix + .computeIfAbsent( + mappedPositions.get(0).method, ignored -> new OutlineFixupBuilder()) + .setMappedPositionsOutline(mappedPositions); + methodMappingInfo.add(OutlineMappingInformation.builder().build()); + } + + int outlineCallersCounter = 0; + // Update memberNaming with the collected positions, merging multiple positions into a // single region whenever possible. for (int i = 0; i < mappedPositions.size(); /* updated in body */ ) { @@ -454,37 +500,40 @@ MappedPosition lastPosition = firstPosition; for (; j < mappedPositions.size(); j++) { // Break if this position cannot be merged with lastPosition. - MappedPosition mp = mappedPositions.get(j); + MappedPosition currentPosition = mappedPositions.get(j); // We allow for ranges being mapped to the same line but not to other ranges: // 1:10:void foo():42:42 -> a // is OK since retrace(a(:7)) = 42, however, the following is not OK: // 1:10:void foo():42:43 -> a // since retrace(a(:7)) = 49, which is not correct. - boolean isSingleLine = mp.originalLine == firstPosition.originalLine; + boolean isSingleLine = currentPosition.originalLine == firstPosition.originalLine; boolean differentDelta = - mp.originalLine - lastPosition.originalLine - != mp.obfuscatedLine - lastPosition.obfuscatedLine; + currentPosition.originalLine - lastPosition.originalLine + != currentPosition.obfuscatedLine - lastPosition.obfuscatedLine; boolean isMappingRangeToSingleLine = firstPosition.obfuscatedLine != lastPosition.obfuscatedLine && firstPosition.originalLine == lastPosition.originalLine; - // Note that mp.caller and lastPosition.class must be deep-compared since multiple - // inlining passes lose the canonical property of the positions. - if (mp.method != lastPosition.method + // Note that currentPosition.caller and lastPosition.class must be deep-compared since + // multiple inlining passes lose the canonical property of the positions. + if (currentPosition.method != lastPosition.method || (!isSingleLine && differentDelta) || (!isSingleLine && isMappingRangeToSingleLine) - || !Objects.equals(mp.caller, lastPosition.caller)) { + || !Objects.equals(currentPosition.caller, lastPosition.caller) + // Break when we see a mapped outline + || currentPosition.outlineCallee != null + // Ensure that we break when we start iterating with an outline caller again. + || firstPosition.outlineCallee != null) { break; } // The mapped positions are not guaranteed to be in order, so maintain first and last // position. - if (firstPosition.obfuscatedLine > mp.obfuscatedLine) { - firstPosition = mp; + if (firstPosition.obfuscatedLine > currentPosition.obfuscatedLine) { + firstPosition = currentPosition; } - if (lastPosition.obfuscatedLine < mp.obfuscatedLine) { - lastPosition = mp; + if (lastPosition.obfuscatedLine < currentPosition.obfuscatedLine) { + lastPosition = currentPosition; } } - Range originalRange = new Range(firstPosition.originalLine, lastPosition.originalLine); Range obfuscatedRange; if (method.getCode().isDexCode() && method.getCode().asDexCode().getDebugInfo() @@ -497,37 +546,49 @@ } ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.get(); MappedRange lastMappedRange = - classNamingBuilder.addMappedRange( + getMappedRangesForPosition( + appView.options().dexItemFactory(), + getOriginalMethodSignature, + classNamingBuilder, + firstPosition.method, + obfuscatedName, obfuscatedRange, - getOriginalMethodSignature.apply(firstPosition.method), - originalRange, - obfuscatedName); - Position caller = firstPosition.caller; - int inlineFramesCount = 0; - while (caller != null) { - inlineFramesCount += 1; - lastMappedRange = - classNamingBuilder.addMappedRange( - obfuscatedRange, - getOriginalMethodSignature.apply(caller.method), - new Range(Math.max(caller.line, 0)), // Prevent against "no-position". - obfuscatedName); - if (caller.removeInnerFrameIfThrowingNpe) { - lastMappedRange.addMappingInformation( - RewriteFrameMappingInformation.builder() - .addCondition( - ThrowsCondition.create( - Reference.classFromDescriptor( - appView.options().dexItemFactory().npeDescriptor.toString()))) - .addRewriteAction(RemoveInnerFramesAction.create(inlineFramesCount)) - .build(), - Unreachable::raise); - } - caller = caller.callerPosition; - } + new Range(firstPosition.originalLine, lastPosition.originalLine), + firstPosition.caller); for (MappingInformation info : methodMappingInfo) { lastMappedRange.addMappingInformation(info, Unreachable::raise); } + // firstPosition will contain a potential outline caller. + if (firstPosition.outlineCallee != null + && OutlineCallsiteMappingInformation.isSupported(mapFileVersion)) { + Int2IntMap positionMap = new Int2IntArrayMap(); + int maxPc = ListUtils.last(mappedPositions).obfuscatedLine; + firstPosition.outlinePositions.forEach( + (line, position) -> { + int placeHolderLineToBeFixed; + if (canUseDexPc) { + placeHolderLineToBeFixed = maxPc + line + 1; + } else { + placeHolderLineToBeFixed = + positionRemapper.createRemappedPosition(position).getSecond().getLine(); + } + positionMap.put((int) line, placeHolderLineToBeFixed); + getMappedRangesForPosition( + appView.options().dexItemFactory(), + getOriginalMethodSignature, + classNamingBuilder, + position.getMethod(), + obfuscatedName, + new Range(placeHolderLineToBeFixed, placeHolderLineToBeFixed), + new Range(position.getLine(), position.getLine()), + position.getCallerPosition()); + }); + outlinesToFix + .computeIfAbsent( + firstPosition.outlineCallee, ignored -> new OutlineFixupBuilder()) + .addMappedRangeForOutlineCallee(lastMappedRange, positionMap); + outlineCallersCounter += 1; + } i = j; } if (method.getCode().isDexCode() @@ -538,9 +599,52 @@ } // for each method of the group } // for each method group, grouped by name } // for each class + + // Fixup all outline positions + outlinesToFix.values().forEach(OutlineFixupBuilder::fixup); + return classNameMapperBuilder.build(); } + private static MappedRange getMappedRangesForPosition( + DexItemFactory factory, + Function<DexMethod, MethodSignature> getOriginalMethodSignature, + Builder classNamingBuilder, + DexMethod method, + String obfuscatedName, + Range obfuscatedRange, + Range originalLine, + Position caller) { + MappedRange lastMappedRange = + classNamingBuilder.addMappedRange( + obfuscatedRange, + getOriginalMethodSignature.apply(method), + originalLine, + obfuscatedName); + int inlineFramesCount = 0; + while (caller != null) { + inlineFramesCount += 1; + lastMappedRange = + classNamingBuilder.addMappedRange( + obfuscatedRange, + getOriginalMethodSignature.apply(caller.getMethod()), + new Range(Math.max(caller.getLine(), 0)), // Prevent against "no-position". + obfuscatedName); + if (caller.isRemoveInnerFramesIfThrowingNpe()) { + lastMappedRange.addMappingInformation( + RewriteFrameMappingInformation.builder() + .addCondition( + ThrowsCondition.create( + Reference.classFromDescriptor(factory.npeDescriptor.toString()))) + .addRewriteAction(RemoveInnerFramesAction.create(inlineFramesCount)) + .build(), + Unreachable::raise); + } + caller = caller.getCallerPosition(); + } + return lastMappedRange; + } + private static boolean verifyMethodsAreKeptDirectlyOrIndirectly( AppView<?> appView, List<DexEncodedMethod> methods) { if (appView.options().isGeneratingClassFiles() || !appView.appInfo().hasClassHierarchy()) { @@ -597,7 +701,7 @@ if (!(instruction instanceof CfPosition)) { continue; } - return ((CfPosition) instruction).getPosition().line; + return ((CfPosition) instruction).getPosition().getLine(); } } return 0; @@ -747,19 +851,14 @@ public void visit(Default defaultEvent) { super.visit(defaultEvent); assert getCurrentLine() >= 0; - Position position = - Position.builder() - .setLine(getCurrentLine()) - .setFile(getCurrentFile()) - .setMethod(getCurrentMethod()) - .setCallerPosition(getCurrentCallerPosition()) - .build(); + Position position = getPositionFromPositionState(this); Position currentPosition = remapAndAdd(position, positionRemapper, mappedPositions); positionEventEmitter.emitPositionEvents(getCurrentPc(), currentPosition); if (currentPosition != position) { inlinedOriginalPosition.set(true); } emittedPc = getCurrentPc(); + resetOutlineInformation(); } // Non-materializing events use super, ie, AdvancePC, AdvanceLine and SetInlineFrame. @@ -810,7 +909,8 @@ if (mappedPositions.size() <= 1 && !hasOverloads && !appView.options().debug - && appView.options().lineNumberOptimization != LineNumberOptimization.OFF) { + && appView.options().lineNumberOptimization != LineNumberOptimization.OFF + && (mappedPositions.isEmpty() || !mappedPositions.get(0).isOutlineCaller())) { dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance()); return mappedPositions; } @@ -829,6 +929,27 @@ return mappedPositions; } + private static Position getPositionFromPositionState(DexDebugPositionState state) { + PositionBuilder<?, ?> positionBuilder; + if (state.getOutlineCallee() != null) { + OutlineCallerPositionBuilder outlineCallerPositionBuilder = + OutlineCallerPosition.builder() + .setOutlineCallee(state.getOutlineCallee()) + .setIsOutline(state.isOutline()); + state.getOutlineCallerPositions().forEach(outlineCallerPositionBuilder::addOutlinePosition); + positionBuilder = outlineCallerPositionBuilder; + } else if (state.isOutline()) { + positionBuilder = OutlinePosition.builder(); + } else { + positionBuilder = SourcePosition.builder(); + } + return positionBuilder + .setLine(state.getCurrentLine()) + .setMethod(state.getCurrentMethod()) + .setCallerPosition(state.getCurrentCallerPosition()) + .build(); + } + private static List<MappedPosition> optimizeDexCodePositionsForPc( DexEncodedMethod method, AppView<?> appView, PositionRemapper positionRemapper) { List<MappedPosition> mappedPositions = new ArrayList<>(); @@ -855,13 +976,8 @@ mappedPositions); } lastPosition.setFirst(getCurrentPc()); - lastPosition.setSecond( - Position.builder() - .setLine(getCurrentLine()) - .setFile(getCurrentFile()) - .setMethod(getCurrentMethod()) - .setCallerPosition(getCurrentCallerPosition()) - .build()); + lastPosition.setSecond(getPositionFromPositionState(this)); + resetOutlineInformation(); } }; @@ -932,7 +1048,13 @@ Position newPosition = remappedPosition.getSecond(); mappedPositions.add( new MappedPosition( - oldPosition.method, oldPosition.line, oldPosition.callerPosition, newPosition.line)); + oldPosition.getMethod(), + oldPosition.getLine(), + oldPosition.getCallerPosition(), + newPosition.getLine(), + oldPosition.isOutline(), + oldPosition.getOutlineCallee(), + oldPosition.getOutlinePositions())); return newPosition; } @@ -945,9 +1067,66 @@ Pair<Position, Position> remappedPosition = remapper.createRemappedPosition(position); Position oldPosition = remappedPosition.getFirst(); for (int currentPc = startPc; currentPc < endPc; currentPc++) { + boolean firstEntry = currentPc == startPc; mappedPositions.add( new MappedPosition( - oldPosition.method, oldPosition.line, oldPosition.callerPosition, currentPc)); + oldPosition.getMethod(), + oldPosition.getLine(), + oldPosition.getCallerPosition(), + currentPc, + // Outline info is placed exactly on the positions that relate to it so we should + // only emit it for the first entry. + firstEntry && oldPosition.isOutline(), + firstEntry ? oldPosition.getOutlineCallee() : null, + firstEntry ? oldPosition.getOutlinePositions() : null)); + } + } + + private static class OutlineFixupBuilder { + + private static int MINIFIED_POSITION_REMOVED = -1; + + private List<MappedPosition> mappedOutlinePositions = null; + private final List<Pair<MappedRange, Int2IntMap>> mappedOutlineCalleePositions = + new ArrayList<>(); + + public void setMappedPositionsOutline(List<MappedPosition> mappedPositionsOutline) { + this.mappedOutlinePositions = mappedPositionsOutline; + } + + public void addMappedRangeForOutlineCallee( + MappedRange mappedRangeForOutline, Int2IntMap calleePositions) { + mappedOutlineCalleePositions.add(Pair.create(mappedRangeForOutline, calleePositions)); + } + + public void fixup() { + assert mappedOutlinePositions != null; + assert !mappedOutlineCalleePositions.isEmpty(); + for (Pair<MappedRange, Int2IntMap> mappingInfo : mappedOutlineCalleePositions) { + MappedRange mappedRange = mappingInfo.getFirst(); + Int2IntMap positions = mappingInfo.getSecond(); + Int2IntSortedMap map = new Int2IntLinkedOpenHashMap(); + positions.forEach( + (outlinePosition, calleePosition) -> { + int minifiedLinePosition = + getMinifiedLinePosition(outlinePosition, mappedOutlinePositions); + if (minifiedLinePosition != MINIFIED_POSITION_REMOVED) { + map.put(minifiedLinePosition, (int) calleePosition); + } + }); + mappedRange.addMappingInformation( + OutlineCallsiteMappingInformation.create(map), Unreachable::raise); + } + } + + private int getMinifiedLinePosition( + int originalPosition, List<MappedPosition> mappedPositions) { + for (MappedPosition mappedPosition : mappedPositions) { + if (mappedPosition.originalLine == originalPosition) { + return mappedPosition.obfuscatedLine; + } + } + return MINIFIED_POSITION_REMOVED; } } }
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java index a7305a2..624fb10 100644 --- a/src/main/java/com/android/tools/r8/utils/SetUtils.java +++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -34,14 +34,22 @@ return result; } + @SafeVarargs + public static <T> HashSet<T> newHashSet(T... elements) { + HashSet<T> result = new HashSet<>(elements.length); + Collections.addAll(result, elements); + return result; + } + public static <T> Set<T> newIdentityHashSet(T element) { Set<T> result = Sets.newIdentityHashSet(); result.add(element); return result; } - public static <T> Set<T> newIdentityHashSet(T[] elements) { - Set<T> result = Sets.newIdentityHashSet(); + @SafeVarargs + public static <T> Set<T> newIdentityHashSet(T... elements) { + Set<T> result = newIdentityHashSet(elements.length); Collections.addAll(result, elements); return result; }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java index bdd70c8..0887329 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java
@@ -81,6 +81,17 @@ } @Override + public void putAll(BidirectionalManyToOneRepresentativeMap<K, V> map) { + map.forEachManyToOneMapping( + (keys, value, representative) -> { + put(keys, value); + if (keys.size() > 1) { + setRepresentative(value, representative); + } + }); + } + + @Override public V remove(K key) { V value = super.remove(key); if (hasExplicitRepresentativeKey(value)) {
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java index 24f91ac..b80cc5b 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java
@@ -8,6 +8,8 @@ public interface MutableBidirectionalManyToOneRepresentativeMap<K, V> extends MutableBidirectionalManyToOneMap<K, V>, BidirectionalManyToOneRepresentativeMap<K, V> { + void putAll(BidirectionalManyToOneRepresentativeMap<K, V> map); + K removeRepresentativeFor(V value); void setRepresentative(V value, K representative);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java index d36d207..12deaa2 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java +++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.utils.ForEachable; import com.google.common.collect.ImmutableMap; import java.util.IdentityHashMap; import java.util.LinkedHashMap; @@ -45,6 +46,12 @@ return result; } + public static ProgramMethodSet create(ForEachable<ProgramMethod> methods) { + ProgramMethodSet result = create(); + methods.forEach(result::add); + return result; + } + public static ProgramMethodSet create(ProgramMethodSet methodSet) { ProgramMethodSet newMethodSet = create(); newMethodSet.addAll(methodSet);
diff --git a/src/main/resources/api_database/api_database_ambiguous.txt b/src/main/resources/api_database/api_database_ambiguous.txt index 3ffb464..5c4f00c 100644 --- a/src/main/resources/api_database/api_database_ambiguous.txt +++ b/src/main/resources/api_database/api_database_ambiguous.txt
@@ -1,4 +1,171 @@ +ebf47efadc4c50002b13da0b1cef0646:18 +106de6ca4d0e49988f3328ce380ff517:18 +2f4aedd17528c30f8a9f16b030ff708b:18 +93aca156e3f15799914f868852723e47:18 +17cbc97604a781acb78c668b2437ffbd:18 +1709449e191fe131236bd0b8c1928b71:18 +a6cf882fc575147513b7b1b505fdd335:18 +1587f690fd5c0c886630334e89e397ff:18 +dc6ad4bf6892204b9fca73d1ee9cab04:18 +9ae673cfbb5d90688edafbf7ec2862e4:18 +acf250fcdbf54725219af8c6a8a614d9:18 +07319a58381faa784dda046d5e8fd8ae:18 +77e978f6318d01be894de8944fdc05b4:18 +81be9a78d26d25c6ff47c7e9af30a92e:18 +e87bf9d0084b39c443e421cdb5d3fc43:18 +9e973ee7f4f5ad967d2ba5bf5d88911c:18 +b47e7bbf50ad208599056f8d0f1cc5ba:18 +47a9e25289b4ece230adcc3b60797712:18 +42e4cc1882d334757afbe74f4d8cec8e:18 +024481748991b858bf7e03b0b87ce125:18 +b6ced7e04b527d83cd660894a0059ea7:18 +717e536c3a13f3602fbaf1f607ee2963:18 +18b540e31006aad6ae7a26d967ec0e0e:18 +f1483fe126d64bd7544dba7fdf0559f4:18 +3c4a0d8270a52e43316a711a2fe83268:18 +2e13bb1bfba423473a6efba65d9c8190:18 +47a5d9010bdeb561176cf070ef483e91:18 +4f2e7d32ed2c7ead316770fbe84d8aae:18 +c625e1090fc4d72afd0673839af5d46f:18 +4cc2dc623ee702db7a5899c5c401a98c:18 +780c7e0f80b5479d00e2c3679974644d:18 +eff51a62305642c720d163085061ed63:18 +69e416f1afc250d6239788b4b1fb9bb4:18 +d71a32697989404ae782e105a5eb5e4c:18 +6d60d78c1ce897131dcf45743a8d6e49:18 +e16e8dcca650cf79557cb76a86edb4e9:18 +bfebe4871ed9c4b5f3300fe8dfa2a0a3:18 +93e215f230901f20bdca460cfe8c42af:18 +8efacada80287ebb7607758a0a82b0a0:18 +487bc1228fd817b641056a1a008984f8:18 +dc6c2915cd6f0a6fd0ab01365349599d:18 +1a3dbf72e4bd135a621b8ad78a7151c1:18 +01fd60d48ba7e1434bb72696282c7113:18 +83c42b475763d64935a9ddfc6a374b64:18 +ed9a26aaf7ddb748bd1e624aeb4fdb81:18 +eeb05b41976f7bb96572330854aaab34:18 +3c17437ba337e7ce8edad092f745a363:18 +8d8fd1ffc3ae24cb43798f4eb41fc4de:18 +003f7ab7088710994a289bab815aacbf:18 +488d943cdcedfca2b076152769fc3c51:18 +cab779407e29ba3377e6a47a4f842ec5:18 +a0ddcf0f7bb4aa9d38db983941f6908d:18 +b64b41d26010783cae2dff88c7e21f1d:18 +aac6d054ad0f9bea1f64702aea698ee3:18 +90b4163293f9e35f7091692b31d3b5d7:18 +9d66fceb1054d5e2ff207591ec216b70:18 +93a65566b576af220207e30cc89ef490:18 +b3248e6a2d819bd8bb42a15179188c52:18 +f95f0545feb040d2d6bfcad6897364d0:18 +058ec3044e95ed7fcaff93a2f3105d6f:18 +39ef3516cb80752cac1c541b43a1c2fa:18 +c1295d79d79340152c6b98f1c9003e8c:18 +c4b4a2994711cb342b0c8c8ef409ce8b:18 +719c3b73816fa0db79747ea264abab5a:18 +1d7847b88e0feaecd299b3c5c18be763:18 +1211e656a8eaa4423e7678533a1e7e2a:18 +e0c7417a74df21b55367f9e1ea6c2d37:18 +e49b0a2bd830bb3b96934490b2bc6ebe:18 +91790d7c5cf146ab3936919184a70015:18 +ec36ea4bb9634f7ec5c4d3c1cc9dd71b:18 +860e7b1a68c02eef7d2e2d7b6a3e13b5:18 +458fda8d9863be24862bb62c910f427d:18 +e05ed0452c12e62ef74e9161159b1231:18 +9419764761c059b9c2f9cf640abfdeb0:18 +ea02175c4ef64b6370f619acd84f3c3c:18 +0b01d40e1ca37aa854e77c98cc153e77:18 +eb12f3e690b201aa171d794a8288af10:18 +b955e5418d490425f99d58f441ba4525:18 +b36bec8fd49158c020b6d8c09cc02baf:26 +e7ca6d1abe4cd55a491cdf58eb8f4602:17 +d8c43560e159bcb899c2180b8e01d8e3:24 +12233f83b06081671815562dce938d3b:24 +ae9c7aee12dfaaa56e43834dcea91f5f:24 +106252e3f31ee17239e1ee4184147deb:24 +1fb2cd38813bad6e5eeef74bcbcb133d:24 +c5bbd4174eee5cb68830801317071dd5:24 +b876313caea388f03200ac720ed528af:24 +dc32c09ecdd9bda4703aa944cf1eb4ca:24 +f4fa53a97c83899fd2da9f4112f35339:24 +07a59dae4166f6d504bb0793c5e75178:24 +efcb5024ce3514453d44735222903a55:24 +43445dc9e50e2c4fc327097d07ff749a:24 +bcb1787dd2def454fd3f31d1b2c0967a:24 +2cc65559141cece33ffdbc4e34a2ce3b:24 +cace738770550444ee1f91000c32c147:24 +b55a09768f05f29b15e1ff3a9feb7c92:24 +54282406a85f4410d4dfc72d5873ee96:24 +1c38913c447705e315ec4486da558c9d:24 +ca3f30645fa7b480b213f4228386a09a:24 +6d8800c5cc8ad9e4f3f28e777207a870:24 +b8dc6f9b9587159f978d643aeb8e4d07:24 +91eb697571f68f5850875286c5e36080:24 +44c0d27dc28d4da28655c3c62a0d7cac:24 +ae908f7abd7d347b0bf39fead3531c12:24 +145e730e204fdca9cf169521a3812ed1:24 +d27ceaaa92270c450aeee582cc17b985:24 +294141fdc8f17ac3c7e4d8687d8ad389:24 +48de820418038280f70eca64f878c67b:24 +8569110aa82eb44ff0b12819867c66f8:24 +cfde9ea95c663bed6d56df8778b01b79:24 +f389ac23dc4ef6641321adf43d712c32:24 +46de27224f4cf6fb745e30bbd6aad267:24 +ef4661e0074b431762177e10c2ac8570:24 +75c79913d4df6b467a67c40ce67a32fc:24 +2a000aca1e17b3b0343ddf151ee313f4:24 +e9d59e0325c6dd1dd78a998365435fac:24 +0a1d88442fc34d0081115d949f1c5fa9:24 +0a7ef5f8b75fa2dd79787b03dff923c9:24 +b247285b58ca44aad532b1c3b5a5dacf:24 +63f61697ff1319a76632338fe0c13ae9:24 +e8df9edc7e29252645d2b8a6a55c4e0f:24 +aaba729f45b1617d59cec26aa41937e3:24 +7e7ae6228d4ab54b90f0f580731941d7:24 +de1c84862deb139962240505133690f0:24 +9dbabf8883b7a4e120fcbceb00face40:24 +9660da0a81f885de5a3b16f1f07a1067:24 +c3f5202b30c04801da693013368a0da2:24 +b73113e1f7c2702cfd997dd84e5ac003:24 +9b5946e54278b49302da6ccd2a3a06e1:24 +c7a5a5244ef8d55b160e29df7e310b76:24 +74fa889a011613ee983f45aed7dcb656:24 +ccb57b751fd0ed3f7244a0e096b19076:24 +dcd61dd6bd1e0fec6888029656162559:24 +c2fa9ac71ea25473f3c384e5d3c3770b:24 +43de03bf819ac106a9a66f3d7b473bd1:24 +3ed4b5f668130ecec1aadb21bf0a671a:24 +ba4d4fc3c411bf5b477323a1329574b3:24 +4e82fa0797beeab61a7a0b9b3ba7505e:24 +1cff05a0b4291e8f289a1794f016eab5:24 +456c3251cceb566a82d3175390b6a663:24 +11e854ed754739797dbae75ff3159e82:24 +2daef0ca0e135ad904bf7e931690cf2b:24 +76fd080fdaa80390ca05399f4d0b49ae:24 +d49bee1da6282ff1efd4017e2c26ff55:24 +94bf636e74064a1f45c5e81777a8b541:24 +67395fd629dae7b237421aa91bc12150:24 +b69dd3d2f26fc766f3b4193b089fa71e:24 +b502fdd440592e2d637704ebef9baf01:24 +5a13a7fac55b3378629744099623199b:24 +6c21462d7f8529e6d017e054b02d96d3:24 +ef1a937ed3ed1277a46cbbfb589bcc7c:24 +a9446b05afd48f274f6469e9617dadde:24 +03b1c2c2b9e04b66ace80c8d891dad71:24 +5af3ef227d419594dc0316392c0fd762:24 +b4146bc53ba79957adf0bf7306b2212c:24 +f1136465059f10586c4fb8379126e010:24 +8796edfd2200b66560e180ecf2c8bd3a:24 +a4fc9c89f980d489cd69b5a9a5379279:28 +beef3d82791567a66852903d9acde2d8:14 +8f8e727dba622a81127060f53efbaf60:14 +a58ec4e669821ad885c5e539bea8f8c0:14 +c183aebc8596eae7dbaf32f831d4bf1a:14 +579a40bd08705acc7099b631706917cd:14 +c979623104ac1df2ff0b56f2eb8f40a3:14 +4a406313d3d1fc7c56f2e4ae88033ef5:14 +0534bb586552faf59923fcbb66c5c9a8:14 828623182530e5b5c55265b6a20263be:9 +027b76c362f10b646fce08dd34457533:29 850665aac5116ea7c5dd3ed4d672cc68:1 d853b59bd420a046a715750d924360d0:1 1a9e947630aeed44da4b6e668e8ac214:1 @@ -74,258 +241,8 @@ 83255b102f7327ffe0819cc9d48d764a:1 a159e58a81a5d7e46b1d44f845486c25:1 c9a7fe526ed238e56f029bdd9eadd4a8:1 -a5352e6f5efd89293cad68bde5359174:21 -0873c171286bf72adca073b8232909ad:21 -75fcc63a7299ec405cbbbae6c6f4124a:21 -4242f75bd579b5860e448896bd97161e:21 -f8e3f0c3200fb1dd44acfbd301d96bed:21 -3e972f384811f77fd62ce384910c7605:21 -746752a0385785d6c4a6af907b506ff8:21 -f2c67cccefcb8826a54b39122e002062:21 -e40adee8247464101fcb01d083025c31:21 -d96183e4ed9b9b2b075bd5f8c4364100:21 -0a8ab6673c7b4e87cb2faf2d7005362a:21 -810a68c672694a924abe0f0c59a9a394:21 -fe284dc96dc744beee1ddbb1a8e4c4ee:21 -d34d323d00595f4051cfe0621331317d:21 -711a52bc9e3eafe73e0c64c4ae6d5e6a:21 -542566d5040f1a84a95caf3c78e8d54f:21 -fadc0a0d51e6ccdc60992c058edd2711:21 -ccb12040fbbda66de180c11e6251049d:21 -052dc8ce89610356ae4c22dacb2668f6:21 -eb911a19cc8e2139a72a6bf025f0ef92:21 -8a9b11e0d4a3993f76485c069942489c:21 -b50ba38de9db0ecba8a7618f6138380b:21 -5a50c9965f9cd4ee5a321bbd530cc5b2:21 -301f4d37021f2c80999eedb2219554bd:21 -391832837fd0c5bd42b6d5d58dcd80f6:21 -535462c1df188932ba4375a241ac0a3b:21 -517cb4d902b3c235f31b41bcd012b184:21 -dd9d0f0182859fbb91bfe9cb3504becf:21 -4634339ec4a3dfef9d6a8a22e18c1354:21 -4015e477f56bfed839fc88a1ba749750:21 -be73d1320eb5c1229bc65e73f82039f3:21 -1c2ab72e50012527678e434999c9044a:21 -a09c3b287f292693c49b6ac6139f3aca:21 -67ce8616e53660f5898eaa146c7f3835:21 -5b728323f7668ae4ff0e895afaf15cea:21 -b070068bc98b0eafc27ab9bb38e7cfbb:21 -eadaf0fe065d92924ebd1cade7c57e35:21 -724567eb08f8a1a1dba97c82bd0a8d00:21 -06065f8f4550b0f3530468ae56e4437b:21 -eea2af8f17a87b391353b3878325bbd1:21 -aa462a0178a7561e7c756b157cbeeb38:21 -3af411c682f8264bb7c70794211c6248:21 -b972f761c2e0edfca60661ae2abf0218:21 -da13bac632b4f32422190d415f31bab8:21 -a7c7530bd46ed4e558e4d448f1ab9b7c:21 -e2c48e8e5e90a5dcc4cd070af2c1b20e:21 -0fc8da4c46369ca9dbcee59fb490681c:21 -5bb64f5d5c0764c9c1076e2c00ee3b3c:21 -f92f02539108f4c7034055e630ce5b1b:21 -21206168597de4cc003cf94bb2476441:21 -80ffcde01a4a187455c15c7b11ae7c33:21 -5adbafe8f34e077daeacc70305eed4f1:21 -d7d607c796c81475b0c57f2646b7e662:21 -4f89f7e86a6cb4ab329ea69918ae6f49:21 -0f88600ff268e2f32d56c63ce955aeba:21 -aeb824437b0a504c4a1a3109729a8817:21 -bd00bb3c1478ff33a781c1b48bfa9112:21 -eabb0f12ea319b61bdf53d9f4a6d7044:21 -b5653063b6061db848ef666076d4a44b:21 -1bf6b15eb573dea85120bcc84f419023:21 -71ae94c8959d23501b3a25269ed58f50:21 -c0673d423ec86b469092b2eedbf4e8db:21 -52986dd229c1677a8a8fea05023f7625:21 -364cf0eb88afb77fbc7209d2c1c7273e:21 -072b20ca146065654d29d72fa24fc89a:21 -50ddd3dc05b1198214cb57e9250d7076:21 -3eb1459c5b79cc1a562b145846a1b957:21 -d84c006146e1a54bba7a7122983ef085:21 -3a16ed65c968daf194834ce7a0225b8a:21 -6321e1881f171dc79e50d8e0cbc6ce7d:21 -5800fc71343c619b5e005bbab60912e0:21 -8e087b8bbcd92fe9389bf2f469f383f6:21 -e7e98eb2a51cc3cbf30bd532d8942294:21 -c173773ee22e94ab734c05bd504d3325:21 -5bef782b74608c4de25f4d053ae543a9:21 -ba7c51aed579c0f1b473ef2035b15a17:21 -169dfedc30ae5b95af848f3184206b4b:21 -549d9811413f0c4b84cd1e84290127f1:21 -668fa5f927950cbb34952cdb7e631eb8:21 -580fe13c6b95c05247c1770fff6d0fe9:21 -02d6383509108efe665de4a2aff24702:21 -68749591b1210c540ab306d416f49c68:21 -75269722fb78a1b61a850da9a73984fc:21 -58c819dd55c2558fed93faef88f45e89:21 -e479c8a303ccdaf45f673c78971d6a44:21 -cd94f79ea296e6126eb563a6e82a73b3:21 -ab652b1e99797dd3cc3bfb7e48db5d18:21 -4d3ab1362ff964f3947b437b16aba576:21 -53dea4d4defbb7512a31f4fa659437ae:21 -252e68c63ccea47b1d02a47837383c4e:21 -a9fff0b0f85b598b277aaf29fe537ad8:21 -caf732f52661f4c573b342a3d1da85bc:21 -9401285d5c41e4e0ee4581c62f4a2fc4:21 -5d0c7f0ad2c60ef78ae47f854b90a194:21 -983db0abe1d8976946a8a19eee412c9d:21 -e7ca6d1abe4cd55a491cdf58eb8f4602:17 -a4fc9c89f980d489cd69b5a9a5379279:28 b63216a8584c12bf002f4ee8c3b0ac3e:19 1ad1cfa76f9692958656f17b710a0ecc:19 -ebf47efadc4c50002b13da0b1cef0646:18 -106de6ca4d0e49988f3328ce380ff517:18 -2f4aedd17528c30f8a9f16b030ff708b:18 -93aca156e3f15799914f868852723e47:18 -17cbc97604a781acb78c668b2437ffbd:18 -1709449e191fe131236bd0b8c1928b71:18 -1587f690fd5c0c886630334e89e397ff:18 -a6cf882fc575147513b7b1b505fdd335:18 -dc6ad4bf6892204b9fca73d1ee9cab04:18 -9ae673cfbb5d90688edafbf7ec2862e4:18 -acf250fcdbf54725219af8c6a8a614d9:18 -07319a58381faa784dda046d5e8fd8ae:18 -77e978f6318d01be894de8944fdc05b4:18 -81be9a78d26d25c6ff47c7e9af30a92e:18 -e87bf9d0084b39c443e421cdb5d3fc43:18 -9e973ee7f4f5ad967d2ba5bf5d88911c:18 -b47e7bbf50ad208599056f8d0f1cc5ba:18 -47a9e25289b4ece230adcc3b60797712:18 -42e4cc1882d334757afbe74f4d8cec8e:18 -024481748991b858bf7e03b0b87ce125:18 -b6ced7e04b527d83cd660894a0059ea7:18 -717e536c3a13f3602fbaf1f607ee2963:18 -18b540e31006aad6ae7a26d967ec0e0e:18 -f1483fe126d64bd7544dba7fdf0559f4:18 -3c4a0d8270a52e43316a711a2fe83268:18 -2e13bb1bfba423473a6efba65d9c8190:18 -47a5d9010bdeb561176cf070ef483e91:18 -4f2e7d32ed2c7ead316770fbe84d8aae:18 -c625e1090fc4d72afd0673839af5d46f:18 -4cc2dc623ee702db7a5899c5c401a98c:18 -eff51a62305642c720d163085061ed63:18 -780c7e0f80b5479d00e2c3679974644d:18 -69e416f1afc250d6239788b4b1fb9bb4:18 -6d60d78c1ce897131dcf45743a8d6e49:18 -e16e8dcca650cf79557cb76a86edb4e9:18 -d71a32697989404ae782e105a5eb5e4c:18 -bfebe4871ed9c4b5f3300fe8dfa2a0a3:18 -93e215f230901f20bdca460cfe8c42af:18 -8efacada80287ebb7607758a0a82b0a0:18 -487bc1228fd817b641056a1a008984f8:18 -dc6c2915cd6f0a6fd0ab01365349599d:18 -1a3dbf72e4bd135a621b8ad78a7151c1:18 -01fd60d48ba7e1434bb72696282c7113:18 -83c42b475763d64935a9ddfc6a374b64:18 -ed9a26aaf7ddb748bd1e624aeb4fdb81:18 -eeb05b41976f7bb96572330854aaab34:18 -3c17437ba337e7ce8edad092f745a363:18 -8d8fd1ffc3ae24cb43798f4eb41fc4de:18 -003f7ab7088710994a289bab815aacbf:18 -488d943cdcedfca2b076152769fc3c51:18 -cab779407e29ba3377e6a47a4f842ec5:18 -a0ddcf0f7bb4aa9d38db983941f6908d:18 -b64b41d26010783cae2dff88c7e21f1d:18 -aac6d054ad0f9bea1f64702aea698ee3:18 -90b4163293f9e35f7091692b31d3b5d7:18 -9d66fceb1054d5e2ff207591ec216b70:18 -93a65566b576af220207e30cc89ef490:18 -b3248e6a2d819bd8bb42a15179188c52:18 -f95f0545feb040d2d6bfcad6897364d0:18 -058ec3044e95ed7fcaff93a2f3105d6f:18 -39ef3516cb80752cac1c541b43a1c2fa:18 -c4b4a2994711cb342b0c8c8ef409ce8b:18 -719c3b73816fa0db79747ea264abab5a:18 -c1295d79d79340152c6b98f1c9003e8c:18 -1211e656a8eaa4423e7678533a1e7e2a:18 -1d7847b88e0feaecd299b3c5c18be763:18 -e0c7417a74df21b55367f9e1ea6c2d37:18 -e49b0a2bd830bb3b96934490b2bc6ebe:18 -91790d7c5cf146ab3936919184a70015:18 -ec36ea4bb9634f7ec5c4d3c1cc9dd71b:18 -860e7b1a68c02eef7d2e2d7b6a3e13b5:18 -458fda8d9863be24862bb62c910f427d:18 -e05ed0452c12e62ef74e9161159b1231:18 -9419764761c059b9c2f9cf640abfdeb0:18 -ea02175c4ef64b6370f619acd84f3c3c:18 -0b01d40e1ca37aa854e77c98cc153e77:18 -eb12f3e690b201aa171d794a8288af10:18 -b955e5418d490425f99d58f441ba4525:18 -34305e28da7351c9fe8a69e055e1c250:4 -f9a8c4364b7598dbeca0186f60e71ea6:4 -6a6e8a76be9fd95bf68f4be6f6cf1bbf:4 -14a880d95891af72380815d1c91baf73:4 -a2fa13ef7ba19d24adf59f8ecc12f0b4:4 -acd568e2592b223aaec17240031ed688:4 -cf994432aef1a88d7b82ffeecbbb96dc:4 -31927400c1d74c4dbcefe4c8c787ab8a:4 -94961af3e92bca38554a63bef2e453a6:4 -c562088538d99567be8dfdc54f830661:4 -459a7fef5f29ed6bafedadf927e315ee:4 -5c9d5847e56faea367acff998cb9e02b:4 -766fb7e580b7f7211bee8e77b98859b7:4 -53dc80d117986033f21a0b5f8609c87c:4 -3d8114f2ba5b5b51c547181a89a3466f:4 -89418ba487f62fbae3843e5dff5b9040:4 -ebe40ec90a872c67a1c27ae2c1eea950:4 -35462fa6c34cbb3610497e0e7ec5604a:4 -4f7b3ab75c9ad8d5b10fec60265a3809:4 -5123777f5c011dbd46ae38689b72a7d4:4 -38c98f315f4b01c1aa53a76a214a919e:4 -25989076c985cb6136599803fbcb48d5:4 -a96139fddc921f9c720e39737bfc1e23:4 -5cc99cf3cb2f9f5b2f3da2068a3c3b14:4 -9d57682c086fd2c37173c59f7df7f7f1:4 -bae7808504fe1ce016d86125d969287f:4 -b0767396a0f7f31b48ac96880c9cbf8b:4 -e2babe17ce0f4f5b30fee91c06c0e8fa:4 -d1c3eecefff445a312cfee0fa59d1431:4 -d5702e33cc1c8a04039657bec033a3e8:4 -1449b4ca79da7eaf4a3e04a2aa7b764d:4 -f50415b6582d0e25f6949d844bf93ee8:4 -3fef2395a8a091e7dea7a3b99dfeaea6:4 -03e302c7776c88610dafb333323263af:4 -b7d9936f2c7c87d2edac782f5403001b:4 -affb389d45f01bc18197bf0f49f4b118:4 -a4eb5a999085dfe2424b2896e38b25d1:4 -9cc9248b4494b77d7b40bbbf4dcef8b3:4 -043d915c3e3d58cb001a8b32deaffea3:4 -f9d3d4bc8a5944b63f51ae472e86813d:4 -a5e0393e11eadd75dd36fb5379ae018c:4 -5980bcaa38c00e4b837f2e9785bda3d2:4 -cc63328c0b60b9a790f8a1457dfaa511:4 -afdf18c7f6d8a437d84e390721b9b8b3:4 -b08cfcf9149aa4e94b5fb9695271c575:4 -10a9f2ae427efa8d37d59f435e83d62e:4 -d689449577a88d1b20d321d4e5fd32d9:4 -75298a5c8b84105fd9b9df7184dc110a:4 -5c8ed88fdd5d5ccb1ad7a61ecdf1ef9e:4 -35a4cc8262e2a04a80fc506d1551c2b7:4 -7ef43aabb95d2f3ad79438accc09cb4c:4 -aa6a754d71d091d666fea53f8769dfe9:4 -c8341c7fe17e7ae1484fdc25aef13df9:4 -4b6c6d5caf6e3d47ffd3dfdb329842f2:4 -a93c08a489709b0b6402127f3287a0a2:4 -500d600d630b1130b906355e694caf25:4 -e2f20c930af14f873866419d98bb33c5:4 -4f67d44d8a9c73b20f9dccb0fe708910:4 -3207a78e01966b63f857a845d312c5db:4 -dc772adf9d92e1330e1e6c1bc8bbdbea:4 -9a0f97a242e5b5c8bf70dd24775d973a:4 -c51604fe4030e75ce4ed3f545e470eee:4 -e8627b4481475f494ae99206e4d614a4:4 -a3abcd010f778136d77b5c00575faa93:4 -d806274a83ce72b89939c7e1fcc7dbf1:4 -e42535765b64f8f96c6a5b13c5aa30e9:4 -0389b0ce442166bc4298bd49f8ecfbff:4 -e6c5da69c2396e1a4c404a82509edb1e:4 -d62e86b3047119ec0941418ab6365bd4:4 -3c957e719e0ffce99b544fcc9a97ab3a:4 -638115e7b634086640ad6b300846ad6d:4 -f42bda5821df4a01dc97cb7d611a98a2:4 -15884ea4526d7af7c6dd5c5c89b81c97:4 -23fb4c5f959189ae9c90b968647934f3:4 -6b538e97c482ee0993607820484dcb08:4 e7e718f70ee49560800db2d60270603f:11 61c32de62fbec72e56083dade766f0f9:11 f9684960dbadc5206f33484afeba57e1:11 @@ -361,84 +278,6 @@ 13c8db7cad8e47068720c9f9b9d8213c:11 d942884e00b9f288de96a8b0733372d9:11 cd5c01104e0c46080a8d499f87de4991:11 -d8c43560e159bcb899c2180b8e01d8e3:24 -ae9c7aee12dfaaa56e43834dcea91f5f:24 -12233f83b06081671815562dce938d3b:24 -106252e3f31ee17239e1ee4184147deb:24 -1fb2cd38813bad6e5eeef74bcbcb133d:24 -c5bbd4174eee5cb68830801317071dd5:24 -b876313caea388f03200ac720ed528af:24 -f4fa53a97c83899fd2da9f4112f35339:24 -dc32c09ecdd9bda4703aa944cf1eb4ca:24 -07a59dae4166f6d504bb0793c5e75178:24 -efcb5024ce3514453d44735222903a55:24 -43445dc9e50e2c4fc327097d07ff749a:24 -bcb1787dd2def454fd3f31d1b2c0967a:24 -cace738770550444ee1f91000c32c147:24 -2cc65559141cece33ffdbc4e34a2ce3b:24 -b55a09768f05f29b15e1ff3a9feb7c92:24 -54282406a85f4410d4dfc72d5873ee96:24 -1c38913c447705e315ec4486da558c9d:24 -ca3f30645fa7b480b213f4228386a09a:24 -6d8800c5cc8ad9e4f3f28e777207a870:24 -b8dc6f9b9587159f978d643aeb8e4d07:24 -44c0d27dc28d4da28655c3c62a0d7cac:24 -91eb697571f68f5850875286c5e36080:24 -ae908f7abd7d347b0bf39fead3531c12:24 -d27ceaaa92270c450aeee582cc17b985:24 -145e730e204fdca9cf169521a3812ed1:24 -294141fdc8f17ac3c7e4d8687d8ad389:24 -cfde9ea95c663bed6d56df8778b01b79:24 -8569110aa82eb44ff0b12819867c66f8:24 -48de820418038280f70eca64f878c67b:24 -f389ac23dc4ef6641321adf43d712c32:24 -ef4661e0074b431762177e10c2ac8570:24 -46de27224f4cf6fb745e30bbd6aad267:24 -75c79913d4df6b467a67c40ce67a32fc:24 -2a000aca1e17b3b0343ddf151ee313f4:24 -e9d59e0325c6dd1dd78a998365435fac:24 -0a1d88442fc34d0081115d949f1c5fa9:24 -0a7ef5f8b75fa2dd79787b03dff923c9:24 -b247285b58ca44aad532b1c3b5a5dacf:24 -63f61697ff1319a76632338fe0c13ae9:24 -e8df9edc7e29252645d2b8a6a55c4e0f:24 -aaba729f45b1617d59cec26aa41937e3:24 -7e7ae6228d4ab54b90f0f580731941d7:24 -de1c84862deb139962240505133690f0:24 -9660da0a81f885de5a3b16f1f07a1067:24 -9dbabf8883b7a4e120fcbceb00face40:24 -c3f5202b30c04801da693013368a0da2:24 -b73113e1f7c2702cfd997dd84e5ac003:24 -9b5946e54278b49302da6ccd2a3a06e1:24 -c7a5a5244ef8d55b160e29df7e310b76:24 -74fa889a011613ee983f45aed7dcb656:24 -ccb57b751fd0ed3f7244a0e096b19076:24 -dcd61dd6bd1e0fec6888029656162559:24 -c2fa9ac71ea25473f3c384e5d3c3770b:24 -43de03bf819ac106a9a66f3d7b473bd1:24 -3ed4b5f668130ecec1aadb21bf0a671a:24 -ba4d4fc3c411bf5b477323a1329574b3:24 -4e82fa0797beeab61a7a0b9b3ba7505e:24 -1cff05a0b4291e8f289a1794f016eab5:24 -11e854ed754739797dbae75ff3159e82:24 -456c3251cceb566a82d3175390b6a663:24 -76fd080fdaa80390ca05399f4d0b49ae:24 -2daef0ca0e135ad904bf7e931690cf2b:24 -d49bee1da6282ff1efd4017e2c26ff55:24 -94bf636e74064a1f45c5e81777a8b541:24 -b502fdd440592e2d637704ebef9baf01:24 -67395fd629dae7b237421aa91bc12150:24 -b69dd3d2f26fc766f3b4193b089fa71e:24 -5a13a7fac55b3378629744099623199b:24 -6c21462d7f8529e6d017e054b02d96d3:24 -ef1a937ed3ed1277a46cbbfb589bcc7c:24 -a9446b05afd48f274f6469e9617dadde:24 -03b1c2c2b9e04b66ace80c8d891dad71:24 -5af3ef227d419594dc0316392c0fd762:24 -b4146bc53ba79957adf0bf7306b2212c:24 -f1136465059f10586c4fb8379126e010:24 -8796edfd2200b66560e180ecf2c8bd3a:24 -b36bec8fd49158c020b6d8c09cc02baf:26 799e6e8b1fe3f2c45ec9703491bd67d5:8 12011f7bb75e99008d3ef5bbe9087e6f:8 53737c53af7f40961557a85fb4d11b55:8 @@ -446,8 +285,8 @@ a85598686e913ff9a84c078990e2b74f:8 9389bbac5d37c3655c2d112b5fb15d79:8 c7143cf932c4620b38f241a67c698de7:8 -6bb42f291857bf93ef2407e3fe8910b3:8 d5360a8c6fd960a52c125ad6ee65b369:8 +6bb42f291857bf93ef2407e3fe8910b3:8 4c0513c3ea8aa4e0d1671697ade55dc0:8 9ab999023c47266804e1c23912c49590:8 c83ba14b99739545b5611680823c5e64:8 @@ -462,12 +301,173 @@ 2248103eacf690b2060d9f624dee5e41:8 7aaff576c6f4f28ee8d80236c3943c3a:8 62068b685c80a1da76ef1824af304e47:5 -beef3d82791567a66852903d9acde2d8:14 -8f8e727dba622a81127060f53efbaf60:14 -a58ec4e669821ad885c5e539bea8f8c0:14 -c183aebc8596eae7dbaf32f831d4bf1a:14 -579a40bd08705acc7099b631706917cd:14 -c979623104ac1df2ff0b56f2eb8f40a3:14 -4a406313d3d1fc7c56f2e4ae88033ef5:14 -0534bb586552faf59923fcbb66c5c9a8:14 -027b76c362f10b646fce08dd34457533:29 +a5352e6f5efd89293cad68bde5359174:21 +0873c171286bf72adca073b8232909ad:21 +75fcc63a7299ec405cbbbae6c6f4124a:21 +4242f75bd579b5860e448896bd97161e:21 +f8e3f0c3200fb1dd44acfbd301d96bed:21 +3e972f384811f77fd62ce384910c7605:21 +746752a0385785d6c4a6af907b506ff8:21 +e40adee8247464101fcb01d083025c31:21 +f2c67cccefcb8826a54b39122e002062:21 +d96183e4ed9b9b2b075bd5f8c4364100:21 +0a8ab6673c7b4e87cb2faf2d7005362a:21 +810a68c672694a924abe0f0c59a9a394:21 +fe284dc96dc744beee1ddbb1a8e4c4ee:21 +d34d323d00595f4051cfe0621331317d:21 +711a52bc9e3eafe73e0c64c4ae6d5e6a:21 +542566d5040f1a84a95caf3c78e8d54f:21 +fadc0a0d51e6ccdc60992c058edd2711:21 +ccb12040fbbda66de180c11e6251049d:21 +052dc8ce89610356ae4c22dacb2668f6:21 +eb911a19cc8e2139a72a6bf025f0ef92:21 +8a9b11e0d4a3993f76485c069942489c:21 +b50ba38de9db0ecba8a7618f6138380b:21 +5a50c9965f9cd4ee5a321bbd530cc5b2:21 +301f4d37021f2c80999eedb2219554bd:21 +391832837fd0c5bd42b6d5d58dcd80f6:21 +535462c1df188932ba4375a241ac0a3b:21 +517cb4d902b3c235f31b41bcd012b184:21 +dd9d0f0182859fbb91bfe9cb3504becf:21 +4634339ec4a3dfef9d6a8a22e18c1354:21 +4015e477f56bfed839fc88a1ba749750:21 +be73d1320eb5c1229bc65e73f82039f3:21 +1c2ab72e50012527678e434999c9044a:21 +a09c3b287f292693c49b6ac6139f3aca:21 +67ce8616e53660f5898eaa146c7f3835:21 +5b728323f7668ae4ff0e895afaf15cea:21 +eadaf0fe065d92924ebd1cade7c57e35:21 +b070068bc98b0eafc27ab9bb38e7cfbb:21 +eea2af8f17a87b391353b3878325bbd1:21 +724567eb08f8a1a1dba97c82bd0a8d00:21 +06065f8f4550b0f3530468ae56e4437b:21 +aa462a0178a7561e7c756b157cbeeb38:21 +3af411c682f8264bb7c70794211c6248:21 +b972f761c2e0edfca60661ae2abf0218:21 +da13bac632b4f32422190d415f31bab8:21 +a7c7530bd46ed4e558e4d448f1ab9b7c:21 +e2c48e8e5e90a5dcc4cd070af2c1b20e:21 +0fc8da4c46369ca9dbcee59fb490681c:21 +5bb64f5d5c0764c9c1076e2c00ee3b3c:21 +f92f02539108f4c7034055e630ce5b1b:21 +21206168597de4cc003cf94bb2476441:21 +80ffcde01a4a187455c15c7b11ae7c33:21 +5adbafe8f34e077daeacc70305eed4f1:21 +d7d607c796c81475b0c57f2646b7e662:21 +4f89f7e86a6cb4ab329ea69918ae6f49:21 +0f88600ff268e2f32d56c63ce955aeba:21 +aeb824437b0a504c4a1a3109729a8817:21 +bd00bb3c1478ff33a781c1b48bfa9112:21 +eabb0f12ea319b61bdf53d9f4a6d7044:21 +b5653063b6061db848ef666076d4a44b:21 +71ae94c8959d23501b3a25269ed58f50:21 +1bf6b15eb573dea85120bcc84f419023:21 +c0673d423ec86b469092b2eedbf4e8db:21 +52986dd229c1677a8a8fea05023f7625:21 +364cf0eb88afb77fbc7209d2c1c7273e:21 +072b20ca146065654d29d72fa24fc89a:21 +50ddd3dc05b1198214cb57e9250d7076:21 +3eb1459c5b79cc1a562b145846a1b957:21 +d84c006146e1a54bba7a7122983ef085:21 +3a16ed65c968daf194834ce7a0225b8a:21 +6321e1881f171dc79e50d8e0cbc6ce7d:21 +5800fc71343c619b5e005bbab60912e0:21 +8e087b8bbcd92fe9389bf2f469f383f6:21 +e7e98eb2a51cc3cbf30bd532d8942294:21 +ba7c51aed579c0f1b473ef2035b15a17:21 +c173773ee22e94ab734c05bd504d3325:21 +5bef782b74608c4de25f4d053ae543a9:21 +169dfedc30ae5b95af848f3184206b4b:21 +668fa5f927950cbb34952cdb7e631eb8:21 +549d9811413f0c4b84cd1e84290127f1:21 +580fe13c6b95c05247c1770fff6d0fe9:21 +68749591b1210c540ab306d416f49c68:21 +02d6383509108efe665de4a2aff24702:21 +75269722fb78a1b61a850da9a73984fc:21 +58c819dd55c2558fed93faef88f45e89:21 +e479c8a303ccdaf45f673c78971d6a44:21 +cd94f79ea296e6126eb563a6e82a73b3:21 +ab652b1e99797dd3cc3bfb7e48db5d18:21 +4d3ab1362ff964f3947b437b16aba576:21 +53dea4d4defbb7512a31f4fa659437ae:21 +252e68c63ccea47b1d02a47837383c4e:21 +a9fff0b0f85b598b277aaf29fe537ad8:21 +caf732f52661f4c573b342a3d1da85bc:21 +9401285d5c41e4e0ee4581c62f4a2fc4:21 +5d0c7f0ad2c60ef78ae47f854b90a194:21 +983db0abe1d8976946a8a19eee412c9d:21 +34305e28da7351c9fe8a69e055e1c250:4 +f9a8c4364b7598dbeca0186f60e71ea6:4 +6a6e8a76be9fd95bf68f4be6f6cf1bbf:4 +14a880d95891af72380815d1c91baf73:4 +a2fa13ef7ba19d24adf59f8ecc12f0b4:4 +acd568e2592b223aaec17240031ed688:4 +cf994432aef1a88d7b82ffeecbbb96dc:4 +31927400c1d74c4dbcefe4c8c787ab8a:4 +94961af3e92bca38554a63bef2e453a6:4 +c562088538d99567be8dfdc54f830661:4 +459a7fef5f29ed6bafedadf927e315ee:4 +5c9d5847e56faea367acff998cb9e02b:4 +766fb7e580b7f7211bee8e77b98859b7:4 +53dc80d117986033f21a0b5f8609c87c:4 +3d8114f2ba5b5b51c547181a89a3466f:4 +89418ba487f62fbae3843e5dff5b9040:4 +ebe40ec90a872c67a1c27ae2c1eea950:4 +35462fa6c34cbb3610497e0e7ec5604a:4 +4f7b3ab75c9ad8d5b10fec60265a3809:4 +5123777f5c011dbd46ae38689b72a7d4:4 +38c98f315f4b01c1aa53a76a214a919e:4 +25989076c985cb6136599803fbcb48d5:4 +a96139fddc921f9c720e39737bfc1e23:4 +5cc99cf3cb2f9f5b2f3da2068a3c3b14:4 +9d57682c086fd2c37173c59f7df7f7f1:4 +e2babe17ce0f4f5b30fee91c06c0e8fa:4 +d1c3eecefff445a312cfee0fa59d1431:4 +bae7808504fe1ce016d86125d969287f:4 +b0767396a0f7f31b48ac96880c9cbf8b:4 +d5702e33cc1c8a04039657bec033a3e8:4 +1449b4ca79da7eaf4a3e04a2aa7b764d:4 +f50415b6582d0e25f6949d844bf93ee8:4 +3fef2395a8a091e7dea7a3b99dfeaea6:4 +03e302c7776c88610dafb333323263af:4 +b7d9936f2c7c87d2edac782f5403001b:4 +affb389d45f01bc18197bf0f49f4b118:4 +a4eb5a999085dfe2424b2896e38b25d1:4 +9cc9248b4494b77d7b40bbbf4dcef8b3:4 +043d915c3e3d58cb001a8b32deaffea3:4 +f9d3d4bc8a5944b63f51ae472e86813d:4 +a5e0393e11eadd75dd36fb5379ae018c:4 +5980bcaa38c00e4b837f2e9785bda3d2:4 +cc63328c0b60b9a790f8a1457dfaa511:4 +afdf18c7f6d8a437d84e390721b9b8b3:4 +b08cfcf9149aa4e94b5fb9695271c575:4 +10a9f2ae427efa8d37d59f435e83d62e:4 +d689449577a88d1b20d321d4e5fd32d9:4 +75298a5c8b84105fd9b9df7184dc110a:4 +5c8ed88fdd5d5ccb1ad7a61ecdf1ef9e:4 +35a4cc8262e2a04a80fc506d1551c2b7:4 +7ef43aabb95d2f3ad79438accc09cb4c:4 +aa6a754d71d091d666fea53f8769dfe9:4 +c8341c7fe17e7ae1484fdc25aef13df9:4 +500d600d630b1130b906355e694caf25:4 +e2f20c930af14f873866419d98bb33c5:4 +4b6c6d5caf6e3d47ffd3dfdb329842f2:4 +a93c08a489709b0b6402127f3287a0a2:4 +4f67d44d8a9c73b20f9dccb0fe708910:4 +3207a78e01966b63f857a845d312c5db:4 +dc772adf9d92e1330e1e6c1bc8bbdbea:4 +9a0f97a242e5b5c8bf70dd24775d973a:4 +c51604fe4030e75ce4ed3f545e470eee:4 +e8627b4481475f494ae99206e4d614a4:4 +a3abcd010f778136d77b5c00575faa93:4 +d806274a83ce72b89939c7e1fcc7dbf1:4 +e42535765b64f8f96c6a5b13c5aa30e9:4 +0389b0ce442166bc4298bd49f8ecfbff:4 +e6c5da69c2396e1a4c404a82509edb1e:4 +d62e86b3047119ec0941418ab6365bd4:4 +3c957e719e0ffce99b544fcc9a97ab3a:4 +638115e7b634086640ad6b300846ad6d:4 +f42bda5821df4a01dc97cb7d611a98a2:4 +15884ea4526d7af7c6dd5c5c89b81c97:4 +23fb4c5f959189ae9c90b968647934f3:4 +6b538e97c482ee0993607820484dcb08:4
diff --git a/src/main/resources/api_database/api_database_api_level.ser b/src/main/resources/api_database/api_database_api_level.ser index e9e539a..ab078a7 100644 --- a/src/main/resources/api_database/api_database_api_level.ser +++ b/src/main/resources/api_database/api_database_api_level.ser Binary files differ
diff --git a/src/main/resources/api_database/api_database_hash_lookup.ser b/src/main/resources/api_database/api_database_hash_lookup.ser index 42a3b61..b332706 100644 --- a/src/main/resources/api_database/api_database_hash_lookup.ser +++ b/src/main/resources/api_database/api_database_hash_lookup.ser Binary files differ
diff --git a/src/test/examples/classmerging/ConflictInGeneratedNameTest.java b/src/test/examples/classmerging/ConflictInGeneratedNameTest.java index 396e73c..4ef0b3e 100644 --- a/src/test/examples/classmerging/ConflictInGeneratedNameTest.java +++ b/src/test/examples/classmerging/ConflictInGeneratedNameTest.java
@@ -11,7 +11,7 @@ } public static class A { - private String name = "A"; + @NeverPropagateValue private String name = "A"; public A() { print("In A.<init>()"); @@ -56,8 +56,8 @@ } public static class B extends A { - private String name = "B"; - private String name$classmerging$ConflictInGeneratedNameTest$A = "C"; + @NeverPropagateValue private String name = "B"; + @NeverPropagateValue private String name$classmerging$ConflictInGeneratedNameTest$A = "C"; public B() { print("In B.<init>()");
diff --git a/src/test/examples/classmerging/NeverPropagateValue.java b/src/test/examples/classmerging/NeverPropagateValue.java new file mode 100644 index 0000000..f023635 --- /dev/null +++ b/src/test/examples/classmerging/NeverPropagateValue.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2021, 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 classmerging; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface NeverPropagateValue {}
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt index 46fcb91..9c7188b 100644 --- a/src/test/examples/classmerging/keep-rules.txt +++ b/src/test/examples/classmerging/keep-rules.txt
@@ -72,4 +72,5 @@ -neverinline class * { @classmerging.NeverInline <methods>; } +-neverpropagatevalue class * { @classmerging.NeverPropagateValue *; } -nohorizontalclassmerging @classmerging.NoHorizontalClassMerging class *
diff --git a/src/test/examplesJava11/com/android/tools/r8/NeverPropagateValue.java b/src/test/examplesJava11/com/android/tools/r8/NeverPropagateValue.java new file mode 100644 index 0000000..b28eece --- /dev/null +++ b/src/test/examplesJava11/com/android/tools/r8/NeverPropagateValue.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2021, 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface NeverPropagateValue {}
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java index cdbe725..1e36fe1 100644 --- a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java +++ b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
@@ -5,6 +5,7 @@ package nesthostexample; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NeverPropagateValue; public class NestPvtMethodCallInlined { @@ -15,6 +16,7 @@ } @NeverInline + @NeverPropagateValue private String notInlinedPvtCall() { return "notInlinedPvtCallInner"; } @@ -31,6 +33,7 @@ } @NeverInline + @NeverPropagateValue private String notInlinedPvtCall() { return "notInlinedPvtCallInnerInterface"; } @@ -44,6 +47,7 @@ } @NeverInline + @NeverPropagateValue default String dispatchInlining(InnerSub iSub) { return iSub.dispatch(this); } @@ -54,6 +58,7 @@ public static class InnerSub extends Inner { @NeverInline + @NeverPropagateValue public String dispatchInlining(InnerInterface impl) { return impl.dispatch(this); } @@ -63,6 +68,7 @@ } @NeverInline + @NeverPropagateValue private String notInlinedPvtCall() { return "notInlinedPvtCallInnerSub"; }
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java index 2a8f1bb..accc8af 100644 --- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java +++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -161,13 +161,10 @@ * import java.util.concurrent.Flow.Subscriber; * import java.util.concurrent.Flow.Subscription; * import java.util.concurrent.SubmissionPublisher; - * import java.util.concurrent.locks.Condition; - * import java.util.concurrent.locks.Lock; - * import java.util.concurrent.locks.ReentrantLock; + * import java.util.concurrent.CountDownLatch; * * public class MySubscriber<T> implements Subscriber<T> { - * final static Lock lock = new ReentrantLock(); - * final static Condition done = lock.newCondition(); + * final static CountDownLatch done = new CountDownLatch(1); * * private Subscription subscription; * @@ -190,25 +187,7 @@ * @Override * public void onComplete() { * System.out.println("Done"); - * signalCondition(done); - * } - * - * public static void awaitCondition(Condition condition) throws Exception { - * lock.lock(); - * try { - * condition.await(); - * } finally { - * lock.unlock(); - * } - * } - * - * public static void signalCondition(Condition condition) { - * lock.lock(); - * try { - * condition.signal(); - * } finally { - * lock.unlock(); - * } + * done.countDown(); * } * * public static void main(String[] args) throws Exception { @@ -220,7 +199,7 @@ * items.forEach(publisher::submit); * publisher.close(); * - * awaitCondition(done); + * done.await(); * } * } * @@ -262,13 +241,7 @@ { fieldVisitor = classWriter.visitField( - ACC_FINAL | ACC_STATIC, "lock", "Ljava/util/concurrent/locks/Lock;", null, null); - fieldVisitor.visitEnd(); - } - { - fieldVisitor = - classWriter.visitField( - ACC_FINAL | ACC_STATIC, "done", "Ljava/util/concurrent/locks/Condition;", null, null); + ACC_FINAL | ACC_STATIC, "done", "Ljava/util/concurrent/CountDownLatch;", null, null); fieldVisitor.visitEnd(); } { @@ -282,7 +255,7 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(10, label0); + methodVisitor.visitLineNumber(8, label0); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); methodVisitor.visitInsn(RETURN); @@ -296,21 +269,21 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(18, label0); + methodVisitor.visitLineNumber(15, label0); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitFieldInsn( PUTFIELD, "MySubscriber", "subscription", "Ljava/util/concurrent/Flow$Subscription;"); Label label1 = new Label(); methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(19, label1); + methodVisitor.visitLineNumber(16, label1); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitInsn(LCONST_1); methodVisitor.visitMethodInsn( INVOKEINTERFACE, "java/util/concurrent/Flow$Subscription", "request", "(J)V", true); Label label2 = new Label(); methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(20, label2); + methodVisitor.visitLineNumber(17, label2); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(3, 2); methodVisitor.visitEnd(); @@ -321,7 +294,7 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(24, label0); + methodVisitor.visitLineNumber(21, label0); methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder"); methodVisitor.visitInsn(DUP); @@ -347,7 +320,7 @@ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); Label label1 = new Label(); methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(25, label1); + methodVisitor.visitLineNumber(22, label1); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitFieldInsn( GETFIELD, "MySubscriber", "subscription", "Ljava/util/concurrent/Flow$Subscription;"); @@ -356,7 +329,7 @@ INVOKEINTERFACE, "java/util/concurrent/Flow$Subscription", "request", "(J)V", true); Label label2 = new Label(); methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(26, label2); + methodVisitor.visitLineNumber(23, label2); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(3, 2); methodVisitor.visitEnd(); @@ -367,13 +340,13 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(29, label0); + methodVisitor.visitLineNumber(26, label0); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitMethodInsn( INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false); Label label1 = new Label(); methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(30, label1); + methodVisitor.visitLineNumber(27, label1); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(1, 2); methodVisitor.visitEnd(); @@ -383,25 +356,21 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(34, label0); + methodVisitor.visitLineNumber(31, label0); methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); methodVisitor.visitLdcInsn("Done"); methodVisitor.visitMethodInsn( INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); Label label1 = new Label(); methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(35, label1); + methodVisitor.visitLineNumber(32, label1); methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "done", "Ljava/util/concurrent/locks/Condition;"); + GETSTATIC, "MySubscriber", "done", "Ljava/util/concurrent/CountDownLatch;"); methodVisitor.visitMethodInsn( - INVOKESTATIC, - "MySubscriber", - "signalCondition", - "(Ljava/util/concurrent/locks/Condition;)V", - false); + INVOKEVIRTUAL, "java/util/concurrent/CountDownLatch", "countDown", "()V", false); Label label2 = new Label(); methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(36, label2); + methodVisitor.visitLineNumber(33, label2); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(2, 1); methodVisitor.visitEnd(); @@ -410,112 +379,6 @@ methodVisitor = classWriter.visitMethod( ACC_PUBLIC | ACC_STATIC, - "awaitCondition", - "(Ljava/util/concurrent/locks/Condition;)V", - null, - new String[] {"java/lang/Exception"}); - methodVisitor.visitCode(); - Label label0 = new Label(); - Label label1 = new Label(); - Label label2 = new Label(); - methodVisitor.visitTryCatchBlock(label0, label1, label2, null); - Label label3 = new Label(); - methodVisitor.visitLabel(label3); - methodVisitor.visitLineNumber(39, label3); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Lock", "lock", "()V", true); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(41, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Condition", "await", "()V", true); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(43, label1); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Lock", "unlock", "()V", true); - Label label4 = new Label(); - methodVisitor.visitLabel(label4); - methodVisitor.visitLineNumber(44, label4); - Label label5 = new Label(); - methodVisitor.visitJumpInsn(GOTO, label5); - methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(43, label2); - methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"}); - methodVisitor.visitVarInsn(ASTORE, 1); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Lock", "unlock", "()V", true); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitInsn(ATHROW); - methodVisitor.visitLabel(label5); - methodVisitor.visitLineNumber(45, label5); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitInsn(RETURN); - methodVisitor.visitMaxs(1, 2); - methodVisitor.visitEnd(); - } - { - methodVisitor = - classWriter.visitMethod( - ACC_PUBLIC | ACC_STATIC, - "signalCondition", - "(Ljava/util/concurrent/locks/Condition;)V", - null, - null); - methodVisitor.visitCode(); - Label label0 = new Label(); - Label label1 = new Label(); - Label label2 = new Label(); - methodVisitor.visitTryCatchBlock(label0, label1, label2, null); - Label label3 = new Label(); - methodVisitor.visitLabel(label3); - methodVisitor.visitLineNumber(48, label3); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Lock", "lock", "()V", true); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(50, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Condition", "signal", "()V", true); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(52, label1); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Lock", "unlock", "()V", true); - Label label4 = new Label(); - methodVisitor.visitLabel(label4); - methodVisitor.visitLineNumber(53, label4); - Label label5 = new Label(); - methodVisitor.visitJumpInsn(GOTO, label5); - methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(52, label2); - methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"}); - methodVisitor.visitVarInsn(ASTORE, 1); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, "java/util/concurrent/locks/Lock", "unlock", "()V", true); - methodVisitor.visitVarInsn(ALOAD, 1); - methodVisitor.visitInsn(ATHROW); - methodVisitor.visitLabel(label5); - methodVisitor.visitLineNumber(54, label5); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitInsn(RETURN); - methodVisitor.visitMaxs(1, 2); - methodVisitor.visitEnd(); - } - { - methodVisitor = - classWriter.visitMethod( - ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, @@ -523,7 +386,7 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(57, label0); + methodVisitor.visitLineNumber(36, label0); methodVisitor.visitTypeInsn(NEW, "java/util/concurrent/SubmissionPublisher"); methodVisitor.visitInsn(DUP); methodVisitor.visitMethodInsn( @@ -531,14 +394,14 @@ methodVisitor.visitVarInsn(ASTORE, 1); Label label1 = new Label(); methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(58, label1); + methodVisitor.visitLineNumber(37, label1); methodVisitor.visitTypeInsn(NEW, "MySubscriber"); methodVisitor.visitInsn(DUP); methodVisitor.visitMethodInsn(INVOKESPECIAL, "MySubscriber", "<init>", "()V", false); methodVisitor.visitVarInsn(ASTORE, 2); Label label2 = new Label(); methodVisitor.visitLabel(label2); - methodVisitor.visitLineNumber(59, label2); + methodVisitor.visitLineNumber(38, label2); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitVarInsn(ALOAD, 2); methodVisitor.visitMethodInsn( @@ -549,7 +412,7 @@ false); Label label3 = new Label(); methodVisitor.visitLabel(label3); - methodVisitor.visitLineNumber(60, label3); + methodVisitor.visitLineNumber(39, label3); methodVisitor.visitLdcInsn("1"); methodVisitor.visitLdcInsn("2"); methodVisitor.visitLdcInsn("3"); @@ -562,7 +425,7 @@ methodVisitor.visitVarInsn(ASTORE, 3); Label label4 = new Label(); methodVisitor.visitLabel(label4); - methodVisitor.visitLineNumber(62, label4); + methodVisitor.visitLineNumber(41, label4); methodVisitor.visitVarInsn(ALOAD, 3); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitInsn(DUP); @@ -596,24 +459,20 @@ INVOKEINTERFACE, "java/util/List", "forEach", "(Ljava/util/function/Consumer;)V", true); Label label5 = new Label(); methodVisitor.visitLabel(label5); - methodVisitor.visitLineNumber(63, label5); + methodVisitor.visitLineNumber(42, label5); methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitMethodInsn( INVOKEVIRTUAL, "java/util/concurrent/SubmissionPublisher", "close", "()V", false); Label label6 = new Label(); methodVisitor.visitLabel(label6); - methodVisitor.visitLineNumber(65, label6); + methodVisitor.visitLineNumber(44, label6); methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "done", "Ljava/util/concurrent/locks/Condition;"); + GETSTATIC, "MySubscriber", "done", "Ljava/util/concurrent/CountDownLatch;"); methodVisitor.visitMethodInsn( - INVOKESTATIC, - "MySubscriber", - "awaitCondition", - "(Ljava/util/concurrent/locks/Condition;)V", - false); + INVOKEVIRTUAL, "java/util/concurrent/CountDownLatch", "await", "()V", false); Label label7 = new Label(); methodVisitor.visitLabel(label7); - methodVisitor.visitLineNumber(66, label7); + methodVisitor.visitLineNumber(45, label7); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(3, 4); methodVisitor.visitEnd(); @@ -623,28 +482,16 @@ methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(11, label0); - methodVisitor.visitTypeInsn(NEW, "java/util/concurrent/locks/ReentrantLock"); + methodVisitor.visitLineNumber(9, label0); + methodVisitor.visitTypeInsn(NEW, "java/util/concurrent/CountDownLatch"); methodVisitor.visitInsn(DUP); + methodVisitor.visitInsn(ICONST_1); methodVisitor.visitMethodInsn( - INVOKESPECIAL, "java/util/concurrent/locks/ReentrantLock", "<init>", "()V", false); + INVOKESPECIAL, "java/util/concurrent/CountDownLatch", "<init>", "(I)V", false); methodVisitor.visitFieldInsn( - PUTSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(12, label1); - methodVisitor.visitFieldInsn( - GETSTATIC, "MySubscriber", "lock", "Ljava/util/concurrent/locks/Lock;"); - methodVisitor.visitMethodInsn( - INVOKEINTERFACE, - "java/util/concurrent/locks/Lock", - "newCondition", - "()Ljava/util/concurrent/locks/Condition;", - true); - methodVisitor.visitFieldInsn( - PUTSTATIC, "MySubscriber", "done", "Ljava/util/concurrent/locks/Condition;"); + PUTSTATIC, "MySubscriber", "done", "Ljava/util/concurrent/CountDownLatch;"); methodVisitor.visitInsn(RETURN); - methodVisitor.visitMaxs(2, 0); + methodVisitor.visitMaxs(3, 0); methodVisitor.visitEnd(); } classWriter.visitEnd();
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java index 2ada4dd..c988660 100644 --- a/src/test/java/com/android/tools/r8/D8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.D8Command.Builder; import com.android.tools.r8.TestBase.Backend; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.AndroidApp; @@ -88,7 +89,7 @@ @Override public D8TestBuilder enableCoreLibraryDesugaring( AndroidApiLevel minApiLevel, - StringConsumer keepRuleConsumer, + KeepRuleConsumer keepRuleConsumer, StringResource desugaredLibraryConfiguration) { if (minApiLevel.getLevel() < AndroidApiLevel.O.getLevel()) { super.enableCoreLibraryDesugaring(
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java index 73c4623..85e7ca6 100644 --- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -181,7 +181,10 @@ ProcessBuilder processBuilder = new ProcessBuilder(command); ProcessResult processResult = ToolHelper.runProcess(processBuilder, getStdoutForTesting()); - assertEquals(processResult.stderr, 0, processResult.exitCode); + assertEquals( + "STDOUT\n:" + processResult.stdout + "\nSTDERR:\n" + processResult.stderr, + 0, + processResult.exitCode); String proguardMap = proguardMapFile.toFile().exists() ? FileUtils.readTextFile(proguardMapFile, Charsets.UTF_8)
diff --git a/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java b/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java index 396e9e0..111b040 100644 --- a/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java +++ b/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
@@ -137,13 +137,12 @@ return this; } - public Builder setKeepRuleConsumer(StringConsumer keepRuleConsumer) { + public Builder setKeepRuleConsumer(KeepRuleConsumer keepRuleConsumer) { withKeepRuleConsumer = false; if (keepRuleConsumer == null) { this.keepRuleConsumer = null; } else { - assert keepRuleConsumer instanceof KeepRuleConsumer; - this.keepRuleConsumer = (KeepRuleConsumer) keepRuleConsumer; + this.keepRuleConsumer = keepRuleConsumer; } return this; }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java index d30e214..2db1e9d 100644 --- a/src/test/java/com/android/tools/r8/R8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.R8Command.Builder; import com.android.tools.r8.TestBase.Backend; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer; import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface; import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner; import com.android.tools.r8.errors.Unreachable; @@ -651,7 +652,7 @@ @Override public T enableCoreLibraryDesugaring( AndroidApiLevel minApiLevel, - StringConsumer keepRuleConsumer, + KeepRuleConsumer keepRuleConsumer, StringResource desugaredLibraryConfiguration) { if (minApiLevel.getLevel() < AndroidApiLevel.O.getLevel()) { super.enableCoreLibraryDesugaring(
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java index 3702430..fbe6f27 100644 --- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java +++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.TestBase.Backend; import com.android.tools.r8.debug.DebugTestConfig; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer; import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer; import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference; import com.android.tools.r8.testing.AndroidBuildVersion; @@ -457,7 +458,7 @@ } public T enableCoreLibraryDesugaring( - AndroidApiLevel minApiLevel, StringConsumer keepRuleConsumer) { + AndroidApiLevel minApiLevel, KeepRuleConsumer keepRuleConsumer) { return enableCoreLibraryDesugaring( minApiLevel, keepRuleConsumer, @@ -471,7 +472,7 @@ public T enableCoreLibraryDesugaring( AndroidApiLevel minApiLevel, - StringConsumer keepRuleConsumer, + KeepRuleConsumer keepRuleConsumer, StringResource desugaredLibraryConfiguration) { assert minApiLevel.getLevel() < AndroidApiLevel.O.getLevel(); return enableLibraryDesugaring(
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java index fddd0d4..375f6f8 100644 --- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java +++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -3,7 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8; +import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.TestRuntime.CfVm; +import com.android.tools.r8.TestRuntime.DexRuntime; import com.android.tools.r8.TestRuntime.NoneRuntime; import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.utils.AndroidApiLevel; @@ -62,10 +64,39 @@ return withCfRuntimeFilter(vm -> vm == runtime); } + /** + * Test using the same runtime as the test is executing under. + * + * <p>This should only be used when there is an explicit dependency in the test that requires the + * host and test to be the same. For example, it could be to fork a subprocess using the same + * runtime. + */ public TestParametersBuilder withSystemRuntime() { return withCfRuntimeFilter(TestParametersBuilder::isSystemJdk); } + /** + * Test using the default DEX VM. + * + * <p>Generally tests should rather use withDexRuntimes(), but if a test really only needs to be + * tested on a single DEX runtime this can be used instead. The test should not have any + * requirements as to which VM it is as the default will change and may not track latest. + */ + public TestParametersBuilder withDefaultDexRuntime() { + return withDexRuntime(DexRuntime.getDefaultDexRuntime().getVersion()); + } + + /** + * Test using the default CF VM. + * + * <p>Generally tests should rather use withCfRuntimes(), but if a test really only needs to be + * tested on a single CF runtime this can be used instead. The test should not have any + * requirements as to which VM it is as the default will change and may not track latest. + */ + public TestParametersBuilder withDefaultCfRuntime() { + return withCfRuntime(CfRuntime.getDefaultCfRuntime().getVm()); + } + /** Add all available CF runtimes. */ public TestParametersBuilder withCfRuntimes() { return withCfRuntimeFilter(vm -> true);
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java index 20d71c6..a4b8697 100644 --- a/src/test/java/com/android/tools/r8/TestRuntime.java +++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -32,6 +32,9 @@ JDK17("jdk17", 61), ; + /** This should generally be the latest checked in CF runtime we fully support. */ + private static final CfVm DEFAULT = JDK9; + private final String name; private final int classfileVersion; @@ -152,6 +155,14 @@ return getCheckedInJdk9(); } + public static CfRuntime getDefaultCfRuntime() { + return TestRuntime.getCheckedInJdk(CfVm.DEFAULT); + } + + public static DexRuntime getDefaultDexRuntime() { + return new DexRuntime(DexVm.Version.NEW_DEFAULT); + } + public static List<TestRuntime> getCheckedInRuntimes() { return ImmutableList.<TestRuntime>builder() .addAll(getCheckedInCfRuntimes())
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 033c3f4..11dfa2d 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -252,9 +252,9 @@ ART_6_0_1_HOST(Version.V6_0_1, Kind.HOST), ART_7_0_0_TARGET(Version.V7_0_0, Kind.TARGET), ART_7_0_0_HOST(Version.V7_0_0, Kind.HOST), + ART_DEFAULT(Version.DEFAULT, Kind.HOST), ART_8_1_0_TARGET(Version.V8_1_0, Kind.TARGET), ART_8_1_0_HOST(Version.V8_1_0, Kind.HOST), - ART_DEFAULT(Version.DEFAULT, Kind.HOST), ART_9_0_0_TARGET(Version.V9_0_0, Kind.TARGET), ART_9_0_0_HOST(Version.V9_0_0, Kind.HOST), ART_10_0_0_TARGET(Version.V10_0_0, Kind.TARGET), @@ -273,11 +273,16 @@ V6_0_1("6.0.1"), V7_0_0("7.0.0"), V8_1_0("8.1.0"), + // TODO(b//204855476): Remove DEFAULT. DEFAULT("default"), V9_0_0("9.0.0"), V10_0_0("10.0.0"), V12_0_0("12.0.0"); + /** This should generally be the latest DEX VM fully supported. */ + // TODO(b/204855476): Rename to DEFAULT alias once the checked in VM is removed. + public static final Version NEW_DEFAULT = DEFAULT; + Version(String shortName) { this.shortName = shortName; } @@ -287,7 +292,7 @@ } public boolean isDefault() { - return this == DEFAULT; + return this == NEW_DEFAULT; } public boolean isLatest() { @@ -995,8 +1000,6 @@ public static AndroidApiLevel getMinApiLevelForDexVm(DexVm dexVm) { switch (dexVm.version) { - case DEFAULT: - return AndroidApiLevel.O; case V12_0_0: return AndroidApiLevel.S; case V10_0_0: @@ -1005,6 +1008,8 @@ return AndroidApiLevel.P; case V8_1_0: return AndroidApiLevel.O_MR1; + case DEFAULT: + return AndroidApiLevel.O; case V7_0_0: return AndroidApiLevel.N; case V6_0_1: @@ -1377,21 +1382,24 @@ return pb; } + @Deprecated public static ProcessResult runJava(Class clazz) throws Exception { String main = clazz.getTypeName(); Path path = getClassPathForTests(); return runJava(path, main); } + @Deprecated public static ProcessResult runJava(Path classpath, String... args) throws IOException { return runJava(ImmutableList.of(classpath), args); } + @Deprecated public static ProcessResult runJava(List<Path> classpath, String... args) throws IOException { return runJava(ImmutableList.of(), classpath, args); } - public static ProcessResult runJava(List<String> vmArgs, List<Path> classpath, String... args) + private static ProcessResult runJava(List<String> vmArgs, List<Path> classpath, String... args) throws IOException { return runJava(TestRuntime.getSystemRuntime().asCf(), vmArgs, classpath, args); }
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java index 19e3299..88d53c1 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -47,7 +47,7 @@ Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_api_level.ser"); private static final Path API_DATABASE_AMBIGUOUS = Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_ambiguous.txt"); - private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.R; + private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.S; @Parameters(name = "{0}") public static TestParametersCollection data() { @@ -107,9 +107,9 @@ })); }); // These numbers will change when updating api-versions.xml - assertEquals(4742, parsedApiClasses.size()); - assertEquals(25144, numberOfFields.get()); - assertEquals(38661, numberOfMethods.get()); + assertEquals(5037, parsedApiClasses.size()); + assertEquals(26362, numberOfFields.get()); + assertEquals(40416, numberOfMethods.get()); } @Test
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java index 299a25a..40b57c5 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java
@@ -55,7 +55,7 @@ Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-versions", "api-versions.xml"); private static final Path API_DATABASE_JAR = Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-database", "api-database.jar"); - private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.R; + private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.S; @Parameters(name = "{0}") public static TestParametersCollection data() { @@ -108,9 +108,9 @@ })); }); // These numbers will change when updating api-versions.xml - assertEquals(4742, parsedApiClasses.size()); - assertEquals(25144, numberOfFields.get()); - assertEquals(38661, numberOfMethods.get()); + assertEquals(5037, parsedApiClasses.size()); + assertEquals(26362, numberOfFields.get()); + assertEquals(40416, numberOfMethods.get()); } @Test
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java index 76a4d56..80e9aef 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -26,7 +26,7 @@ public abstract class ApiModelingTestHelper { - static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>> + public static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>> ThrowableConsumer<T> setMockApiLevelForMethod(Method method, AndroidApiLevel apiLevel) { return compilerBuilder -> { compilerBuilder.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java index da83514..c5284c0 100644 --- a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java +++ b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.utils.StringUtils; @@ -80,19 +81,20 @@ @Test public void testWithVersionUpgrade() throws Exception { - testForR8(parameters.getBackend()) - .addProgramClassFileData(getDowngradedClass(Runner.class)) - // Here the main class is not downgraded, thus the output may upgrade to that version. - .addProgramClasses(TestClass.class) - .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(TestClass.class) - // We cannot keep class Runner, as that prohibits getClass optimization. - // Instead disable minification and inlining of the Runner class and method. - .noMinification() - .enableInliningAnnotations() - .enableNeverClassInliningAnnotations() - .run(parameters.getRuntime(), TestClass.class) - .assertSuccessWithOutput(EXPECTED) + R8TestRunResult run = + testForR8(parameters.getBackend()) + .addProgramClassFileData(getDowngradedClass(Runner.class)) + // Here the main class is not downgraded, thus the output may upgrade to that version. + .addProgramClasses(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + // We cannot keep class Runner, as that prohibits getClass optimization. + // Instead disable minification and inlining of the Runner class and method. + .noMinification() + .enableInliningAnnotations() + .enableNeverClassInliningAnnotations() + .run(parameters.getRuntime(), TestClass.class); + run.assertSuccessWithOutput(EXPECTED) .inspect( inspector -> { if (parameters.isCfRuntime()) { @@ -101,7 +103,11 @@ assertTrue(CfVersion.V1_4.isLessThan(cfVersionForRuntime)); // Check that the downgraded class has been bumped to at least SE 1.5 (version 49). CfVersion cfVersionAfterUpgrade = getVersion(inspector, Runner.class); - assertTrue(CfVersion.V1_4.isLessThan(cfVersionAfterUpgrade)); + boolean lessThan = CfVersion.V1_4.isLessThan(cfVersionAfterUpgrade); + if (!lessThan) { + run.disassemble(); + } + assertTrue("Got version: " + cfVersionAfterUpgrade, lessThan); } // Check that the method uses a const class instruction. assertTrue(
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java index d1dc197..cdecb76 100644 --- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java +++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -64,8 +64,8 @@ @BeforeClass public static void beforeAll() throws Exception { if (data().stream().count() > 0) { - r8R8Debug = compileR8(CompilationMode.DEBUG); r8R8Release = compileR8(CompilationMode.RELEASE); + r8R8Debug = compileR8(CompilationMode.DEBUG); } }
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java index 699bb50..407165c 100644 --- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -35,6 +35,7 @@ import com.android.tools.r8.utils.AndroidApp.Builder; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -241,6 +242,7 @@ String main = "classmerging.ConflictInGeneratedNameTest"; Path[] programFiles = new Path[] { + CF_DIR.resolve("NeverPropagateValue.class"), CF_DIR.resolve("ConflictInGeneratedNameTest.class"), CF_DIR.resolve("ConflictInGeneratedNameTest$A.class"), CF_DIR.resolve("ConflictInGeneratedNameTest$B.class") @@ -254,7 +256,6 @@ testForR8(parameters.getBackend()) .addKeepRules(getProguardConfig(EXAMPLE_KEEP)) .addOptionsModification(this::configure) - .addOptionsModification(options -> options.enableValuePropagation = false) .addOptionsModification( options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE)) @@ -1065,15 +1066,17 @@ }; // SimpleInterface cannot be merged into SimpleInterfaceImpl because SimpleInterfaceImpl // is in a different package and is not public. - ImmutableSet<String> preservedClassNames = - ImmutableSet.of( + Set<String> preservedClassNames = + SetUtils.newHashSet( "classmerging.SimpleInterfaceAccessTest", - "classmerging.SimpleInterfaceAccessTest$1", "classmerging.SimpleInterfaceAccessTest$SimpleInterface", "classmerging.SimpleInterfaceAccessTest$OtherSimpleInterface", "classmerging.SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl", "classmerging.pkg.SimpleInterfaceImplRetriever", "classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl"); + if (parameters.isCfRuntime()) { + preservedClassNames.add("classmerging.SimpleInterfaceAccessTest$1"); + } runTest( testForR8(parameters.getBackend()) .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java index baa98a2..5232896 100644 --- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java +++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -1072,8 +1072,7 @@ artCommandBuilder.appendArtOption("-Xcompiler-option"); artCommandBuilder.appendArtOption("--debuggable"); } - if (ToolHelper.getDexVm().getVersion().isNewerThanOrEqual(DexVm.Version.V9_0_0) - && ToolHelper.getDexVm().getVersion() != DexVm.Version.DEFAULT) { + if (ToolHelper.getDexVm().getVersion().isNewerThanOrEqual(DexVm.Version.V9_0_0)) { artCommandBuilder.appendArtOption("-XjdwpProvider:internal"); } if (DEBUG_TESTS && ToolHelper.getDexVm().getVersion().isNewerThan(Version.V4_4_4)) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java index f75e870..4ab080f 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.DiagnosticsHandler; import com.android.tools.r8.L8Command; import com.android.tools.r8.L8TestBuilder; import com.android.tools.r8.LibraryDesugaringTestConfiguration; @@ -22,6 +23,7 @@ import com.android.tools.r8.TestState; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser; import com.android.tools.r8.tracereferences.TraceReferences; @@ -318,6 +320,21 @@ public interface KeepRuleConsumer extends StringConsumer { String get(); + + static KeepRuleConsumer emptyConsumer() { + return new KeepRuleConsumer() { + + @Override + public String get() { + throw new Unreachable(); + } + + @Override + public void accept(String string, DiagnosticsHandler handler) { + // Intentionally empty. + } + }; + } } protected static class ClassFileInfo {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java index e1889a8..f86dacf 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -19,6 +19,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.ThrowingSupplier; import com.android.tools.r8.utils.codeinspector.CheckCastInstructionSubject; @@ -98,15 +99,19 @@ String expectedInstanceOfTypes; if (parameters.getApiLevel().getLevel() >= 26) { expectedInvokeHolders = - ImmutableSet.of( - "java.time.Clock", "java.time.LocalDate", "java.time.ZoneOffset", "java.time.ZoneId"); + SetUtils.newHashSet("java.time.Clock", "java.time.LocalDate", "java.time.ZoneId"); + if (!isR8) { + expectedInvokeHolders.add("java.time.ZoneOffset"); + } expectedCatchGuards = ImmutableSet.of("java.time.format.DateTimeParseException"); expectedCheckCastType = ImmutableSet.of("java.time.ZoneId"); expectedInstanceOfTypes = "java.time.ZoneOffset"; } else { expectedInvokeHolders = - ImmutableSet.of( - "j$.time.Clock", "j$.time.LocalDate", "j$.time.ZoneOffset", "j$.time.ZoneId"); + SetUtils.newHashSet("j$.time.Clock", "j$.time.LocalDate", "j$.time.ZoneId"); + if (!isR8) { + expectedInvokeHolders.add("j$.time.ZoneOffset"); + } expectedCatchGuards = ImmutableSet.of("j$.time.format.DateTimeParseException"); expectedCheckCastType = ImmutableSet.of("j$.time.ZoneId"); expectedInstanceOfTypes = "j$.time.ZoneOffset";
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramInterfaceWithLibraryMethod.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramInterfaceWithLibraryMethod.java new file mode 100644 index 0000000..089f18a --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramInterfaceWithLibraryMethod.java
@@ -0,0 +1,167 @@ +// Copyright (c) 2021, 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.desugar.desugaredlibrary; + +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.LibraryDesugaringTestConfiguration; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Consumer; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +// See b/204518518. +@RunWith(Parameterized.class) +public class ProgramInterfaceWithLibraryMethod extends DesugaredLibraryTestBase { + + @Parameter(0) + public TestParameters parameters; + + private static final String EXPECTED_RESULT = StringUtils.lines("Hello, world!"); + private static Path CUSTOM_LIB_DEX; + private static Path CUSTOM_LIB_CF; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + } + + @BeforeClass + public static void compileCustomLib() throws Exception { + CUSTOM_LIB_DEX = getStaticTemp().newFolder().toPath().resolve("customLibDex.jar"); + testForD8(getStaticTemp()) + .addProgramClasses(LibraryClass.class) + .setMinApi(AndroidApiLevel.B) + .compile() + .writeToZip(CUSTOM_LIB_DEX); + CUSTOM_LIB_CF = getStaticTemp().newFolder().toPath().resolve("customLibCf.jar"); + ZipBuilder.builder(CUSTOM_LIB_CF) + .addBytes( + DescriptorUtils.getPathFromJavaType(LibraryClass.class), + Files.readAllBytes(ToolHelper.getClassFileForTestClass(LibraryClass.class))) + .build(); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + testForD8() + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P)) + .addLibraryClasses(LibraryClass.class) + .setMinApi(parameters.getApiLevel()) + .addProgramClasses(Executor.class, ProgramInterface.class, ProgramClass.class) + .enableCoreLibraryDesugaring( + LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel())) + .compile() + .addRunClasspathFiles(CUSTOM_LIB_DEX) + .run(parameters.getRuntime(), Executor.class) + .applyIf( + parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N), + r -> r.assertSuccessWithOutput(EXPECTED_RESULT), + r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class)); + } + + @Test + public void testD8CfToCf() throws Exception { + Path jar = + testForD8(Backend.CF) + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P)) + .addLibraryClasses(LibraryClass.class) + .addProgramClasses(Executor.class, ProgramInterface.class, ProgramClass.class) + .setMinApi(parameters.getApiLevel()) + .enableCoreLibraryDesugaring( + LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel())) + .compile() + .writeToZip(); + if (parameters.getRuntime().isDex()) { + testForD8() + .addProgramFiles(jar) + .setMinApi(parameters.getApiLevel()) + .disableDesugaring() + .compile() + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, parameters.getApiLevel()) + .addRunClasspathFiles(CUSTOM_LIB_DEX) + .run(parameters.getRuntime(), Executor.class) + .applyIf( + parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N), + r -> r.assertSuccessWithOutput(EXPECTED_RESULT), + r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class)); + } else { + testForJvm() + .addProgramFiles(jar) + .addRunClasspathFiles(getDesugaredLibraryInCF(parameters, options -> {})) + .addRunClasspathFiles(CUSTOM_LIB_CF) + .run(parameters.getRuntime(), Executor.class) + .applyIf( + parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N), + r -> r.assertSuccessWithOutput(EXPECTED_RESULT), + r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class)); + } + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P)) + .addLibraryClasses(LibraryClass.class) + .setMinApi(parameters.getApiLevel()) + .addProgramClasses(Executor.class, ProgramInterface.class, ProgramClass.class) + .addKeepMainRule(Executor.class) + .enableCoreLibraryDesugaring( + LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel())) + .compile() + .addRunClasspathFiles(parameters.isDexRuntime() ? CUSTOM_LIB_DEX : CUSTOM_LIB_CF) + .run(parameters.getRuntime(), Executor.class) + .applyIf( + parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N), + r -> r.assertSuccessWithOutput(EXPECTED_RESULT), + r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class)); + } + + static class Executor { + + public static void main(String[] args) { + invoke(new ProgramClass()); + } + + static void invoke(ProgramInterface i) { + i.methodTakingConsumer(null); + } + } + + interface ProgramInterface { + void methodTakingConsumer(Consumer<String> consumer); + } + + static class ProgramClass extends LibraryClass implements ProgramInterface { + // TODO(b/204518518): Adding this forwarding method fixes the issue. + // public void methodTakingConsumer(Consumer<String> consumer) { + // super.methodTakingConsumer(consumer); + // } + } + + // This class will be put at compilation time as library and on the runtime class path. + // This class is convenient for easy testing. Each method plays the role of methods in the + // platform APIs for which argument/return values need conversion. + static class LibraryClass { + + public void methodTakingConsumer(Consumer<String> consumer) { + System.out.println("Hello, world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java index 58cf498..832e72d 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
@@ -154,6 +154,7 @@ .addOptionsModification(options -> options.enableNestReduction = false) .addProgramFiles(JAR) .addInliningAnnotations() + .addMemberValuePropagationAnnotations() .setMinApi(minApi) .compile(); }
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java index bc389c9..735e59c 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -136,7 +136,6 @@ .addOptionsModification( options -> { // Disable optimizations else additional classes are removed since they become unused. - options.enableValuePropagation = false; options.enableClassInlining = false; }) .addProgramFiles(classesMatching(outerNestName))
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java index 3e8fc2a..c3806df 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
@@ -93,7 +93,6 @@ options -> { // Disable optimizations else additional classes are removed since they become // unused. - options.enableValuePropagation = false; options.enableClassInlining = false; options.enableNestReduction = false; })
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java index 726f391..8eb08f4 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
@@ -58,11 +58,11 @@ .noMinification() .addOptionsModification( options -> { - options.enableValuePropagation = false; options.enableClassInlining = false; options.enableVerticalClassMerging = false; }) .enableInliningAnnotations() + .enableMemberValuePropagationAnnotations() .addProgramFiles(toCompile) .compile() .inspect(this::assertMethodsInlined)
diff --git a/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java b/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java index b1f2693..2f57c56 100644 --- a/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java +++ b/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
@@ -16,7 +16,40 @@ } public static void printArray(int[] array) { - for (int i : array) System.out.println(i); + System.out.print("["); + if (array.length > 0) { + System.out.print(array[0]); + for (int i = 1; i < array.length; i++) { + System.out.print(","); + System.out.print(i); + } + } + System.out.println("]"); + } + + static void printIntermediate(boolean b) { + System.out.print(b); + System.out.print(","); + } + + static void printIntermediate(double d) { + System.out.print(d); + System.out.print(","); + } + + static void printIntermediate(float f) { + System.out.print(f); + System.out.print(","); + } + + static void printIntermediate(int i) { + System.out.print(i); + System.out.print(","); + } + + static void printIntermediate(long l) { + System.out.print(l); + System.out.print(","); } public static void test() { @@ -88,24 +121,24 @@ int[][][][] i4 = new int[n][n][n][n]; int[][][][][] i5 = new int[n][n][n][n][n]; int[][][][][][] i6 = new int[n][n][n][n][n][n]; - System.out.println(i2.length); - System.out.println(i3.length); - System.out.println(i4.length); - System.out.println(i5.length); + printIntermediate(i2.length); + printIntermediate(i3.length); + printIntermediate(i4.length); + printIntermediate(i5.length); System.out.println(i6.length); } public static void newMultiDimensionalArrays2(int n1, int n2, int n3, int n4, int n5, int n6) { int[][] i2 = new int[n1][n2]; - System.out.println(i2.length); + printIntermediate(i2.length); int[][][] i3 = new int[n1][n2][n3]; - System.out.println(i3.length); + printIntermediate(i3.length); int[][][][] i4 = new int[n1][n2][n3][n4]; - System.out.println(i4.length); + printIntermediate(i4.length); int[][][][][] i5 = new int[n1][n2][n3][n4][n5]; - System.out.println(i5.length); + printIntermediate(i5.length); int[][][][][][] i6 = new int[n1][n2][n3][n4][n5][n6]; - System.out.println(i6.length); + printIntermediate(i6.length); int[][][][][][] i7 = new int[n1][n2][n1][n4][n5][n1]; System.out.println(i7.length); } @@ -115,9 +148,9 @@ int[][][][] i4 = new int[n][n][][]; int[][][][][][][] i7 = new int[n][n][n][n][n][n][]; int[][][][][][][][] i8 = new int[n][n][n][n][n][n][][]; - System.out.println(i3.length); - System.out.println(i4.length); - System.out.println(i7.length); + printIntermediate(i3.length); + printIntermediate(i4.length); + printIntermediate(i7.length); System.out.println(i8.length); } @@ -130,21 +163,21 @@ float[][] a6 = new float[11][12]; double[][] a7 = new double[13][14]; A[][] a8 = new A[15][16]; - System.out.println(a1[0].length); - System.out.println(a2[0].length); - System.out.println(a3[0].length); - System.out.println(a4[0].length); - System.out.println(a5[0].length); - System.out.println(a6[0].length); - System.out.println(a7[0].length); - System.out.println(a8[0].length); - System.out.println(a1[0][0]); - System.out.println(a2[0][0]); - System.out.println(a3[0][0]); - System.out.println(a4[0][0]); - System.out.println(a5[0][0]); - System.out.println(a6[0][0]); - System.out.println(a7[0][0]); + printIntermediate(a1[0].length); + printIntermediate(a2[0].length); + printIntermediate(a3[0].length); + printIntermediate(a4[0].length); + printIntermediate(a5[0].length); + printIntermediate(a6[0].length); + printIntermediate(a7[0].length); + printIntermediate(a8[0].length); + printIntermediate(a1[0][0]); + printIntermediate(a2[0][0]); + printIntermediate(a3[0][0]); + printIntermediate(a4[0][0]); + printIntermediate(a5[0][0]); + printIntermediate(a6[0][0]); + printIntermediate(a7[0][0]); System.out.println(a8[0][0]); }
diff --git a/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java b/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java index 676f7dd..d980c97 100644 --- a/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java +++ b/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java
@@ -8,9 +8,8 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestRuntime; +import com.google.common.collect.ImmutableList; import java.util.List; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -23,7 +22,39 @@ private final TestParameters parameters; private final CompilationMode mode; - private static String referenceOut; + private static final List<String> EXPECTED = + ImmutableList.of( + "[]", + "[0]", + "[0,1]", + "[0,1,2]", + "[0,1,2,3]", + "[0,1,2,3,4]", + "[0,1,2,3,4,5]", + "[0,1,2,3,4,5,6]", + "[0,1,2,3,4,5,6,7]", + "[0,1,2,3,4,5,6,7]", + "[]", + "[0]", + "[0,1]", + "[0,1,2]", + "[0,1,2,3]", + "[0,1,2,3,4]", + "[0,1,2,3,4,5]", + "[0,1,2,3,4,5,6,7,8,9,10]", + "[]", + "[0]", + "[0,1]", + "[0,1,2]", + "[0,1,2,3]", + "[0,1,2,3,4]", + "[0,1,2,3,4,5]", + "[0,1,2,3,4,5,6]", + "[0,1,2,3,4,5,6,7]", + "6,6,6,6,6", + "1,1,1,1,1,1", + "8,8,8,8", + "2,4,6,8,10,12,14,16,false,0,0,0,0,0.0,0.0,null"); @Parameterized.Parameters(name = "{0}, {1}") public static List<Object[]> data() { @@ -36,14 +67,13 @@ this.mode = mode; } - @BeforeClass - public static void runReference() throws Exception { - referenceOut = - testForJvm(getStaticTemp()) - .addProgramClassesAndInnerClasses(CLASS) - .run(TestRuntime.getDefaultJavaRuntime(), CLASS) - .assertSuccess() - .getStdOut(); + @Test + public void runReference() throws Exception { + assumeTrue(parameters.isCfRuntime() && mode == CompilationMode.DEBUG); + testForJvm(getStaticTemp()) + .addProgramClassesAndInnerClasses(CLASS) + .run(parameters.getRuntime(), CLASS) + .assertSuccessWithOutputLines(EXPECTED); } @Test @@ -54,7 +84,7 @@ .setMinApi(parameters.getApiLevel()) .setMode(mode) .run(parameters.getRuntime(), CLASS) - .assertSuccessWithOutput(referenceOut); + .assertSuccessWithOutputLines(EXPECTED); } @Test @@ -65,6 +95,6 @@ .setMinApi(parameters.getApiLevel()) .setMode(mode) .run(parameters.getRuntime(), CLASS) - .assertSuccessWithOutput(referenceOut); + .assertSuccessWithOutputLines(EXPECTED); } }
diff --git a/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java b/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java index 4f71913..9b85698 100644 --- a/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java +++ b/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.DexDebugEvent.AdvancePC; import com.android.tools.r8.graph.DexDebugEvent.Default; -import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SourcePosition; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -133,9 +133,9 @@ List<DexDebugEvent> events = new ArrayList<>(); DexDebugEventBuilder.emitAdvancementEvents( pc, - Position.builder().setLine(line).setMethod(method).build(), + SourcePosition.builder().setLine(line).setMethod(method).build(), nextPc, - Position.builder().setLine(nextLine).setMethod(method).build(), + SourcePosition.builder().setLine(nextLine).setMethod(method).build(), events, factory, false);
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java index 3f5ad48..97d8b7a 100644 --- a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java +++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ThrowableConsumer; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.AssertionUtils; import com.google.common.collect.Sets; @@ -39,10 +38,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters() - .withDexRuntime(Version.DEFAULT) - .withApiLevel(AndroidApiLevel.L) - .build(); + return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.L).build(); } public GMSCoreLatestTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java index 4c9b99f..9fe8e54 100644 --- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java +++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -17,7 +17,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ThrowableConsumer; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.google.common.collect.Sets; import java.nio.file.Path; @@ -42,10 +41,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters() - .withDexRuntime(Version.DEFAULT) - .withApiLevel(AndroidApiLevel.L) - .build(); + return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.L).build(); } public GMSCoreV10Test(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/internal/Regression127524985.java b/src/test/java/com/android/tools/r8/internal/Regression127524985.java index e5e6dcd..a39814c 100644 --- a/src/test/java/com/android/tools/r8/internal/Regression127524985.java +++ b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.nio.file.Paths; @@ -27,11 +26,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters() - .withCfRuntimes() - .withDexRuntime(Version.DEFAULT) - .withAllApiLevels() - .build(); + return getTestParameters().withAllRuntimesAndApiLevels().build(); } private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java index b7ccb02..f1f134f 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java
@@ -20,13 +20,11 @@ import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.ResourceException; -import com.android.tools.r8.StringConsumer; import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ThrowableConsumer; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -54,10 +52,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters() - .withDexRuntime(Version.DEFAULT) - .withApiLevel(AndroidApiLevel.L) - .build(); + return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.L).build(); } public YouTubeV1612Test(TestParameters parameters) { @@ -85,7 +80,7 @@ public void testProtoRewriting() throws Exception { assumeTrue(shouldRunSlowTests()); - StringConsumer keepRuleConsumer = StringConsumer.emptyConsumer(); + KeepRuleConsumer keepRuleConsumer = KeepRuleConsumer.emptyConsumer(); R8TestCompileResult r8CompileResult = compileApplicationWithR8( keepRuleConsumer, @@ -105,13 +100,13 @@ reporter.failIfPendingErrors(); } - private R8TestCompileResult compileApplicationWithR8(StringConsumer keepRuleConsumer) + private R8TestCompileResult compileApplicationWithR8(KeepRuleConsumer keepRuleConsumer) throws IOException, CompilationFailedException { return compileApplicationWithR8(keepRuleConsumer, ThrowableConsumer.empty()); } private R8TestCompileResult compileApplicationWithR8( - StringConsumer keepRuleConsumer, ThrowableConsumer<R8FullTestBuilder> configuration) + KeepRuleConsumer keepRuleConsumer, ThrowableConsumer<R8FullTestBuilder> configuration) throws IOException, CompilationFailedException { return testForR8(parameters.getBackend()) .addProgramFiles(getProgramFiles())
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java index 2030032..fc4fba0 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
@@ -20,13 +20,11 @@ import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.ResourceException; -import com.android.tools.r8.StringConsumer; import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ThrowableConsumer; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -54,10 +52,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters() - .withDexRuntime(Version.DEFAULT) - .withApiLevel(AndroidApiLevel.L) - .build(); + return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.L).build(); } public YouTubeV1620Test(TestParameters parameters) { @@ -85,7 +80,7 @@ public void testProtoRewriting() throws Exception { assumeTrue(shouldRunSlowTests()); - StringConsumer keepRuleConsumer = StringConsumer.emptyConsumer(); + KeepRuleConsumer keepRuleConsumer = KeepRuleConsumer.emptyConsumer(); R8TestCompileResult r8CompileResult = compileApplicationWithR8( keepRuleConsumer, @@ -105,13 +100,13 @@ reporter.failIfPendingErrors(); } - private R8TestCompileResult compileApplicationWithR8(StringConsumer keepRuleConsumer) + private R8TestCompileResult compileApplicationWithR8(KeepRuleConsumer keepRuleConsumer) throws IOException, CompilationFailedException { return compileApplicationWithR8(keepRuleConsumer, ThrowableConsumer.empty()); } private R8TestCompileResult compileApplicationWithR8( - StringConsumer keepRuleConsumer, ThrowableConsumer<R8FullTestBuilder> configuration) + KeepRuleConsumer keepRuleConsumer, ThrowableConsumer<R8FullTestBuilder> configuration) throws IOException, CompilationFailedException { return testForR8(parameters.getBackend()) .addProgramFiles(getProgramFiles())
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java index d6a04df..c0f2478 100644 --- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java +++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.google.common.collect.ImmutableList; @@ -35,7 +34,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withDexRuntime(Version.DEFAULT).withAllApiLevels().build(); + return getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(); } public Proto2BuilderOnlyReferencedFromDynamicMethodTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java index f8ae6c5..3f9e228 100644 --- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java +++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.utils.StringUtils; @@ -58,7 +57,7 @@ "proto2.BuilderWithProtoSetterTestClass", "proto2.BuilderWithReusedSettersTestClass", "proto2.HasFlaggedOffExtensionBuilderTestClass")), - getTestParameters().withDexRuntime(Version.DEFAULT).withAllApiLevels().build()); + getTestParameters().withDefaultDexRuntime().withAllApiLevels().build()); } public Proto2BuilderShrinkingTest(List<String> mains, TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java index 5eda02a..fcc6f54 100644 --- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java +++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -65,7 +64,7 @@ return buildParameters( BooleanUtils.values(), BooleanUtils.values(), - getTestParameters().withDexRuntime(Version.DEFAULT).withAllApiLevels().build()); + getTestParameters().withDefaultDexRuntime().withAllApiLevels().build()); } public Proto2ShrinkingTest(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java index f641510..e2b8e51 100644 --- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java +++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -13,7 +13,6 @@ import static org.junit.Assert.assertEquals; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -42,7 +41,7 @@ return buildParameters( BooleanUtils.values(), BooleanUtils.values(), - getTestParameters().withDexRuntime(Version.DEFAULT).withAllApiLevels().build()); + getTestParameters().withDefaultDexRuntime().withAllApiLevels().build()); } public Proto3ShrinkingTest(
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java index 252b11d..82bc605 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -25,6 +25,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DirectMappedDexApplication; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.MethodProcessorWithWave; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; @@ -85,7 +86,7 @@ OptimizationFeedbackMock feedback = new OptimizationFeedbackMock(); FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis(); FieldAccessAnalysis fieldAccessAnalysis = - new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis); + new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis, null); DexProgramClass clazz = appView.appInfo().classes().iterator().next(); assertEquals(TestClass.class.getTypeName(), clazz.type.toSourceString()); @@ -93,7 +94,8 @@ clazz.forEachProgramMethod( method -> { IRCode code = method.buildIR(appView); - fieldAccessAnalysis.recordFieldAccesses(code, feedback, new PrimaryMethodProcessorMock()); + fieldAccessAnalysis.recordFieldAccesses( + code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock()); }); int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java index 2485f20..e4ee9bc 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -19,6 +19,7 @@ import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator; @@ -75,7 +76,7 @@ block.setNumber(basicBlockNumberGenerator.next()); IRMetadata metadata = IRMetadata.unknown(); - Position position = Position.testingPosition(); + Position position = SyntheticPosition.builder().disableMethodCheck().setLine(0).build(); Value v3 = new Value(3, TypeElement.getLong(), null); v3.setNeedsRegister(true);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java index 9fc16f8..868036c 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -24,6 +24,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.ir.code.Throw; import com.android.tools.r8.ir.code.Value; @@ -63,7 +64,7 @@ // block2: // return final NumberGenerator basicBlockNumberGenerator = new NumberGenerator(); - Position position = Position.testingPosition(); + Position position = SyntheticPosition.builder().setLine(0).disableMethodCheck().build(); BasicBlock block2 = new BasicBlock(); BasicBlock block0 = BasicBlock.createGotoBlock(basicBlockNumberGenerator.next(), position, metadata, block2); @@ -127,7 +128,7 @@ // block3: // goto block3 final NumberGenerator basicBlockNumberGenerator = new NumberGenerator(); - Position position = Position.testingPosition(); + Position position = SyntheticPosition.builder().setLine(0).disableMethodCheck().build(); BasicBlock block0 = new BasicBlock(); block0.setNumber(basicBlockNumberGenerator.next()); BasicBlock block2 = new BasicBlock();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java index be5c5a2..c9cc17b 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
@@ -30,7 +30,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimes().build(); + return getTestParameters().withAllRuntimesAndApiLevels().build(); } public B135918413(TestParameters parameters) { @@ -43,7 +43,7 @@ .addInnerClasses(B135918413.class) .addKeepMainRule(TestClass.class) .enableInliningAnnotations() - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .compile() .inspect(this::inspect) .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java new file mode 100644 index 0000000..0c4e336 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
@@ -0,0 +1,102 @@ +// Copyright (c) 2021, 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.optimize.membervaluepropagation; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ir.optimize.membervaluepropagation.FieldReadForWriteTest.R.anim; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FieldReadForWriteTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection parameters() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .enableNoHorizontalClassMergingAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + ClassSubject animClassSubject = inspector.clazz(anim.class); + if (parameters.isCfRuntime()) { + assertThat(animClassSubject, isPresent()); + assertThat(animClassSubject.uniqueFieldWithName("abc_fade_in"), isPresent()); + } else { + assertThat(animClassSubject, isAbsent()); + } + }); + } + + static class Main { + + public static void main(String[] args) { + int packageId = args.length; + R.onResourcesLoaded(packageId); + } + } + + @NoHorizontalClassMerging + static class R { + + static boolean sResourcesDidLoad; + + @NoHorizontalClassMerging + static class anim { + public static int abc_fade_in = 0x7f010039; + } + + static void onResourcesLoaded(int packageId) { + if (sResourcesDidLoad) { + return; + } + sResourcesDidLoad = true; + int packageIdTransform = (packageId ^ 0x7f) << 24; + onResourcesLoadedAnim(packageIdTransform); + } + + static void onResourcesLoadedAnim(int packageIdTransform) { + // Arithmethic binop (add, div, mul, rem, sub). + anim.abc_fade_in += packageIdTransform; + anim.abc_fade_in /= packageIdTransform; + anim.abc_fade_in *= packageIdTransform; + anim.abc_fade_in %= packageIdTransform; + anim.abc_fade_in -= packageIdTransform; + // Logical binop (and, or, shl, shr, ush, xor). + anim.abc_fade_in &= packageIdTransform; + anim.abc_fade_in |= packageIdTransform; + anim.abc_fade_in <<= packageIdTransform; + anim.abc_fade_in >>= packageIdTransform; + anim.abc_fade_in >>>= packageIdTransform; + anim.abc_fade_in ^= packageIdTransform; + // Unop (number conversion, but also: inc, neg, not). + anim.abc_fade_in = (int) ((long) anim.abc_fade_in); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java index faf62b7..8943139 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import java.util.Deque; import java.util.Optional; import org.junit.Test; @@ -46,7 +45,7 @@ .assertSuccessWithOutputLines("42"); } - private void waveModifier(Deque<SortedProgramMethodSet> waves) { + private void waveModifier(Deque<ProgramMethodSet> waves) { ProgramMethodSet initialWave = waves.getFirst(); Optional<ProgramMethod> printFieldMethod = initialWave.stream() @@ -55,7 +54,7 @@ assertTrue(printFieldMethod.isPresent()); initialWave.remove(printFieldMethod.get().getDefinition()); - SortedProgramMethodSet lastWave = SortedProgramMethodSet.create(); + ProgramMethodSet lastWave = ProgramMethodSet.create(); lastWave.add(printFieldMethod.get()); waves.addLast(lastWave); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java index f5b13eb..b493f1d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
@@ -18,7 +18,7 @@ import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; -import com.android.tools.r8.utils.collections.SortedProgramMethodSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.function.Function; import java.util.function.Predicate; import org.junit.Test; @@ -49,7 +49,7 @@ options -> { options.testing.waveModifier = (waves) -> { - Function<String, Predicate<SortedProgramMethodSet>> wavePredicate = + Function<String, Predicate<ProgramMethodSet>> wavePredicate = methodName -> wave -> wave.stream()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java index 1979d34..8a540ba 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.ir.optimize.outliner; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoHorizontalClassMerging; @@ -75,6 +74,7 @@ HorizontallyMergedClassesInspector::assertNoClassesMerged) .enableInliningAnnotations() .setMinApi(parameters.getApiLevel()) + .enableExperimentalMapFileVersion() .compile() .run( parameters.getRuntime(), @@ -90,12 +90,7 @@ // and one for // new ArrayStoreException("Foo") assertEquals(5, inspector.allClasses().size()); - // TODO(b/201397823): Should always be equal. - if (throwInFirstOutline && !throwOnFirstCall) { - assertNotEquals(expectedStackTrace, stackTrace); - } else { - assertEquals(expectedStackTrace, stackTrace); - } + assertEquals(expectedStackTrace, stackTrace); }); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineWithInlineMappingInformationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineWithInlineMappingInformationTest.java new file mode 100644 index 0000000..fddf01c --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineWithInlineMappingInformationTest.java
@@ -0,0 +1,152 @@ +// Copyright (c) 2021, 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.optimize.outliner; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.naming.retrace.StackTrace; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +@RunWith(Parameterized.class) +public class OutlineWithInlineMappingInformationTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public boolean throwInFirstOutline; + + @Parameter(2) + public boolean throwOnFirstCall; + + @Parameterized.Parameters(name = "{0}, throwInFirstOutline: {1}, throwOnFirstCall: {2}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), + BooleanUtils.values(), + BooleanUtils.values()); + } + + StackTrace expectedStackTrace; + + @Before + public void setup() throws Exception { + expectedStackTrace = + testForRuntime(parameters) + .addProgramClasses(TestClass.class, TestClass2.class, Greeter.class) + .run( + parameters.getRuntime(), + TestClass.class, + throwInFirstOutline ? "0" : "1", + throwOnFirstCall ? "0" : "1") + .assertFailureWithErrorThatThrows(ArrayStoreException.class) + .getStackTrace(); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(TestClass.class, TestClass2.class, Greeter.class) + .addKeepMainRule(TestClass.class) + .addOptionsModification( + options -> { + options.outline.threshold = 2; + options.outline.minSize = 2; + }) + .addKeepAttributeLineNumberTable() + .addKeepAttributeSourceFile() + .enableNoHorizontalClassMergingAnnotations() + .noHorizontalClassMergingOfSynthetics() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .enableExperimentalMapFileVersion() + .compile() + .run( + parameters.getRuntime(), + TestClass.class, + throwInFirstOutline ? "0" : "1", + throwOnFirstCall ? "0" : "1") + .assertFailureWithErrorThatThrows(ArrayStoreException.class) + .inspectStackTrace( + (stackTrace, inspector) -> { + // Two outlines are created, one for + // Greeter.throwExceptionFirst(); + // Greeter.throwExceptionSecond(); + // and one for + // new ArrayStoreException("Foo") + assertEquals(5, inspector.allClasses().size()); + assertEquals(expectedStackTrace, stackTrace); + }); + } + + @NoHorizontalClassMerging + static class TestClass { + + public static boolean shouldThrowInGreeter; + public static boolean throwOnFirst; + + public static void main(String... args) { + shouldThrowInGreeter = args[0].equals("0"); + throwOnFirst = args[1].equals("0"); + greet(); + shouldThrowInGreeter = true; + TestClass2.greet(); + } + + @NeverInline + static void greet() { + Greeter.throwExceptionFirst(); + inlinee(); + } + + static void inlinee() { + Greeter.throwExceptionSecond(); + } + } + + @NoHorizontalClassMerging + static class TestClass2 { + + @NeverInline + static void greet() { + // Keep on same line + inlinee(); Greeter.throwExceptionSecond(); + } + + static void inlinee() { + Greeter.throwExceptionFirst(); + } + } + + @NoHorizontalClassMerging + public static class Greeter { + + @NeverInline + public static void throwExceptionFirst() { + if (TestClass.shouldThrowInGreeter && TestClass.throwOnFirst) { + throw new ArrayStoreException("Foo"); + } + } + + @NeverInline + public static void throwExceptionSecond() { + if (TestClass.shouldThrowInGreeter && !TestClass.throwOnFirst) { + throw new ArrayStoreException("Foo"); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java index 80863b5..441b85f 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -53,10 +53,12 @@ import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Phi.RegisterReadType; import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.ValueTypeConstraint; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.SourceCode; @@ -870,7 +872,7 @@ ProgramMethod programMethod = new ProgramMethod(programClass, method); IRCode ir = code.buildIR(programMethod, appView, Origin.unknown()); RegisterAllocator allocator = new LinearScanRegisterAllocator(appView, ir); - method.setCode(ir, allocator, appView); + method.setCode(ir, BytecodeMetadataProvider.empty(), allocator, appView); directMethods[i] = method; } programClass.getMethodCollection().addDirectMethods(Arrays.asList(directMethods)); @@ -902,7 +904,12 @@ private final Position position; public ReturnVoidCode(DexMethod method, Position callerPosition) { - this.position = Position.synthetic(0, method, callerPosition); + this.position = + SyntheticPosition.builder() + .setLine(0) + .setMethod(method) + .setCallerPosition(callerPosition) + .build(); } @Override
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java b/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java index 7df0c10..29a2fce 100644 --- a/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java
@@ -4,9 +4,17 @@ package com.android.tools.r8.memberrebinding; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static java.util.Collections.emptyList; +import static org.hamcrest.MatcherAssert.assertThat; + import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.MethodSubject; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.nio.file.Path; @@ -68,15 +76,54 @@ private void test(Path compileTimeLibrary, Path runtimeLibrary) throws Exception { testForR8(parameters.getBackend()) - .addProgramClasses(TestClass.class) - .addKeepClassAndMembersRules(TestClass.class) + .addProgramClasses(Main.class) + .addKeepClassAndMembersRules(Main.class) .addLibraryFiles(compileTimeLibrary) .addDefaultRuntimeLibrary(parameters) + .apply(setMockApiLevelForMethod(LibraryI.class.getDeclaredMethod("m"), AndroidApiLevel.B)) + .apply(setMockApiLevelForMethod(LibraryC.class.getDeclaredMethod("m"), AndroidApiLevel.B)) + .apply(setMockApiLevelForMethod(LibraryA.class.getDeclaredMethod("m"), AndroidApiLevel.N)) .setMinApi(parameters.getApiLevel()) .compile() + .inspect( + inspector -> { + MethodSubject testMethodSubject = + inspector.clazz(Main.class).uniqueMethodWithName("test"); + assertThat(testMethodSubject, isPresent()); + assertThat( + testMethodSubject, + invokesMethod( + "int", + getExpectedMemberRebindingTarget(compileTimeLibrary).getTypeName(), + "m", + emptyList())); + }) .addRunClasspathFiles(buildOnDexRuntime(parameters, runtimeLibrary)) - .run(parameters.getRuntime(), TestClass.class) - .assertSuccessWithOutputLines("42"); + .run(parameters.getRuntime(), Main.class) + .applyIf( + // Compiling to an API level above the API where LibraryA.m() was defined with a new + // android.jar, and running this on an older runtime (correctly) results in a NSME. + parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N) + && compileTimeLibrary == newRuntimeJar + && runtimeLibrary == oldRuntimeJar, + runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class), + runResult -> runResult.assertSuccessWithOutputLines("42")); + } + + private Class<?> getExpectedMemberRebindingTarget(Path compileTimeLibrary) { + if (compileTimeLibrary == newRuntimeJar) { + // If we are compiling to a new runtime with a new android.jar, we should rebind to LibraryA. + if (parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) { + return LibraryA.class; + } + // If we are compiling to an old runtime with a new android.jar, we should rebind to LibraryB. + return LibraryB.class; + } + // Otherwise, we are compiling to an old android.jar, in which case we should rebind to + // LibraryI. + return LibraryI.class; } private static Path createJar(Collection<Class<?>> programClasses, byte[]... programClassFileData) @@ -97,33 +144,33 @@ .transform(); } - static class TestClass { + public static class Main { public static void main(String[] args) { test(new LibraryC()); } - static void test(LibraryB b) { + private static void test(LibraryB b) { System.out.println(b.m()); } } - interface LibraryI { + public interface LibraryI { int m(); } - static class LibraryA { + public static class LibraryA { - // Added in API level X, so we can't rebind to this in APIs < X. + // Added in API N, so we can't rebind to this in APIs < N. public int m() { return 42; } } - abstract static class LibraryB extends LibraryA implements LibraryI {} + public abstract static class LibraryB extends LibraryA implements LibraryI {} - static class LibraryC extends LibraryB { + public static class LibraryC extends LibraryB { @Override public int m() {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java index 48c9501..605ddf7 100644 --- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -160,9 +160,9 @@ assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass")); assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass")); // For the next three - test that we re-bind to library methods (holder is java.util.ArrayList). - assertTrue(iterator.next().holder().is("java.util.ArrayList")); - assertTrue(iterator.next().holder().is("java.util.ArrayList")); - assertTrue(iterator.next().holder().is("java.util.ArrayList")); + assertTrue(iterator.next().holder().is("java.util.AbstractList")); + assertTrue(iterator.next().holder().is("java.util.AbstractList")); + assertTrue(iterator.next().holder().is("java.util.AbstractList")); assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClassInTheMiddle")); assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClassInTheMiddle")); // For the next three - test that we re-bind to the lowest library class.
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java index 90617f7..cc7900e 100644 --- a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java +++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.codeinspector.MethodSubject; import it.unimi.dsi.fastutil.ints.IntArraySet; import it.unimi.dsi.fastutil.ints.IntSet; @@ -25,7 +24,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withDexRuntime(Version.DEFAULT).withAllApiLevels().build(); + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); } public DebuginfoForInlineFrameRegressionTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java index 7138094..01d1c30 100644 --- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java +++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -62,14 +62,13 @@ List<FoundClassSubject> classes = inspector.allClasses(); - // Check that the synthetic class is still present. - assertEquals(3, classes.size()); + // Check that the synthetic class is still present when generating class files. + assertEquals(parameters.isCfRuntime() ? 3 : 2, classes.size()); assertEquals( - 1, + parameters.isCfRuntime(), classes.stream() .map(FoundClassSubject::getOriginalName) - .filter(name -> name.endsWith("$1")) - .count()); + .anyMatch(name -> name.endsWith("$1"))); } @Test @@ -94,13 +93,12 @@ List<FoundClassSubject> classes = inspector.allClasses(); - // Check that the synthetic class is still present. - assertEquals(3, classes.size()); + // The synthetic class is still present when generating class files. + assertEquals(parameters.isCfRuntime() ? 3 : 2, classes.size()); assertEquals( - 1, + parameters.isCfRuntime(), classes.stream() .map(FoundClassSubject::getOriginalName) - .filter(name -> name.endsWith("$1")) - .count()); + .anyMatch(name -> name.endsWith("$1"))); } }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java index af4995f..19138f1 100644 --- a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java +++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; import com.android.tools.r8.TestParameters; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,6 +54,7 @@ .addDontWarn(MissingSuperType.class) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() + .enableNoHorizontalClassMergingAnnotations() .compile() .inspect( inspector -> { @@ -77,6 +79,7 @@ } @NeverClassInline + @NoHorizontalClassMerging public static class ClassWithSuperCall extends MissingSuperType { @Override @@ -88,6 +91,7 @@ } @NeverClassInline + @NoHorizontalClassMerging public static class ClassWithoutSuperCall extends MissingSuperType { @Override
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java index 2787932..b80febc 100644 --- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java +++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java
@@ -28,7 +28,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimes().build(); + return getTestParameters().withAllRuntimesAndApiLevels().build(); } private final TestParameters parameters; @@ -51,7 +51,7 @@ " void info(...);", "}") .noMinification() - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutput(EXPECTED) .inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java index 9afdd92..0468d27 100644 --- a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java +++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
@@ -14,9 +14,7 @@ import com.android.tools.r8.TestCompileResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.TestShrinkerBuilder; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.Subject; import org.hamcrest.Matcher; @@ -32,7 +30,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withCfRuntime(CfVm.JDK9).withDexRuntime(Version.DEFAULT).build(); + return getTestParameters().withDefaultCfRuntime().withDefaultDexRuntime().build(); } public NegatedKeepRulesTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java index 242d5d2..042bec1 100644 --- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java +++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -1649,6 +1649,7 @@ @Test public void b113145696_superClassUseFirst() throws Exception { SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME); + builder.addDefaultConstructor(); // Code where the outline argument is first used as java.lang.Object and afterwards // java.util.ArrayList. @@ -1727,6 +1728,7 @@ @Test public void b113145696_superInterfaceUseFirst() throws Exception { SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME); + builder.addDefaultConstructor(); List<String> codeToOutline = ImmutableList.of( " invoke-interface { v1 }, Ljava/lang/Iterable;->iterator()Ljava/util/Iterator;", @@ -1801,6 +1803,7 @@ @Test public void b113145696_interfaceUseFirst() throws Exception { SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME); + builder.addDefaultConstructor(); // Code where the outline argument is first used as java.lang.Iterable and afterwards // java.util.ArrayList. @@ -1877,6 +1880,7 @@ @Test public void b113145696_classUseFirst() throws Exception { SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME); + builder.addDefaultConstructor(); // Code where the outline argument is first used as java.lang.Iterable and afterwards // java.util.ArrayList.
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java index 7c882b4..40fcc0f 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -258,7 +258,7 @@ new Object2IntOpenHashMap<>(code.getInstructions().size()); for (CfInstruction insn : code.getInstructions()) { if (insn instanceof CfPosition) { - currentLine = ((CfPosition) insn).getPosition().line; + currentLine = ((CfPosition) insn).getPosition().getLine(); } if (currentLine != -1) { lineNumberTable.put(new CfInstructionSubject(insn, this), currentLine);
diff --git a/third_party/android_jar/api-versions.tar.gz.sha1 b/third_party/android_jar/api-versions.tar.gz.sha1 index 849abf3..0c98c4f 100644 --- a/third_party/android_jar/api-versions.tar.gz.sha1 +++ b/third_party/android_jar/api-versions.tar.gz.sha1
@@ -1 +1 @@ -021b38c29b9be789f4312f95543a3f08baf66a19 \ No newline at end of file +788aea6588610917a910af5d0e658fb13e8f7baf \ No newline at end of file
diff --git a/third_party/chrome/chrome_180917_ffbaa8.tar.gz.sha1 b/third_party/chrome/chrome_180917_ffbaa8.tar.gz.sha1 index 3ac92ca..b716149 100644 --- a/third_party/chrome/chrome_180917_ffbaa8.tar.gz.sha1 +++ b/third_party/chrome/chrome_180917_ffbaa8.tar.gz.sha1
@@ -1 +1 @@ -c32dc7f70022946c04bfe9316e35e60e8d2ee0cb \ No newline at end of file +ac82e65cc398a3dff8f3fccf43f8a21ebc8609ef \ No newline at end of file
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 index 4e9a38b..4011aa4 100644 --- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 +++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@ -ae9eb01960d396ab603c611960f456d14ce7a1a8 \ No newline at end of file +4523245af5a856bb5b27b7a26795775ce4d54591 \ No newline at end of file
diff --git a/tools/test.py b/tools/test.py index 4a5d79c..7235b6e 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -460,7 +460,10 @@ def print_jstacks(): processes = subprocess.check_output(['ps', 'aux']) for l in processes.splitlines(): + if 'art' in l or 'dalvik' in l: + print('Running art of dalvik process: \n%s' % l) if 'java' in l and 'openjdk' in l: + print('Running jstack on process: \n%s' % l) # Example line: # ricow 184313 2.6 0.0 36839068 31808 ? Sl 09:53 0:00 /us.. columns = l.split()
diff --git a/tools/trigger.py b/tools/trigger.py index 984cc08..33b44a7 100755 --- a/tools/trigger.py +++ b/tools/trigger.py
@@ -17,12 +17,12 @@ import utils LUCI_SCHEDULE = os.path.join(utils.REPO_ROOT, 'infra', 'config', 'global', - 'luci-scheduler.cfg') + 'generated', 'luci-scheduler.cfg') # Trigger lines have the format: # triggers: "BUILDER_NAME" TRIGGERS_RE = r'^ triggers: "(\w.*)"' -DESUGAR_BOT = 'archive_lib_desugar' +DESUGAR_BOT = 'lib_desugar-archive' def ParseOptions(): result = optparse.OptionParser() @@ -49,20 +49,20 @@ for line in lines: if 'branch-gitiles-trigger' in line: is_release = True + if 'main-gitiles-trigger' in line: + is_release = False match = re.match(TRIGGERS_RE, line) if match: builder = match.group(1) if is_release: - assert 'release' in builder + assert 'release' in builder, builder release_builders.append(builder) else: - assert 'release' not in builder + assert 'release' not in builder, builder main_builders.append(builder) - assert DESUGAR_BOT in main_builders - print 'Desugar builder:\n ' + DESUGAR_BOT - main_builders.remove(DESUGAR_BOT) - print 'Main builders:\n ' + '\n '.join(main_builders) - print 'Release builders:\n ' + '\n '.join(release_builders) + print('Desugar builder:\n ' + DESUGAR_BOT) + print('Main builders:\n ' + '\n '.join(main_builders)) + print('Release builders:\n ' + '\n '.join(release_builders)) return (main_builders, release_builders) def sanity_check_url(url):