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):