Merge commit 'b7074ccebdab2c54493b49ed3fc53f8e26c2cb6c' into dev-release
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index ef8708f..782094c 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -256,7 +256,7 @@
if (superTarget != null) {
addMethod(superTarget.getReference());
}
- for (DexType type : method.getDefinition().parameters().values) {
+ for (DexType type : method.getDefinition().getParameters()) {
registerTypeReference(type);
}
for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
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 489d0b6..2d91d71 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -125,8 +125,7 @@
this.callSiteOptimizationInfoPropagator = null;
}
- this.libraryMethodSideEffectModelCollection =
- new LibraryMethodSideEffectModelCollection(dexItemFactory());
+ this.libraryMethodSideEffectModelCollection = new LibraryMethodSideEffectModelCollection(this);
this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
if (enableWholeProgramOptimizations() && options().protoShrinking().isProtoShrinkingEnabled()) {
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 fc56e08..8060cbe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -394,12 +394,16 @@
return method;
}
- public DexMethodSignature getSignature() {
- return new DexMethodSignature(method);
+ public DexType getParameter(int index) {
+ return getReference().getParameter(index);
}
- public DexTypeList parameters() {
- return method.proto.parameters;
+ public DexTypeList getParameters() {
+ return getReference().getParameters();
+ }
+
+ public DexMethodSignature getSignature() {
+ return new DexMethodSignature(method);
}
public DexType returnType() {
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 fd33483..e775ac6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -293,11 +293,6 @@
createString(Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX);
public final DexString thisName = createString("this");
-
- // As much as possible, R8 should rely on the content of the static enum field, using
- // enumMembers.isValuesFieldCandidate or checking the object state in the optimization info.
- // The field name is unrealiable since the filed can be minified prior to this compilation.
- // We keep enumValuesFieldName as a heuristic only.
public final DexString enumValuesFieldName = createString("$VALUES");
public final DexString enabledFieldName = createString("ENABLED");
@@ -440,6 +435,7 @@
createStaticallyKnownType("Ljava/util/logging/Logger;");
public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
+ public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
@@ -683,6 +679,17 @@
createString("makeConcat")
);
+ public Map<DexMethod, int[]> libraryMethodsNonNullParamOrThrow =
+ buildLibraryMethodsNonNullParamOrThrow();
+
+ private Map<DexMethod, int[]> buildLibraryMethodsNonNullParamOrThrow() {
+ ImmutableMap.Builder<DexMethod, int[]> builder = ImmutableMap.builder();
+ for (DexMethod requireNonNullMethod : objectsMethods.requireNonNullMethods()) {
+ builder.put(requireNonNullMethod, new int[] {0});
+ }
+ return builder.build();
+ }
+
public Set<DexMethod> libraryMethodsReturningReceiver =
ImmutableSet.<DexMethod>builder()
.addAll(stringBufferMethods.appendMethods)
@@ -719,6 +726,7 @@
public Set<DexType> libraryTypesAssumedToBePresent =
ImmutableSet.<DexType>builder()
.add(
+ androidAppActivity,
callableType,
enumType,
npeType,
@@ -1390,17 +1398,21 @@
return field == nameField || field == ordinalField;
}
- public boolean isEnumField(DexEncodedField staticField, DexType enumType) {
- assert staticField.isStatic();
- return staticField.getType() == enumType && staticField.isEnum() && staticField.isFinal();
+ public boolean isValuesMethod(DexMethod method, DexClass enumClass) {
+ assert enumClass.isEnum();
+ return method.holder == enumClass.type
+ && method.proto.returnType == enumClass.type.toArrayType(1, DexItemFactory.this)
+ && method.proto.parameters.size() == 0
+ && method.name == valuesMethodName;
}
- public boolean isValuesFieldCandidate(DexEncodedField staticField, DexType enumType) {
- assert staticField.isStatic();
- return staticField.getType().isArrayType()
- && staticField.getType().toArrayElementType(DexItemFactory.this) == enumType
- && staticField.isSynthetic()
- && staticField.isFinal();
+ public boolean isValueOfMethod(DexMethod method, DexClass enumClass) {
+ assert enumClass.isEnum();
+ return method.holder == enumClass.type
+ && method.proto.returnType == enumClass.type
+ && method.proto.parameters.size() == 1
+ && method.proto.parameters.values[0] == stringType
+ && method.name == valueOfMethodName;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 56f9a92..c488caf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralMapping;
@@ -44,6 +43,7 @@
"$r8$twr$utility",
"$-DC",
"$$ServiceLoaderMethods",
+ "com.android.tools.r8.GeneratedOutlineSupport",
"-$$Lambda$");
public final DexString descriptor;
@@ -338,7 +338,6 @@
// Any entry that is removed from here must be added to OLD_SYNTHESIZED_NAMES to ensure that
// newer releases can be used to merge previous builds.
return name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
- || name.contains(OutlineOptions.CLASS_NAME) // Global singleton.
|| name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME); // Global singleton.
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index fc01602..281215f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -12,6 +12,9 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
+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.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -23,7 +26,6 @@
import com.android.tools.r8.ir.analysis.value.NullOrAbstractValue;
import com.android.tools.r8.ir.analysis.value.ObjectState;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
-import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -38,13 +40,10 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
-import java.util.IdentityHashMap;
-import java.util.Map;
public class StaticFieldValueAnalysis extends FieldValueAnalysis {
private final StaticFieldValues.Builder builder;
- private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
private StaticFieldValueAnalysis(
AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
@@ -64,7 +63,7 @@
timing.begin("Analyze class initializer");
StaticFieldValues result =
new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
- .analyze(classInitializerDefaultsResult);
+ .analyze(classInitializerDefaultsResult, code.context().getHolderType());
timing.end();
return result;
}
@@ -79,7 +78,8 @@
return this;
}
- StaticFieldValues analyze(ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+ StaticFieldValues analyze(
+ ClassInitializerDefaultsResult classInitializerDefaultsResult, DexType holderType) {
computeFieldOptimizationInfo(classInitializerDefaultsResult);
return builder.build();
}
@@ -218,41 +218,16 @@
return null;
}
assert !value.hasAliasedValue();
- if (value.isPhi()) {
- return null;
- }
- if (value.definition.isNewArrayEmpty()) {
+ if (isEnumValuesArray(value)) {
return computeSingleEnumFieldValueForValuesArray(value);
}
- if (value.definition.isNewInstance()) {
- return computeSingleEnumFieldValueForInstance(value);
- }
- return null;
+ return computeSingleEnumFieldValueForInstance(value);
}
private SingleFieldValue computeSingleEnumFieldValueForValuesArray(Value value) {
- if (!value.definition.isNewArrayEmpty()) {
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty)) {
return null;
}
- AbstractValue valuesValue = computedValues.get(value);
- if (valuesValue != null) {
- // This implicitely answers null if the value could not get computed.
- if (valuesValue.isSingleFieldValue()) {
- SingleFieldValue fieldValue = valuesValue.asSingleFieldValue();
- if (fieldValue.getState().isEnumValuesObjectState()) {
- return fieldValue;
- }
- }
- return null;
- }
- SingleFieldValue singleFieldValue = internalComputeSingleEnumFieldValueForValuesArray(value);
- computedValues.put(
- value, singleFieldValue == null ? UnknownValue.getInstance() : singleFieldValue);
- return singleFieldValue;
- }
-
- private SingleFieldValue internalComputeSingleEnumFieldValueForValuesArray(Value value) {
- assert value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty);
NewArrayEmpty newArrayEmpty = value.definition.asNewArrayEmpty();
if (newArrayEmpty.type.toBaseType(appView.dexItemFactory()) != context.getHolder().type) {
@@ -292,9 +267,7 @@
// We need the state of all fields for the analysis to be valuable.
return null;
}
- if (!valuesArrayIndexMatchesOrdinal(index, objectState)) {
- return null;
- }
+ assert verifyValuesArrayIndexMatchesOrdinal(index, objectState);
if (valuesState[index] != null) {
return null;
}
@@ -353,25 +326,24 @@
return ObjectState.empty();
}
- private boolean valuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
+ private boolean verifyValuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
DexEncodedField ordinalField =
appView
.appInfo()
.resolveField(appView.dexItemFactory().enumMembers.ordinalField, context)
.getResolvedField();
- if (ordinalField == null) {
- return false;
- }
+ assert ordinalField != null;
AbstractValue ordinalState = objectState.getAbstractFieldValue(ordinalField);
- if (ordinalState == null || !ordinalState.isSingleNumberValue()) {
- return false;
- }
- int intValue = ordinalState.asSingleNumberValue().getIntValue();
- return intValue == ordinal;
+ assert ordinalState != null;
+ assert ordinalState.isSingleNumberValue();
+ assert ordinalState.asSingleNumberValue().getIntValue() == ordinal;
+ return true;
}
private SingleFieldValue computeSingleEnumFieldValueForInstance(Value value) {
- assert value.isDefinedByInstructionSatisfying(Instruction::isNewInstance);
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+ return null;
+ }
NewInstance newInstance = value.definition.asNewInstance();
// Some enums have direct subclasses, and the subclass is instantiated here.
@@ -487,7 +459,30 @@
}
private boolean isEnumValuesArray(Value value) {
- SingleFieldValue singleFieldValue = computeSingleEnumFieldValueForValuesArray(value);
- return singleFieldValue != null && singleFieldValue.getState().isEnumValuesObjectState();
+ assert context.getHolder().isEnum();
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexField valuesField =
+ dexItemFactory.createField(
+ context.getHolderType(),
+ context.getHolderType().toArrayType(1, dexItemFactory),
+ dexItemFactory.enumValuesFieldName);
+
+ Value root = value.getAliasedValue();
+ if (root.isPhi()) {
+ return false;
+ }
+
+ Instruction definition = root.definition;
+ if (definition.isNewArrayEmpty()) {
+ for (Instruction user : root.aliasedUsers()) {
+ if (user.isStaticPut() && user.asStaticPut().getField() == valuesField) {
+ return true;
+ }
+ }
+ } else if (definition.isStaticGet()) {
+ return definition.asStaticGet().getField() == valuesField;
+ }
+
+ return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
index 65e1ea3..b2dcdcd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
@@ -37,10 +37,17 @@
// All the abstract values stored here may match a pinned field, using them requires therefore
// to check the field is not pinned or prove it is no longer pinned.
public static class EnumStaticFieldValues extends StaticFieldValues {
- private final ImmutableMap<DexField, ObjectState> enumAbstractValues;
+ private final ImmutableMap<DexField, AbstractValue> enumAbstractValues;
+ private final DexField valuesField;
+ private final AbstractValue valuesAbstractValue;
- public EnumStaticFieldValues(ImmutableMap<DexField, ObjectState> enumAbstractValues) {
+ public EnumStaticFieldValues(
+ ImmutableMap<DexField, AbstractValue> enumAbstractValues,
+ DexField valuesField,
+ AbstractValue valuesAbstractValue) {
this.enumAbstractValues = enumAbstractValues;
+ this.valuesField = valuesField;
+ this.valuesAbstractValue = valuesAbstractValue;
}
static StaticFieldValues.Builder builder() {
@@ -48,38 +55,33 @@
}
public static class Builder extends StaticFieldValues.Builder {
- private final ImmutableMap.Builder<DexField, ObjectState> enumObjectStateBuilder =
+ private final ImmutableMap.Builder<DexField, AbstractValue> enumAbstractValuesBuilder =
ImmutableMap.builder();
- private AbstractValue valuesCandidateAbstractValue;
+ private DexField valuesFields;
+ private AbstractValue valuesAbstractValue;
Builder() {}
@Override
public void recordStaticField(
DexEncodedField staticField, AbstractValue value, DexItemFactory factory) {
- if (factory.enumMembers.isValuesFieldCandidate(staticField, staticField.getHolderType())) {
- if (value.isSingleFieldValue()
- && value.asSingleFieldValue().getState().isEnumValuesObjectState()) {
- assert valuesCandidateAbstractValue == null
- || valuesCandidateAbstractValue.equals(value);
- valuesCandidateAbstractValue = value;
- enumObjectStateBuilder.put(staticField.field, value.asSingleFieldValue().getState());
- }
- } else if (factory.enumMembers.isEnumField(staticField, staticField.getHolderType())) {
- if (value.isSingleFieldValue() && !value.asSingleFieldValue().getState().isEmpty()) {
- enumObjectStateBuilder.put(staticField.field, value.asSingleFieldValue().getState());
- }
+ // TODO(b/166532388): Stop relying on the values name.
+ if (staticField.getName() == factory.enumValuesFieldName) {
+ valuesFields = staticField.field;
+ valuesAbstractValue = value;
+ } else if (staticField.isEnum()) {
+ enumAbstractValuesBuilder.put(staticField.field, value);
}
}
@Override
public StaticFieldValues build() {
- ImmutableMap<DexField, ObjectState> enumAbstractValues = enumObjectStateBuilder.build();
- if (enumAbstractValues.isEmpty()) {
+ ImmutableMap<DexField, AbstractValue> enumAbstractValues =
+ enumAbstractValuesBuilder.build();
+ if (valuesAbstractValue == null && enumAbstractValues.isEmpty()) {
return EmptyStaticValues.getInstance();
}
- assert enumAbstractValues.values().stream().noneMatch(ObjectState::isEmpty);
- return new EnumStaticFieldValues(enumAbstractValues);
+ return new EnumStaticFieldValues(enumAbstractValues, valuesFields, valuesAbstractValue);
}
}
@@ -94,7 +96,20 @@
}
public ObjectState getObjectStateForPossiblyPinnedField(DexField field) {
- return enumAbstractValues.get(field);
+ AbstractValue fieldValue = enumAbstractValues.get(field);
+ if (fieldValue == null || fieldValue.isZero()) {
+ return null;
+ }
+ if (fieldValue.isSingleFieldValue()) {
+ return fieldValue.asSingleFieldValue().getState();
+ }
+ assert fieldValue.isUnknown();
+ return ObjectState.empty();
+ }
+
+ public AbstractValue getValuesAbstractValueForPossiblyPinnedField(DexField field) {
+ assert valuesField == field || valuesAbstractValue == null;
+ return valuesAbstractValue;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 0f0d6e9..85d90e6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -328,6 +328,9 @@
}
public boolean onlyUsedInBlock(BasicBlock block) {
+ if (hasPhiUsers() || hasDebugUsers()) {
+ return false;
+ }
for (Instruction user : uniqueUsers()) {
if (user.getBlock() != block) {
return false;
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 dd9bee9..11a9d00 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
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -101,7 +100,6 @@
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -838,9 +836,8 @@
outliner.identifyOutlineSites(code);
},
executorService);
- DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
- appView.appInfo().addSynthesizedClass(outlineClass, true);
- optimizeSynthesizedClass(outlineClass, executorService);
+ List<ProgramMethod> outlineMethods = outliner.buildOutlineMethods();
+ optimizeSynthesizedMethods(outlineMethods, executorService);
forEachSelectedOutliningMethod(
methodsSelectedForOutlining,
code -> {
@@ -852,8 +849,7 @@
executorService);
feedback.updateVisibleOptimizationInfo();
assert outliner.checkAllOutlineSitesFoundAgain();
- builder.addSynthesizedClass(outlineClass);
- clearDexMethodCompilationState(outlineClass);
+ outlineMethods.forEach(m -> m.getDefinition().markNotProcessed());
}
timing.end();
}
@@ -1037,27 +1033,11 @@
}
}
- // Find an unused name for the outlining class. When multiple runs produces additional
- // outlining the default outlining class might already be present.
- private DexType computeOutlineClassType() {
- DexType result;
- int count = 0;
- String prefix = appView.options().synthesizedClassPrefix.replace('/', '.');
- do {
- String name =
- prefix + OutlineOptions.CLASS_NAME + (count == 0 ? "" : Integer.toString(count));
- count++;
- result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name));
-
- } while (appView.appInfo().definitionForWithoutExistenceAssert(result) != null);
- return result;
- }
-
- public void optimizeSynthesizedClass(
- DexProgramClass clazz, ExecutorService executorService)
+ public void optimizeSynthesizedMethods(
+ List<ProgramMethod> programMethods, ExecutorService executorService)
throws ExecutionException {
// Process the generated class, but don't apply any outlining.
- SortedProgramMethodSet methods = SortedProgramMethodSet.create(clazz::forEachProgramMethod);
+ SortedProgramMethodSet methods = SortedProgramMethodSet.create(programMethods::forEach);
processMethodsConcurrently(methods, executorService);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 994e2ba..09fd687 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -165,11 +165,9 @@
public void processSynthesizedClasses(IRConverter converter, ExecutorService executor)
throws ExecutionException {
while (!synthesizedMethods.isEmpty()) {
- List<ProgramMethod> methods = new ArrayList<>(synthesizedMethods);
+ ArrayList<ProgramMethod> methods = new ArrayList<>(synthesizedMethods);
synthesizedMethods.clear();
- for (ProgramMethod method : methods) {
- converter.optimizeSynthesizedClass(method.getHolder(), executor);
- }
+ converter.optimizeSynthesizedMethods(methods, executor);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 23342df..b176db1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -165,7 +165,7 @@
finalFieldPuts.forEach(
(field, put) -> {
DexType fieldType = field.field.type;
- Value value = put.value();
+ Value value = put.value().getAliasedValue();
if (unnecessaryStaticPuts.contains(put)) {
if (fieldType == dexItemFactory.stringType) {
fieldsWithStaticValues.put(field, getDexStringValue(value, context.getHolderType()));
@@ -404,7 +404,7 @@
}
DexField fieldReference = put.getField();
DexEncodedField field = context.getHolder().lookupField(fieldReference);
- Value value = put.value();
+ Value value = put.value().getAliasedValue();
TypeElement valueType = value.getType();
if (field != null) {
if (isReadBefore.contains(field)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 11b4ddf..dbad5bd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -10,26 +10,16 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
@@ -65,8 +55,8 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.ListUtils;
@@ -76,7 +66,6 @@
import com.android.tools.r8.utils.collections.ProgramMethodMultiset;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -102,9 +91,9 @@
* {@link OutlineOptions#threshold}). Each selected method is then converted back to IR and
* passed to {@link Outliner#identifyOutlineSites(IRCode)}, which then stores concrete
* outlining candidates in {@link Outliner#outlineSites}.
- * <li>Third, {@link Outliner#buildOutlinerClass(DexType)} is called to construct the <em>outline
- * support class</em> containing a static helper method for each outline candidate that occurs
- * frequently enough. Each selected method is then converted to IR, passed to {@link
+ * <li>Third, {@link Outliner#buildOutlineMethods()} is called to construct the <em>outline
+ * support classes</em> containing a static helper method for each outline candidate that
+ * occurs frequently enough. Each selected method is then converted to IR, passed to {@link
* Outliner#applyOutliningCandidate(IRCode)} to perform the outlining, and converted back to
* the output format (DEX or CF).
* </ul>
@@ -116,7 +105,7 @@
new ArrayList<>();
/** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
private final Map<Outline, List<ProgramMethod>> outlineSites = new HashMap<>();
- /** Result of third step (see {@link Outliner#buildOutlinerClass(DexType)}. */
+ /** Result of third step (see {@link Outliner#buildOutlineMethods()}. */
private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
static final int MAX_IN_SIZE = 5; // Avoid using ranged calls for outlined code.
@@ -581,11 +570,6 @@
return proto;
}
- // Build the DexMethod for this outline.
- DexMethod buildMethod(DexType clazz, DexString name) {
- return appView.dexItemFactory().createMethod(clazz, buildProto(), name);
- }
-
@Override
public boolean equals(Object other) {
if (!(other instanceof Outline)) {
@@ -1355,67 +1339,40 @@
return methodsSelectedForOutlining;
}
- public DexProgramClass buildOutlinerClass(DexType type) {
- // Build the outlined methods.
- // By now the candidates are the actual selected outlines. Name the generated methods in a
- // consistent order, to provide deterministic output.
+ public List<ProgramMethod> buildOutlineMethods() {
+ List<ProgramMethod> outlineMethods = new ArrayList<>();
+ // By now the candidates are the actual selected outlines. Iterate the outlines in a
+ // consistent order, to provide deterministic naming of the internal-synthetics.
+ // The choice of 'representative' will ensure deterministic naming of the external names.
List<Outline> outlines = selectOutlines();
outlines.sort(Comparator.naturalOrder());
- DexEncodedMethod[] direct = new DexEncodedMethod[outlines.size()];
- int count = 0;
for (Outline outline : outlines) {
- MethodAccessFlags methodAccess =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
- DexString methodName =
- appView.dexItemFactory().createString(OutlineOptions.METHOD_PREFIX + count);
- DexMethod method = outline.buildMethod(type, methodName);
List<ProgramMethod> sites = outlineSites.get(outline);
assert !sites.isEmpty();
- direct[count] =
- new DexEncodedMethod(
- method,
- methodAccess,
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- new OutlineCode(outline),
- true);
- if (appView.options().isGeneratingClassFiles()) {
- direct[count].upgradeClassFileVersion(sites.get(0).getDefinition().getClassFileVersion());
- }
- generatedOutlines.put(outline, method);
- count++;
+ ProgramMethod representative = findDeterministicRepresentative(sites);
+ ProgramMethod outlineMethod =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.OUTLINE,
+ representative,
+ appView.dexItemFactory(),
+ builder -> {
+ builder
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC, false))
+ .setProto(outline.buildProto())
+ .setCode(m -> new OutlineCode(outline));
+ if (appView.options().isGeneratingClassFiles()) {
+ builder.setClassFileVersion(
+ representative.getDefinition().getClassFileVersion());
+ }
+ });
+ generatedOutlines.put(outline, outlineMethod.getReference());
+ outlineMethods.add(outlineMethod);
}
- // No need to sort the direct methods as they are generated in sorted order.
-
- // Build the outliner class.
- DexTypeList interfaces = DexTypeList.empty();
- DexString sourceFile = appView.dexItemFactory().createString("outline");
- ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
- assert !appView.options().encodeChecksums;
- // The outliner is R8 only and checksum is not a supported part of R8 compilation.
- ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
- return new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("outlining", getClass()),
- accessFlags,
- appView.dexItemFactory().objectType,
- interfaces,
- sourceFile,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY, // Static fields.
- DexEncodedField.EMPTY_ARRAY, // Instance fields.
- direct,
- DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
- appView.dexItemFactory().getSkipNameValidationForTesting(),
- checksumSupplier);
+ return outlineMethods;
}
private List<Outline> selectOutlines() {
@@ -1430,6 +1387,18 @@
return result;
}
+ private static ProgramMethod findDeterministicRepresentative(List<ProgramMethod> members) {
+ // Pick a deterministic member as representative.
+ ProgramMethod smallest = members.get(0);
+ for (int i = 1; i < members.size(); i++) {
+ ProgramMethod next = members.get(i);
+ if (next.getReference().compareTo(smallest.getReference()) < 0) {
+ smallest = next;
+ }
+ }
+ return smallest;
+ }
+
public void applyOutliningCandidate(IRCode code) {
assert !code.method().getCode().isOutlineCode();
ListIterator<BasicBlock> blocksIterator = code.listIterator();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index cf0ecb5..166022e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -491,36 +491,34 @@
// Step 1: We iterate over the field to find direct enum instance information and the values
// fields.
for (DexEncodedField staticField : enumClass.staticFields()) {
- if (factory.enumMembers.isEnumField(staticField, enumClass.type)) {
+ if (EnumUnboxingCandidateAnalysis.isEnumField(staticField, enumClass.type)) {
ObjectState enumState =
enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
- if (enumState == null) {
- if (!isFinalFieldInitialized(staticField, enumClass)) {
- continue;
+ if (enumState != null) {
+ OptionalInt optionalOrdinal = getOrdinal(enumState);
+ if (!optionalOrdinal.isPresent()) {
+ return null;
}
- // Tracking the content of the field yielded either an empty object state, or something
- // incoherent. We bail out.
+ int ordinal = optionalOrdinal.getAsInt();
+ unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal));
+ ordinalToObjectState.put(ordinal, enumState);
+ }
+ } else if (EnumUnboxingCandidateAnalysis.matchesValuesField(
+ staticField, enumClass.type, factory)) {
+ AbstractValue valuesValue =
+ enumStaticFieldValues.getValuesAbstractValueForPossiblyPinnedField(staticField.field);
+ if (valuesValue == null || valuesValue.isZero()) {
+ // Unused field
+ continue;
+ }
+ if (valuesValue.isUnknown()) {
return null;
}
- OptionalInt optionalOrdinal = getOrdinal(enumState);
- if (!optionalOrdinal.isPresent()) {
+ assert valuesValue.isSingleFieldValue();
+ ObjectState valuesState = valuesValue.asSingleFieldValue().getState();
+ if (!valuesState.isEnumValuesObjectState()) {
return null;
}
- int ordinal = optionalOrdinal.getAsInt();
- unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal));
- ordinalToObjectState.put(ordinal, enumState);
- } else if (factory.enumMembers.isValuesFieldCandidate(staticField, enumClass.type)) {
- ObjectState valuesState =
- enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
- if (valuesState == null) {
- if (!isFinalFieldInitialized(staticField, enumClass)) {
- continue;
- }
- // We could not track the content of that field, and the field could be a values field.
- // We conservatively bail out.
- return null;
- }
- assert valuesState.isEnumValuesObjectState();
assert valuesContents == null
|| valuesContents.equals(valuesState.asEnumValuesObjectState());
valuesContents = valuesState.asEnumValuesObjectState();
@@ -566,13 +564,6 @@
valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
}
- private boolean isFinalFieldInitialized(DexEncodedField staticField, DexProgramClass enumClass) {
- assert staticField.isFinal();
- return appView
- .appInfo()
- .isFieldOnlyWrittenInMethodIgnoringPinning(staticField, enumClass.getClassInitializer());
- }
-
private EnumInstanceFieldData computeEnumFieldData(
DexField instanceField,
DexProgramClass enumClass,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 40b0c86..88845d9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -112,7 +112,7 @@
// Browse annotation values types in search for enum.
// Each annotation value is represented by a virtual method.
for (DexEncodedMethod method : clazz.virtualMethods()) {
- assert method.parameters().isEmpty()
+ assert method.getParameters().isEmpty()
|| appView.options().testing.allowInjectedAnnotationMethods;
DexType valueType = method.returnType().toBaseType(appView.dexItemFactory());
if (enumToUnboxCandidates.isCandidate(valueType)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 4c7581e..6c20c4f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -434,7 +434,7 @@
return factory.createField(
relocator.getNewMemberLocationFor(enumType),
factory.intArrayType,
- "$$values$$field$" + compatibleName(enumType));
+ factory.enumValuesFieldName + "$field$" + compatibleName(enumType));
}
private DexEncodedField computeValuesEncodedField(DexField field) {
@@ -451,7 +451,7 @@
return factory.createMethod(
relocator.getNewMemberLocationFor(enumType),
factory.createProto(factory.intArrayType),
- "$$values$$method$" + compatibleName(enumType));
+ factory.enumValuesFieldName + "$method$" + compatibleName(enumType));
}
private DexEncodedMethod computeValuesEncodedMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index 33b7242..ded60a3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -85,7 +85,7 @@
}
for (int i = 0; i < method.method.getArity(); i++) {
staticTypes[i + argOffset] =
- TypeElement.fromDexType(method.parameters().values[i], maybeNull(), appView);
+ TypeElement.fromDexType(method.getParameter(i), maybeNull(), appView);
}
return staticTypes;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 7f5d173..93a330e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -52,6 +52,10 @@
public abstract BitSet getNonNullParamOrThrow();
+ public final boolean hasNonNullParamOnNormalExits() {
+ return getNonNullParamOnNormalExits() != null;
+ }
+
public abstract BitSet getNonNullParamOnNormalExits();
public abstract boolean hasBeenInlinedIntoSingleCallSite();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 9868a64..3aaba06 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -156,8 +156,8 @@
definition, code, feedback, instanceFieldInitializationInfos, timing);
computeMayHaveSideEffects(feedback, definition, code, timing);
computeReturnValueOnlyDependsOnArguments(feedback, definition, code, timing);
- computeNonNullParamOrThrow(feedback, definition, code, timing);
- computeNonNullParamOnNormalExits(feedback, code, timing);
+ BitSet nonNullParamOrThrow = computeNonNullParamOrThrow(feedback, definition, code, timing);
+ computeNonNullParamOnNormalExits(feedback, code, nonNullParamOrThrow, timing);
}
private void identifyBridgeInfo(
@@ -1165,18 +1165,19 @@
}
}
- private void computeNonNullParamOrThrow(
+ private BitSet computeNonNullParamOrThrow(
OptimizationFeedback feedback, DexEncodedMethod method, IRCode code, Timing timing) {
timing.begin("Compute non-null-param-or-throw");
- computeNonNullParamOrThrow(feedback, method, code);
+ BitSet nonNullParamOrThrow = computeNonNullParamOrThrow(feedback, method, code);
timing.end();
+ return nonNullParamOrThrow;
}
// Track usage of parameters and compute their nullability and possibility of NPE.
- private void computeNonNullParamOrThrow(
+ private BitSet computeNonNullParamOrThrow(
OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
if (method.getOptimizationInfo().getNonNullParamOrThrow() != null) {
- return;
+ return null;
}
List<Value> arguments = code.collectArguments();
BitSet paramsCheckedForNull = new BitSet();
@@ -1196,66 +1197,80 @@
paramsCheckedForNull.set(index);
}
}
- if (paramsCheckedForNull.length() > 0) {
+ if (!paramsCheckedForNull.isEmpty()) {
feedback.setNonNullParamOrThrow(method, paramsCheckedForNull);
+ return paramsCheckedForNull;
}
+ return null;
}
private void computeNonNullParamOnNormalExits(
- OptimizationFeedback feedback, IRCode code, Timing timing) {
+ OptimizationFeedback feedback, IRCode code, BitSet nonNullParamOrThrow, Timing timing) {
timing.begin("Compute non-null-param-on-normal-exits");
- computeNonNullParamOnNormalExits(feedback, code);
+ computeNonNullParamOnNormalExits(feedback, code, nonNullParamOrThrow);
timing.end();
}
- private void computeNonNullParamOnNormalExits(OptimizationFeedback feedback, IRCode code) {
+ private void computeNonNullParamOnNormalExits(
+ OptimizationFeedback feedback, IRCode code, BitSet nonNullParamOrThrow) {
Set<BasicBlock> normalExits = Sets.newIdentityHashSet();
normalExits.addAll(code.computeNormalExitBlocks());
DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
List<Value> arguments = code.collectArguments();
BitSet facts = new BitSet();
- Set<BasicBlock> nullCheckedBlocks = Sets.newIdentityHashSet();
+ if (nonNullParamOrThrow != null) {
+ facts.or(nonNullParamOrThrow);
+ }
for (int index = 0; index < arguments.size(); index++) {
+ if (facts.get(index)) {
+ continue;
+ }
Value argument = arguments.get(index);
- // Consider reference-type parameter only.
- if (!argument.getType().isReferenceType()) {
- continue;
- }
- // The receiver is always non-null on normal exits.
- if (argument.isThis()) {
+ if (argument.getType().isReferenceType()
+ && isNonNullOnNormalExit(code, argument, dominatorTree, normalExits)) {
facts.set(index);
- continue;
- }
- // Collect basic blocks that check nullability of the parameter.
- nullCheckedBlocks.clear();
- for (Instruction user : argument.uniqueUsers()) {
- if (user.isAssumeWithNonNullAssumption()) {
- nullCheckedBlocks.add(user.asAssume().getBlock());
- }
- if (user.isIf()
- && user.asIf().isZeroTest()
- && (user.asIf().getType() == If.Type.EQ || user.asIf().getType() == If.Type.NE)) {
- nullCheckedBlocks.add(user.asIf().targetFromNonNullObject());
- }
- }
- if (!nullCheckedBlocks.isEmpty()) {
- boolean allExitsCovered = true;
- for (BasicBlock normalExit : normalExits) {
- if (!isNormalExitDominated(normalExit, code, dominatorTree, nullCheckedBlocks)) {
- allExitsCovered = false;
- break;
- }
- }
- if (allExitsCovered) {
- facts.set(index);
- }
}
}
- if (facts.length() > 0) {
+ if (!facts.isEmpty()) {
feedback.setNonNullParamOnNormalExits(code.method(), facts);
}
}
+ private boolean isNonNullOnNormalExit(
+ IRCode code, Value value, DominatorTree dominatorTree, Set<BasicBlock> normalExits) {
+ assert value.getType().isReferenceType();
+
+ // The receiver is always non-null on normal exits.
+ if (value.isThis()) {
+ return true;
+ }
+
+ // Collect basic blocks that check nullability of the parameter.
+ Set<BasicBlock> nullCheckedBlocks = Sets.newIdentityHashSet();
+ for (Instruction user : value.aliasedUsers()) {
+ if (user.isAssumeWithNonNullAssumption()
+ || user.throwsNpeIfValueIsNull(value, appView, code.context())) {
+ nullCheckedBlocks.add(user.getBlock());
+ }
+ if (user.isIf()
+ && user.asIf().isZeroTest()
+ && (user.asIf().getType() == If.Type.EQ || user.asIf().getType() == If.Type.NE)) {
+ nullCheckedBlocks.add(user.asIf().targetFromNonNullObject());
+ }
+ }
+
+ if (nullCheckedBlocks.isEmpty()) {
+ return false;
+ }
+
+ for (BasicBlock normalExit : normalExits) {
+ if (!isNormalExitDominated(normalExit, code, dominatorTree, nullCheckedBlocks)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean isNormalExitDominated(
BasicBlock normalExit,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
index ae91ac8..3703148 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.ir.optimize.library;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.library.sideeffects.JavaLangObjectsSideEffectCollection;
import com.android.tools.r8.utils.BiPredicateUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -25,23 +27,31 @@
private final Set<DexMethod> nonFinalMethodsWithoutSideEffects;
- public LibraryMethodSideEffectModelCollection(DexItemFactory dexItemFactory) {
- finalMethodsWithoutSideEffects = buildFinalMethodsWithoutSideEffects(dexItemFactory);
+ public LibraryMethodSideEffectModelCollection(AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ finalMethodsWithoutSideEffects = buildFinalMethodsWithoutSideEffects(appView, dexItemFactory);
unconditionalFinalMethodsWithoutSideEffects =
buildUnconditionalFinalMethodsWithoutSideEffects(dexItemFactory);
nonFinalMethodsWithoutSideEffects = buildNonFinalMethodsWithoutSideEffects(dexItemFactory);
}
private static Map<DexMethod, BiPredicate<DexMethod, List<Value>>>
- buildFinalMethodsWithoutSideEffects(DexItemFactory dexItemFactory) {
+ buildFinalMethodsWithoutSideEffects(AppView<?> appView, DexItemFactory dexItemFactory) {
ImmutableMap.Builder<DexMethod, BiPredicate<DexMethod, List<Value>>> builder =
ImmutableMap.<DexMethod, BiPredicate<DexMethod, List<Value>>>builder()
.put(
+ dexItemFactory.objectsMethods.toStringWithObject,
+ (method, arguments) ->
+ !JavaLangObjectsSideEffectCollection.toStringMayHaveSideEffects(
+ appView, arguments))
+ .put(
dexItemFactory.stringMembers.constructor,
(method, arguments) -> arguments.get(1).isNeverNull())
.put(
dexItemFactory.stringMembers.valueOf,
- (method, arguments) -> arguments.get(0).isNeverNull());
+ (method, arguments) ->
+ !JavaLangObjectsSideEffectCollection.toStringMayHaveSideEffects(
+ appView, arguments));
putAll(
builder,
dexItemFactory.stringBufferMethods.constructorMethods,
@@ -71,8 +81,11 @@
.add(dexItemFactory.shortMembers.toString)
.add(dexItemFactory.stringBufferMethods.toString)
.add(dexItemFactory.stringBuilderMethods.toString)
+ .add(dexItemFactory.stringMembers.length)
.add(dexItemFactory.stringMembers.hashCode)
+ .add(dexItemFactory.stringMembers.isEmpty)
.add(dexItemFactory.stringMembers.toString)
+ .add(dexItemFactory.stringMembers.trim)
.addAll(dexItemFactory.classMethods.getNames)
.addAll(dexItemFactory.boxedValueOfMethods())
.build();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index ecbb3e4..1141303 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -45,6 +45,7 @@
void run(Set<DexEncodedField> finalLibraryFields) {
modelInstanceInitializers();
modelStaticFinalLibraryFields(finalLibraryFields);
+ modelLibraryMethodsNonNullParamOrThrow();
modelLibraryMethodsReturningNonNull();
modelLibraryMethodsReturningReceiver();
modelLibraryMethodsWithoutSideEffects();
@@ -87,6 +88,30 @@
}
}
+ private void modelLibraryMethodsNonNullParamOrThrow() {
+ dexItemFactory.libraryMethodsNonNullParamOrThrow.forEach(
+ (method, nonNullParamOrThrow) -> {
+ DexEncodedMethod definition = lookupMethod(method);
+ if (definition != null) {
+ assert nonNullParamOrThrow.length > 0;
+ int size = nonNullParamOrThrow[nonNullParamOrThrow.length - 1] + 1;
+ BitSet bitSet = new BitSet(size);
+ for (int argumentIndex : nonNullParamOrThrow) {
+ assert argumentIndex < size;
+ bitSet.set(argumentIndex);
+ }
+ feedback.setNonNullParamOrThrow(definition, bitSet);
+
+ // Also set non-null-param-on-normal-exits info.
+ if (definition.getOptimizationInfo().hasNonNullParamOnNormalExits()) {
+ definition.getOptimizationInfo().getNonNullParamOnNormalExits().or(bitSet);
+ } else {
+ feedback.setNonNullParamOnNormalExits(definition, (BitSet) bitSet.clone());
+ }
+ }
+ });
+ }
+
private void modelLibraryMethodsReturningNonNull() {
for (DexMethod method : dexItemFactory.libraryMethodsReturningNonNull) {
DexEncodedMethod definition = lookupMethod(method);
@@ -131,14 +156,6 @@
DexEncodedMethod definition = lookupMethod(requireNonNullMethod);
if (definition != null) {
feedback.methodReturnsArgument(definition, 0);
-
- BitSet nonNullParamOrThrow = new BitSet();
- nonNullParamOrThrow.set(0);
- feedback.setNonNullParamOrThrow(definition, nonNullParamOrThrow);
-
- BitSet nonNullParamOnNormalExits = new BitSet();
- nonNullParamOnNormalExits.set(0);
- feedback.setNonNullParamOnNormalExits(definition, nonNullParamOnNormalExits);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
index a303082..831236b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -5,19 +5,15 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexItemFactory.ObjectsMethods;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Set;
public class ObjectsMethodOptimizer extends StatelessLibraryMethodModelCollection {
@@ -90,44 +86,6 @@
invoke.outValue().replaceUsers(object);
}
instructionIterator.removeOrReplaceByDebugLocalRead();
- return;
- }
-
- // Remove Objects.toString() if it is unused and does not have side effects.
- if (!invoke.hasOutValue() || !invoke.outValue().hasNonDebugUsers()) {
- // Calling toString() on an array does not call toString() on the array elements.
- if (type.isArrayType()) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- return;
- }
-
- assert type.isClassType();
-
- // Check if this is a library class with a toString() method that does not have side effects.
- DexType classType = type.asClassType().getClassType();
- DexMethod toStringMethodReference =
- dexItemFactory.objectMembers.toString.withHolder(classType, dexItemFactory);
- if (appView
- .getLibraryMethodSideEffectModelCollection()
- .isSideEffectFreeFinalMethod(toStringMethodReference, invoke.arguments())) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- return;
- }
-
- // Check if this is a program class with a toString() method that does not have side effects.
- AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
- if (appInfo != null) {
- DexClass clazz = appInfo.definitionFor(classType, code.context());
- if (clazz != null && clazz.isEffectivelyFinal(appView)) {
- SingleResolutionResult resolutionResult =
- appInfo.resolveMethodOn(clazz, toStringMethodReference).asSingleResolution();
- if (resolutionResult != null
- && !resolutionResult.getResolvedMethod().getOptimizationInfo().mayHaveSideEffects()) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- return;
- }
- }
- }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index a8fabe3..8c62340 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -19,7 +20,6 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.ValueUtils;
import java.util.Set;
public class StringMethodOptimizer extends StatelessLibraryMethodModelCollection {
@@ -48,7 +48,7 @@
if (singleTargetReference == dexItemFactory.stringMembers.equals) {
optimizeEquals(code, instructionIterator, invoke.asInvokeVirtual());
} else if (singleTargetReference == dexItemFactory.stringMembers.valueOf) {
- optimizeValueOf(instructionIterator, invoke.asInvokeStatic());
+ optimizeValueOf(code, instructionIterator, invoke.asInvokeStatic(), affectedValues);
}
}
@@ -65,12 +65,30 @@
}
}
- private void optimizeValueOf(InstructionListIterator instructionIterator, InvokeStatic invoke) {
- // Optimize String.valueOf(stringBuilder) if unused.
- if (ValueUtils.isNonNullStringBuilder(invoke.getFirstArgument(), dexItemFactory)) {
- if (!invoke.hasOutValue() || !invoke.outValue().hasNonDebugUsers()) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
+ private void optimizeValueOf(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeStatic invoke,
+ Set<Value> affectedValues) {
+ Value object = invoke.getFirstArgument();
+ TypeElement type = object.getType();
+
+ // Optimize String.valueOf(null) into "null".
+ if (type.isDefinitelyNull()) {
+ instructionIterator.replaceCurrentInstructionWithConstString(appView, code, "null");
+ if (invoke.hasOutValue()) {
+ affectedValues.addAll(invoke.outValue().affectedValues());
}
+ return;
+ }
+
+ // Optimize String.valueOf(nonNullString) into nonNullString.
+ if (type.isDefinitelyNotNull() && type.isStringType(dexItemFactory)) {
+ if (invoke.hasOutValue()) {
+ affectedValues.addAll(invoke.outValue().affectedValues());
+ invoke.outValue().replaceUsers(object);
+ }
+ instructionIterator.removeOrReplaceByDebugLocalRead();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
new file mode 100644
index 0000000..5f74465
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
@@ -0,0 +1,62 @@
+// 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.library.sideeffects;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+
+public class JavaLangObjectsSideEffectCollection {
+
+ public static boolean toStringMayHaveSideEffects(AppView<?> appView, List<Value> arguments) {
+ // Calling toString() on an array does not call toString() on the array elements.
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ TypeElement type = arguments.get(0).getType();
+ if (type.isArrayType() || type.isNullType()) {
+ return false;
+ }
+
+ assert type.isClassType();
+
+ // Check if this is a library class with a toString() method that does not have side effects.
+ DexType classType = type.asClassType().getClassType();
+ DexMethod toStringMethodReference =
+ dexItemFactory.objectMembers.toString.withHolder(classType, dexItemFactory);
+ if (appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isSideEffectFreeFinalMethod(toStringMethodReference, arguments)) {
+ return false;
+ }
+
+ if (appView.appInfo().hasLiveness()) {
+ AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
+
+ // Check if there is an -assumenosideeffects rule for the toString() method.
+ if (appInfo.isAssumeNoSideEffectsMethod(toStringMethodReference)) {
+ return false;
+ }
+
+ // Check if this is a program class with a toString() method that does not have side effects.
+ DexClass clazz = appInfo.definitionFor(classType);
+ if (clazz != null && clazz.isEffectivelyFinal(appView)) {
+ SingleResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(clazz, toStringMethodReference).asSingleResolution();
+ if (resolutionResult != null
+ && !resolutionResult.getResolvedMethod().getOptimizationInfo().mayHaveSideEffects()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index af2dee2..3c6c003 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -299,10 +299,6 @@
// Find Class#get*Name() with a constant-class and replace it with a const-string if possible.
public void rewriteClassGetName(AppView<?> appView, IRCode code) {
- // Conflict with {@link CodeRewriter#collectClassInitializerDefaults}.
- if (code.method().isClassInitializer()) {
- return;
- }
Set<Value> affectedValues = Sets.newIdentityHashSet();
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
@@ -363,11 +359,16 @@
// while its name is used to compute hash code, which won't be optimized, it's better not to
// compute the name.
if (!appView.options().testing.forceNameReflectionOptimization) {
- EscapeAnalysis escapeAnalysis =
- new EscapeAnalysis(appView, StringOptimizerEscapeAnalysisConfiguration.getInstance());
- if (mayBeRenamed || escapeAnalysis.isEscaping(code, out)) {
+ if (mayBeRenamed) {
continue;
}
+ if (invokedMethod != factory.classMethods.getSimpleName) {
+ EscapeAnalysis escapeAnalysis =
+ new EscapeAnalysis(appView, StringOptimizerEscapeAnalysisConfiguration.getInstance());
+ if (escapeAnalysis.isEscaping(code, out)) {
+ continue;
+ }
+ }
}
String descriptor = baseType.toDescriptorString();
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 f7ac800..bc50542 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -109,7 +109,6 @@
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -1724,8 +1723,6 @@
|| !options.testing.checkForNotExpandingMainDexTracingResult
|| previousMainDexTracingResult.isRoot(clazz)
|| clazz.toSourceString().contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX)
- // TODO(b/177847090): Consider not outlining anything in main dex.
- || clazz.toSourceString().contains(OutlineOptions.CLASS_NAME)
: "Class " + clazz.toSourceString() + " was not a main dex root in the first round";
// Mark types in inner-class attributes referenced.
@@ -3042,6 +3039,10 @@
ImmutableSet<ProguardKeepRuleBase> keepAllSet =
ImmutableSet.of(appView.options().getProguardConfiguration().getKeepAllRule());
for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (appView.getSyntheticItems().isNonLegacySynthetic(clazz)) {
+ // Don't treat compiler synthesized classes as kept roots.
+ continue;
+ }
enqueueRootClass(clazz, keepAllSet);
clazz.forEachProgramMethod(method -> enqueueRootMethod(method, keepAllSet));
clazz.forEachProgramField(field -> enqueueRootField(field, keepAllSet));
@@ -4050,7 +4051,7 @@
}
private void markParameterAndReturnTypesAsLive(ProgramMethod method) {
- for (DexType parameterType : method.getDefinition().parameters().values) {
+ for (DexType parameterType : method.getDefinition().getParameters()) {
markTypeAsLive(parameterType, method);
}
markTypeAsLive(method.getDefinition().returnType(), method);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index a802761..bf9106a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -134,6 +134,7 @@
assert !syntheticMethodsMap.containsKey(from);
syntheticMethodsMap.put(from, to);
methodMap.put(from, to);
+ typeMap.put(from.getHolderType(), to.getHolderType());
}
void move(DexType from, DexType to) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 657db43..d526570 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -201,6 +201,10 @@
return pending.legacyClasses.containsKey(type);
}
+ public boolean isNonLegacySynthetic(DexProgramClass clazz) {
+ return isCommittedSynthetic(clazz.type) || isPendingSynthetic(clazz.type);
+ }
+
public boolean isSyntheticClass(DexType type) {
return isCommittedSynthetic(type)
|| isPendingSynthetic(type)
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index b33e260..fc5b278 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -32,7 +32,8 @@
THROW_ICCE("ThrowICCE", true),
THROW_NSME("ThrowNSME", true),
TWR_CLOSE_RESOURCE("TwrCloseResource", true),
- SERVICE_LOADER("ServiceLoad", true);
+ SERVICE_LOADER("ServiceLoad", true),
+ OUTLINE("Outline", true);
public final String descriptor;
public final boolean isSingleSyntheticMethod;
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 895f31e..286c83d 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -467,7 +467,7 @@
if (superTarget != null) {
addMethod(superTarget.getReference());
}
- for (DexType type : method.getDefinition().parameters().values) {
+ for (DexType type : method.getDefinition().getParameters()) {
registerTypeReference(type);
}
for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
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 a8bec4a..a9f55ee 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1067,10 +1067,6 @@
}
public static class OutlineOptions {
-
- public static final String CLASS_NAME = "com.android.tools.r8.GeneratedOutlineSupport";
- public static final String METHOD_PREFIX = "outline";
-
public boolean enabled = true;
public int minSize = 3;
public int maxSize = 99;
diff --git a/src/test/java/com/android/tools/r8/R8TestRunResult.java b/src/test/java/com/android/tools/r8/R8TestRunResult.java
index bbd5836..028ed02 100644
--- a/src/test/java/com/android/tools/r8/R8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestRunResult.java
@@ -38,6 +38,11 @@
}
@Override
+ public boolean isR8TestRunResult() {
+ return true;
+ }
+
+ @Override
protected R8TestRunResult self() {
return this;
}
diff --git a/src/test/java/com/android/tools/r8/SingleTestRunResult.java b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
index 875e50d..d532547 100644
--- a/src/test/java/com/android/tools/r8/SingleTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
@@ -34,6 +34,10 @@
this.result = result;
}
+ public boolean isR8TestRunResult() {
+ return false;
+ }
+
public AndroidApp app() {
return app;
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
index b21cbd0..89395ef 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
@@ -97,6 +97,13 @@
return testForR8(Backend.CF)
.addProgramClasses(ToStringLib.class, ToStringLib.LibEnum.class)
.addKeepRules(enumKeepRules.getKeepRules())
+ // TODO(b/160535629): Work-around on some optimizations relying on $VALUES name.
+ .addKeepRules(
+ "-keep enum "
+ + ToStringLib.LibEnum.class.getName()
+ + " { static "
+ + ToStringLib.LibEnum.class.getName()
+ + "[] $VALUES; }")
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.addKeepMethodRules(
Reference.methodFromMethod(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/NoOutliningForClassFileBuildsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/NoOutliningForClassFileBuildsTest.java
index 4af2593..2a837bc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/NoOutliningForClassFileBuildsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/NoOutliningForClassFileBuildsTest.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -72,8 +72,9 @@
assertThat(classSubject.uniqueMethodWithName("foo"), isPresent());
assertThat(classSubject.uniqueMethodWithName("bar"), isPresent());
boolean hasOutlineClass =
- inspector.allClasses().stream()
- .anyMatch(c -> c.getFinalName().equals(OutlineOptions.CLASS_NAME));
+ inspector
+ .clazz(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0))
+ .isPresent();
assertEquals(forceOutline || parameters.isDexRuntime(), hasOutlineClass);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
index 59602da..6b7bc4a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -93,9 +93,10 @@
}
private void validateOutlining(CodeInspector inspector, Class<?> main) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
- assertThat(outlineClass, isPresent());
- MethodSubject outlineMethod = outlineClass.uniqueMethodWithName("outline0");
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(main, 0));
+ MethodSubject outlineMethod =
+ outlineClass.uniqueMethodWithName(SyntheticItemsTestUtils.syntheticMethodName());
assertThat(outlineMethod, isPresent());
ClassSubject argClass = inspector.clazz(TestArg.class);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
index 099afa8..440b036 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -37,11 +37,12 @@
}
private void validateOutlining(CodeInspector inspector) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0));
MethodSubject outline0Method =
outlineClass.method(
"void",
- "outline0",
+ SyntheticItemsTestUtils.syntheticMethodName(),
ImmutableList.of(
TestClass.class.getTypeName() + "[]", TestClass.class.getTypeName() + "[]"));
assertThat(outline0Method, isPresent());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
index fb4417f9..062516c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
@@ -11,8 +11,8 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -44,12 +44,15 @@
private void validateOutlining(CodeInspector inspector) {
// No outlining when arrays of interfaces are involved, see b/132420510 - unless the testing
// option is set.
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0));
if (allowOutlinerInterfaceArrayArguments && parameters.isCfRuntime()) {
assertThat(outlineClass, isPresent());
MethodSubject outline0Method =
outlineClass.method(
- "void", "outline0", ImmutableList.of("java.lang.Object[]", "java.lang.Object[]"));
+ "void",
+ SyntheticItemsTestUtils.syntheticMethodName(),
+ ImmutableList.of("java.lang.Object[]", "java.lang.Object[]"));
assertThat(outline0Method, isPresent());
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
index 7a69926..36d9ad5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -37,9 +37,13 @@
}
private void validateOutlining(CodeInspector inspector) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0));
MethodSubject outline0Method =
- outlineClass.method("void", "outline0", ImmutableList.of("int[]", "int[]"));
+ outlineClass.method(
+ "void",
+ SyntheticItemsTestUtils.syntheticMethodName(),
+ ImmutableList.of("int[]", "int[]"));
assertThat(outline0Method, isPresent());
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
index 9fcc6b7..b4dd6b7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -98,11 +99,14 @@
.inspector();
for (FoundClassSubject clazz : inspector.allClasses()) {
- clazz.forAllMethods(method -> {
- if (method.hasCode() && !method.getFinalName().startsWith("outline")) {
- verifyAbsenceOfStringBuilderAppend(method.streamInstructions());
- }
- });
+ if (!SyntheticItemsTestUtils.isExternalOutlineClass(clazz.getFinalReference())) {
+ clazz.forAllMethods(
+ method -> {
+ if (method.hasCode()) {
+ verifyAbsenceOfStringBuilderAppend(method.streamInstructions());
+ }
+ });
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
index 66c42e8..031fd9d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ir.optimize.outliner.b133215941.B133215941.TestClass.ClassWithStaticMethod;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -41,11 +41,12 @@
}
private void validateOutlining(CodeInspector inspector) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0));
MethodSubject outline0Method =
outlineClass.method(
"void",
- "outline0",
+ SyntheticItemsTestUtils.syntheticMethodName(),
ImmutableList.of(TestClass.class.getTypeName(), TestClass.class.getTypeName()));
assertThat(outline0Method, isPresent());
ClassSubject classSubject = inspector.clazz(TestClass.class);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
index 438aa81..dd6dace 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.outliner.b149971007;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
@@ -15,7 +16,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.dexsplitter.SplitterTestBase;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -24,6 +25,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -64,10 +67,17 @@
}
private void checkOutlineFromFeature(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
- assertThat(clazz, isPresent());
- assertEquals(2, clazz.allMethods().size());
- assertTrue(clazz.allMethods().stream().anyMatch(this::referenceFeatureClass));
+ // There are two expected outlines, each is currently in its own class.
+ List<FoundMethodSubject> allMethods = new ArrayList<>();
+ for (int i = 0; i < 2; i++) {
+ ClassSubject clazz =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, i));
+ assertThat(clazz, isPresent());
+ assertEquals(1, clazz.allMethods().size());
+ allMethods.addAll(clazz.allMethods());
+ }
+ // One of the methods is StringBuilder the other references the feature.
+ assertTrue(allMethods.stream().anyMatch(this::referenceFeatureClass));
}
@Test
@@ -85,24 +95,44 @@
// Check that parts of method1, ..., method4 in FeatureClass was outlined.
ClassSubject featureClass = compileResult.inspector().clazz(FeatureClass.class);
assertThat(featureClass, isPresent());
- String outlineClassName =
+
+ // Find the final names of the two outline classes.
+ String outlineClassName0 =
ClassNameMapper.mapperFromString(compileResult.getProguardMap())
.getObfuscatedToOriginalMapping()
.inverse
- .get(OutlineOptions.CLASS_NAME);
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName));
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName));
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName));
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method4"), outlineClassName));
+ .get(
+ SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 0).getTypeName());
+ String outlineClassName1 =
+ ClassNameMapper.mapperFromString(compileResult.getProguardMap())
+ .getObfuscatedToOriginalMapping()
+ .inverse
+ .get(
+ SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 1).getTypeName());
+
+ // Verify they are called from the feature methods.
+ // Note: should the choice of synthetic grouping change these expectations will too.
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName0));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName0));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName1));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method4"), outlineClassName1));
compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput("123456");
}
private void checkNoOutlineFromFeature(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
- assertThat(clazz, isPresent());
- assertEquals(1, clazz.allMethods().size());
- assertTrue(clazz.allMethods().stream().noneMatch(this::referenceFeatureClass));
+ // The features do not give rise to an outline.
+ ClassSubject featureOutlineClass =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 0).getTypeName());
+ assertThat(featureOutlineClass, isAbsent());
+ // The main TestClass entry does.
+ ClassSubject mainOutlineClazz =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0).getTypeName());
+ assertThat(mainOutlineClazz, isPresent());
+ assertEquals(1, mainOutlineClazz.allMethods().size());
+ assertTrue(mainOutlineClazz.allMethods().stream().noneMatch(this::referenceFeatureClass));
}
@Test
@@ -124,12 +154,16 @@
// Check that parts of method1, ..., method4 in FeatureClass was not outlined.
CodeInspector featureInspector = new CodeInspector(featureCode);
ClassSubject featureClass = featureInspector.clazz(FeatureClass.class);
+
+ // Note, this code does not really check a valid property now as the name of the outline is not
+ // known.
assertThat(featureClass, isPresent());
String outlineClassName =
ClassNameMapper.mapperFromString(compileResult.getProguardMap())
.getObfuscatedToOriginalMapping()
.inverse
- .get(OutlineOptions.CLASS_NAME);
+ .get(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0).getTypeName());
+
assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName));
assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName));
assertFalse(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
index eab6811..39c95f1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -37,11 +37,12 @@
}
private void validateOutlining(CodeInspector inspector) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(TestClass.class, 0));
MethodSubject outline0Method =
outlineClass.method(
"void",
- "outline0",
+ SyntheticItemsTestUtils.syntheticMethodName(),
ImmutableList.of(
StringBuilder.class.getTypeName(),
String.class.getTypeName(),
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
index db3ef77..4b81714 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -38,10 +38,13 @@
}
private void validateOutlining(CodeInspector inspector, Class<?> testClass, String argumentType) {
- ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ ClassSubject outlineClass =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(testClass, 0));
MethodSubject outline0Method =
outlineClass.method(
- "java.lang.String", "outline0", ImmutableList.of(argumentType, argumentType));
+ "java.lang.String",
+ SyntheticItemsTestUtils.syntheticMethodName(),
+ ImmutableList.of(argumentType, argumentType));
assertThat(outline0Method, isPresent());
ClassSubject classSubject = inspector.clazz(testClass);
assertThat(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java
index 5852266..b96ee7f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.string;
+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 static org.junit.Assert.assertEquals;
@@ -22,7 +23,6 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.Streams;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -93,17 +93,17 @@
}
private long countStringLength(MethodSubject method) {
- return Streams.stream(method.iterateInstructions(instructionSubject -> {
- if (instructionSubject.isInvoke()) {
- return isStringLength(instructionSubject.getMethod());
- }
- return false;
- })).count();
+ return method
+ .streamInstructions()
+ .filter(
+ instructionSubject ->
+ instructionSubject.isInvoke() && isStringLength(instructionSubject.getMethod()))
+ .count();
}
private long countNonZeroConstNumber(MethodSubject method) {
- return Streams.stream(method.iterateInstructions(InstructionSubject::isConstNumber)).count()
- - Streams.stream(method.iterateInstructions(instr -> instr.isConstNumber(0))).count();
+ return method.streamInstructions().filter(InstructionSubject::isConstNumber).count()
+ - method.streamInstructions().filter(instr -> instr.isConstNumber(0)).count();
}
private void test(
@@ -117,9 +117,13 @@
ClassSubject mainClass = codeInspector.clazz(MAIN);
MethodSubject clinit = mainClass.clinit();
- assertThat(clinit, isPresent());
- assertEquals(expectedStringLengthCountInClinit, countStringLength(clinit));
- assertEquals(expectedConstNumberCountInClinit, countNonZeroConstNumber(clinit));
+ if (result.isR8TestRunResult()) {
+ assertThat(clinit, isAbsent());
+ } else {
+ assertThat(clinit, isPresent());
+ assertEquals(expectedStringLengthCountInClinit, countStringLength(clinit));
+ assertEquals(expectedConstNumberCountInClinit, countNonZeroConstNumber(clinit));
+ }
MethodSubject m = mainClass.uniqueMethodWithName("instanceMethod");
assertThat(m, isPresent());
@@ -154,7 +158,7 @@
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
// TODO(b/125303292): NAME_LENGTH is still not computed at compile time.
- test(result, 1, 0, 0, 1);
+ test(result, 0, 0, 0, 1);
}
@Test
@@ -171,7 +175,6 @@
.assertSuccessWithOutput(JAVA_OUTPUT);
// No canonicalization in CF.
int expectedConstNumber = parameters.isCfRuntime() ? 2 : 1;
- // TODO(b/125303292): NAME_LENGTH is still not computed at compile time.
- test(result, 1, 0, 0, expectedConstNumber);
+ test(result, 0, 0, 0, expectedConstNumber);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
index 30f5c93..d8a7d6b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionMappingTest.java
@@ -78,8 +78,8 @@
if (method.getFinalName().equals("main")) {
continue;
}
- foundZeroArgumentMethod |= method.getMethod().parameters().size() == 0;
- foundOneArgumentMethod |= method.getMethod().parameters().size() == 1;
+ foundZeroArgumentMethod |= method.getMethod().getParameters().size() == 0;
+ foundOneArgumentMethod |= method.getMethod().getParameters().size() == 1;
}
assertTrue(foundZeroArgumentMethod);
assertTrue(foundOneArgumentMethod);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
index 94392e6..8f88d08 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
@@ -80,7 +80,6 @@
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNoStaticClassMergingAnnotations()
- .noMinification()
.setMinApi(parameters.getApiLevel())
.compile();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
new file mode 100644
index 0000000..867b1ec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
@@ -0,0 +1,155 @@
+// 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MainDexListFromGenerateMainHorizontalMergingTest extends TestBase {
+
+ private static List<ClassReference> mainDexList;
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ mainDexList =
+ testForMainDexListGenerator(getStaticTemp())
+ .addInnerClasses(MainDexListFromGenerateMainHorizontalMergingTest.class)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " { public static void foo(); }")
+ .run()
+ .getMainDexList();
+ }
+
+ public MainDexListFromGenerateMainHorizontalMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ assertEquals(3, mainDexList.size());
+ Set<String> mainDexReferences =
+ mainDexList.stream().map(TypeReference::getTypeName).collect(Collectors.toSet());
+ assertTrue(mainDexReferences.contains(A.class.getTypeName()));
+ assertTrue(mainDexReferences.contains(B.class.getTypeName()));
+ assertTrue(mainDexReferences.contains(Main.class.getTypeName()));
+
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInliningAnnotations()
+ .addKeepClassAndMembersRules(Main.class, Outside.class)
+ .addMainDexListClassReferences(mainDexList)
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspector(
+ horizontallyMergedClassesInspector -> {
+ horizontallyMergedClassesInspector.assertMergedInto(B.class, A.class);
+ })
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject fooMethodSubject = mainClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ // TODO(b/178460068): Should be present, but was merged with A.
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isAbsent());
+
+ MethodSubject fooASubject = aClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooASubject, isPresent());
+
+ assertThat(fooMethodSubject, invokesMethod(fooASubject));
+
+ compileResult.inspectMainDexClasses(
+ mainDexClasses -> {
+ assertEquals(
+ ImmutableSet.of(mainClassSubject.getFinalName(), aClassSubject.getFinalName()),
+ mainDexClasses);
+ });
+
+ compileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputThatMatches(containsString(Outside.class.getTypeName()));
+ }
+
+ static class Main {
+
+ // public static B b;
+
+ public static void main(String[] args) {
+ B.print();
+ }
+
+ public static void foo() {
+ A.foo();
+ }
+ }
+
+ public static class Outside {}
+
+ public static class A {
+
+ @NeverInline
+ public static void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+
+ @NeverInline
+ public static void print() {
+ System.out.println(Outside.class);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java
new file mode 100644
index 0000000..e3d49f5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java
@@ -0,0 +1,159 @@
+// 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MainDexListFromGenerateMainVerticalMergingTest extends TestBase {
+
+ private static List<ClassReference> mainDexList;
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ mainDexList =
+ testForMainDexListGenerator(getStaticTemp())
+ .addInnerClasses(MainDexListFromGenerateMainVerticalMergingTest.class)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " { public static void foo(); }")
+ .run()
+ .getMainDexList();
+ }
+
+ public MainDexListFromGenerateMainVerticalMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ assertEquals(3, mainDexList.size());
+ Set<String> mainDexReferences =
+ mainDexList.stream().map(TypeReference::getTypeName).collect(Collectors.toSet());
+ assertTrue(mainDexReferences.contains(A.class.getTypeName()));
+ assertTrue(mainDexReferences.contains(B.class.getTypeName()));
+ assertTrue(mainDexReferences.contains(Main.class.getTypeName()));
+
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInliningAnnotations()
+ .addKeepClassAndMembersRules(Main.class, Outside.class)
+ .addMainDexListClassReferences(mainDexList)
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject fooMethodSubject = mainClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ // TODO(b/178460068): Should be present, but was merged with B.
+ assertThat(aClassSubject, isAbsent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ FieldSubject outsideFieldSubject = bClassSubject.uniqueFieldWithName("outsideField");
+ assertThat(outsideFieldSubject, isPresent());
+
+ MethodSubject fooBMethodSubject = bClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooBMethodSubject, isPresent());
+
+ assertThat(fooMethodSubject, invokesMethod(fooBMethodSubject));
+
+ // TODO(b/178460068): B should not be in main dex.
+ compileResult.inspectMainDexClasses(
+ mainDexClasses -> {
+ assertEquals(
+ ImmutableSet.of(mainClassSubject.getFinalName(), bClassSubject.getFinalName()),
+ mainDexClasses);
+ });
+
+ compileResult.run(parameters.getRuntime(), Main.class).assertSuccessWithOutputLines("B::print");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new B().print();
+ }
+
+ public static void foo() {
+ A.foo();
+ }
+ }
+
+ public static class Outside {}
+
+ public static class A {
+
+ @NeverInline
+ public static void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends A {
+
+ public static Outside outsideField;
+
+ {
+ outsideField = System.currentTimeMillis() > 0 ? null : new Outside();
+ }
+
+ @NeverInline
+ public void print() {
+ if (outsideField == null) {
+ System.out.println("B::print");
+ }
+ }
+ }
+}
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 04ac085..efe3826 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -30,14 +30,18 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
@@ -50,6 +54,10 @@
public class OutlineTest extends SmaliTestBase {
+ private static ClassReference OUTLINE_CLASS =
+ SyntheticItemsTestUtils.syntheticOutlineClass(
+ Reference.classFromTypeName(DEFAULT_CLASS_NAME), 0);
+
private static final String stringBuilderAppendSignature =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;";
private static final String stringBuilderAppendDoubleSignature =
@@ -85,12 +93,13 @@
}
private String firstOutlineMethodName() {
- return OutlineOptions.CLASS_NAME + '.' + OutlineOptions.METHOD_PREFIX + "0";
+ return OUTLINE_CLASS.getTypeName() + '.' + SyntheticItemsTestUtils.syntheticMethodName();
}
- private boolean isOutlineMethodName(String qualifiedName) {
- String qualifiedPrefix = OutlineOptions.CLASS_NAME + '.' + OutlineOptions.METHOD_PREFIX;
- return qualifiedName.indexOf(qualifiedPrefix) == 0;
+ private static boolean isOutlineMethodName(DexMethod method) {
+ return SyntheticItemsTestUtils.isExternalOutlineClass(
+ Reference.classFromDescriptor(method.holder.toDescriptorString()))
+ && method.name.toString().equals(SyntheticItemsTestUtils.syntheticMethodName());
}
@Test
@@ -142,7 +151,6 @@
AndroidApp originalApplication = buildApplication(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -151,7 +159,7 @@
assertTrue(code.instructions[0] instanceof ConstString);
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -211,7 +219,6 @@
AndroidApp originalApplication = buildApplication(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -225,7 +232,7 @@
}
assertTrue(code.instructions[firstOutlineInvoke] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[firstOutlineInvoke];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -273,7 +280,6 @@
Consumer<InternalOptions> options = configureOutlineOptions(outline -> outline.threshold = 1);
AndroidApp originalApplication = buildApplication(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -282,7 +288,7 @@
assertTrue(code.instructions[0] instanceof ConstString);
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -336,7 +342,6 @@
Consumer<InternalOptions> options = configureOutlineOptions(outline -> outline.threshold = 1);
AndroidApp originalApplication = buildApplication(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -346,7 +351,7 @@
assertTrue(code.instructions[1] instanceof ConstString);
assertTrue(code.instructions[2] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[2];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -402,7 +407,6 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -412,13 +416,13 @@
if (i < 3) {
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
} else {
assertTrue(code.instructions[1] instanceof InvokeVirtual);
assertTrue(code.instructions[2] instanceof InvokeVirtual);
assertTrue(code.instructions[3] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[3];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
}
// Run code and check result.
@@ -480,7 +484,6 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -490,13 +493,13 @@
if (i < 3) {
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
} else {
assertTrue(code.instructions[1] instanceof InvokeVirtual);
assertTrue(code.instructions[2] instanceof InvokeVirtual);
assertTrue(code.instructions[3] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[3];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
}
// Run code and check result.
@@ -553,7 +556,6 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed main method for inspection.
DexEncodedMethod mainMethod = getMethod(processedApplication, mainSignature);
@@ -569,14 +571,14 @@
}
if (i == 2) {
InvokeStatic invoke = (InvokeStatic) mainCode.instructions[4];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
} else if (i == 3) {
InvokeStatic invoke = (InvokeStatic) mainCode.instructions[1];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
} else {
assert i == 4 || i == 5;
InvokeStatic invoke = (InvokeStatic) mainCode.instructions[2];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
}
// Run code and check result.
@@ -653,19 +655,19 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
+ assertEquals(3, getNumberOfProgramClasses(processedApplication));
DexCode code1 = getMethod(processedApplication, signature1).getCode().asDexCode();
assertEquals(4, code1.instructions.length);
assertTrue(code1.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke1 = (InvokeStatic) code1.instructions[1];
- assertTrue(isOutlineMethodName(invoke1.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke1.getMethod()));
DexCode code2 = getMethod(processedApplication, signature2).getCode().asDexCode();
assertEquals(5, code2.instructions.length);
assertTrue(code2.instructions[2] instanceof InvokeStatic);
InvokeStatic invoke2 = (InvokeStatic) code2.instructions[2];
- assertTrue(isOutlineMethodName(invoke1.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke1.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -721,7 +723,6 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
DexCode code = getMethod(processedApplication, signature).getCode().asDexCode();
int outlineInstructionIndex;
@@ -739,10 +740,10 @@
Instruction instruction = code.instructions[outlineInstructionIndex];
if (instruction instanceof InvokeStatic) {
InvokeStatic invoke = (InvokeStatic) instruction;
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
} else {
InvokeStaticRange invoke = (InvokeStaticRange) instruction;
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
}
// Run code and check result.
@@ -791,7 +792,7 @@
InvokeStatic invoke;
assertTrue(code.instructions[0] instanceof InvokeStatic);
invoke = (InvokeStatic) code.instructions[0];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -859,16 +860,15 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
+ assertEquals(4, getNumberOfProgramClasses(processedApplication));
// Check that three outlining methods was created.
CodeInspector inspector = new CodeInspector(processedApplication);
- ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
- assertTrue(clazz.isPresent());
- assertEquals(3, clazz.getDexProgramClass().getMethodCollection().numberOfDirectMethods());
+ List<DexEncodedMethod> outlineMethods = getOutlineMethods(inspector);
+ assertEquals(3, outlineMethods.size());
// Collect the return types of the outlines for the body of method1 and method2.
List<DexType> r = new ArrayList<>();
- for (DexEncodedMethod directMethod : clazz.getDexProgramClass().directMethods()) {
+ for (DexEncodedMethod directMethod : outlineMethods) {
if (directMethod.getCode().asDexCode().instructions[0] instanceof InvokeVirtual) {
r.add(directMethod.method.proto.returnType);
}
@@ -885,6 +885,16 @@
assertEquals("TestTestTestTestTest", result);
}
+ private static List<DexEncodedMethod> getOutlineMethods(CodeInspector inspector) {
+ List<DexEncodedMethod> outlineMethods = new ArrayList<>();
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ if (SyntheticItemsTestUtils.isExternalOutlineClass(clazz.getFinalReference())) {
+ clazz.forAllMethods(m -> outlineMethods.add(m.getMethod()));
+ }
+ }
+ return outlineMethods;
+ }
+
@Test
public void outlineMultipleTimes() throws Exception {
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
@@ -931,15 +941,18 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
+ assertEquals(3, getNumberOfProgramClasses(processedApplication));
final int count = 10;
- // Process the application several times. Each time will outline the previous outline.
- for (int i = 0; i < count; i++) {
+ // Process the application several times. Each time will outline the previous outlines.
+ for (int i = 1; i < count; i++) {
// Build a new application with the Outliner class.
originalApplication = processedApplication;
- processedApplication = processApplication(originalApplication, options);
- assertEquals(i + 3, getNumberOfProgramClasses(processedApplication));
+ processedApplication =
+ processApplication(
+ originalApplication,
+ options.andThen(o -> o.testing.allowConflictingSyntheticTypes = true));
+ assertEquals((i + 1) * 3, getNumberOfProgramClasses(processedApplication));
}
// Process the application several times. No more outlining as threshold has been raised.
@@ -954,7 +967,7 @@
// Build a new application with the Outliner class.
originalApplication = processedApplication;
processedApplication = processApplication(originalApplication, options);
- assertEquals(count - 1 + 3, getNumberOfProgramClasses(processedApplication));
+ assertEquals(count * 3, getNumberOfProgramClasses(processedApplication));
}
// Run the application with several levels of outlining.
@@ -1195,7 +1208,6 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method1 = getMethod(processedApplication, signature1);
@@ -1205,7 +1217,7 @@
assertTrue(code1.instructions[1] instanceof MoveResult);
assertTrue(code1.instructions[2] instanceof Return);
InvokeStatic invoke1 = (InvokeStatic) code1.instructions[0];
- assertTrue(isOutlineMethodName(invoke1.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke1.getMethod()));
DexEncodedMethod method2 = getMethod(processedApplication, signature2);
DexCode code2 = method2.getCode().asDexCode();
@@ -1287,7 +1299,7 @@
assertTrue(code.instructions[5] instanceof Const4);
assertTrue(code.instructions[6] instanceof Return);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod()));
// Run code and check result.
String result = runArt(processedApplication);
@@ -1614,15 +1626,13 @@
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
AndroidApp processedApplication = processApplication(originalApplication, options);
- assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Verify the code.
runDex2Oat(processedApplication);
}
private static boolean isOutlineInvoke(Instruction instruction) {
- return instruction instanceof InvokeStatic
- && instruction.getMethod().holder.toSourceString().equals(OutlineOptions.CLASS_NAME);
+ return instruction instanceof InvokeStatic && isOutlineMethodName(instruction.getMethod());
}
private void assertHasOutlineInvoke(DexEncodedMethod method) {
@@ -1699,10 +1709,10 @@
assertHasOutlineInvoke(getMethod(processedApplication, signature2));
assertThat(
new CodeInspector(processedApplication)
- .clazz(OutlineOptions.CLASS_NAME)
+ .clazz(OUTLINE_CLASS)
.method(
"boolean",
- "outline0",
+ SyntheticItemsTestUtils.syntheticMethodName(),
ImmutableList.of("java.io.PrintStream", "java.util.ArrayList")),
isPresent());
@@ -1774,8 +1784,11 @@
assertHasOutlineInvoke(getMethod(processedApplication, signature2));
assertThat(
new CodeInspector(processedApplication)
- .clazz(OutlineOptions.CLASS_NAME)
- .method("boolean", "outline0", ImmutableList.of("java.util.List")),
+ .clazz(OUTLINE_CLASS)
+ .method(
+ "boolean",
+ SyntheticItemsTestUtils.syntheticMethodName(),
+ ImmutableList.of("java.util.List")),
isPresent());
// Run code and check result.
@@ -1848,8 +1861,11 @@
assertHasOutlineInvoke(getMethod(processedApplication, signature2));
assertThat(
new CodeInspector(processedApplication)
- .clazz(OutlineOptions.CLASS_NAME)
- .method("boolean", "outline0", ImmutableList.of("java.util.ArrayList")),
+ .clazz(OUTLINE_CLASS)
+ .method(
+ "boolean",
+ SyntheticItemsTestUtils.syntheticMethodName(),
+ ImmutableList.of("java.util.ArrayList")),
isPresent());
// Run code and check result.
@@ -1922,8 +1938,11 @@
assertHasOutlineInvoke(getMethod(processedApplication, signature2));
assertThat(
new CodeInspector(processedApplication)
- .clazz(OutlineOptions.CLASS_NAME)
- .method("boolean", "outline0", ImmutableList.of("java.util.ArrayList")),
+ .clazz(OUTLINE_CLASS)
+ .method(
+ "boolean",
+ SyntheticItemsTestUtils.syntheticMethodName(),
+ ImmutableList.of("java.util.ArrayList")),
isPresent());
// Run code and check result.
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 438d3f9..3f9c6af 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -16,6 +16,10 @@
public class SyntheticItemsTestUtils {
+ public static String syntheticMethodName() {
+ return SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX;
+ }
+
public static ClassReference syntheticCompanionClass(Class<?> clazz) {
return Reference.classFromDescriptor(
InterfaceMethodRewriter.getCompanionClassDescriptor(
@@ -23,8 +27,11 @@
}
private static ClassReference syntheticClass(Class<?> clazz, SyntheticKind kind, int id) {
- return SyntheticNaming.makeSyntheticReferenceForTest(
- Reference.classFromClass(clazz), kind, "" + id);
+ return syntheticClass(Reference.classFromClass(clazz), kind, id);
+ }
+
+ private static ClassReference syntheticClass(ClassReference clazz, SyntheticKind kind, int id) {
+ return SyntheticNaming.makeSyntheticReferenceForTest(clazz, kind, "" + id);
}
public static MethodReference syntheticBackportMethod(Class<?> clazz, int id, Method method) {
@@ -33,10 +40,18 @@
MethodReference originalMethod = Reference.methodFromMethod(method);
return Reference.methodFromDescriptor(
syntheticHolder.getDescriptor(),
- SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX,
+ syntheticMethodName(),
originalMethod.getMethodDescriptor());
}
+ public static ClassReference syntheticOutlineClass(Class<?> clazz, int id) {
+ return syntheticClass(clazz, SyntheticKind.OUTLINE, id);
+ }
+
+ public static ClassReference syntheticOutlineClass(ClassReference clazz, int id) {
+ return syntheticClass(clazz, SyntheticKind.OUTLINE, id);
+ }
+
public static ClassReference syntheticLambdaClass(Class<?> clazz, int id) {
return syntheticClass(clazz, SyntheticNaming.SyntheticKind.LAMBDA, id);
}
@@ -67,6 +82,10 @@
return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.TWR_CLOSE_RESOURCE);
}
+ public static boolean isExternalOutlineClass(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.OUTLINE);
+ }
+
public static Matcher<String> containsInternalSyntheticReference() {
return containsString(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
}
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 179a50f..783b30d 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -9,7 +9,7 @@
import re
import subprocess
import sys
-import urllib
+import urllib.request
import xml.etree.ElementTree
import zipfile
@@ -70,7 +70,7 @@
# Verify that the merge point from master is not empty.
merge_diff_output = subprocess.check_output([
- 'git', 'diff', 'HEAD..%s' % commithash])
+ 'git', 'diff', 'HEAD..%s' % commithash]).decode('utf-8')
other_diff = version_change_diff(
merge_diff_output, old_version, "master")
if not other_diff:
@@ -94,14 +94,14 @@
'git', 'commit', '-a', '-m', 'Version %s' % version])
version_diff_output = subprocess.check_output([
- 'git', 'diff', '%s..HEAD' % commithash])
+ 'git', 'diff', '%s..HEAD' % commithash]).decode('utf-8')
validate_version_change_diff(version_diff_output, "master", version)
# Double check that we want to push the release.
if not args.dry_run:
- input = raw_input('Publish dev release version %s [y/N]:' % version)
- if input != 'y':
+ answer = input('Publish dev release version %s [y/N]:' % version)
+ if answer != 'y':
print('Aborting dev release for %s' % version)
sys.exit(1)
@@ -144,10 +144,10 @@
print(version_diff_output)
print("=" * 80)
accept_string = 'THE DIFF IS OK!'
- input = raw_input(
+ answer = input(
"Accept the additonal diff as part of the release? "
"Type '%s' to accept: " % accept_string)
- if input != accept_string:
+ if answer != accept_string:
print("You did not type '%s'" % accept_string)
print('Aborting dev release for %s' % version)
sys.exit(1)
@@ -233,9 +233,9 @@
release_id, "com.android.tools:r8:%s" % args.version, "r8lib.jar")
print
- input = raw_input("Continue with publishing [y/N]:")
+ answer = input("Continue with publishing [y/N]:")
- if input != 'y':
+ if answer != 'y':
print('Aborting release to Google maven')
sys.exit(1)
@@ -305,7 +305,7 @@
def g4_change(version):
return subprocess.check_output(
'g4 change --desc "Update R8 to version %s\n"' % (version),
- shell=True)
+ shell=True).decode('utf-8')
def get_cl_id(c4_change_output):
startIndex = c4_change_output.find('Change ') + len('Change ')
@@ -323,7 +323,7 @@
def download_file(version, file, dst):
- urllib.urlretrieve(
+ urllib.request.urlretrieve(
('https://storage.googleapis.com/r8-releases/raw/%s/%s' % (version, file)),
dst)
@@ -332,13 +332,13 @@
print('Unexpected gfile prefix for %s' % gfile)
sys.exit(1)
- urllib.urlretrieve(
+ urllib.request.urlretrieve(
'https://storage.googleapis.com/%s' % gfile[len('/bigstore/'):],
dst)
def blaze_run(target):
return subprocess.check_output(
- 'blaze run %s' % target, shell=True, stderr=subprocess.STDOUT)
+ 'blaze run %s' % target, shell=True, stderr=subprocess.STDOUT).decode('utf-8')
def prepare_google3(args):
@@ -353,7 +353,7 @@
return 'DryRun: omitting g3 release for %s' % options.version
google3_base = subprocess.check_output(
- ['p4', 'g4d', '-f', args.p4_client]).rstrip()
+ ['p4', 'g4d', '-f', args.p4_client]).decode('utf-8').rstrip()
third_party_r8 = os.path.join(google3_base, 'third_party', 'java', 'r8')
today = datetime.date.today()
with utils.ChangedWorkingDirectory(third_party_r8):
@@ -504,9 +504,9 @@
library_jar)
print("")
- input = raw_input("Continue with publishing [y/N]:")
+ answer = input("Continue with publishing [y/N]:")
- if input != 'y':
+ if answer != 'y':
print('Aborting release to Google maven')
sys.exit(1)
@@ -542,11 +542,11 @@
def check_no_google3_client(args, client_name):
if not args.use_existing_work_branch:
- clients = subprocess.check_output('g4 myclients', shell=True)
+ clients = subprocess.check_output('g4 myclients', shell=True).decode('utf-8')
if ':%s:' % client_name in clients:
- print("Remove the existing '%s' client before continuing " +
+ print(("Remove the existing '%s' client before continuing " +
"(force delete: 'g4 citc -d -f %s'), " +
- "or use option --use-existing-work-branch.") % (client_name, client_name)
+ "or use option --use-existing-work-branch.") % (client_name, client_name))
sys.exit(1)
@@ -701,8 +701,8 @@
# Double check that we want to create a new release branch.
if not options.dry_run:
- input = raw_input('Create new branch for %s [y/N]:' % branch_version)
- if input != 'y':
+ answer = input('Create new branch for %s [y/N]:' % branch_version)
+ if answer != 'y':
print('Aborting new branch for %s' % branch_version)
sys.exit(1)