Version 1.4.75
Cherry-pick:
Disallow direct manipulation of field arrays.
Also, verify the absence of duplicate fields.
CL: https://r8-review.googlesource.com/35700
Cherry-pick:
Remove a wrong logging.
CL: https://r8-review.googlesource.com/c/r8/+/35801
Bug: 127932803
Change-Id: I7ba45c73fe7b464ab271194acf737e6aa1da023e
diff --git a/src/main/java/com/android/tools/r8/ResourceShrinker.java b/src/main/java/com/android/tools/r8/ResourceShrinker.java
index 6267db1..d7805b3 100644
--- a/src/main/java/com/android/tools/r8/ResourceShrinker.java
+++ b/src/main/java/com/android/tools/r8/ResourceShrinker.java
@@ -233,11 +233,11 @@
private void processAnnotations(DexProgramClass classDef) {
Stream<DexAnnotation> instanceFieldAnnotations =
- Arrays.stream(classDef.instanceFields())
+ classDef.instanceFields().stream()
.filter(DexEncodedField::hasAnnotation)
.flatMap(f -> Arrays.stream(f.annotations.annotations));
Stream<DexAnnotation> staticFieldAnnotations =
- Arrays.stream(classDef.staticFields())
+ classDef.staticFields().stream()
.filter(DexEncodedField::hasAnnotation)
.flatMap(f -> Arrays.stream(f.annotations.annotations));
Stream<DexAnnotation> virtualMethodAnnotations =
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 3acb55b..3f82b47 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.74";
+ public static final String LABEL = "1.4.75";
private Version() {
}
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 e8ebbbb..9e7c09b 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -562,8 +562,8 @@
item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotationsList));
}
- private void writeEncodedFields(DexEncodedField[] fields) {
- assert PresortedComparable.isSorted(Arrays.asList(fields));
+ private void writeEncodedFields(List<DexEncodedField> fields) {
+ assert PresortedComparable.isSorted(fields);
int currentOffset = 0;
for (DexEncodedField field : fields) {
int nextOffset = mapping.getOffsetFor(field.field);
@@ -600,8 +600,8 @@
private void writeClassData(DexProgramClass clazz) {
assert clazz.hasMethodsOrFields();
mixedSectionOffsets.setOffsetFor(clazz, dest.position());
- dest.putUleb128(clazz.staticFields().length);
- dest.putUleb128(clazz.instanceFields().length);
+ dest.putUleb128(clazz.staticFields().size());
+ dest.putUleb128(clazz.instanceFields().size());
dest.putUleb128(clazz.directMethods().size());
dest.putUleb128(clazz.virtualMethods().size());
writeEncodedFields(clazz.staticFields());
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index 1ef2f68..f283d46 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.OrderedMergingIterator;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
@@ -38,11 +37,10 @@
parameterAnnotations.add(method);
}
}
- assert isSorted(Arrays.asList(clazz.staticFields()));
- assert isSorted(Arrays.asList(clazz.instanceFields()));
+ assert isSorted(clazz.staticFields());
+ assert isSorted(clazz.instanceFields());
OrderedMergingIterator<DexEncodedField, DexField> fields =
- new OrderedMergingIterator<>(
- Arrays.asList(clazz.staticFields()), Arrays.asList(clazz.instanceFields()));
+ new OrderedMergingIterator<>(clazz.staticFields(), clazz.instanceFields());
fieldAnnotations = new ArrayList<>();
while (fields.hasNext()) {
DexEncodedField field = fields.next();
@@ -68,7 +66,6 @@
return fieldAnnotations;
}
-
/**
* DexAnnotationDirectory of a class can be canonicalized only if a clazz has annotations and
* does not contains annotations for his fields, methods or parameters. Indeed, if a field, method
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 5e65aa3..5220d64 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -23,8 +23,11 @@
public abstract class DexClass extends DexDefinition {
- public interface MethodSetter {
+ public interface FieldSetter {
+ void setField(int index, DexEncodedField field);
+ }
+ public interface MethodSetter {
void setMethod(int index, DexEncodedMethod method);
}
@@ -171,8 +174,8 @@
assert verifyNoDuplicateMethods();
}
- public void setDirectMethods(DexEncodedMethod[] values) {
- directMethods = MoreObjects.firstNonNull(values, NO_METHODS);
+ public void setDirectMethods(DexEncodedMethod[] methods) {
+ directMethods = MoreObjects.firstNonNull(methods, NO_METHODS);
assert verifyCorrectnessOfMethodHolders(directMethods());
assert verifyNoDuplicateMethods();
}
@@ -221,14 +224,14 @@
assert verifyNoDuplicateMethods();
}
- public void setVirtualMethods(DexEncodedMethod[] values) {
- virtualMethods = MoreObjects.firstNonNull(values, NO_METHODS);
+ public void setVirtualMethods(DexEncodedMethod[] methods) {
+ virtualMethods = MoreObjects.firstNonNull(methods, NO_METHODS);
assert verifyCorrectnessOfMethodHolders(virtualMethods());
assert verifyNoDuplicateMethods();
}
private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
- assert method.method.holder == type
+ assert method.method.getHolder() == type
: "Expected method `"
+ method.method.toSourceString()
+ "` to have holder `"
@@ -340,12 +343,53 @@
}
}
- public DexEncodedField[] staticFields() {
- return staticFields;
+ public List<DexEncodedField> staticFields() {
+ assert staticFields != null;
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableList(Arrays.asList(staticFields));
+ }
+ return Arrays.asList(staticFields);
}
- public void setStaticFields(DexEncodedField[] values) {
- staticFields = MoreObjects.firstNonNull(values, NO_FIELDS);
+ public void appendStaticField(DexEncodedField field) {
+ DexEncodedField[] newFields = new DexEncodedField[staticFields.length + 1];
+ System.arraycopy(staticFields, 0, newFields, 0, staticFields.length);
+ newFields[staticFields.length] = field;
+ staticFields = newFields;
+ assert verifyCorrectnessOfFieldHolder(field);
+ assert verifyNoDuplicateFields();
+ }
+
+ public void appendStaticFields(Collection<DexEncodedField> fields) {
+ DexEncodedField[] newFields = new DexEncodedField[staticFields.length + fields.size()];
+ System.arraycopy(staticFields, 0, newFields, 0, staticFields.length);
+ int i = staticFields.length;
+ for (DexEncodedField field : fields) {
+ newFields[i] = field;
+ i++;
+ }
+ staticFields = newFields;
+ assert verifyCorrectnessOfFieldHolders(fields);
+ assert verifyNoDuplicateFields();
+ }
+
+ public void removeStaticField(int index) {
+ DexEncodedField[] newFields = new DexEncodedField[staticFields.length - 1];
+ System.arraycopy(staticFields, 0, newFields, 0, index);
+ System.arraycopy(staticFields, index + 1, newFields, index, staticFields.length - index - 1);
+ staticFields = newFields;
+ }
+
+ public void setStaticField(int index, DexEncodedField field) {
+ staticFields[index] = field;
+ assert verifyCorrectnessOfFieldHolder(field);
+ assert verifyNoDuplicateFields();
+ }
+
+ public void setStaticFields(DexEncodedField[] fields) {
+ staticFields = MoreObjects.firstNonNull(fields, NO_FIELDS);
+ assert verifyCorrectnessOfFieldHolders(staticFields());
+ assert verifyNoDuplicateFields();
}
public boolean definesStaticField(DexField field) {
@@ -357,12 +401,80 @@
return false;
}
- public DexEncodedField[] instanceFields() {
- return instanceFields;
+ public List<DexEncodedField> instanceFields() {
+ assert instanceFields != null;
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableList(Arrays.asList(instanceFields));
+ }
+ return Arrays.asList(instanceFields);
}
- public void setInstanceFields(DexEncodedField[] values) {
- instanceFields = MoreObjects.firstNonNull(values, NO_FIELDS);
+ public void appendInstanceField(DexEncodedField field) {
+ DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + 1];
+ System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length);
+ newFields[instanceFields.length] = field;
+ instanceFields = newFields;
+ assert verifyCorrectnessOfFieldHolder(field);
+ assert verifyNoDuplicateFields();
+ }
+
+ public void appendInstanceFields(Collection<DexEncodedField> fields) {
+ DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + fields.size()];
+ System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length);
+ int i = instanceFields.length;
+ for (DexEncodedField field : fields) {
+ newFields[i] = field;
+ i++;
+ }
+ instanceFields = newFields;
+ assert verifyCorrectnessOfFieldHolders(fields);
+ assert verifyNoDuplicateFields();
+ }
+
+ public void removeInstanceField(int index) {
+ DexEncodedField[] newFields = new DexEncodedField[instanceFields.length - 1];
+ System.arraycopy(instanceFields, 0, newFields, 0, index);
+ System.arraycopy(
+ instanceFields, index + 1, newFields, index, instanceFields.length - index - 1);
+ instanceFields = newFields;
+ }
+
+ public void setInstanceField(int index, DexEncodedField field) {
+ instanceFields[index] = field;
+ assert verifyCorrectnessOfFieldHolder(field);
+ assert verifyNoDuplicateFields();
+ }
+
+ public void setInstanceFields(DexEncodedField[] fields) {
+ instanceFields = MoreObjects.firstNonNull(fields, NO_FIELDS);
+ assert verifyCorrectnessOfFieldHolders(instanceFields());
+ assert verifyNoDuplicateFields();
+ }
+
+ private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
+ assert field.field.getHolder() == type
+ : "Expected field `"
+ + field.field.toSourceString()
+ + "` to have holder `"
+ + type.toSourceString()
+ + "`";
+ return true;
+ }
+
+ private boolean verifyCorrectnessOfFieldHolders(Iterable<DexEncodedField> fields) {
+ for (DexEncodedField field : fields) {
+ assert verifyCorrectnessOfFieldHolder(field);
+ }
+ return true;
+ }
+
+ private boolean verifyNoDuplicateFields() {
+ Set<DexField> unique = Sets.newIdentityHashSet();
+ for (DexEncodedField field : fields()) {
+ boolean changed = unique.add(field.field);
+ assert changed : "Duplicate field `" + field.field.toSourceString() + "`";
+ }
+ return true;
}
public DexEncodedField[] allFieldsSorted() {
@@ -380,14 +492,14 @@
* Find static field in this class matching field
*/
public DexEncodedField lookupStaticField(DexField field) {
- return lookupTarget(staticFields(), field);
+ return lookupTarget(staticFields, field);
}
/**
* Find instance field in this class matching field.
*/
public DexEncodedField lookupInstanceField(DexField field) {
- return lookupTarget(instanceFields(), field);
+ return lookupTarget(instanceFields, field);
}
/**
@@ -598,7 +710,7 @@
}
public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
- return Arrays.stream(staticFields())
+ return staticFields().stream()
.anyMatch(field -> field.getStaticValue().mayTriggerAllocation());
}
@@ -669,6 +781,8 @@
public boolean isValid() {
assert !isInterface()
|| Arrays.stream(virtualMethods).noneMatch(method -> method.accessFlags.isFinal());
+ assert verifyCorrectnessOfFieldHolders(fields());
+ assert verifyNoDuplicateFields();
assert verifyCorrectnessOfMethodHolders(methods());
assert verifyNoDuplicateMethods();
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 57b08fb..4312bfd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -225,7 +225,7 @@
}
public boolean hasMethodsOrFields() {
- int numberOfFields = staticFields().length + instanceFields().length;
+ int numberOfFields = staticFields().size() + instanceFields().size();
int numberOfMethods = directMethods().size() + virtualMethods().size();
return numberOfFields + numberOfMethods > 0;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index a620ea9..44bba62 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.utils.InternalOptions;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -93,7 +92,7 @@
if (!clazz.accessFlags.isSynthetic() && !clazz.hasClassInitializer()) {
return;
}
- List<DexEncodedField> switchMapFields = Arrays.stream(clazz.staticFields())
+ List<DexEncodedField> switchMapFields = clazz.staticFields().stream()
.filter(this::maybeIsSwitchMap).collect(Collectors.toList());
if (!switchMapFields.isEmpty()) {
IRCode initializer =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 37441ac..0b98926 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -209,7 +209,7 @@
// of class inlining
//
- if (eligibleClassDefinition.instanceFields().length > 0) {
+ if (eligibleClassDefinition.instanceFields().size() > 0) {
return false;
}
if (eligibleClassDefinition.type.hasSubtypes()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
index ede37b1..5a39e71 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
@@ -16,6 +16,7 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.Comparator;
+import java.util.List;
import java.util.function.IntFunction;
// While mapping fields representing lambda captures we rearrange fields to make sure
@@ -106,8 +107,8 @@
}
// Compute capture signature based on lambda class capture fields.
- public static String getCaptureSignature(DexEncodedField[] fields) {
- return getCaptureSignature(fields.length, i -> fields[i].field.type);
+ public static String getCaptureSignature(List<DexEncodedField> fields) {
+ return getCaptureSignature(fields.size(), i -> fields.get(i).field.type);
}
// Compute capture signature based on type list.
@@ -118,7 +119,7 @@
// Having a list of fields of lambda captured values, maps one of them into
// an index of the appropriate field in normalized capture signature.
public static int mapFieldIntoCaptureIndex(
- String capture, DexEncodedField[] lambdaFields, DexField fieldToMap) {
+ String capture, List<DexEncodedField> lambdaFields, DexField fieldToMap) {
char fieldKind = fieldToMap.type.toShorty();
int numberOfSameCaptureKind = 0;
int result = -1;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index f3fecde..375e1cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -109,16 +109,16 @@
return lambdas.get(lambda).id;
}
- protected final DexEncodedField[] lambdaCaptureFields(DexType lambda) {
+ protected final List<DexEncodedField> lambdaCaptureFields(DexType lambda) {
assert lambdas.containsKey(lambda);
return lambdas.get(lambda).clazz.instanceFields();
}
protected final DexEncodedField lambdaSingletonField(DexType lambda) {
assert lambdas.containsKey(lambda);
- DexEncodedField[] fields = lambdas.get(lambda).clazz.staticFields();
- assert fields.length < 2;
- return fields.length == 0 ? null : fields[0];
+ List<DexEncodedField> fields = lambdas.get(lambda).clazz.staticFields();
+ assert fields.size() < 2;
+ return fields.size() == 0 ? null : fields.get(0);
}
// Contains less than 2 elements?
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index e8fcd40..21a3083 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
+import java.util.List;
import java.util.function.IntFunction;
// Represents a j-style lambda group created to combine several lambda classes
@@ -152,8 +153,8 @@
}
@Override
- int getInstanceInitializerSize(DexEncodedField[] captures) {
- return captures.length + 2;
+ int getInstanceInitializerSize(List<DexEncodedField> captures) {
+ return captures.size() + 2;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index f677b46..292e120 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
+import java.util.List;
import java.util.function.IntFunction;
// Represents a k-style lambda group created to combine several lambda classes
@@ -158,8 +159,8 @@
}
@Override
- int getInstanceInitializerSize(DexEncodedField[] captures) {
- return captures.length + 3;
+ int getInstanceInitializerSize(List<DexEncodedField> captures) {
+ return captures.size() + 3;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
index 2385f35..d49a2bd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.List;
// Encapsulates the logic of deep-checking of the lambda class assumptions.
//
@@ -118,14 +119,14 @@
}
}
- abstract int getInstanceInitializerSize(DexEncodedField[] captures);
+ abstract int getInstanceInitializerSize(List<DexEncodedField> captures);
abstract int validateInstanceInitializerEpilogue(
com.android.tools.r8.code.Instruction[] instructions, int index) throws LambdaStructureError;
private void validateInstanceInitializer(DexClass lambda, Code code)
throws LambdaStructureError {
- DexEncodedField[] captures = lambda.instanceFields();
+ List<DexEncodedField> captures = lambda.instanceFields();
com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
int index = 0;
@@ -142,7 +143,7 @@
assert index == instructions.length;
}
- private int validateInstanceInitializerParameterMapping(DexEncodedField[] captures,
+ private int validateInstanceInitializerParameterMapping(List<DexEncodedField> captures,
Instruction[] instructions, int index) throws LambdaStructureError {
int wideFieldsSeen = 0;
for (DexEncodedField field : captures) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 234bcc6..63e0684 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -111,30 +111,30 @@
}
void validateStaticFields(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
- DexEncodedField[] staticFields = lambda.staticFields();
- if (staticFields.length == 1) {
- DexEncodedField field = staticFields[0];
+ List<DexEncodedField> staticFields = lambda.staticFields();
+ if (staticFields.size() == 1) {
+ DexEncodedField field = staticFields.get(0);
if (field.field.name != kotlin.functional.kotlinStyleLambdaInstanceName ||
field.field.type != lambda.type || !field.accessFlags.isPublic() ||
!field.accessFlags.isFinal() || !field.accessFlags.isStatic()) {
throw new LambdaStructureError("unexpected static field " + field.toSourceString());
}
// No state if the lambda is a singleton.
- if (lambda.instanceFields().length > 0) {
+ if (lambda.instanceFields().size() > 0) {
throw new LambdaStructureError("has instance fields along with INSTANCE");
}
checkAccessFlags("static field access flags", field.accessFlags, SINGLETON_FIELD_FLAGS);
checkFieldAnnotations(field);
- } else if (staticFields.length > 1) {
+ } else if (staticFields.size() > 1) {
throw new LambdaStructureError(
- "only one static field max expected, found " + staticFields.length);
+ "only one static field max expected, found " + staticFields.size());
}
}
String validateInstanceFields(DexClass lambda, boolean accessRelaxed)
throws LambdaStructureError {
- DexEncodedField[] instanceFields = lambda.instanceFields();
+ List<DexEncodedField> instanceFields = lambda.instanceFields();
for (DexEncodedField field : instanceFields) {
checkAccessFlags("capture field access flags", field.accessFlags,
accessRelaxed ? CAPTURE_FIELD_FLAGS_RELAXED : CAPTURE_FIELD_FLAGS);
@@ -147,7 +147,7 @@
for (DexEncodedMethod method : lambda.directMethods()) {
if (method.isClassInitializer()) {
// We expect to see class initializer only if there is a singleton field.
- if (lambda.staticFields().length != 1) {
+ if (lambda.staticFields().size() != 1) {
throw new LambdaStructureError("has static initializer, but no singleton field");
}
checkAccessFlags("unexpected static initializer access flags",
@@ -162,15 +162,15 @@
// Lambda class is expected to have one constructor
// with parameters matching capture signature.
DexType[] parameters = method.method.proto.parameters.values;
- DexEncodedField[] instanceFields = lambda.instanceFields();
- if (parameters.length != instanceFields.length) {
+ List<DexEncodedField> instanceFields = lambda.instanceFields();
+ if (parameters.length != instanceFields.size()) {
throw new LambdaStructureError("constructor parameters don't match captured values.");
}
for (int i = 0; i < parameters.length; i++) {
// Kotlin compiler sometimes reshuffles the parameters so that their order
// in the constructor don't match order of capture fields. We could add
// support for it, but it happens quite rarely so don't bother for now.
- if (parameters[i] != instanceFields[i].field.type) {
+ if (parameters[i] != instanceFields.get(i).field.type) {
throw new LambdaStructureError(
"constructor parameters don't match captured values.", false);
}
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 443a2f4..bf205b3 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
@@ -141,18 +141,18 @@
}
// High-level limitations on what classes we consider eligible.
- if (cls.isInterface() || // Must not be an interface or an abstract class.
- cls.accessFlags.isAbstract() ||
+ if (cls.isInterface() // Must not be an interface or an abstract class.
+ || cls.accessFlags.isAbstract()
// Don't support candidates with instance fields
- cls.instanceFields().length > 0 ||
+ || cls.instanceFields().size() > 0
// Only support classes directly extending java.lang.Object
- cls.superType != factory.objectType ||
+ || cls.superType != factory.objectType
// Instead of requiring the class being final,
// just ensure it does not have subtypes
- cls.type.hasSubtypes() ||
+ || cls.type.hasSubtypes()
// Staticizing classes implementing interfaces is more
// difficult, so don't support it until we really need it.
- !cls.interfaces.isEmpty()) {
+ || !cls.interfaces.isEmpty()) {
notEligible.add(cls.type);
}
});
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 ff4a12e..4f4d4ef 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
@@ -46,7 +46,6 @@
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
final class StaticizingProcessor {
@@ -119,7 +118,7 @@
// CHECK: instance initializer used to create an instance is trivial.
// NOTE: Along with requirement that candidate does not have instance
// fields this should guarantee that the constructor is empty.
- assert candidateClass.instanceFields().length == 0;
+ assert candidateClass.instanceFields().size() == 0;
assert constructorUsed.isProcessed();
TrivialInitializer trivialInitializer =
constructorUsed.getOptimizationInfo().getTrivialInitializerInfo();
@@ -225,7 +224,7 @@
// extend java.lang.Object guarantees that the constructor is actually
// empty and does not need to be inlined.
assert candidateInfo.candidate.superType == factory().objectType;
- assert candidateInfo.candidate.instanceFields().length == 0;
+ assert candidateInfo.candidate.instanceFields().size() == 0;
Value singletonValue = instruction.outValue();
assert singletonValue != null;
@@ -313,7 +312,7 @@
// From staticizer's viewpoint, `sp` is trivial in the sense that it is composed of values that
// refer to the same singleton field. If so, we can safely relax the assertion; remove uses of
// field reads; remove quasi-trivial phis; and then remove original field reads.
- private boolean testAndcollectPhisComposedOfSameFieldRead(
+ private boolean testAndCollectPhisComposedOfSameFieldRead(
Set<Phi> phisToCheck, DexField field, Set<Phi> trivialPhis) {
for (Phi phi : phisToCheck) {
Set<Phi> chainedPhis = Sets.newIdentityHashSet();
@@ -330,7 +329,7 @@
}
}
if (!chainedPhis.isEmpty()) {
- if (!testAndcollectPhisComposedOfSameFieldRead(chainedPhis, field, trivialPhis)) {
+ if (!testAndCollectPhisComposedOfSameFieldRead(chainedPhis, field, trivialPhis)) {
return false;
}
}
@@ -347,7 +346,7 @@
// However, it may be not true if re-processing introduces phis after optimizing common suffix.
Set<Phi> trivialPhis = Sets.newIdentityHashSet();
boolean hasTrivialPhis =
- testAndcollectPhisComposedOfSameFieldRead(dest.uniquePhiUsers(), field, trivialPhis);
+ testAndCollectPhisComposedOfSameFieldRead(dest.uniquePhiUsers(), field, trivialPhis);
assert dest.numberOfPhiUsers() == 0 || hasTrivialPhis;
Set<Instruction> users = new HashSet<>(dest.uniqueUsers());
// If that is the case, method calls we want to fix up include users of those phis.
@@ -510,12 +509,13 @@
private boolean classMembersConflict(DexClass a, DexClass b) {
assert Streams.stream(a.methods()).allMatch(DexEncodedMethod::isStatic);
- assert a.instanceFields().length == 0;
- return Stream.of(a.staticFields()).anyMatch(fld -> b.lookupField(fld.field) != null) ||
- Streams.stream(a.methods()).anyMatch(method -> b.lookupMethod(method.method) != null);
+ assert a.instanceFields().size() == 0;
+ return a.staticFields().stream().anyMatch(fld -> b.lookupField(fld.field) != null)
+ || Streams.stream(a.methods()).anyMatch(method -> b.lookupMethod(method.method) != null);
}
- private void moveMembersIntoHost(Set<DexEncodedMethod> staticizedMethods,
+ private void moveMembersIntoHost(
+ Set<DexEncodedMethod> staticizedMethods,
DexProgramClass candidateClass,
DexType hostType, DexClass hostClass,
BiMap<DexMethod, DexMethod> methodMapping,
@@ -523,26 +523,36 @@
candidateToHostMapping.put(candidateClass.type, hostType);
// Process static fields.
- // Append fields first.
- if (candidateClass.staticFields().length > 0) {
- DexEncodedField[] oldFields = hostClass.staticFields();
- DexEncodedField[] extraFields = candidateClass.staticFields();
- DexEncodedField[] newFields = new DexEncodedField[oldFields.length + extraFields.length];
- System.arraycopy(oldFields, 0, newFields, 0, oldFields.length);
- System.arraycopy(extraFields, 0, newFields, oldFields.length, extraFields.length);
- hostClass.setStaticFields(newFields);
- }
-
- // Fixup field types.
- DexEncodedField[] staticFields = hostClass.staticFields();
- for (int i = 0; i < staticFields.length; i++) {
- DexEncodedField field = staticFields[i];
+ int numOfHostStaticFields = hostClass.staticFields().size();
+ DexEncodedField[] newFields =
+ candidateClass.staticFields().size() > 0
+ ? new DexEncodedField[numOfHostStaticFields + candidateClass.staticFields().size()]
+ : new DexEncodedField[numOfHostStaticFields];
+ List<DexEncodedField> oldFields = hostClass.staticFields();
+ for (int i = 0; i < oldFields.size(); i++) {
+ DexEncodedField field = oldFields.get(i);
DexField newField = mapCandidateField(field.field, candidateClass.type, hostType);
if (newField != field.field) {
- staticFields[i] = field.toTypeSubstitutedField(newField);
+ newFields[i] = field.toTypeSubstitutedField(newField);
fieldMapping.put(field.field, newField);
+ } else {
+ newFields[i] = field;
}
}
+ if (candidateClass.staticFields().size() > 0) {
+ List<DexEncodedField> extraFields = candidateClass.staticFields();
+ for (int i = 0; i < extraFields.size(); i++) {
+ DexEncodedField field = extraFields.get(i);
+ DexField newField = mapCandidateField(field.field, candidateClass.type, hostType);
+ if (newField != field.field) {
+ newFields[numOfHostStaticFields + i] = field.toTypeSubstitutedField(newField);
+ fieldMapping.put(field.field, newField);
+ } else {
+ newFields[numOfHostStaticFields + i] = field;
+ }
+ }
+ }
+ hostClass.setStaticFields(newFields);
// Process static methods.
List<DexEncodedMethod> extraMethods = candidateClass.directMethods();
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
index e3d09b6..0fdeebc 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
@@ -72,7 +72,7 @@
}
}
- private void writeFields(DexEncodedField[] fields, StringBuilder out) {
+ private void writeFields(List<DexEncodedField> fields, StringBuilder out) {
for (DexEncodedField encodedField : fields) {
DexField field = encodedField.field;
DexString renamed = namingLens.lookupName(field);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
index a447e37..78dad02 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClass.FieldSetter;
import com.android.tools.r8.graph.DexClass.MethodSetter;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
@@ -326,8 +327,8 @@
clazz.annotations = substituteTypesIn(clazz.annotations);
fixupMethods(clazz.directMethods(), clazz::setDirectMethod);
fixupMethods(clazz.virtualMethods(), clazz::setVirtualMethod);
- clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
- clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
+ fixupFields(clazz.staticFields(), clazz::setStaticField);
+ fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
private void fixupMethods(List<DexEncodedMethod> methods, MethodSetter setter) {
@@ -352,12 +353,12 @@
}
}
- private DexEncodedField[] substituteTypesIn(DexEncodedField[] fields) {
+ private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
if (fields == null) {
- return null;
+ return;
}
- for (int i = 0; i < fields.length; i++) {
- DexEncodedField encodedField = fields[i];
+ for (int i = 0; i < fields.size(); i++) {
+ DexEncodedField encodedField = fields.get(i);
DexField appliedField = appliedLense.lookupField(encodedField.field);
DexType newHolderType = substituteType(appliedField.clazz, null);
DexType newFieldType = substituteType(appliedField.type, null);
@@ -370,9 +371,8 @@
newField = appliedField;
}
// Explicitly fix members.
- fields[i] = encodedField.toTypeSubstitutedField(newField);
+ setter.setField(i, encodedField.toTypeSubstitutedField(newField));
}
- return fields;
}
private DexProto substituteTypesIn(DexProto proto, DexEncodedMethod context) {
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 42b7e21..9d68c91 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -31,7 +31,6 @@
import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Streams;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -259,14 +258,14 @@
if (appView.appInfo().neverMerge.contains(clazz.type)) {
return MergeGroup.DONT_MERGE;
}
- if (clazz.staticFields().length + clazz.directMethods().size() + clazz.virtualMethods().size()
+ if (clazz.staticFields().size() + clazz.directMethods().size() + clazz.virtualMethods().size()
== 0) {
return MergeGroup.DONT_MERGE;
}
- if (clazz.instanceFields().length > 0) {
+ if (clazz.instanceFields().size() > 0) {
return MergeGroup.DONT_MERGE;
}
- if (Arrays.stream(clazz.staticFields())
+ if (clazz.staticFields().stream()
.anyMatch(field -> appView.appInfo().isPinned(field.field))) {
return MergeGroup.DONT_MERGE;
}
@@ -284,7 +283,7 @@
// TODO(christofferqa): Remove the invariant that the graph lense should not
// modify any methods from the sets alwaysInline and noSideEffects.
|| appView.appInfo().alwaysInline.contains(method.method)
- || appView.appInfo().noSideEffects.keySet().contains(method))) {
+ || appView.appInfo().noSideEffects.keySet().contains(method.method))) {
return MergeGroup.DONT_MERGE;
}
if (clazz.classInitializationMayHaveSideEffects(appView.appInfo())) {
@@ -311,6 +310,7 @@
// Disallow interfaces from being representatives, since interface methods require desugaring.
return !clazz.isInterface();
}
+
private boolean merge(DexProgramClass clazz, MergeGroup group) {
assert satisfiesMergeCriteria(clazz) == group;
assert group != MergeGroup.DONT_MERGE;
@@ -451,15 +451,15 @@
.allMatch(method -> method.accessFlags.isPrivate() || method.accessFlags.isPublic())) {
return false;
}
- if (!Arrays.stream(clazz.staticFields())
- .allMatch(method -> method.accessFlags.isPrivate() || method.accessFlags.isPublic())) {
+ if (!clazz.staticFields().stream()
+ .allMatch(field -> field.accessFlags.isPrivate() || field.accessFlags.isPublic())) {
return false;
}
// Note that a class is only considered a candidate if it has no instance fields and all of its
// virtual methods are private. Therefore, we don't need to consider check if there are any
// package-private or protected instance fields or virtual methods here.
- assert Arrays.stream(clazz.instanceFields()).count() == 0;
+ assert clazz.instanceFields().size() == 0;
assert clazz.virtualMethods().stream().allMatch(method -> method.accessFlags.isPrivate());
// Check that no methods access package-private or protected members.
@@ -485,8 +485,8 @@
}
assert targetClass.accessFlags.isAtLeastAsVisibleAs(sourceClass.accessFlags);
- assert sourceClass.instanceFields().length == 0;
- assert targetClass.instanceFields().length == 0;
+ assert sourceClass.instanceFields().size() == 0;
+ assert targetClass.instanceFields().size() == 0;
numberOfMergedClasses++;
@@ -534,27 +534,31 @@
}
private DexEncodedField[] mergeFields(
- DexEncodedField[] sourceFields, DexEncodedField[] targetFields, DexProgramClass targetClass) {
- DexEncodedField[] result = new DexEncodedField[sourceFields.length + targetFields.length];
+ List<DexEncodedField> sourceFields,
+ List<DexEncodedField> targetFields,
+ DexProgramClass targetClass) {
+ DexEncodedField[] result = new DexEncodedField[sourceFields.size() + targetFields.size()];
// Move all target fields to result.
- System.arraycopy(targetFields, 0, result, 0, targetFields.length);
+ int index = 0;
+ for (DexEncodedField targetField : targetFields) {
+ result[index++] = targetField;
+ }
// Move source fields to result one by one, renaming them if needed.
FieldSignatureEquivalence equivalence = FieldSignatureEquivalence.get();
Set<Wrapper<DexField>> existingFields =
- Arrays.stream(targetFields)
+ targetFields.stream()
.map(targetField -> equivalence.wrap(targetField.field))
.collect(Collectors.toSet());
Predicate<DexField> availableFieldSignatures =
field -> !existingFields.contains(equivalence.wrap(field));
- int i = targetFields.length;
for (DexEncodedField sourceField : sourceFields) {
DexEncodedField sourceFieldAfterMove =
renameFieldIfNeeded(sourceField, targetClass, availableFieldSignatures);
- result[i++] = sourceFieldAfterMove;
+ result[index++] = sourceFieldAfterMove;
DexField originalField =
fieldMapping.inverse().getOrDefault(sourceField.field, sourceField.field);
@@ -563,6 +567,7 @@
existingFields.add(equivalence.wrap(sourceFieldAfterMove.field));
}
+ assert index == result.length;
return result;
}
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 8017878..833cac3 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -109,8 +108,14 @@
if (reachableVirtualMethods != null) {
clazz.setVirtualMethods(reachableVirtualMethods);
}
- clazz.setInstanceFields(reachableFields(clazz.instanceFields()));
- clazz.setStaticFields(reachableFields(clazz.staticFields()));
+ DexEncodedField[] reachableInstanceFields = reachableFields(clazz.instanceFields());
+ if (reachableInstanceFields != null) {
+ clazz.setInstanceFields(reachableInstanceFields);
+ }
+ DexEncodedField[] reachableStaticFields = reachableFields(clazz.staticFields());
+ if (reachableStaticFields != null) {
+ clazz.setStaticFields(reachableStaticFields);
+ }
clazz.removeInnerClasses(this::isAttributeReferencingPrunedType);
clazz.removeEnclosingMethod(this::isAttributeReferencingPrunedItem);
usagePrinter.visited();
@@ -221,36 +226,37 @@
usagePrinter.printUnusedMethod(method);
}
}
- return reachableMethods.toArray(new DexEncodedMethod[reachableMethods.size()]);
+ return reachableMethods.isEmpty()
+ ? DexEncodedMethod.EMPTY_ARRAY
+ : reachableMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
}
- private DexEncodedField[] reachableFields(DexEncodedField[] fields) {
+ private DexEncodedField[] reachableFields(List<DexEncodedField> fields) {
Predicate<DexField> isReachableOrReferencedField =
field ->
appInfo.liveFields.contains(field)
|| appInfo.isFieldRead(field)
|| appInfo.isFieldWritten(field);
- int firstUnreachable =
- firstUnreachableIndex(Arrays.asList(fields), isReachableOrReferencedField);
+ int firstUnreachable = firstUnreachableIndex(fields, isReachableOrReferencedField);
// Return the original array if all fields are used.
if (firstUnreachable == -1) {
- return fields;
+ return null;
}
if (Log.ENABLED) {
- Log.debug(getClass(), "Removing field: " + fields[firstUnreachable]);
+ Log.debug(getClass(), "Removing field %s.", fields.get(firstUnreachable));
}
- usagePrinter.printUnusedField(fields[firstUnreachable]);
- ArrayList<DexEncodedField> reachableOrReferencedFields = new ArrayList<>(fields.length);
+ usagePrinter.printUnusedField(fields.get(firstUnreachable));
+ ArrayList<DexEncodedField> reachableOrReferencedFields = new ArrayList<>(fields.size());
for (int i = 0; i < firstUnreachable; i++) {
- reachableOrReferencedFields.add(fields[i]);
+ reachableOrReferencedFields.add(fields.get(i));
}
- for (int i = firstUnreachable + 1; i < fields.length; i++) {
- DexEncodedField field = fields[i];
+ for (int i = firstUnreachable + 1; i < fields.size(); i++) {
+ DexEncodedField field = fields.get(i);
if (isReachableOrReferencedField.test(field.field)) {
reachableOrReferencedFields.add(field);
} else {
if (Log.ENABLED) {
- Log.debug(getClass(), "Removing field: " + field);
+ Log.debug(getClass(), "Removing field %s.", field.field);
}
usagePrinter.printUnusedField(field);
}
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 acfc42f..f6941d3 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClass.FieldSetter;
import com.android.tools.r8.graph.DexClass.MethodSetter;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -1259,11 +1260,11 @@
}
private DexEncodedField[] mergeFields(
- DexEncodedField[] sourceFields,
- DexEncodedField[] targetFields,
+ Collection<DexEncodedField> sourceFields,
+ Collection<DexEncodedField> targetFields,
Predicate<DexField> availableFieldSignatures,
Set<DexString> existingFieldNames) {
- DexEncodedField[] result = new DexEncodedField[sourceFields.length + targetFields.length];
+ DexEncodedField[] result = new DexEncodedField[sourceFields.size() + targetFields.size()];
// Add fields from source
int i = 0;
for (DexEncodedField field : sourceFields) {
@@ -1274,21 +1275,10 @@
i++;
}
// Add fields from target.
- System.arraycopy(targetFields, 0, result, i, targetFields.length);
- return result;
- }
-
- private DexEncodedMethod[] mergeMethods(
- Collection<DexEncodedMethod> sourceMethods, List<DexEncodedMethod> targetMethods) {
- DexEncodedMethod[] result = new DexEncodedMethod[sourceMethods.size() + targetMethods.size()];
- // Add methods from source.
- int i = 0;
- for (DexEncodedMethod method : sourceMethods) {
- result[i] = method;
+ for (DexEncodedField field : targetFields) {
+ result[i] = field;
i++;
}
- // Add methods from target.
- System.arraycopy(targetMethods, 0, result, i, targetMethods.size());
return result;
}
@@ -1434,8 +1424,8 @@
for (DexProgramClass clazz : appInfo.classes()) {
fixupMethods(clazz.directMethods(), clazz::setDirectMethod);
fixupMethods(clazz.virtualMethods(), clazz::setVirtualMethod);
- clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
- clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
+ fixupFields(clazz.staticFields(), clazz::setStaticField);
+ fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
synthesizedBridge.updateMethodSignatures(this::fixupMethod);
@@ -1462,22 +1452,21 @@
}
}
- private DexEncodedField[] substituteTypesIn(DexEncodedField[] fields) {
+ private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
if (fields == null) {
- return null;
+ return;
}
- for (int i = 0; i < fields.length; i++) {
- DexEncodedField encodedField = fields[i];
+ for (int i = 0; i < fields.size(); i++) {
+ DexEncodedField encodedField = fields.get(i);
DexField field = encodedField.field;
DexType newType = fixupType(field.type);
DexType newHolder = fixupType(field.clazz);
DexField newField = application.dexItemFactory.createField(newHolder, newType, field.name);
if (newField != encodedField.field) {
lense.move(encodedField.field, newField);
- fields[i] = encodedField.toTypeSubstitutedField(newField);
+ setter.setField(i, encodedField.toTypeSubstitutedField(newField));
}
}
- return fields;
}
private DexMethod fixupMethod(DexMethod method) {
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
index 9c9e785..e92e0f3 100644
--- a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
@@ -94,7 +94,7 @@
assertThat(clazz, isPresent());
// Redundant fields have been removed.
- assertEquals(1, clazz.getDexClass().instanceFields().length);
- assertEquals(1, clazz.getDexClass().staticFields().length);
+ assertEquals(1, clazz.getDexClass().instanceFields().size());
+ assertEquals(1, clazz.getDexClass().staticFields().size());
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index 7e9269d..82f8688 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -242,7 +242,7 @@
private static int getLambdaSingletons(DexClass clazz) {
assertEquals(1, clazz.interfaces.size());
- return clazz.staticFields().length;
+ return clazz.staticFields().size();
}
private static boolean isLambdaOrGroup(DexClass clazz) {
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index d06809e..e247098 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -310,8 +310,8 @@
}
private static int countRenamedClassIdentifier(
- CodeInspector inspector, DexEncodedField[] fields) {
- return Arrays.stream(fields)
+ CodeInspector inspector, List<DexEncodedField> fields) {
+ return fields.stream()
.filter(encodedField -> encodedField.getStaticValue() instanceof DexValueString)
.reduce(0, (cnt, encodedField) -> {
String cnstString =
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index e5f3517..6196bb7 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -6,8 +6,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.shaking.PrintUsageTest.PrintUsageInspector.ClassSubject;
@@ -21,20 +19,16 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -50,9 +44,6 @@
private final List<String> keepRulesFiles;
private final Consumer<PrintUsageInspector> inspection;
- @Rule
- public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
public PrintUsageTest(
Backend backend,
String test,
@@ -68,33 +59,19 @@
@Before
public void runR8andGetPrintUsage() throws Exception {
Path out = temp.getRoot().toPath();
- R8Command.Builder builder =
- ToolHelper.addProguardConfigurationConsumer(
- R8Command.builder(),
- pgConfig -> {
- pgConfig.setPrintUsage(true);
- pgConfig.setPrintUsageFile(out.resolve(test + PRINT_USAGE_FILE_SUFFIX));
- })
- .addProgramFiles(Paths.get(programFile))
- .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get));
-
- if (backend == Backend.DEX) {
- builder
- .setOutput(out, OutputMode.DexIndexed)
- .addLibraryFiles(ToolHelper.getDefaultAndroidJar());
- } else {
- builder.setOutput(out, OutputMode.ClassFile).addLibraryFiles(ToolHelper.getJava8RuntimeJar());
- }
- ToolHelper.runR8(
- builder.build(),
- options -> {
- // Disable inlining to make this test not depend on inlining decisions.
- options.enableInlining = false;
- });
+ testForR8(backend)
+ .addProgramFiles(Paths.get(programFile))
+ .addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
+ .addKeepRules(
+ "-printusage " + out.resolve(test + PRINT_USAGE_FILE_SUFFIX)
+ )
+ // Disable inlining to make this test not depend on inlining decisions.
+ .addOptionsModification(o -> o.enableInlining = false)
+ .compile();
}
@Test
- public void printUsageTest() throws IOException, ExecutionException {
+ public void printUsageTest() throws IOException {
Path out = temp.getRoot().toPath();
Path printUsageFile = out.resolve(test + PRINT_USAGE_FILE_SUFFIX);
if (inspection != null) {
@@ -118,20 +95,20 @@
List<Object[]> testCases = new ArrayList<>();
for (Backend backend : Backend.values()) {
- Set<String> usedInspections = new HashSet<>();
- for (String test : tests) {
- File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
- .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
- for (File keepFile : keepFiles) {
- String keepName = keepFile.getName();
- Consumer<PrintUsageInspector> inspection =
- getTestOptionalParameter(inspections, usedInspections, test, keepName);
- if (inspection != null) {
+ Set<String> usedInspections = new HashSet<>();
+ for (String test : tests) {
+ File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
+ .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+ for (File keepFile : keepFiles) {
+ String keepName = keepFile.getName();
+ Consumer<PrintUsageInspector> inspection =
+ getTestOptionalParameter(inspections, usedInspections, test, keepName);
+ if (inspection != null) {
testCases.add(
new Object[] {backend, test, ImmutableList.of(keepFile.getPath()), inspection});
+ }
}
}
- }
assert usedInspections.size() == inspections.size();
}
return testCases;
@@ -152,6 +129,9 @@
}
private static void inspectShaking1(PrintUsageInspector inspector) {
+ Optional<ClassSubject> shaking1 = inspector.clazz("shaking1.Shaking");
+ assertTrue(shaking1.isPresent());
+ assertTrue(shaking1.get().method("void", "<init>", ImmutableList.of()));
assertTrue(inspector.clazz("shaking1.Unused").isPresent());
assertTrue(inspector.clazz("shaking1.Used").isPresent());
ClassSubject used = inspector.clazz("shaking1.Used").get();
@@ -165,10 +145,10 @@
assertTrue(staticFields.get().field("int", "unused"));
Optional<ClassSubject> subClass1 = inspector.clazz("shaking2.SubClass1");
assertTrue(subClass1.isPresent());
- assertTrue(subClass1.get().method("void", "unusedVirtualMethod", Collections.emptyList()));
+ assertTrue(subClass1.get().method("void", "unusedVirtualMethod", ImmutableList.of()));
Optional<ClassSubject> superClass = inspector.clazz("shaking2.SuperClass");
assertTrue(superClass.isPresent());
- assertTrue(superClass.get().method("void", "unusedStaticMethod", Collections.emptyList()));
+ assertTrue(superClass.get().method("void", "unusedStaticMethod", ImmutableList.of()));
}
private static void inspectShaking4(PrintUsageInspector inspector) {
@@ -188,15 +168,15 @@
assertFalse(superClass.isPresent());
Optional<ClassSubject> subClass = inspector.clazz("shaking9.Subclass");
assertTrue(subClass.isPresent());
- assertTrue(subClass.get().method("void", "aMethod", Collections.emptyList()));
- assertFalse(subClass.get().method("void", "<init>", Collections.emptyList()));
+ assertTrue(subClass.get().method("void", "aMethod", ImmutableList.of()));
+ assertFalse(subClass.get().method("void", "<init>", ImmutableList.of()));
}
private static void inspectShaking12(PrintUsageInspector inspector) {
assertFalse(inspector.clazz("shaking12.PeopleClass").isPresent());
Optional<ClassSubject> animal = inspector.clazz("shaking12.AnimalClass");
assertTrue(animal.isPresent());
- assertTrue(animal.get().method("java.lang.String", "getName", Collections.emptyList()));
+ assertTrue(animal.get().method("java.lang.String", "getName", ImmutableList.of()));
}
static class PrintUsageInspector {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index ebaf5af..058a19d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -109,12 +109,12 @@
@Override
public void forAllFields(Consumer<FoundFieldSubject> inspection) {
CodeInspector.forAll(
- Arrays.asList(dexClass.staticFields()),
+ dexClass.staticFields(),
(dexField, clazz) -> new FoundFieldSubject(codeInspector, dexField, clazz),
this,
inspection);
CodeInspector.forAll(
- Arrays.asList(dexClass.instanceFields()),
+ dexClass.instanceFields(),
(dexField, clazz) -> new FoundFieldSubject(codeInspector, dexField, clazz),
this,
inspection);
@@ -175,7 +175,7 @@
return dexClass.accessFlags.isAnnotation();
}
- private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
+ private DexEncodedField findField(List<DexEncodedField> fields, DexField dexField) {
for (DexEncodedField field : fields) {
if (field.field.equals(dexField)) {
return field;