Merge commit '7f5bdd94cd22d02c72aa1119f56a11e328c07cf2' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 79d8121..d0cc5af 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -67,10 +67,10 @@
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
+import com.android.tools.r8.optimize.RedundantBridgeRemover;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index a664d9d..97ceb6e 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -747,7 +747,7 @@
dataResourceConsumer.accept(adapted, options.reporter);
} else {
options.reporter.warning(
- new StringDiagnostic("Resource '" + file.getName() + "' already exists."));
+ new StringDiagnostic("Resource '" + adapted.getName() + "' already exists."));
}
options.reporter.failIfPendingErrors();
}
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java b/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java
index 93c2501..6af977e 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexCompareHelper.java
@@ -11,7 +11,7 @@
static final int DEX_ITEM_CONST_STRING_COMPARE_ID;
static final int DEX_RECORD_FIELD_VALUES_COMPARE_ID;
- private static int HIGHEST_DEX_OPCODE = 0xFF;
+ private static final int HIGHEST_DEX_OPCODE = 0xFF;
static {
int lastId = HIGHEST_DEX_OPCODE;
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 f9a5279..f30986c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -475,9 +475,9 @@
return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory);
}
- public boolean isDefaultInstanceInitializer() {
+ public boolean isDefaultInitializer() {
checkIfObsolete();
- return isInstanceInitializer() && getParameters().isEmpty();
+ return isInstanceInitializer() && getReference().proto.parameters.isEmpty();
}
public boolean isClassInitializer() {
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 2dfb637..13c5ab8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -602,9 +602,6 @@
public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
- public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
- public final DexType androidAppZygotePreload =
- createStaticallyKnownType("Landroid/app/ZygotePreload;");
public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index e5d9dfe..47b1c27 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -25,12 +25,12 @@
public static final FieldAccessInfoImpl MISSING_FIELD_ACCESS_INFO = new FieldAccessInfoImpl(null);
- public static int FLAG_IS_READ_FROM_ANNOTATION = 1 << 0;
- public static int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 1;
- public static int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 2;
- public static int FLAG_HAS_REFLECTIVE_READ = 1 << 3;
- public static int FLAG_HAS_REFLECTIVE_WRITE = 1 << 4;
- public static int FLAG_IS_READ_FROM_RECORD_INVOKE_DYNAMIC = 1 << 5;
+ public static final int FLAG_IS_READ_FROM_ANNOTATION = 1 << 0;
+ public static final int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 1;
+ public static final int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 2;
+ public static final int FLAG_HAS_REFLECTIVE_READ = 1 << 3;
+ public static final int FLAG_HAS_REFLECTIVE_WRITE = 1 << 4;
+ public static final int FLAG_IS_READ_FROM_RECORD_INVOKE_DYNAMIC = 1 << 5;
// A direct reference to the definition of the field.
private DexField field;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index d871616..637026f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -69,6 +70,7 @@
IRCode code,
BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder,
OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
MethodProcessor methodProcessor) {
if (!methodProcessor.isPrimaryMethodProcessor()) {
return;
@@ -85,7 +87,8 @@
appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
if (field != null) {
if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
+ fieldAssignmentTracker.recordFieldAccess(
+ fieldInstruction, field, fieldReadBeforeWriteAnalysis);
}
if (fieldBitAccessAnalysis != null) {
fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0db5825..dc90a85 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteArrayTypeFieldState;
import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteClassTypeFieldState;
import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcretePrimitiveTypeFieldState;
@@ -34,6 +35,8 @@
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.FieldPut;
+import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
@@ -179,16 +182,23 @@
});
}
- void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) {
+ void recordFieldAccess(
+ FieldInstruction instruction,
+ ProgramField field,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
if (instruction.isFieldPut()) {
- recordFieldPut(field, instruction.value(), context);
+ recordFieldPut(instruction.asFieldPut(), field, fieldReadBeforeWriteAnalysis);
}
}
- private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) {
+ private void recordFieldPut(
+ FieldPut fieldPut,
+ ProgramField field,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
// For now only attempt to prove that fields are definitely null. In order to prove a single
// value for fields that are not definitely null, we need to prove that the given field is never
// read before it is written.
+ Value value = fieldPut.value();
AbstractValue abstractValue =
value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown();
fieldStates.compute(
@@ -203,12 +213,8 @@
return ConcretePrimitiveTypeFieldState.create(abstractValue);
}
assert fieldType.isClassType();
- DynamicType dynamicType =
- WideningUtils.widenDynamicNonReceiverType(
- appView,
- value.getDynamicType(appView).withNullability(Nullability.maybeNull()),
- field.getType());
- return ConcreteClassTypeFieldState.create(abstractValue, dynamicType);
+ return ConcreteClassTypeFieldState.create(
+ abstractValue, getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis));
}
if (fieldState.isUnknown()) {
@@ -231,10 +237,31 @@
ConcreteClassTypeFieldState classFieldState = fieldState.asClass();
return classFieldState.mutableJoin(
- appView, abstractValue, value.getDynamicType(appView), field);
+ appView,
+ abstractValue,
+ getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis),
+ field);
});
}
+ private DynamicType getDynamicType(
+ FieldPut fieldPut,
+ ProgramField field,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+ DynamicTypeWithUpperBound dynamicType = fieldPut.value().getDynamicType(appView);
+ if (fieldPut.isInstancePut()) {
+ InstancePut instancePut = fieldPut.asInstancePut();
+ if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
+ instancePut.object(), field.getDefinition(), instancePut)) {
+ dynamicType = dynamicType.withNullability(Nullability.maybeNull());
+ }
+ } else if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
+ field.getDefinition(), fieldPut.asStaticPut())) {
+ dynamicType = dynamicType.withNullability(Nullability.maybeNull());
+ }
+ return WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType());
+ }
+
void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
abstractInstanceFieldValues.get(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
new file mode 100644
index 0000000..fb6bae0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+
+public abstract class FieldReadBeforeWriteAnalysis {
+
+ public static FieldReadBeforeWriteAnalysis create(
+ AppView<?> appView, IRCode code, ProgramMethod context) {
+ if (appView.hasLiveness()) {
+ return new FieldReadBeforeWriteAnalysisImpl(appView.withLiveness(), code, context);
+ }
+ return trivial();
+ }
+
+ public static TrivialFieldReadBeforeWriteAnalysis trivial() {
+ return new TrivialFieldReadBeforeWriteAnalysis();
+ }
+
+ public abstract boolean isInstanceFieldMaybeReadBeforeInstruction(
+ Value receiver, DexEncodedField field, Instruction instruction);
+
+ public abstract boolean isStaticFieldMaybeReadBeforeInstruction(
+ DexEncodedField field, Instruction instruction);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
new file mode 100644
index 0000000..7e7ac89
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
@@ -0,0 +1,209 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.KnownFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DequeUtils;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+class FieldReadBeforeWriteAnalysisImpl extends FieldReadBeforeWriteAnalysis {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final IRCode code;
+ private final ProgramMethod context;
+
+ private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+
+ public FieldReadBeforeWriteAnalysisImpl(
+ AppView<AppInfoWithLiveness> appView, IRCode code, ProgramMethod context) {
+ this.appView = appView;
+ this.code = code;
+ this.context = context;
+ }
+
+ @Override
+ public boolean isInstanceFieldMaybeReadBeforeInstruction(
+ Value receiver, DexEncodedField field, Instruction instruction) {
+ if (!code.context().getDefinition().isInstanceInitializer()
+ || receiver.getAliasedValue() != code.getThis()) {
+ return true;
+ }
+ return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
+ }
+
+ @Override
+ public boolean isStaticFieldMaybeReadBeforeInstruction(
+ DexEncodedField field, Instruction instruction) {
+ if (!code.context().getDefinition().isClassInitializer()
+ || field.getHolderType() != code.context().getHolderType()) {
+ return true;
+ }
+ return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
+ }
+
+ public boolean isFieldMaybeReadBeforeInstructionInInitializer(
+ DexEncodedField field, Instruction instruction) {
+ BasicBlock block = instruction.getBlock();
+
+ // First check if the field may be read in any of the (transitive) predecessor blocks.
+ if (fieldMaybeReadBeforeBlock(field, block)) {
+ return true;
+ }
+
+ // Then check if any of the instructions that precede the given instruction in the current block
+ // may read the field.
+ InstructionIterator instructionIterator = block.iterator();
+ while (instructionIterator.hasNext()) {
+ Instruction current = instructionIterator.next();
+ if (current == instruction) {
+ break;
+ }
+ if (current.readSet(appView, context).contains(field)) {
+ return true;
+ }
+ }
+
+ // Otherwise, the field is not read prior to the given instruction.
+ return false;
+ }
+
+ private boolean fieldMaybeReadBeforeBlock(DexEncodedField field, BasicBlock block) {
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ if (fieldMaybeReadBeforeBlockInclusive(field, predecessor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean fieldMaybeReadBeforeBlockInclusive(DexEncodedField field, BasicBlock block) {
+ return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(field);
+ }
+
+ private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
+ if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
+ fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
+ }
+ return fieldsMaybeReadBeforeBlockInclusiveCache;
+ }
+
+ /**
+ * Eagerly creates a mapping from each block to the set of fields that may be read in that block
+ * and its transitive predecessors.
+ */
+ private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
+ Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
+ Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
+ while (!worklist.isEmpty()) {
+ BasicBlock block = worklist.removeFirst();
+ boolean seenBefore = result.containsKey(block);
+ AbstractFieldSet readSet =
+ result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
+ if (readSet.isTop()) {
+ // We already have unknown information for this block.
+ continue;
+ }
+
+ assert readSet.isKnownFieldSet();
+ KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
+ int oldSize = seenBefore ? knownReadSet.size() : -1;
+
+ // Everything that is read in the predecessor blocks should also be included in the read set
+ // for the current block, so here we join the information from the predecessor blocks into the
+ // current read set.
+ boolean blockOrPredecessorMaybeReadAnyField = false;
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ AbstractFieldSet predecessorReadSet =
+ result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
+ if (predecessorReadSet.isBottom()) {
+ continue;
+ }
+ if (predecessorReadSet.isTop()) {
+ blockOrPredecessorMaybeReadAnyField = true;
+ break;
+ }
+ assert predecessorReadSet.isConcreteFieldSet();
+ if (!knownReadSet.isConcreteFieldSet()) {
+ knownReadSet = new ConcreteMutableFieldSet();
+ }
+ knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
+ }
+
+ if (!blockOrPredecessorMaybeReadAnyField) {
+ // Finally, we update the read set with the fields that are read by the instructions in the
+ // current block. This can be skipped if the block has already been processed.
+ if (seenBefore) {
+ assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
+ } else {
+ for (Instruction instruction : block.getInstructions()) {
+ AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+ if (instructionReadSet.isBottom()) {
+ continue;
+ }
+ if (instructionReadSet.isTop()) {
+ blockOrPredecessorMaybeReadAnyField = true;
+ break;
+ }
+ if (!knownReadSet.isConcreteFieldSet()) {
+ knownReadSet = new ConcreteMutableFieldSet();
+ }
+ knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
+ }
+ }
+ }
+
+ boolean changed = false;
+ if (blockOrPredecessorMaybeReadAnyField) {
+ // Record that this block reads all fields.
+ result.put(block, UnknownFieldSet.getInstance());
+ changed = true;
+ } else {
+ if (knownReadSet != readSet) {
+ result.put(block, knownReadSet.asConcreteFieldSet());
+ }
+ if (knownReadSet.size() != oldSize) {
+ assert knownReadSet.size() > oldSize;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ // Rerun the analysis for all successors because the state of the current block changed.
+ worklist.addAll(block.getSuccessors());
+ }
+ }
+ return result;
+ }
+
+ private boolean verifyFieldSetContainsAllFieldReadsInBlock(
+ KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
+ for (Instruction instruction : block.getInstructions()) {
+ AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+ assert !instructionReadSet.isTop();
+ if (instructionReadSet.isBottom()) {
+ continue;
+ }
+ for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
+ assert readSet.contains(field);
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
new file mode 100644
index 0000000..9cd76dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+
+class TrivialFieldReadBeforeWriteAnalysis extends FieldReadBeforeWriteAnalysis {
+
+ @Override
+ public boolean isInstanceFieldMaybeReadBeforeInstruction(
+ Value receiver, DexEncodedField field, Instruction instruction) {
+ return true;
+ }
+
+ @Override
+ public boolean isStaticFieldMaybeReadBeforeInstruction(
+ DexEncodedField field, Instruction instruction) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index b18d02a..854d046 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -39,7 +39,7 @@
return this;
}
- Set<DexEncodedField> getFields() {
+ public Set<DexEncodedField> getFields() {
if (InternalOptions.assertionsEnabled()) {
return Collections.unmodifiableSet(fields);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 9c09ef2..50c075e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -10,13 +10,14 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.DominatorTree.Assumption;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
@@ -24,14 +25,11 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.ListUtils;
import java.util.ArrayList;
-import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
public abstract class FieldValueAnalysis {
@@ -53,16 +51,20 @@
final OptimizationFeedback feedback;
private DominatorTree dominatorTree;
- private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+ private FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis;
final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>();
FieldValueAnalysis(
- AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
this.appView = appView;
this.code = code;
- this.feedback = feedback;
this.context = code.context();
+ this.feedback = feedback;
+ this.fieldReadBeforeWriteAnalysis = fieldReadBeforeWriteAnalysis;
}
DominatorTree getOrCreateDominatorTree() {
@@ -72,13 +74,6 @@
return dominatorTree;
}
- private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
- if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
- fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
- }
- return fieldsMaybeReadBeforeBlockInclusiveCache;
- }
-
boolean isInstanceFieldValueAnalysis() {
return false;
}
@@ -95,9 +90,9 @@
return null;
}
- abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field);
+ abstract boolean isSubjectToOptimizationIgnoringPinning(ProgramField field);
- abstract boolean isSubjectToOptimization(DexEncodedField field);
+ abstract boolean isSubjectToOptimization(ProgramField field);
void recordFieldPut(DexEncodedField field, Instruction instruction) {
recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance());
@@ -127,13 +122,12 @@
DexField field = fieldPut.getField();
ProgramField programField = appInfo.resolveField(field).getProgramField();
if (programField != null) {
- DexEncodedField encodedField = programField.getDefinition();
- if (isSubjectToOptimization(encodedField)) {
- recordFieldPut(encodedField, fieldPut);
+ if (isSubjectToOptimization(programField)) {
+ recordFieldPut(programField.getDefinition(), fieldPut);
} else if (isStaticFieldValueAnalysis()
&& programField.getHolder().isEnum()
- && isSubjectToOptimizationIgnoringPinning(encodedField)) {
- recordFieldPut(encodedField, fieldPut);
+ && isSubjectToOptimizationIgnoringPinning(programField)) {
+ recordFieldPut(programField.getDefinition(), fieldPut);
}
}
} else if (isInstanceFieldValueAnalysis()
@@ -144,186 +138,54 @@
}
}
+ boolean checkDominance = !isStraightLineCode;
List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) {
- DexEncodedField field = entry.getKey();
- List<FieldInitializationInfo> fieldPuts = entry.getValue();
- if (fieldPuts.size() > 1) {
- continue;
- }
- FieldInitializationInfo info = ListUtils.first(fieldPuts);
- Instruction instruction = info.instruction;
- if (instruction.isInvokeDirect()) {
- asInstanceFieldValueAnalysis()
- .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo);
- continue;
- }
- FieldInstruction fieldPut = instruction.asFieldInstruction();
- if (!isStraightLineCode) {
- if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
- continue;
- }
- }
- boolean priorReadsWillReadSameValue =
- !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
- if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
- // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
- if (isStaticFieldValueAnalysis()) {
- // At this point the value read in the field can be only the default static value, if read
- // prior to the put, or the value put, if read after the put. We still want to record it
- // because the default static value is typically null/0, so code present after a null/0
- // check can take advantage of the optimization.
- DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
- asStaticFieldValueAnalysis()
- .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut);
- }
- continue;
- }
- updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
- }
- }
-
- private boolean fieldMaybeReadBeforeInstruction(
- DexEncodedField encodedField, Instruction instruction) {
- BasicBlock block = instruction.getBlock();
-
- // First check if the field may be read in any of the (transitive) predecessor blocks.
- if (fieldMaybeReadBeforeBlock(encodedField, block)) {
- return true;
- }
-
- // Then check if any of the instructions that precede the given instruction in the current block
- // may read the field.
- InstructionIterator instructionIterator = block.iterator();
- while (instructionIterator.hasNext()) {
- Instruction current = instructionIterator.next();
- if (current == instruction) {
- break;
- }
- if (current.readSet(appView, context).contains(encodedField)) {
- return true;
- }
- }
-
- // Otherwise, the field is not read prior to the given instruction.
- return false;
- }
-
- private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
- for (BasicBlock predecessor : block.getPredecessors()) {
- if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean fieldMaybeReadBeforeBlockInclusive(
- DexEncodedField encodedField, BasicBlock block) {
- return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
- }
-
- /**
- * Eagerly creates a mapping from each block to the set of fields that may be read in that block
- * and its transitive predecessors.
- */
- private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
- Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
- Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
- while (!worklist.isEmpty()) {
- BasicBlock block = worklist.removeFirst();
- boolean seenBefore = result.containsKey(block);
- AbstractFieldSet readSet =
- result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
- if (readSet.isTop()) {
- // We already have unknown information for this block.
- continue;
- }
-
- assert readSet.isKnownFieldSet();
- KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
- int oldSize = seenBefore ? knownReadSet.size() : -1;
-
- // Everything that is read in the predecessor blocks should also be included in the read set
- // for the current block, so here we join the information from the predecessor blocks into the
- // current read set.
- boolean blockOrPredecessorMaybeReadAnyField = false;
- for (BasicBlock predecessor : block.getPredecessors()) {
- AbstractFieldSet predecessorReadSet =
- result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
- if (predecessorReadSet.isBottom()) {
- continue;
- }
- if (predecessorReadSet.isTop()) {
- blockOrPredecessorMaybeReadAnyField = true;
- break;
- }
- assert predecessorReadSet.isConcreteFieldSet();
- if (!knownReadSet.isConcreteFieldSet()) {
- knownReadSet = new ConcreteMutableFieldSet();
- }
- knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
- }
-
- if (!blockOrPredecessorMaybeReadAnyField) {
- // Finally, we update the read set with the fields that are read by the instructions in the
- // current block. This can be skipped if the block has already been processed.
- if (seenBefore) {
- assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
- } else {
- for (Instruction instruction : block.getInstructions()) {
- AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
- if (instructionReadSet.isBottom()) {
- continue;
- }
- if (instructionReadSet.isTop()) {
- blockOrPredecessorMaybeReadAnyField = true;
- break;
- }
- if (!knownReadSet.isConcreteFieldSet()) {
- knownReadSet = new ConcreteMutableFieldSet();
- }
- knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
+ putsPerField.forEach(
+ (field, fieldPuts) -> {
+ if (fieldPuts.size() > 1) {
+ return;
}
- }
- }
-
- boolean changed = false;
- if (blockOrPredecessorMaybeReadAnyField) {
- // Record that this block reads all fields.
- result.put(block, UnknownFieldSet.getInstance());
- changed = true;
- } else {
- if (knownReadSet != readSet) {
- result.put(block, knownReadSet.asConcreteFieldSet());
- }
- if (knownReadSet.size() != oldSize) {
- assert knownReadSet.size() > oldSize;
- changed = true;
- }
- }
-
- if (changed) {
- // Rerun the analysis for all successors because the state of the current block changed.
- worklist.addAll(block.getSuccessors());
- }
- }
- return result;
- }
-
- private boolean verifyFieldSetContainsAllFieldReadsInBlock(
- KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
- for (Instruction instruction : block.getInstructions()) {
- AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
- assert !instructionReadSet.isTop();
- if (instructionReadSet.isBottom()) {
- continue;
- }
- for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
- assert readSet.contains(field);
- }
- }
- return true;
+ FieldInitializationInfo info = ListUtils.first(fieldPuts);
+ Instruction instruction = info.instruction;
+ if (instruction.isInvokeDirect()) {
+ asInstanceFieldValueAnalysis()
+ .recordInstanceFieldIsInitializedWithInfo(
+ field, info.instanceFieldInitializationInfo);
+ return;
+ }
+ FieldInstruction fieldPut = instruction.asFieldInstruction();
+ if (checkDominance
+ && !getOrCreateDominatorTree()
+ .dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+ return;
+ }
+ boolean priorReadsWillReadSameValue =
+ !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
+ if (!priorReadsWillReadSameValue) {
+ if (fieldPut.isInstancePut()) {
+ InstancePut instancePut = fieldPut.asInstancePut();
+ if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
+ instancePut.object(), field, instancePut)) {
+ return;
+ }
+ } else {
+ if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
+ field, fieldPut)) {
+ // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
+ // At this point the value read in the field can be only the default static value,
+ // if read prior to the put, or the value put, if read after the put. We still want
+ // to record it because the default static value is typically null/0, so code
+ // present after a null/0 check can take advantage of the optimization.
+ DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
+ asStaticFieldValueAnalysis()
+ .updateFieldOptimizationInfoWith2Values(
+ field, fieldPut.value(), valueBeforePut);
+ return;
+ }
+ }
+ }
+ updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
+ });
}
abstract void updateFieldOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index c4061ec..9ae014f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -11,7 +11,9 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -50,9 +52,10 @@
AppView<AppInfoWithLiveness> appView,
IRCode code,
OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
DexClassAndMethod parentConstructor,
InvokeDirect parentConstructorCall) {
- super(appView, code, feedback);
+ super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
this.factory = appView.instanceFieldInitializationInfoFactory();
this.parentConstructor = parentConstructor;
this.parentConstructorCall = parentConstructorCall;
@@ -67,10 +70,11 @@
IRCode code,
ClassInitializerDefaultsResult classInitializerDefaultsResult,
OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
Timing timing) {
timing.begin("Analyze instance initializer");
InstanceFieldInitializationInfoCollection result =
- run(appView, code, classInitializerDefaultsResult, feedback);
+ run(appView, code, classInitializerDefaultsResult, feedback, fieldReadBeforeWriteAnalysis);
timing.end();
return result;
}
@@ -79,7 +83,8 @@
AppView<?> appView,
IRCode code,
ClassInitializerDefaultsResult classInitializerDefaultsResult,
- OptimizationFeedback feedback) {
+ OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
assert appView.appInfo().hasLiveness();
assert appView.enableWholeProgramOptimizations();
assert code.context().getDefinition().isInstanceInitializer();
@@ -101,6 +106,7 @@
appView.withLiveness(),
code,
feedback,
+ fieldReadBeforeWriteAnalysis,
parentConstructor,
parentConstructorCall);
analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult);
@@ -119,12 +125,12 @@
}
@Override
- boolean isSubjectToOptimization(DexEncodedField field) {
- return !field.isStatic() && field.getHolderType() == context.getHolderType();
+ boolean isSubjectToOptimization(ProgramField field) {
+ return !field.getAccessFlags().isStatic() && field.getHolderType() == context.getHolderType();
}
@Override
- boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
+ boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
throw new Unreachable("Used by static analysis only.");
}
@@ -203,7 +209,7 @@
if (abstractValue.isSingleValue()) {
return abstractValue.asSingleValue();
}
- DexType fieldType = field.type();
+ DexType fieldType = field.getType();
if (fieldType.isClassType()) {
ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
@@ -229,7 +235,7 @@
private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit(
DexEncodedField field, InstancePut instancePut) {
- if (field.isFinal()) {
+ if (field.getAccessFlags().isFinal()) {
return true;
}
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 4df6b75..5f8381d 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
@@ -15,6 +15,8 @@
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.graph.ProgramField;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -49,8 +51,11 @@
private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
private StaticFieldValueAnalysis(
- AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
- super(appView, code, feedback);
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+ super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
builder = StaticFieldValues.builder(code.context().getHolder());
}
@@ -59,13 +64,15 @@
IRCode code,
ClassInitializerDefaultsResult classInitializerDefaultsResult,
OptimizationFeedback feedback,
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
Timing timing) {
assert appView.appInfo().hasLiveness();
assert appView.enableWholeProgramOptimizations();
assert code.context().getDefinition().isClassInitializer();
timing.begin("Analyze class initializer");
StaticFieldValues result =
- new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
+ new StaticFieldValueAnalysis(
+ appView.withLiveness(), code, feedback, fieldReadBeforeWriteAnalysis)
.analyze(classInitializerDefaultsResult);
timing.end();
return result;
@@ -117,19 +124,22 @@
}
@Override
- boolean isSubjectToOptimization(DexEncodedField field) {
- return field.isStatic()
- && field.getHolderType() == context.getHolderType()
- && appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
- }
-
- @Override
- boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
- return field.isStatic()
+ boolean isSubjectToOptimization(ProgramField field) {
+ return field.getAccessFlags().isStatic()
&& field.getHolderType() == context.getHolderType()
&& appView
.appInfo()
- .isFieldOnlyWrittenInMethodIgnoringPinning(field, context.getDefinition());
+ .isFieldOnlyWrittenInMethod(field.getDefinition(), context.getDefinition());
+ }
+
+ @Override
+ boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
+ return field.getAccessFlags().isStatic()
+ && field.getHolderType() == context.getHolderType()
+ && appView
+ .appInfo()
+ .isFieldOnlyWrittenInMethodIgnoringPinning(
+ field.getDefinition(), context.getDefinition());
}
@Override
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 fc8d1f2..bc02268 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
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
@@ -979,10 +980,16 @@
timing.end();
}
+ FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis =
+ FieldReadBeforeWriteAnalysis.create(appView, code, method);
if (fieldAccessAnalysis != null) {
timing.begin("Analyze field accesses");
fieldAccessAnalysis.recordFieldAccesses(
- code, bytecodeMetadataProviderBuilder, feedback, methodProcessor);
+ code,
+ bytecodeMetadataProviderBuilder,
+ feedback,
+ fieldReadBeforeWriteAnalysis,
+ methodProcessor);
if (classInitializerDefaultsResult != null) {
fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
}
@@ -999,11 +1006,21 @@
if (method.getDefinition().isClassInitializer()) {
staticFieldValues =
StaticFieldValueAnalysis.run(
- appView, code, classInitializerDefaultsResult, feedback, timing);
+ appView,
+ code,
+ classInitializerDefaultsResult,
+ feedback,
+ fieldReadBeforeWriteAnalysis,
+ timing);
} else {
instanceFieldInitializationInfos =
InstanceFieldValueAnalysis.run(
- appView, code, classInitializerDefaultsResult, feedback, timing);
+ appView,
+ code,
+ classInitializerDefaultsResult,
+ feedback,
+ fieldReadBeforeWriteAnalysis,
+ timing);
}
}
enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index c2e13af..125b53c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -86,7 +86,7 @@
continue;
}
- if (method.getDefinition().isDefaultInstanceInitializer()
+ if (method.getDefinition().isDefaultInitializer()
&& appView.hasProguardCompatibilityActions()
&& appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 5f9e4f2..c147322 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -96,6 +96,7 @@
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingExactDynamicEnumTypeForEnumWithSubtypesReason;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingInstanceFieldValueForEnumInstanceReason;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingObjectStateForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnboxedValueNonComparable;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedLibraryInvokeReason;
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
@@ -1369,6 +1370,24 @@
return reason;
}
+ private Reason comparableAsUnboxedValues(InvokeMethod invoke) {
+ assert invoke.inValues().size() == 2;
+ TypeElement type1 = invoke.getFirstArgument().getType();
+ TypeElement type2 = invoke.getLastArgument().getType();
+ DexProgramClass candidate1 = getEnumUnboxingCandidateOrNull(type1);
+ DexProgramClass candidate2 = getEnumUnboxingCandidateOrNull(type2);
+ assert candidate1 != null || candidate2 != null;
+ if (type1.isNullType() || type2.isNullType()) {
+ // Comparing an unboxed enum to null is always allowed.
+ return Reason.ELIGIBLE;
+ }
+ if (candidate1 == candidate2) {
+ // Comparing two unboxed enum values is valid only if they come from the same enum.
+ return Reason.ELIGIBLE;
+ }
+ return new UnboxedValueNonComparable(invoke.getInvokedMethod(), type1, type2);
+ }
+
private Reason analyzeLibraryInvoke(
InvokeMethod invoke,
IRCode code,
@@ -1382,13 +1401,9 @@
// TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
if (singleTargetReference == factory.enumMembers.compareTo
|| singleTargetReference == factory.enumMembers.compareToWithObject) {
- DexProgramClass otherEnumClass =
- getEnumUnboxingCandidateOrNull(invoke.getLastArgument().getType());
- if (otherEnumClass == enumClass || invoke.getLastArgument().getType().isNullType()) {
- return Reason.ELIGIBLE;
- }
+ return comparableAsUnboxedValues(invoke);
} else if (singleTargetReference == factory.enumMembers.equals) {
- return Reason.ELIGIBLE;
+ return comparableAsUnboxedValues(invoke);
} else if (singleTargetReference == factory.enumMembers.nameMethod
|| singleTargetReference == factory.enumMembers.toString) {
assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
@@ -1415,6 +1430,17 @@
// This is a hidden null check.
return Reason.ELIGIBLE;
}
+ if (singleTargetReference == factory.objectMembers.toString) {
+ assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
+ addRequiredNameData(enumClass);
+ return Reason.ELIGIBLE;
+ }
+ if (singleTargetReference == factory.objectMembers.hashCode) {
+ return Reason.ELIGIBLE;
+ }
+ if (singleTargetReference == factory.objectMembers.equals) {
+ return comparableAsUnboxedValues(invoke);
+ }
return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index 26c35b2..bfd215d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.google.common.collect.ImmutableList;
public abstract class Reason {
@@ -268,6 +269,43 @@
}
}
+ public static class UnboxedValueNonComparable extends Reason {
+
+ private final DexMethod invokedMethod;
+ private final TypeElement type1;
+ private final TypeElement type2;
+
+ public UnboxedValueNonComparable(
+ DexMethod invokedMethod, TypeElement type1, TypeElement type2) {
+ this.invokedMethod = invokedMethod;
+ this.type1 = type1;
+ this.type2 = type2;
+ }
+
+ @Override
+ public Object getKind() {
+ return ImmutableList.of(getClass(), invokedMethod);
+ }
+
+ private static String typeInformation(TypeElement type) {
+ if (type.isClassType()) {
+ return type.asClassType().getClassType().toSourceString();
+ }
+ return type.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "NonComparableElements("
+ + invokedMethod.toSourceString()
+ + " - "
+ + typeInformation(type1)
+ + " vs "
+ + typeInformation(type2)
+ + ")";
+ }
+ }
+
public static class UnsupportedStaticFieldReason extends Reason {
private final DexField field;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index f518d5e..8b7bd6b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -28,16 +28,16 @@
public static final DefaultMethodOptimizationInfo DEFAULT_INSTANCE =
new DefaultMethodOptimizationInfo();
- static Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
- static int UNKNOWN_RETURNED_ARGUMENT = -1;
- static boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
- static AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
- static boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
- static boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
- static boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
- static boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
- static BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
- static BitSet NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS = null;
+ static final Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
+ static final int UNKNOWN_RETURNED_ARGUMENT = -1;
+ static final boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
+ static final AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
+ static final boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
+ static final boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
+ static final boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
+ static final boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
+ static final BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
+ static final BitSet NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS = null;
protected DefaultMethodOptimizationInfo() {}
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 f8e784e..c92342f 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
@@ -420,9 +420,10 @@
}
builder.setParent(invokedMethod);
} else {
- builder
- .markAllFieldsAsRead()
- .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+ builder.markAllFieldsAsRead();
+ if (invoke.instructionMayHaveSideEffects(appView, context)) {
+ builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+ }
for (Value inValue : invoke.inValues()) {
if (couldBeReceiverValue(inValue, receiver, aliasesThroughAssumeAndCheckCasts)) {
builder.setReceiverMayEscapeOutsideConstructorChain();
@@ -453,9 +454,10 @@
case INVOKE_VIRTUAL:
{
InvokeMethod invoke = instruction.asInvokeMethod();
- builder
- .markAllFieldsAsRead()
- .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+ builder.markAllFieldsAsRead();
+ if (invoke.instructionMayHaveSideEffects(appView, context)) {
+ builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
+ }
for (Value argument : invoke.arguments()) {
if (couldBeReceiverValue(argument, receiver, aliasesThroughAssumeAndCheckCasts)) {
builder.setReceiverMayEscapeOutsideConstructorChain();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index c3fc071..1d92c11 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -17,7 +17,7 @@
public class ProguardMapSupplier {
- public static int PG_MAP_ID_LENGTH = 7;
+ public static final int PG_MAP_ID_LENGTH = 7;
// Hash of the Proguard map (excluding the header up to and including the hash marker).
public static class ProguardMapId {
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
rename to src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index c050859..763c40f 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -1,10 +1,9 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize.redundantbridgeremoval;
+package com.android.tools.r8.optimize;
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.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -16,35 +15,23 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.optimize.InvokeSingleTargetExtractor;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
-import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Iterables;
-import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
public class RedundantBridgeRemover {
private final AppView<AppInfoWithLiveness> appView;
- private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
-
- private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
- new InvokedReflectivelyFromPlatformAnalysis();
public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.redundantBridgeRemovalOptions =
- appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -65,9 +52,6 @@
if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
return null;
}
- if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
- return null;
- }
// This is a visibility forward, so check for the direct target.
DexClassAndMethod targetMethod =
appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -252,88 +236,4 @@
});
appView.pruneItems(prunedItemsBuilder.build(), executorService);
}
-
- class InvokedReflectivelyFromPlatformAnalysis {
-
- // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
- // android.app.ZygotePreload.
- private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
-
- boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
- return method.getDefinition().isDefaultInstanceInitializer()
- && !method.getHolder().isAbstract()
- && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
- }
-
- private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
- Boolean cacheResult = cache.get(clazz);
- if (cacheResult != null) {
- return cacheResult;
- }
- WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
- .process(WorklistItem::accept);
- assert cache.containsKey(clazz);
- return cache.get(clazz);
- }
-
- abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
-
- protected final DexClass clazz;
-
- WorklistItem(DexClass clazz) {
- this.clazz = clazz;
- }
-
- Iterable<DexClass> getImmediateSupertypes() {
- return IterableUtils.flatMap(
- clazz.allImmediateSupertypes(),
- supertype -> {
- DexClass definition = appView.definitionFor(supertype);
- return definition != null
- ? Collections.singletonList(definition)
- : Collections.emptyList();
- });
- }
- }
-
- class NotProcessedWorklistItem extends WorklistItem {
-
- NotProcessedWorklistItem(DexClass clazz) {
- super(clazz);
- }
-
- @Override
- public void accept(WorkList<WorklistItem> worklist) {
- // Enqueue a worklist item to process the current class after processing its super classes.
- worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
- // Enqueue all superclasses for processing.
- for (DexClass supertype : getImmediateSupertypes()) {
- if (!cache.containsKey(supertype)) {
- worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
- }
- }
- }
- }
-
- class ProcessedWorklistItem extends WorklistItem {
-
- ProcessedWorklistItem(DexClass clazz) {
- super(clazz);
- }
-
- @Override
- public void accept(WorkList<WorklistItem> worklist) {
- cache.put(
- clazz,
- Iterables.any(
- getImmediateSupertypes(),
- supertype ->
- cache.get(supertype)
- || (supertype.isLibraryClass()
- && redundantBridgeRemovalOptions
- .isPlatformReflectingOnDefaultConstructorInSubclasses(
- supertype.asLibraryClass()))));
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
deleted file mode 100644
index a902764..0000000
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2023, 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.optimize.redundantbridgeremoval;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexLibraryClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.SetUtils;
-import java.util.Collections;
-import java.util.Set;
-
-public class RedundantBridgeRemovalOptions {
-
- private final InternalOptions options;
-
- private Set<DexType> noConstructorShrinkingHierarchies;
-
- public RedundantBridgeRemovalOptions(InternalOptions options) {
- this.options = options;
- }
-
- public void clearNoConstructorShrinkingHierarchiesForTesting() {
- noConstructorShrinkingHierarchies = Collections.emptySet();
- }
-
- public RedundantBridgeRemovalOptions ensureInitialized() {
- if (noConstructorShrinkingHierarchies == null) {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- noConstructorShrinkingHierarchies =
- SetUtils.newIdentityHashSet(
- dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
- }
- return this;
- }
-
- public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
- return noConstructorShrinkingHierarchies.contains(clazz.getType());
- }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 39d8035..07a5bb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -954,14 +954,6 @@
return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
}
- public boolean mayPropagateArgumentsTo(ProgramMethod method) {
- DexMethod reference = method.getReference();
- return method.getDefinition().hasCode()
- && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
- && !neverReprocess.contains(reference)
- && !keepInfo.getMethodInfo(method).isPinned(options());
- }
-
public boolean mayPropagateValueFor(
AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
assert checkIfObsolete();
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 ca51281..86cdb5a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1624,9 +1624,9 @@
static class FieldAccessMetadata {
- private static int DEFERRED_MASK = 1;
- private static int FROM_METHOD_HANDLE_MASK = 2;
- private static int FROM_RECORD_METHOD_HANDLE_MASK = 4;
+ private static final int DEFERRED_MASK = 1;
+ private static final int FROM_METHOD_HANDLE_MASK = 2;
+ private static final int FROM_RECORD_METHOD_HANDLE_MASK = 4;
static FieldAccessMetadata DEFAULT = new FieldAccessMetadata(0);
static FieldAccessMetadata FROM_METHOD_HANDLE =
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 7286734..722a412 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -74,7 +74,6 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.profile.art.ArtProfileOptions;
@@ -890,8 +889,6 @@
private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
- private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
- new RedundantBridgeRemovalOptions(this);
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -960,10 +957,6 @@
return cfCodeAnalysisOptions;
}
- public RedundantBridgeRemovalOptions getRedundantBridgeRemovalOptions() {
- return redundantBridgeRemovalOptions;
- }
-
public DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
@@ -1677,6 +1670,7 @@
private boolean enable =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
+ private boolean enableInitial = true;
// TODO(b/205611444): Enable by default.
private boolean enableClassInitializerDeadlockDetection = true;
private boolean enableInterfaceMerging =
@@ -1692,6 +1686,10 @@
enable = false;
}
+ public void disableInitialRoundOfClassMerging() {
+ enableInitial = false;
+ }
+
public void disableSyntheticMerging() {
enableSyntheticMerging = false;
}
@@ -1729,7 +1727,7 @@
return false;
}
if (mode.isInitial()) {
- return inlinerOptions.enableInlining && isShrinking();
+ return enableInitial && inlinerOptions.enableInlining && isShrinking();
}
assert mode.isFinal();
return true;
@@ -2064,7 +2062,7 @@
}
}
- public static int NO_LIMIT = -1;
+ public static final int NO_LIMIT = -1;
public ArgumentPropagatorEventConsumer argumentPropagatorEventConsumer =
ArgumentPropagatorEventConsumer.emptyConsumer();
@@ -2919,7 +2917,8 @@
}
public boolean canHaveNonReboundConstructorInvoke() {
- return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ // TODO(b/246679983): Turned off while diagnosing b/246679983.
+ return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
// b/238399429 Some art 6 vms have issues with multiple monitors in the same method
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 206a8ab..c22f43e 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -58,7 +58,8 @@
* runtime error.
*/
public boolean canHaveNonReboundConstructorInvoke() {
- return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ // TODO(b/246679983): Turned off while diagnosing b/246679983.
+ return false && isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
public boolean canUseDefaultAndStaticInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
new file mode 100644
index 0000000..70f55b4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2023, 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.classmerging.horizontal;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class HorizontalClassMergingAfterConstructorShrinkingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+ .build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ // TODO(b/276385221): Disabled constructor shrinking for now
+ Assume.assumeTrue(parameters.canHaveNonReboundConstructorInvoke());
+ assertTrue(parameters.canHaveNonReboundConstructorInvoke());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ // TODO(b/276385221): Should not trigger A.<init>.
+ .assertSuccessWithOutputLines("Ouch!", "B");
+ }
+
+ static class Main {
+
+ static {
+ new B().setFieldOnB().printFieldOnB();
+ }
+
+ public static void main(String[] args) {
+ if (System.currentTimeMillis() < 0) {
+ new A().setFieldOnA().printFieldOnA();
+ }
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ Object field;
+
+ A() {
+ System.out.println("Ouch!");
+ }
+
+ @NeverInline
+ A setFieldOnA() {
+ field = "A";
+ return this;
+ }
+
+ @NeverInline
+ void printFieldOnA() {
+ System.out.println(field);
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ Object field;
+
+ // Removed by constructor shrinking.
+ B() {}
+
+ @NeverInline
+ B setFieldOnB() {
+ field = "B";
+ return this;
+ }
+
+ @NeverInline
+ void printFieldOnB() {
+ System.out.println(field);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
index e8d7572..b7bb268 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
@@ -13,14 +13,12 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.collect.ImmutableSet;
import java.util.List;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,9 +47,6 @@
@Test
public void test() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 22529e5..3d2be09 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -223,9 +223,6 @@
// This test has a cycle in the call graph consisting of the methods A.<init> and B.<init>.
@Test
public void testCallGraphCycle() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
String main = "classmerging.CallGraphCycleTest";
Path[] programFiles =
new Path[] {
@@ -319,9 +316,6 @@
@Test
public void testFieldCollision() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
String main = "classmerging.FieldCollisionTest";
Path[] programFiles =
new Path[] {
@@ -429,9 +423,6 @@
@Test
public void testPinnedParameterTypes() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
String main = "classmerging.PinnedParameterTypesTest";
Path[] programFiles =
new Path[] {
@@ -457,9 +448,6 @@
@Test
public void testPinnedArrayParameterTypes() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
String main = "classmerging.PinnedArrayParameterTypesTest";
Path[] programFiles =
new Path[] {
@@ -853,9 +841,6 @@
// }
@Test
public void testSuperCallToMergedClassIsRewritten() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0));
assumeTrue(parameters.isDexRuntime()); // Due to smali input.
assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V5_1_1);
assumeFalse(parameters.getRuntime().asDex().getVm().getVersion() == Version.V6_0_1);
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 6676e2c..265bb19 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -36,7 +36,6 @@
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.stream.Collectors;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
@@ -143,10 +142,6 @@
public void testContinuousSingleStep() throws Throwable {
DebugTestConfig config = compiledJars.apply(jarPath);
assert config != null;
- Assume.assumeTrue(
- "b/273921056",
- config.isCfRuntime()
- || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V14_0_0));
runContinuousTest(mainClass, config, MAIN_METHOD_NAME);
}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index a2254be..35178f8 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
import java.util.ArrayList;
@@ -15,7 +14,6 @@
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -279,10 +277,6 @@
@Test
public void testLocals_MoreThan16() throws Throwable {
- Assume.assumeTrue(
- "b/273921056",
- config.isCfRuntime()
- || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V14_0_0));
final int minIndex = 1;
final int maxIndex = 16;
Map<String, Value> arrayLocals = new HashMap<>();
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/CallToOtherEnumCompareToMethodNegativeUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/CallToOtherEnumCompareToMethodNegativeUnboxingTest.java
new file mode 100644
index 0000000..f190a34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/CallToOtherEnumCompareToMethodNegativeUnboxingTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2023, 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.enumunboxing;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CallToOtherEnumCompareToMethodNegativeUnboxingTest extends EnumUnboxingTestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+ runResult -> runResult.assertFailureWithErrorThatThrows(ClassCastException.class),
+ runResult -> runResult.assertSuccessWithOutputLines("0"));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(Foo.class)
+ .addEnumUnboxingInspector(EnumUnboxingInspector::assertNoEnumsUnboxed)
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+ runResult -> runResult.assertFailureWithErrorThatThrows(ClassCastException.class),
+ runResult -> runResult.assertSuccessWithOutputLines("0"));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Enum<?> fooEnum = Foo.A;
+ Enum<Bar> fooEnumInDisguise = (Enum<Bar>) fooEnum;
+ System.out.println(fooEnumInDisguise.compareTo(Bar.B));
+ }
+ }
+
+ enum Foo {
+ A
+ }
+
+ enum Bar {
+ B
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InvalidEqualsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InvalidEqualsEnumUnboxingTest.java
new file mode 100644
index 0000000..2ed2d40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InvalidEqualsEnumUnboxingTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2023, 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.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
+import java.util.List;
+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 InvalidEqualsEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public InvalidEqualsEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(EnumEquals.class)
+ .addEnumUnboxingInspector(EnumUnboxingInspector::assertNoEnumsUnboxed)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), EnumEquals.class)
+ .assertSuccessWithOutputLines("false", "false");
+ }
+
+ static class EnumEquals {
+
+ @NeverClassInline
+ enum MyEnumEquals {
+ A,
+ B
+ }
+
+ @NeverClassInline
+ enum MyEnumEquals1 {
+ A,
+ B
+ }
+
+ @NeverClassInline
+ enum MyEnumEquals2 {
+ A,
+ B
+ }
+
+ public static void main(String[] args) {
+ Object guineaPig = new Object();
+ System.out.println(MyEnumEquals.A.equals(guineaPig));
+ System.out.println(MyEnumEquals1.A.equals(MyEnumEquals2.A));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 2cdeffd..bb382f8 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
@@ -96,7 +97,11 @@
method -> {
IRCode code = method.buildIR(appView);
fieldAccessAnalysis.recordFieldAccesses(
- code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock());
+ code,
+ BytecodeMetadataProvider.builder(),
+ feedback,
+ FieldReadBeforeWriteAnalysis.trivial(),
+ new PrimaryMethodProcessorMock());
});
int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
new file mode 100644
index 0000000..972cc79
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NonNullInstanceFieldPropagationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+ assertEquals(1, inspector.allClasses().size());
+ assertEquals(1, mainClassSubject.allMethods().size());
+ assertEquals(0, mainClassSubject.allFields().size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Main p = new Main();
+ p.mThreadChecker.assertOnValidThread();
+ }
+
+ public final ThreadChecker mThreadChecker = new ThreadChecker();
+ }
+
+ static class ThreadChecker {
+ public void assertOnValidThread() {
+ if (System.currentTimeMillis() == 0) {
+ throw new RuntimeException();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index a7c2581..d843a4c 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -72,6 +72,10 @@
return dexVm.getVersion().isNewerThanOrEqual(Version.V4_4_4);
}
+ static boolean isAndroidK(DexVm dexVm, Tool tool) {
+ return dexVm.getVersion().isEqualTo(Version.V4_4_4);
+ }
+
static boolean isAndroidLOrAbove(DexVm dexVm, Tool tool) {
return dexVm.getVersion().isNewerThan(Version.V4_4_4);
}
@@ -126,11 +130,13 @@
.put("ClassObjectReference.ReflectedType002Test", RunJdwpTests::isAndroidLOrAbove)
.put("ClassType.InvokeMethodTest", RunJdwpTests::isAndroidLOrAbove)
.put("ClassType.InvokeMethod002Test", RunJdwpTests::isAndroidLOrAbove)
- .put("ClassType.InvokeMethodAfterMultipleThreadSuspensionTest",
+ .put(
+ "ClassType.InvokeMethodAfterMultipleThreadSuspensionTest",
RunJdwpTests::isAndroidNOrAbove)
.put("ClassType.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
.put("ClassType.NewInstanceTest", RunJdwpTests::isAndroidLOrAbove)
- .put("ClassType.NewInstanceAfterMultipleThreadSuspensionTest",
+ .put(
+ "ClassType.NewInstanceAfterMultipleThreadSuspensionTest",
RunJdwpTests::isAndroidNOrAbove)
.put("ClassType.NewInstanceStringTest", RunJdwpTests::isAndroidOOrAbove)
.put("ClassType.NewInstanceTagTest", RunJdwpTests::isAndroidNOrAbove)
@@ -164,7 +170,9 @@
.put("Method.IsObsoleteTest", RunJdwpTests::isAndroidNOrAbove)
.put("Method.LineTableTest", or(RunJdwpTests::isAndroidKOrAbove, RunJdwpTests::isJava))
.put("Method.VariableTableTest", RunJdwpTests::isAndroidOOrAbove)
- .put("Method.VariableTableWithGenericTest", RunJdwpTests::isAndroidOOrAbove)
+ .put(
+ "Method.VariableTableWithGenericTest",
+ or(RunJdwpTests::isAndroidOOrAbove, RunJdwpTests::isAndroidK))
.put("MultiSession.AttachConnectorTest", RunJdwpTests::isAndroidLOrAbove)
.put("MultiSession.BreakpointTest", RunJdwpTests::isAndroidLOrAbove)
.put("MultiSession.ClassObjectIDTest", RunJdwpTests::isAndroidLOrAbove)
@@ -183,7 +191,8 @@
.put("ObjectReference.GetValues002Test", RunJdwpTests::isAndroidLOrAbove)
.put("ObjectReference.InvokeMethodDefaultTest", RunJdwpTests::isAndroidNOrAbove)
.put("ObjectReference.InvokeMethodDefault002Test", RunJdwpTests::isAndroidNOrAbove)
- .put("ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest",
+ .put(
+ "ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest",
RunJdwpTests::isAndroidNOrAbove)
.put("ObjectReference.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
.put("ObjectReference.IsCollectedTest", RunJdwpTests::isAndroidLOrAbove)
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 92404f0..8078ed6 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -101,6 +101,7 @@
" void <init>();",
"}",
"-neverclassinline class *",
+ "-nohorizontalclassmerging class adaptresourcefilenames.B",
"-nohorizontalclassmerging class adaptresourcefilenames.pkg.C",
"-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D");
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index c83d81d..2d38860 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -75,22 +75,15 @@
}
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- inspect(profileInspector, inspector, false, false);
+ inspect(profileInspector, inspector, false);
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- inspect(
- profileInspector,
- inspector,
- parameters.canHaveNonReboundConstructorInvoke(),
- parameters.isCfRuntime());
+ inspect(profileInspector, inspector, parameters.isCfRuntime());
}
public void inspect(
- ArtProfileInspector profileInspector,
- CodeInspector inspector,
- boolean canHaveNonReboundConstructorInvoke,
- boolean canUseLambdas) {
+ ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
assertThat(mainClassSubject, isPresent());
@@ -103,26 +96,21 @@
assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
- assertThat(
- lambdaInitializerSubject,
- notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+ assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaMainMethodSubject =
lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
if (canUseLambdas) {
- profileInspector.assertContainsMethodRule(mainMethodSubject);
+ profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
} else {
profileInspector
.assertContainsClassRules(lambdaClassSubject)
- .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
- .applyIf(
- !canHaveNonReboundConstructorInvoke,
- i -> i.assertContainsMethodRule(lambdaInitializerSubject));
+ .assertContainsMethodRules(
+ mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
+ .assertContainsNoOtherRules();
}
-
- profileInspector.assertContainsNoOtherRules();
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 577518d..7d9e9ab 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -104,22 +104,17 @@
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector)
throws Exception {
- inspect(profileInspector, inspector, false, parameters.canUseNestBasedAccessesWhenDesugaring());
+ inspect(profileInspector, inspector, parameters.canUseNestBasedAccessesWhenDesugaring());
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector)
throws Exception {
- inspect(
- profileInspector,
- inspector,
- parameters.canHaveNonReboundConstructorInvoke(),
- parameters.canUseNestBasedAccesses());
+ inspect(profileInspector, inspector, parameters.canUseNestBasedAccesses());
}
private void inspect(
ArtProfileInspector profileInspector,
CodeInspector inspector,
- boolean canHaveNonReboundConstructorInvoke,
boolean canUseNestBasedAccesses)
throws Exception {
ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
@@ -133,7 +128,7 @@
syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
MethodSubject instanceInitializer = nestMemberClassSubject.init();
- assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
+ assertThat(instanceInitializer, isPresent());
MethodSubject instanceInitializerWithSyntheticArgumentSubject =
syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 6d03704..5f1886d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -149,7 +149,6 @@
profileInspector,
inspector,
SyntheticItemsTestUtils.syntheticRecordTagClass(),
- false,
parameters.canUseNestBasedAccessesWhenDesugaring(),
parameters.canUseRecordsWhenDesugaring());
}
@@ -159,7 +158,6 @@
profileInspector,
inspector,
RECORD_REFERENCE,
- parameters.canHaveNonReboundConstructorInvoke(),
parameters.canUseNestBasedAccesses(),
parameters.canUseRecords());
}
@@ -168,7 +166,6 @@
ArtProfileInspector profileInspector,
CodeInspector inspector,
ClassReference recordClassReference,
- boolean canHaveNonReboundConstructorInvoke,
boolean canUseNestBasedAccesses,
boolean canUseRecords) {
ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -180,14 +177,11 @@
ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
if (!canUseRecords) {
- assertEquals(
- canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
+ assertEquals(1, recordTagClassSubject.allMethods().size());
}
MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
- assertThat(
- recordTagInstanceInitializerSubject,
- notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
+ assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
assertThat(personRecordClassSubject, isPresent());
@@ -296,13 +290,11 @@
hashCodeHelperClassSubject,
toStringHelperClassSubject)
.assertContainsMethodRules(
+ recordTagInstanceInitializerSubject,
equalsHelperMethodSubject,
getFieldsAsObjectsMethodSubject,
hashCodeHelperMethodSubject,
- toStringHelperMethodSubject)
- .applyIf(
- !canHaveNonReboundConstructorInvoke,
- j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
+ toStringHelperMethodSubject))
.assertContainsNoOtherRules();
}
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index ef6b3a3..385f94b 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -68,7 +68,6 @@
public void inspect(
ArtProfileInspector profileInspector,
CodeInspector inspector,
- boolean canHaveNonReboundConstructorInvoke,
boolean canUseLambdas,
boolean canAccessModifyLambdaImplementationMethods) {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
@@ -107,9 +106,7 @@
assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
- assertThat(
- lambdaInitializerSubject,
- notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+ assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaMainMethodSubject =
lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -122,9 +119,7 @@
MethodSubject otherLambdaInitializerSubject =
otherLambdaClassSubject.uniqueInstanceInitializer();
- assertThat(
- otherLambdaInitializerSubject,
- notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+ assertThat(otherLambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
MethodSubject otherLambdaMainMethodSubject =
otherLambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -154,12 +149,8 @@
// interface method implementation does not need to be included in the profile.
profileInspector
.assertContainsClassRules(lambdaClassSubject, otherLambdaClassSubject)
- .assertContainsMethodRules(mainMethodSubject)
- .applyIf(
- !canHaveNonReboundConstructorInvoke,
- i ->
- i.assertContainsMethodRules(
- lambdaInitializerSubject, otherLambdaInitializerSubject))
+ .assertContainsMethodRules(
+ mainMethodSubject, lambdaInitializerSubject, otherLambdaInitializerSubject)
.assertContainsNoOtherRules();
break;
case IMPLEMENTATION_METHOD:
@@ -230,16 +221,11 @@
}
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- artProfileInputOutput.inspect(profileInspector, inspector, false, false, true);
+ artProfileInputOutput.inspect(profileInspector, inspector, false, true);
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- artProfileInputOutput.inspect(
- profileInspector,
- inspector,
- parameters.canHaveNonReboundConstructorInvoke(),
- parameters.isCfRuntime(),
- false);
+ artProfileInputOutput.inspect(profileInspector, inspector, parameters.isCfRuntime(), false);
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
index 31d557e..babd7a7 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
@@ -59,7 +59,10 @@
Bar value = new Bar();
public static void main(String[] args) {
- new Foo();
+ Foo foo = new Foo();
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(foo);
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 7dacf64..534bbb9 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,21 +5,14 @@
package com.android.tools.r8.shaking.constructor;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.apimodel.ApiModelingTestHelper;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -29,168 +22,48 @@
@RunWith(Parameterized.class)
public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
- private static final String APPLICATION_INFO_DESCRIPTOR = "Landroid/content/pm/ApplicationInfo;";
- private static final String FRAGMENT_DESCRIPTOR = "Landroid/app/Fragment;";
- private static final String ZYGOTE_PRELOAD_DESCRIPTOR = "Landroid/app/ZygotePreload;";
-
- private static final String EXPECTED_OUTPUT =
- StringUtils.lines(
- "Fragment.onCreate()", "MyFragment.onCreate()", "MyZygotePreload.doPreload()");
-
- private static List<byte[]> transformedProgramClassFileData;
- private static List<byte[]> transformedLibraryClassFileData;
-
@Parameter(0)
- public boolean enableModeling;
-
- @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{1}, modeling: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
- }
-
- @BeforeClass
- public static void setup() throws Exception {
- transformedProgramClassFileData = getTransformedProgramClasses();
- transformedLibraryClassFileData = getTransformedLibraryClasses();
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
- public void testRuntime() throws Exception {
- assumeFalse(enableModeling);
- testForRuntime(parameters)
- .addProgramClassFileData(transformedProgramClassFileData)
- .addProgramClassFileData(transformedLibraryClassFileData)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
- }
-
- @Test
- public void testR8() throws Exception {
+ public void test() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClassFileData(transformedProgramClassFileData)
- .addLibraryClassFileData(transformedLibraryClassFileData)
+ .addProgramClasses(Main.class, MyFragment.class)
+ .addLibraryClasses(Fragment.class)
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.addKeepMainRule(Main.class)
- .applyIf(
- !enableModeling,
- testBuilder ->
- testBuilder.addOptionsModification(
- options ->
- options
- .getRedundantBridgeRemovalOptions()
- .clearNoConstructorShrinkingHierarchiesForTesting()))
- // Since Fragment is first defined in API 11.
- .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
- .addRunClasspathClassFileData(transformedLibraryClassFileData)
+ .addBootClasspathClasses(Fragment.class)
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
- runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
- runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
- }
-
- private static List<byte[]> getTransformedProgramClasses() throws Exception {
- return ImmutableList.of(
- transformer(Main.class)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
- .transform(),
- transformer(MyFragment.class)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
- .setSuper(FRAGMENT_DESCRIPTOR)
- .transform(),
- transformer(MyZygotePreload.class)
- .replaceClassDescriptorInMembers(
- descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
- .setImplementsClassDescriptors(ZYGOTE_PRELOAD_DESCRIPTOR)
- .transform());
- }
-
- private static List<byte[]> getTransformedLibraryClasses() throws Exception {
- return ImmutableList.of(
- transformer(ApplicationInfo.class)
- .setClassDescriptor(APPLICATION_INFO_DESCRIPTOR)
- .transform(),
- transformer(Fragment.class).setClassDescriptor(FRAGMENT_DESCRIPTOR).transform(),
- transformer(Platform.class)
- .replaceClassDescriptorInMembers(descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
- .replaceClassDescriptorInMembers(
- descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
- .transform(),
- transformer(ZygotePreload.class)
- .replaceClassDescriptorInMembers(
- descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
- .setClassDescriptor(ZYGOTE_PRELOAD_DESCRIPTOR)
- .transform());
+ .assertSuccessWithOutputLines("Instantiating");
}
private void inspect(CodeInspector inspector) {
ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
assertThat(myFragmentClassSubject, isPresent());
- assertThat(
- myFragmentClassSubject.init(),
- onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
-
- ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
- assertThat(myZygotePreloadClassSubject, isPresent());
- assertThat(
- myZygotePreloadClassSubject.init(),
- onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+ assertThat(myFragmentClassSubject.init(), isPresent());
}
- // Library classes.
-
- public abstract static class ApplicationInfo {}
-
public abstract static class Fragment {
- public void onCreate() {
- System.out.println("Fragment.onCreate()");
+ public Fragment newInstance() throws Exception {
+ System.out.println("Instantiating");
+ return getClass().getDeclaredConstructor().newInstance();
}
}
- public interface ZygotePreload {
-
- void doPreload(ApplicationInfo applicationInfo);
- }
-
- public static class Platform {
-
- public static void accept(Fragment fragment) throws Exception {
- Fragment newFragment = fragment.getClass().getDeclaredConstructor().newInstance();
- newFragment.onCreate();
- }
-
- public static void accept(ZygotePreload runnable) throws Exception {
- ZygotePreload newZygotePreload = runnable.getClass().getDeclaredConstructor().newInstance();
- newZygotePreload.doPreload(null);
- }
- }
-
- // Program classes.
-
public static class Main {
public static void main(String[] args) throws Exception {
- Platform.accept(new MyFragment());
- Platform.accept(new MyZygotePreload());
+ new MyFragment().newInstance();
}
}
@@ -198,21 +71,5 @@
public static class MyFragment extends Fragment {
public MyFragment() {}
-
- @Override
- public void onCreate() {
- super.onCreate();
- System.out.println("MyFragment.onCreate()");
- }
- }
-
- public static class MyZygotePreload implements ZygotePreload {
-
- public MyZygotePreload() {}
-
- @Override
- public void doPreload(ApplicationInfo applicationInfo) {
- System.out.println("MyZygotePreload.doPreload()");
- }
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
index c075406..3cbb42d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -22,6 +22,11 @@
this.unboxedEnums = unboxedEnums;
}
+ public EnumUnboxingInspector assertNoEnumsUnboxed() {
+ assertTrue(unboxedEnums.isEmpty());
+ return this;
+ }
+
public EnumUnboxingInspector assertUnboxed(String typeName) {
assertTrue(
unboxedEnums.isUnboxedEnum(
diff --git a/third_party/jdwp-tests.tar.gz.sha1 b/third_party/jdwp-tests.tar.gz.sha1
index d3eeb60..0da9451 100644
--- a/third_party/jdwp-tests.tar.gz.sha1
+++ b/third_party/jdwp-tests.tar.gz.sha1
@@ -1 +1 @@
-db49b6093abaa165e422734a755cdcecf36c03db
\ No newline at end of file
+86eeaa4cfe57d20b03af02310acac89892db3bd3
\ No newline at end of file
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index c845f51..f548507 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-e01c698069f2b52bd80864b70adfeaf0c27e5f4f
\ No newline at end of file
+99369b53116d6ab88384f57930b203cd7de55c0a
\ No newline at end of file