Merge commit '1dfab304817ad501af6ed49156e54379d3da7588' into dev-release
Change-Id: Ibae215a1d5266bc511fd7194eafcdbab2b0173c3
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
index 9b9989a..4b2ab5a 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -36,7 +36,7 @@
import com.android.tools.r8.graph.ThrowExceptionCode;
import com.android.tools.r8.ir.conversion.PrimaryD8L8IRConverter;
import com.android.tools.r8.ir.desugar.TypeRewriter;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.records.RecordTagSynthesizer;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
import com.android.tools.r8.jar.CfApplicationWriter;
@@ -182,7 +182,7 @@
List<ProgramMethod> methodsToProcess = new ArrayList<>();
// Add global synthetic class for records.
- RecordDesugaring.ensureRecordClassHelper(
+ RecordTagSynthesizer.ensureRecordClassHelper(
appView,
synthesizingContext,
recordTagClass -> recordTagClass.programMethods().forEach(methodsToProcess::add),
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 74f9c92..f84fd22 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -45,8 +45,8 @@
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
import com.android.tools.r8.ir.desugar.records.RecordFieldValuesRewriter;
+import com.android.tools.r8.ir.desugar.records.RecordInstructionDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
@@ -327,8 +327,8 @@
if (options.enableEnumUnboxing) {
EnumUnboxingCfMethods.registerSynthesizedCodeReferences(appView.dexItemFactory());
}
- if (options.shouldDesugarRecords()) {
- RecordDesugaring.registerSynthesizedCodeReferences(appView.dexItemFactory());
+ if (options.desugarRecordState().isNotOff()) {
+ RecordInstructionDesugaring.registerSynthesizedCodeReferences(appView.dexItemFactory());
}
if (options.shouldDesugarVarHandle()) {
VarHandleDesugaring.registerSynthesizedCodeReferences(appView.dexItemFactory());
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index bf1f0c0..1adf080 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -1324,6 +1324,7 @@
internal.loadAllClassDefinitions = l8Shrinking;
if (l8Shrinking) {
internal.apiModelingOptions().disableStubbingOfClasses();
+ internal.ignoreUnusedProguardRules = true;
}
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
diff --git a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
index b5f3a31..63636d9 100644
--- a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
+++ b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
@@ -16,7 +16,8 @@
public static ApplicationReaderMap getInstance(InternalOptions options) {
ApplicationReaderMap result = new EmptyMap();
- if (options.shouldDesugarRecords() && !options.testing.disableRecordApplicationReaderMap) {
+ if (options.desugarRecordState().isNotOff()
+ && !options.testing.disableRecordApplicationReaderMap) {
result = new RecordMap(options.dexItemFactory());
}
return result;
diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
index d6c5bdf..6c3be81 100644
--- a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.records.RecordFullInstructionDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -162,14 +162,15 @@
}
public void checkFieldForRecord(DexField dexField, ClassKind<?> classKind) {
- if (options.shouldDesugarRecords() && RecordDesugaring.refersToRecord(dexField, getFactory())) {
+ if (options.desugarRecordState().isFull()
+ && RecordFullInstructionDesugaring.refersToRecord(dexField, getFactory())) {
addRecordWitness(dexField.getHolderType(), classKind);
}
}
public void checkMethodForRecord(DexMethod dexMethod, ClassKind<?> classKind) {
- if (options.shouldDesugarRecords()
- && RecordDesugaring.refersToRecord(dexMethod, getFactory())) {
+ if (options.desugarRecordState().isFull()
+ && RecordFullInstructionDesugaring.refersToRecord(dexMethod, getFactory())) {
addRecordWitness(dexMethod.getHolderType(), classKind);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java
index dd5b2c9..3d61f0e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraproceduralDataflowAnalysis;
import com.android.tools.r8.ir.analysis.path.state.PathConstraintAnalysisState;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameterFactory;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
/**
@@ -37,11 +38,15 @@
public class PathConstraintAnalysis
extends IntraproceduralDataflowAnalysis<PathConstraintAnalysisState> {
- public PathConstraintAnalysis(AppView<AppInfoWithLiveness> appView, IRCode code) {
+ public PathConstraintAnalysis(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ MethodParameterFactory methodParameterFactory) {
super(
appView,
PathConstraintAnalysisState.bottom(),
code,
- new PathConstraintAnalysisTransferFunction(appView.abstractValueFactory()));
+ new PathConstraintAnalysisTransferFunction(
+ appView.abstractValueFactory(), code.context(), methodParameterFactory));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java
index 6b86310..794a9a6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.path;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
import com.android.tools.r8.ir.analysis.path.state.PathConstraintAnalysisState;
@@ -11,6 +12,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameterFactory;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeBuilder;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
@@ -19,8 +21,12 @@
private final ComputationTreeBuilder computationTreeBuilder;
- PathConstraintAnalysisTransferFunction(AbstractValueFactory abstractValueFactory) {
- computationTreeBuilder = new ComputationTreeBuilder(abstractValueFactory);
+ PathConstraintAnalysisTransferFunction(
+ AbstractValueFactory abstractValueFactory,
+ ProgramMethod method,
+ MethodParameterFactory methodParameterFactory) {
+ computationTreeBuilder =
+ new ComputationTreeBuilder(abstractValueFactory, method, methodParameterFactory);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index b83c92e..6350b1e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.function.IntFunction;
@@ -29,6 +30,11 @@
return this;
}
+ @Override
+ public MethodParameter getSingleOpenVariable() {
+ return null;
+ }
+
public abstract boolean isNonTrivial();
public boolean isSingleBoolean() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
index f785b37..c6765f4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterL8Synthesizer;
import com.android.tools.r8.ir.desugar.itf.ProgramEmulatedInterfaceSynthesizer;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.records.RecordClassDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
@@ -35,7 +35,7 @@
}
synthesizers.add(new DesugaredLibraryWrapperSynthesizer(appView));
}
- RecordDesugaring recordRewriter = RecordDesugaring.create(appView);
+ RecordClassDesugaring recordRewriter = RecordClassDesugaring.create(appView);
if (recordRewriter != null) {
synthesizers.add(recordRewriter);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index a0db01b..e442766 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarerPostProcessor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterPostProcessor;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.records.RecordClassDesugaring;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -72,7 +72,7 @@
if (apiCallbackSynthesizor != null) {
desugarings.add(apiCallbackSynthesizor);
}
- RecordDesugaring recordRewriter = RecordDesugaring.create(appView);
+ RecordClassDesugaring recordRewriter = RecordClassDesugaring.create(appView);
if (recordRewriter != null) {
desugarings.add(recordRewriter);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index fc40466..5aa1c3f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -30,7 +30,7 @@
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.records.RecordInstructionDesugaring;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.desugar.twr.TwrInstructionDesugaring;
import com.android.tools.r8.ir.desugar.typeswitch.TypeSwitchDesugaring;
@@ -59,7 +59,6 @@
private final List<CfInstructionDesugaring> yieldingDesugarings = new ArrayList<>();
private final NestBasedAccessDesugaring nestBasedAccessDesugaring;
- private final RecordDesugaring recordRewriter;
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
@@ -81,7 +80,6 @@
}
if (appView.options().desugarState.isOff()) {
this.nestBasedAccessDesugaring = null;
- this.recordRewriter = null;
this.desugaredLibraryRetargeter = null;
this.interfaceMethodRewriter = null;
this.desugaredLibraryAPIConverter = null;
@@ -111,7 +109,7 @@
if (appView.options().enableTypeSwitchDesugaring) {
desugarings.add(new TypeSwitchDesugaring(appView));
}
- recordRewriter = RecordDesugaring.create(appView);
+ RecordInstructionDesugaring recordRewriter = RecordInstructionDesugaring.create(appView);
if (recordRewriter != null) {
desugarings.add(recordRewriter);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordClassDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordClassDesugaring.java
new file mode 100644
index 0000000..74a616f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordClassDesugaring.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.records;
+
+import static com.android.tools.r8.ir.desugar.records.RecordTagSynthesizer.ensureRecordClass;
+
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplicationReadFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+public class RecordClassDesugaring
+ implements CfClassSynthesizerDesugaring, CfPostProcessingDesugaring {
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+
+ public static RecordClassDesugaring create(AppView<?> appView) {
+ return appView.options().desugarRecordState().isFull()
+ ? new RecordClassDesugaring(appView)
+ : null;
+ }
+
+ private RecordClassDesugaring(AppView<?> appView) {
+ this.appView = appView;
+ factory = appView.dexItemFactory();
+ }
+
+ @Override
+ public String uniqueIdentifier() {
+ return "$record";
+ }
+
+ @Override
+ public void synthesizeClasses(
+ ClassSynthesisDesugaringContext processingContext,
+ CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+ DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
+ if (flags.hasReadRecordReferenceFromProgramClass()) {
+ List<DexProgramClass> classes = new ArrayList<>(flags.getRecordWitnesses().size());
+ for (DexType recordWitness : flags.getRecordWitnesses()) {
+ DexClass dexClass = appView.contextIndependentDefinitionFor(recordWitness);
+ assert dexClass != null;
+ assert dexClass.isProgramClass();
+ classes.add(dexClass.asProgramClass());
+ }
+ ensureRecordClass(eventConsumer, classes, appView);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("ReferenceEquality")
+ public void postProcessingDesugaring(
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService) {
+ for (DexProgramClass clazz : programClasses) {
+ if (clazz.isRecord()) {
+ assert clazz.superType == factory.recordType;
+ clazz.accessFlags.unsetRecord();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java
new file mode 100644
index 0000000..ee8f224
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2024, 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.desugar.records;
+
+import static com.android.tools.r8.ir.desugar.records.RecordTagSynthesizer.ensureRecordClass;
+
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfTypeInstruction;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+
+public class RecordFullInstructionDesugaring extends RecordInstructionDesugaring {
+
+ RecordFullInstructionDesugaring(AppView<?> appView) {
+ super(appView);
+ }
+
+ @Override
+ public void scan(
+ ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
+ CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
+ for (CfInstruction instruction : cfCode.getInstructions()) {
+ scanInstruction(instruction, eventConsumer, programMethod);
+ }
+ }
+
+ // The record rewriter scans the cf instructions to figure out if the record class needs to
+ // be added in the output. the analysis cannot be done in desugarInstruction because the analysis
+ // does not rewrite any instruction, and desugarInstruction is expected to rewrite at least one
+ // instruction for assertions to be valid.
+ private void scanInstruction(
+ CfInstruction instruction,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context) {
+ assert !instruction.isInitClass();
+ if (instruction.isInvoke()) {
+ CfInvoke cfInvoke = instruction.asInvoke();
+ if (refersToRecord(cfInvoke.getMethod(), factory)) {
+ ensureRecordClass(eventConsumer, context, appView);
+ }
+ return;
+ }
+ if (instruction.isFieldInstruction()) {
+ CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
+ if (refersToRecord(fieldInstruction.getField(), factory)) {
+ ensureRecordClass(eventConsumer, context, appView);
+ }
+ return;
+ }
+ if (instruction.isTypeInstruction()) {
+ CfTypeInstruction typeInstruction = instruction.asTypeInstruction();
+ if (refersToRecord(typeInstruction.getType(), factory)) {
+ ensureRecordClass(eventConsumer, context, appView);
+ }
+ return;
+ }
+ // TODO(b/179146128): Analyse MethodHandle and MethodType.
+ }
+
+ public static boolean refersToRecord(DexField field, DexItemFactory factory) {
+ assert !refersToRecord(field.holder, factory) : "The java.lang.Record class has no fields.";
+ return refersToRecord(field.type, factory);
+ }
+
+ public static boolean refersToRecord(DexMethod method, DexItemFactory factory) {
+ if (refersToRecord(method.holder, factory)) {
+ return true;
+ }
+ return refersToRecord(method.proto, factory);
+ }
+
+ private static boolean refersToRecord(DexProto proto, DexItemFactory factory) {
+ if (refersToRecord(proto.returnType, factory)) {
+ return true;
+ }
+ return refersToRecord(proto.parameters.values, factory);
+ }
+
+ private static boolean refersToRecord(DexType[] types, DexItemFactory factory) {
+ for (DexType type : types) {
+ if (refersToRecord(type, factory)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("ReferenceEquality")
+ private static boolean refersToRecord(DexType type, DexItemFactory factory) {
+ return type == factory.recordType;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java
similarity index 61%
rename from src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
rename to src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java
index 71527c8..268c8a3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordInstructionDesugaring.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2024, 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.
@@ -12,22 +12,16 @@
import com.android.tools.r8.cf.code.CfConstClass;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfDexItemBasedConstString;
-import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfTypeInstruction;
-import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexApplicationReadFlags;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -36,55 +30,34 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
-import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
-import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.DesugarDescription;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.ProgramAdditions;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
-import com.android.tools.r8.ir.synthetic.CallObjectInitCfCodeProvider;
import com.android.tools.r8.ir.synthetic.RecordCfCodeProvider.RecordEqualsCfCodeProvider;
import com.android.tools.r8.ir.synthetic.RecordCfCodeProvider.RecordGetFieldsAsObjectsCfCodeProvider;
import com.android.tools.r8.ir.synthetic.SyntheticCfCodeProvider;
-import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
import org.objectweb.asm.Opcodes;
-public class RecordDesugaring
- implements CfInstructionDesugaring, CfClassSynthesizerDesugaring, CfPostProcessingDesugaring {
+public class RecordInstructionDesugaring implements CfInstructionDesugaring {
- private final AppView<?> appView;
- private final DexItemFactory factory;
+ final AppView<?> appView;
+ final DexItemFactory factory;
private final DexProto recordToStringHelperProto;
private final DexProto recordHashCodeHelperProto;
public static final String GET_FIELDS_AS_OBJECTS_METHOD_NAME = "$record$getFieldsAsObjects";
public static final String EQUALS_RECORD_METHOD_NAME = "$record$equals";
- public static RecordDesugaring create(AppView<?> appView) {
- return appView.options().shouldDesugarRecords() ? new RecordDesugaring(appView) : null;
- }
-
- public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
- RecordCfMethods.registerSynthesizedCodeReferences(factory);
- RecordGetFieldsAsObjectsCfCodeProvider.registerSynthesizedCodeReferences(factory);
- RecordEqualsCfCodeProvider.registerSynthesizedCodeReferences(factory);
- }
-
- private RecordDesugaring(AppView<?> appView) {
+ RecordInstructionDesugaring(AppView<?> appView) {
this.appView = appView;
factory = appView.dexItemFactory();
recordToStringHelperProto =
@@ -94,6 +67,24 @@
factory.createProto(factory.intType, factory.classType, factory.objectArrayType);
}
+ public static RecordInstructionDesugaring create(AppView<?> appView) {
+ switch (appView.options().desugarRecordState()) {
+ case OFF:
+ return null;
+ case PARTIAL:
+ return new RecordInstructionDesugaring(appView);
+ case FULL:
+ return new RecordFullInstructionDesugaring(appView);
+ }
+ throw new Unreachable();
+ }
+
+ public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
+ RecordCfMethods.registerSynthesizedCodeReferences(factory);
+ RecordGetFieldsAsObjectsCfCodeProvider.registerSynthesizedCodeReferences(factory);
+ RecordEqualsCfCodeProvider.registerSynthesizedCodeReferences(factory);
+ }
+
@Override
public void prepare(
ProgramMethod method,
@@ -128,47 +119,6 @@
throw new Unreachable("Invoke dynamic needs record desugaring but could not be desugared.");
}
- @Override
- public void scan(
- ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
- CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
- for (CfInstruction instruction : cfCode.getInstructions()) {
- scanInstruction(instruction, eventConsumer, programMethod);
- }
- }
-
- // The record rewriter scans the cf instructions to figure out if the record class needs to
- // be added in the output. the analysis cannot be done in desugarInstruction because the analysis
- // does not rewrite any instruction, and desugarInstruction is expected to rewrite at least one
- // instruction for assertions to be valid.
- private void scanInstruction(
- CfInstruction instruction,
- CfInstructionDesugaringEventConsumer eventConsumer,
- ProgramMethod context) {
- assert !instruction.isInitClass();
- if (instruction.isInvoke()) {
- CfInvoke cfInvoke = instruction.asInvoke();
- if (refersToRecord(cfInvoke.getMethod(), factory)) {
- ensureRecordClass(eventConsumer, context);
- }
- return;
- }
- if (instruction.isFieldInstruction()) {
- CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
- if (refersToRecord(fieldInstruction.getField(), factory)) {
- ensureRecordClass(eventConsumer, context);
- }
- return;
- }
- if (instruction.isTypeInstruction()) {
- CfTypeInstruction typeInstruction = instruction.asTypeInstruction();
- if (refersToRecord(typeInstruction.getType(), factory)) {
- ensureRecordClass(eventConsumer, context);
- }
- return;
- }
- // TODO(b/179146128): Analyse MethodHandle and MethodType.
- }
@Override
@SuppressWarnings("ReferenceEquality")
@@ -182,9 +132,9 @@
}
if (instruction.isInvoke()) {
CfInvoke cfInvoke = instruction.asInvoke();
- if (needsDesugaring(cfInvoke.getMethod(), cfInvoke.isInvokeSuper(context.getHolderType()))) {
- DexMethod newMethod =
- rewriteMethod(cfInvoke.getMethod(), cfInvoke.isInvokeSuper(context.getHolderType()));
+ boolean invokeSuper = cfInvoke.isInvokeSuper(context.getHolderType());
+ if (needsDesugaring(cfInvoke.getMethod(), invokeSuper)) {
+ DexMethod newMethod = rewriteMethod(cfInvoke.getMethod(), invokeSuper);
assert newMethod != cfInvoke.getMethod();
return desugarInvoke(cfInvoke, newMethod);
} else {
@@ -294,7 +244,7 @@
return result;
}
- private DexMethod ensureEqualsRecord(
+ private void ensureEqualsRecord(
RecordInvokeDynamic recordInvokeDynamic,
ProgramAdditions programAdditions,
ProgramMethod context,
@@ -308,7 +258,6 @@
programAdditions.ensureMethod(
method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
eventConsumer.acceptRecordEqualsHelperMethod(equalsHelperMethod, context);
- return method;
}
private DexMethod ensureGetFieldsAsObjects(
@@ -397,8 +346,7 @@
MethodProcessingContext methodProcessingContext) {
localStackAllocator.allocateLocalStack(2);
DexMethod getFieldsAsObjects = getFieldsAsObjectsMethod(recordInvokeDynamic.getRecordType());
- assert recordInvokeDynamic.getRecordClass().lookupProgramMethod(getFieldsAsObjects)
- != null;
+ assert recordInvokeDynamic.getRecordClass().lookupProgramMethod(getFieldsAsObjects) != null;
ArrayList<CfInstruction> instructions = new ArrayList<>();
instructions.add(new CfInvoke(Opcodes.INVOKESPECIAL, getFieldsAsObjects, false));
instructions.add(new CfConstClass(recordInvokeDynamic.getRecordType(), true));
@@ -420,122 +368,6 @@
return instructions;
}
- private void ensureRecordClass(
- RecordInstructionDesugaringEventConsumer eventConsumer, ProgramMethod context) {
- internalEnsureRecordClass(eventConsumer, null, eventConsumer, ImmutableList.of(context));
- }
-
- private void ensureRecordClass(
- RecordClassSynthesizerDesugaringEventConsumer eventConsumer,
- Collection<DexProgramClass> recordClasses) {
- internalEnsureRecordClass(eventConsumer, eventConsumer, null, recordClasses);
- }
-
- /**
- * If java.lang.Record is referenced from a class' supertype or a program method/field signature,
- * then the global synthetic is generated upfront of the compilation to avoid confusing D8/R8.
- *
- * <p>However, if java.lang.Record is referenced only from an instruction, for example, the code
- * contains "x instance of java.lang.Record" but no record type is present, then the global
- * synthetic is generated during instruction desugaring scanning.
- */
- private DexProgramClass internalEnsureRecordClass(
- RecordDesugaringEventConsumer eventConsumer,
- RecordClassSynthesizerDesugaringEventConsumer recordClassSynthesizerDesugaringEventConsumer,
- RecordInstructionDesugaringEventConsumer recordInstructionDesugaringEventConsumer,
- Collection<? extends ProgramDefinition> contexts) {
- DexItemFactory factory = appView.dexItemFactory();
- checkRecordTagNotPresent(factory);
- return ensureRecordClassHelper(
- appView,
- contexts,
- eventConsumer,
- recordClassSynthesizerDesugaringEventConsumer,
- recordInstructionDesugaringEventConsumer);
- }
-
- public static DexProgramClass ensureRecordClassHelper(
- AppView<?> appView,
- Collection<? extends ProgramDefinition> contexts,
- RecordDesugaringEventConsumer eventConsumer,
- RecordClassSynthesizerDesugaringEventConsumer recordClassSynthesizerDesugaringEventConsumer,
- RecordInstructionDesugaringEventConsumer recordInstructionDesugaringEventConsumer) {
- return appView
- .getSyntheticItems()
- .ensureGlobalClass(
- () -> new MissingGlobalSyntheticsConsumerDiagnostic("Record desugaring"),
- kinds -> kinds.RECORD_TAG,
- appView.dexItemFactory().recordType,
- contexts,
- appView,
- builder -> {
- DexEncodedMethod init = synthesizeRecordInitMethod(appView);
- builder.setAbstract().setDirectMethods(ImmutableList.of(init));
- },
- eventConsumer::acceptRecordClass,
- clazz -> {
- if (recordClassSynthesizerDesugaringEventConsumer != null) {
- for (ProgramDefinition context : contexts) {
- recordClassSynthesizerDesugaringEventConsumer.acceptRecordClassContext(
- clazz, context.asClass());
- }
- }
- if (recordInstructionDesugaringEventConsumer != null) {
- for (ProgramDefinition context : contexts) {
- recordInstructionDesugaringEventConsumer.acceptRecordClassContext(
- clazz, context.asMethod());
- }
- }
- });
- }
-
- private void checkRecordTagNotPresent(DexItemFactory factory) {
- DexClass r8RecordClass =
- appView.appInfo().definitionForWithoutExistenceAssert(factory.recordTagType);
- if (r8RecordClass != null && r8RecordClass.isProgramClass()) {
- appView
- .options()
- .reporter
- .error(
- "D8/R8 is compiling a mix of desugared and non desugared input using"
- + " java.lang.Record, but the application reader did not import correctly "
- + factory.recordTagType);
- }
- }
-
- public static boolean refersToRecord(DexField field, DexItemFactory factory) {
- assert !refersToRecord(field.holder, factory) : "The java.lang.Record class has no fields.";
- return refersToRecord(field.type, factory);
- }
-
- public static boolean refersToRecord(DexMethod method, DexItemFactory factory) {
- if (refersToRecord(method.holder, factory)) {
- return true;
- }
- return refersToRecord(method.proto, factory);
- }
-
- private static boolean refersToRecord(DexProto proto, DexItemFactory factory) {
- if (refersToRecord(proto.returnType, factory)) {
- return true;
- }
- return refersToRecord(proto.parameters.values, factory);
- }
-
- private static boolean refersToRecord(DexType[] types, DexItemFactory factory) {
- for (DexType type : types) {
- if (refersToRecord(type, factory)) {
- return true;
- }
- }
- return false;
- }
-
- @SuppressWarnings("ReferenceEquality")
- private static boolean refersToRecord(DexType type, DexItemFactory factory) {
- return type == factory.recordType;
- }
-
@SuppressWarnings("ReferenceEquality")
private boolean needsDesugaring(DexMethod method, boolean isSuper) {
return rewriteMethod(method, isSuper) != method;
@@ -565,55 +397,4 @@
assert method == factory.recordMembers.hashCode;
return factory.objectMembers.hashCode;
}
-
- private static DexEncodedMethod synthesizeRecordInitMethod(AppView<?> appView) {
- MethodAccessFlags methodAccessFlags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_PROTECTED, true);
- return DexEncodedMethod.syntheticBuilder()
- .setMethod(appView.dexItemFactory().recordMembers.constructor)
- .setAccessFlags(methodAccessFlags)
- .setCode(
- new CallObjectInitCfCodeProvider(appView, appView.dexItemFactory().recordTagType)
- .generateCfCode())
- // Will be traced by the enqueuer.
- .disableAndroidApiLevelCheck()
- .build();
- }
-
- @Override
- public String uniqueIdentifier() {
- return "$record";
- }
-
- @Override
- public void synthesizeClasses(
- ClassSynthesisDesugaringContext processingContext,
- CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
- DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
- if (flags.hasReadRecordReferenceFromProgramClass()) {
- List<DexProgramClass> classes = new ArrayList<>(flags.getRecordWitnesses().size());
- for (DexType recordWitness : flags.getRecordWitnesses()) {
- DexClass dexClass = appView.contextIndependentDefinitionFor(recordWitness);
- assert dexClass != null;
- assert dexClass.isProgramClass();
- classes.add(dexClass.asProgramClass());
- }
- ensureRecordClass(eventConsumer, classes);
- }
- }
-
- @Override
- @SuppressWarnings("ReferenceEquality")
- public void postProcessingDesugaring(
- Collection<DexProgramClass> programClasses,
- CfPostProcessingDesugaringEventConsumer eventConsumer,
- ExecutorService executorService) {
- for (DexProgramClass clazz : programClasses) {
- if (clazz.isRecord()) {
- assert clazz.superType == factory.recordType;
- clazz.accessFlags.unsetRecord();
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordTagSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordTagSynthesizer.java
new file mode 100644
index 0000000..95b67e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordTagSynthesizer.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2024, 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.desugar.records;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.synthetic.CallObjectInitCfCodeProvider;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+
+public class RecordTagSynthesizer {
+
+ static void ensureRecordClass(
+ RecordInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ AppView<?> appView) {
+ internalEnsureRecordClass(
+ eventConsumer, null, eventConsumer, ImmutableList.of(context), appView);
+ }
+
+ static void ensureRecordClass(
+ RecordClassSynthesizerDesugaringEventConsumer eventConsumer,
+ Collection<DexProgramClass> recordClasses,
+ AppView<?> appView) {
+ internalEnsureRecordClass(eventConsumer, eventConsumer, null, recordClasses, appView);
+ }
+
+ /**
+ * If java.lang.Record is referenced from a class' supertype or a program method/field signature,
+ * then the global synthetic is generated upfront of the compilation to avoid confusing D8/R8.
+ *
+ * <p>However, if java.lang.Record is referenced only from an instruction, for example, the code
+ * contains "x instance of java.lang.Record" but no record type is present, then the global
+ * synthetic is generated during instruction desugaring scanning.
+ */
+ private static void internalEnsureRecordClass(
+ RecordDesugaringEventConsumer eventConsumer,
+ RecordClassSynthesizerDesugaringEventConsumer recordClassSynthesizerDesugaringEventConsumer,
+ RecordInstructionDesugaringEventConsumer recordInstructionDesugaringEventConsumer,
+ Collection<? extends ProgramDefinition> contexts,
+ AppView<?> appView) {
+ checkRecordTagNotPresent(appView);
+ ensureRecordClassHelper(
+ appView,
+ contexts,
+ eventConsumer,
+ recordClassSynthesizerDesugaringEventConsumer,
+ recordInstructionDesugaringEventConsumer);
+ }
+
+ public static void ensureRecordClassHelper(
+ AppView<?> appView,
+ Collection<? extends ProgramDefinition> contexts,
+ RecordDesugaringEventConsumer eventConsumer,
+ RecordClassSynthesizerDesugaringEventConsumer recordClassSynthesizerDesugaringEventConsumer,
+ RecordInstructionDesugaringEventConsumer recordInstructionDesugaringEventConsumer) {
+ appView
+ .getSyntheticItems()
+ .ensureGlobalClass(
+ () -> new MissingGlobalSyntheticsConsumerDiagnostic("Record desugaring"),
+ kinds -> kinds.RECORD_TAG,
+ appView.dexItemFactory().recordType,
+ contexts,
+ appView,
+ builder -> {
+ DexEncodedMethod init = synthesizeRecordInitMethod(appView);
+ builder.setAbstract().setDirectMethods(ImmutableList.of(init));
+ },
+ eventConsumer::acceptRecordClass,
+ clazz -> {
+ if (recordClassSynthesizerDesugaringEventConsumer != null) {
+ for (ProgramDefinition context : contexts) {
+ recordClassSynthesizerDesugaringEventConsumer.acceptRecordClassContext(
+ clazz, context.asClass());
+ }
+ }
+ if (recordInstructionDesugaringEventConsumer != null) {
+ for (ProgramDefinition context : contexts) {
+ recordInstructionDesugaringEventConsumer.acceptRecordClassContext(
+ clazz, context.asMethod());
+ }
+ }
+ });
+ }
+
+ private static void checkRecordTagNotPresent(AppView<?> appView) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexClass r8RecordClass =
+ appView.appInfo().definitionForWithoutExistenceAssert(factory.recordTagType);
+ if (r8RecordClass != null && r8RecordClass.isProgramClass()) {
+ appView
+ .options()
+ .reporter
+ .error(
+ "D8/R8 is compiling a mix of desugared and non desugared input using"
+ + " java.lang.Record, but the application reader did not import correctly "
+ + factory.recordTagType);
+ }
+ }
+
+ private static DexEncodedMethod synthesizeRecordInitMethod(AppView<?> appView) {
+ MethodAccessFlags methodAccessFlags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_SYNTHETIC | Constants.ACC_PROTECTED, true);
+ return DexEncodedMethod.syntheticBuilder()
+ .setMethod(appView.dexItemFactory().recordMembers.constructor)
+ .setAccessFlags(methodAccessFlags)
+ .setCode(
+ new CallObjectInitCfCodeProvider(appView, appView.dexItemFactory().recordTagType)
+ .generateCfCode())
+ // Will be traced by the enqueuer.
+ .disableAndroidApiLevelCheck()
+ .build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java
index c6d68d3..d5a3582 100644
--- a/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java
@@ -21,7 +21,7 @@
private final NamingLens namingLens;
public static NamingLens createRecordRewritingNamingLens(AppView<?> appView) {
- if (appView.options().shouldDesugarRecords()
+ if (appView.options().desugarRecordState().isFull()
&& appView
.appInfo()
.definitionForWithoutExistenceAssert(appView.dexItemFactory().recordType)
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 8b19ac3..7a8cbdb 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -724,7 +724,7 @@
int argumentIndex,
Value argument,
ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
- ValueState modeledState =
+ NonEmptyValueState modeledState =
modeling.modelParameterStateForArgumentToFunction(
invoke, singleTarget, argumentIndex, argument, context);
if (modeledState != null) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScannerModeling.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScannerModeling.java
index b4f5f95..0a021d2 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScannerModeling.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScannerModeling.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
import com.android.tools.r8.optimize.compose.ArgumentPropagatorComposeModeling;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -25,7 +25,7 @@
: null;
}
- ValueState modelParameterStateForArgumentToFunction(
+ NonEmptyValueState modelParameterStateForArgumentToFunction(
InvokeMethod invoke,
ProgramMethod singleTarget,
int argumentIndex,
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java
index 852bd2b..ec8d43e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java
@@ -34,7 +34,7 @@
ConcreteValueState inState);
/** Returns true if the given {@param inFlow} is a declared input of this abstract function. */
- boolean containsBaseInFlow(BaseInFlow inFlow);
+ boolean verifyContainsBaseInFlow(BaseInFlow inFlow);
/**
* Returns the program field or parameter graph nodes that this function depends on. Upon any
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomValueState.java
index bcd03fc..609b769 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomValueState.java
@@ -26,4 +26,9 @@
public final ValueState mutableCopy() {
return this;
}
+
+ @Override
+ public ValueState mutableCopyWithoutInFlow() {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java
index 660c42a..a4eb77e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java
@@ -28,8 +28,9 @@
}
@Override
- public boolean containsBaseInFlow(BaseInFlow inFlow) {
- return inFlow.equals(this.inFlow);
+ public boolean verifyContainsBaseInFlow(BaseInFlow inFlow) {
+ assert inFlow.equals(this.inFlow);
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java
index 053d921..26dc74a 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.utils.SetUtils;
import java.util.Collections;
import java.util.Set;
+import java.util.function.Supplier;
public class ConcreteArrayTypeValueState extends ConcreteReferenceTypeValueState {
@@ -83,8 +84,8 @@
}
@Override
- public boolean isEffectivelyBottom() {
- return nullability.isBottom() && !hasInFlow();
+ public boolean isEffectivelyBottomIgnoringInFlow() {
+ return nullability.isBottom();
}
@Override
@@ -93,8 +94,8 @@
}
@Override
- public ValueState mutableCopy() {
- return new ConcreteArrayTypeValueState(nullability, copyInFlow());
+ public ConcreteArrayTypeValueState internalMutableCopy(Supplier<Set<InFlow>> inFlowSupplier) {
+ return new ConcreteArrayTypeValueState(nullability, inFlowSupplier.get());
}
public NonEmptyValueState mutableJoin(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java
index 6378112..f6a608d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.utils.SetUtils;
import java.util.Collections;
import java.util.Set;
+import java.util.function.Supplier;
public class ConcreteClassTypeValueState extends ConcreteReferenceTypeValueState {
@@ -105,8 +106,8 @@
}
@Override
- public boolean isEffectivelyBottom() {
- return abstractValue.isBottom() && dynamicType.isBottom() && !hasInFlow();
+ public boolean isEffectivelyBottomIgnoringInFlow() {
+ return abstractValue.isBottom() && dynamicType.isBottom();
}
@Override
@@ -115,8 +116,8 @@
}
@Override
- public ValueState mutableCopy() {
- return new ConcreteClassTypeValueState(abstractValue, dynamicType, copyInFlow());
+ public ConcreteClassTypeValueState internalMutableCopy(Supplier<Set<InFlow>> inFlowSupplier) {
+ return new ConcreteClassTypeValueState(abstractValue, dynamicType, inFlowSupplier.get());
}
public NonEmptyValueState mutableJoin(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java
index dedf78d..864c16e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.utils.SetUtils;
import java.util.Collections;
import java.util.Set;
+import java.util.function.Supplier;
public class ConcretePrimitiveTypeValueState extends ConcreteValueState {
@@ -44,8 +45,8 @@
}
@Override
- public ConcretePrimitiveTypeValueState mutableCopy() {
- return new ConcretePrimitiveTypeValueState(abstractValue, copyInFlow());
+ public ConcretePrimitiveTypeValueState internalMutableCopy(Supplier<Set<InFlow>> inFlowSupplier) {
+ return new ConcretePrimitiveTypeValueState(abstractValue, inFlowSupplier.get());
}
public NonEmptyValueState mutableJoin(
@@ -108,8 +109,8 @@
}
@Override
- public boolean isEffectivelyBottom() {
- return abstractValue.isBottom() && !hasInFlow();
+ public boolean isEffectivelyBottomIgnoringInFlow() {
+ return abstractValue.isBottom();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java
index abc4b6d..350d7f9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.utils.Action;
import java.util.Collections;
import java.util.Set;
+import java.util.function.Supplier;
public class ConcreteReceiverValueState extends ConcreteReferenceTypeValueState {
@@ -71,8 +72,8 @@
}
@Override
- public boolean isEffectivelyBottom() {
- return dynamicType.isBottom() && !hasInFlow();
+ public boolean isEffectivelyBottomIgnoringInFlow() {
+ return dynamicType.isBottom();
}
@Override
@@ -92,8 +93,8 @@
}
@Override
- public ValueState mutableCopy() {
- return new ConcreteReceiverValueState(dynamicType, copyInFlow());
+ public ConcreteReceiverValueState internalMutableCopy(Supplier<Set<InFlow>> inFlowSupplier) {
+ return new ConcreteReceiverValueState(dynamicType, inFlowSupplier.get());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java
index d6193f6..7261359 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java
@@ -13,6 +13,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Supplier;
public abstract class ConcreteValueState extends NonEmptyValueState {
@@ -87,7 +88,11 @@
public abstract ConcreteParameterStateKind getKind();
- public abstract boolean isEffectivelyBottom();
+ public final boolean isEffectivelyBottom() {
+ return isEffectivelyBottomIgnoringInFlow() && !hasInFlow();
+ }
+
+ public abstract boolean isEffectivelyBottomIgnoringInFlow();
public abstract boolean isEffectivelyUnknown();
@@ -102,6 +107,23 @@
}
@Override
+ public final ConcreteValueState mutableCopy() {
+ return internalMutableCopy(this::copyInFlow);
+ }
+
+ protected abstract ConcreteValueState internalMutableCopy(Supplier<Set<InFlow>> inFlowSupplier);
+
+ @Override
+ public ValueState mutableCopyWithoutInFlow() {
+ if (isEffectivelyBottomIgnoringInFlow()) {
+ return getCorrespondingBottom();
+ }
+ ConcreteValueState result = internalMutableCopy(Collections::emptySet);
+ assert !result.isEffectivelyBottom();
+ return result;
+ }
+
+ @Override
public final NonEmptyValueState mutableJoin(
AppView<AppInfoWithLiveness> appView,
ValueState inState,
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
index fce1a27..4b19a1d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
@@ -42,13 +42,13 @@
@Override
public ValueState getState(DexField field) {
- assert abstractFunction.containsBaseInFlow(new FieldValue(field));
+ assert abstractFunction.verifyContainsBaseInFlow(new FieldValue(field));
return flowGraph.getState(field);
}
@Override
public ValueState getState(BaseInFlow inFlow, Supplier<ValueState> defaultStateProvider) {
- assert abstractFunction.containsBaseInFlow(inFlow);
+ assert abstractFunction.verifyContainsBaseInFlow(inFlow);
return flowGraph.getState(inFlow, defaultStateProvider);
}
};
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java
index 7b20e77..1b33495b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java
@@ -26,7 +26,7 @@
}
@Override
- public boolean containsBaseInFlow(BaseInFlow inFlow) {
+ public boolean verifyContainsBaseInFlow(BaseInFlow inFlow) {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java
index 560f00d..893a043 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java
@@ -47,8 +47,9 @@
}
@Override
- public boolean containsBaseInFlow(BaseInFlow inFlow) {
- return inFlow.equals(receiver) || inFlow.isFieldValue(field);
+ public boolean verifyContainsBaseInFlow(BaseInFlow inFlow) {
+ assert inFlow.equals(receiver) || inFlow.isFieldValue(field);
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
index 1c83bfa..138725e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
@@ -7,10 +7,14 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.Objects;
+import java.util.function.IntFunction;
-public class MethodParameter implements BaseInFlow {
+public class MethodParameter implements BaseInFlow, ComputationTreeNode {
private final DexMethod method;
private final int index;
@@ -43,11 +47,22 @@
return index;
}
+ @Override
+ public MethodParameter getSingleOpenVariable() {
+ return this;
+ }
+
public DexType getType() {
return method.getArgumentType(index, isMethodStatic);
}
@Override
+ public AbstractValue evaluate(
+ IntFunction<AbstractValue> argumentAssignment, AbstractValueFactory abstractValueFactory) {
+ return argumentAssignment.apply(index);
+ }
+
+ @Override
public int internalCompareToSameKind(InFlow other) {
MethodParameter methodParameter = other.asMethodParameter();
int result = method.compareTo(methodParameter.method);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java
index c5f9cc5..8e84a76 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java
@@ -43,12 +43,14 @@
}
@Override
- public boolean containsBaseInFlow(BaseInFlow otherInFlow) {
+ public boolean verifyContainsBaseInFlow(BaseInFlow otherInFlow) {
if (inFlow.isAbstractFunction()) {
- return inFlow.asAbstractFunction().containsBaseInFlow(otherInFlow);
+ assert inFlow.asAbstractFunction().verifyContainsBaseInFlow(otherInFlow);
+ } else {
+ assert inFlow.isBaseInFlow();
+ assert inFlow.equals(otherInFlow);
}
- assert inFlow.isBaseInFlow();
- return inFlow.equals(otherInFlow);
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
index 4a4a665..dd06a31 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
@@ -26,7 +26,7 @@
}
@Override
- public boolean containsBaseInFlow(BaseInFlow inFlow) {
+ public boolean verifyContainsBaseInFlow(BaseInFlow inFlow) {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownValueState.java
index dbc2069..6fdedc4 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownValueState.java
@@ -37,6 +37,11 @@
}
@Override
+ public ValueState mutableCopyWithoutInFlow() {
+ return this;
+ }
+
+ @Override
public UnknownValueState mutableJoin(
AppView<AppInfoWithLiveness> appView,
ValueState inState,
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java
index 06cb1eb..b88d240 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java
@@ -118,6 +118,8 @@
public abstract ValueState mutableCopy();
+ public abstract ValueState mutableCopyWithoutInFlow();
+
public final ValueState mutableJoin(
AppView<AppInfoWithLiveness> appView,
ValueState inState,
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeArgumentNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeArgumentNode.java
deleted file mode 100644
index 2ea279f..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeArgumentNode.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2024, 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.argumentpropagation.computation;
-
-import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.utils.ArrayUtils;
-import java.util.Objects;
-import java.util.function.IntFunction;
-
-/** Represents the read of an argument. */
-public class ComputationTreeArgumentNode extends ComputationTreeBaseNode {
-
- private static final int NUM_CANONICALIZED_INSTANCES = 32;
- private static final ComputationTreeArgumentNode[] CANONICALIZED_INSTANCES =
- ArrayUtils.initialize(
- new ComputationTreeArgumentNode[NUM_CANONICALIZED_INSTANCES],
- ComputationTreeArgumentNode::new);
-
- private final int argumentIndex;
-
- private ComputationTreeArgumentNode(int argumentIndex) {
- this.argumentIndex = argumentIndex;
- }
-
- public static ComputationTreeArgumentNode create(int argumentIndex) {
- return argumentIndex < NUM_CANONICALIZED_INSTANCES
- ? CANONICALIZED_INSTANCES[argumentIndex]
- : new ComputationTreeArgumentNode(argumentIndex);
- }
-
- @Override
- public AbstractValue evaluate(
- IntFunction<AbstractValue> argumentAssignment, AbstractValueFactory abstractValueFactory) {
- return argumentAssignment.apply(argumentIndex);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof ComputationTreeArgumentNode)) {
- return false;
- }
- ComputationTreeArgumentNode node = (ComputationTreeArgumentNode) obj;
- assert argumentIndex >= NUM_CANONICALIZED_INSTANCES
- || node.argumentIndex >= NUM_CANONICALIZED_INSTANCES;
- return argumentIndex == node.argumentIndex;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getClass(), argumentIndex);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeBuilder.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeBuilder.java
index aca8a03..7e7b541 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeBuilder.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeBuilder.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
import static com.android.tools.r8.ir.code.Opcodes.IF;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -17,13 +18,21 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameterFactory;
public class ComputationTreeBuilder {
private final AbstractValueFactory abstractValueFactory;
+ private final ProgramMethod method;
+ private final MethodParameterFactory methodParameterFactory;
- public ComputationTreeBuilder(AbstractValueFactory abstractValueFactory) {
+ public ComputationTreeBuilder(
+ AbstractValueFactory abstractValueFactory,
+ ProgramMethod method,
+ MethodParameterFactory methodParameterFactory) {
this.abstractValueFactory = abstractValueFactory;
+ this.method = method;
+ this.methodParameterFactory = methodParameterFactory;
}
// TODO(b/302281503): "Long lived" computation trees (i.e., the ones that survive past the IR
@@ -44,7 +53,7 @@
{
Argument argument = instruction.asArgument();
if (argument.getOutType().isInt()) {
- return ComputationTreeArgumentNode.create(argument.getIndex());
+ return methodParameterFactory.create(method, argument.getIndex());
}
break;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopNode.java
index 8f0c95f..d7cf46b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopNode.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.optimize.argumentpropagation.computation;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
public abstract class ComputationTreeLogicalBinopNode extends ComputationTreeBaseNode {
@@ -20,6 +21,15 @@
return NumericType.INT;
}
+ @Override
+ public final MethodParameter getSingleOpenVariable() {
+ MethodParameter openVariable = left.getSingleOpenVariable();
+ if (openVariable != null) {
+ return right.getSingleOpenVariable() == null ? openVariable : null;
+ }
+ return right.getSingleOpenVariable();
+ }
+
boolean internalIsEqualTo(ComputationTreeLogicalBinopNode node) {
return left.equals(node.left) && right.equals(node.right);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java
index f055cff..1231d76 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
import java.util.function.IntFunction;
/**
@@ -16,6 +17,8 @@
AbstractValue evaluate(
IntFunction<AbstractValue> argumentAssignment, AbstractValueFactory abstractValueFactory);
+ MethodParameter getSingleOpenVariable();
+
default boolean isUnknown() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopNode.java
index 3c3fc1e..8ac1ca9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopNode.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize.argumentpropagation.computation;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
+
public abstract class ComputationTreeUnopNode extends ComputationTreeBaseNode {
final ComputationTreeNode operand;
@@ -12,6 +14,11 @@
this.operand = operand;
}
+ @Override
+ public MethodParameter getSingleOpenVariable() {
+ return operand.getSingleOpenVariable();
+ }
+
boolean internalIsEqualTo(ComputationTreeUnopNode node) {
return operand.equals(node.operand);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ArgumentPropagatorComposeModeling.java b/src/main/java/com/android/tools/r8/optimize/compose/ArgumentPropagatorComposeModeling.java
index 6890b1c..69a0331 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ArgumentPropagatorComposeModeling.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ArgumentPropagatorComposeModeling.java
@@ -23,8 +23,8 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.FieldValue;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.OrAbstractFunction;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.Iterables;
@@ -93,7 +93,7 @@
* }
* </pre>
*/
- public ValueState modelParameterStateForChangedOrDefaultArgumentToComposableFunction(
+ public NonEmptyValueState modelParameterStateForChangedOrDefaultArgumentToComposableFunction(
InvokeMethod invoke,
ProgramMethod singleTarget,
int argumentIndex,
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
index 46c4d58..e57b4fb 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
@@ -106,12 +106,14 @@
}
@Override
- public boolean containsBaseInFlow(BaseInFlow otherInFlow) {
+ public boolean verifyContainsBaseInFlow(BaseInFlow otherInFlow) {
if (inFlow.isAbstractFunction()) {
- return inFlow.asAbstractFunction().containsBaseInFlow(otherInFlow);
+ assert inFlow.asAbstractFunction().verifyContainsBaseInFlow(otherInFlow);
+ } else {
+ assert inFlow.isBaseInFlow();
+ assert inFlow.equals(otherInFlow);
}
- assert inFlow.isBaseInFlow();
- return inFlow.equals(otherInFlow);
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 4836910..e821da8 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1931,6 +1931,9 @@
if (!options.isShrinking()) {
return;
}
+ if (options.ignoreUnusedProguardRules) {
+ return;
+ }
List<ProguardConfigurationRule> rules = options.getProguardConfiguration().getRules();
if (rules == null) {
return;
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 8ac9315..4e50c82 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -699,8 +699,31 @@
return !canUseNestBasedAccess();
}
- public boolean shouldDesugarRecords() {
- return desugarState.isOn() && !canUseRecords();
+ public enum DesugarRecordState {
+ OFF,
+ PARTIAL,
+ FULL;
+
+ public boolean isFull() {
+ return this == FULL;
+ }
+
+ public boolean isNotOff() {
+ return this != OFF;
+ }
+ }
+
+ public boolean recordPartialDesugaring =
+ System.getProperty("com.android.tools.r8.recordPartialDesugaring") != null;
+
+ public DesugarRecordState desugarRecordState() {
+ if (desugarState.isOff()) {
+ return DesugarRecordState.OFF;
+ }
+ if (!canUseRecords()) {
+ return DesugarRecordState.FULL;
+ }
+ return recordPartialDesugaring ? DesugarRecordState.PARTIAL : DesugarRecordState.OFF;
}
public boolean canUseDesugarBufferCovariantReturnType() {
@@ -930,6 +953,7 @@
return testing.readInputStackMaps ? testing.readInputStackMaps : isGeneratingClassFiles();
}
+ public boolean ignoreUnusedProguardRules = false;
public boolean ignoreMissingClasses = false;
public boolean reportMissingClassesInEnclosingMethodAttribute = false;
public boolean reportMissingClassesInInnerClassAttributes = false;
@@ -1162,6 +1186,10 @@
List<DexType> sortedKeys =
ListUtils.sort(warningLibraryProgramDuplicates.keySet(), DexType::compareTo);
for (DexType key : sortedKeys) {
+ // Allow for suppression of the duplicates with -dontwarn
+ if (appViewWithLiveness.getDontWarnConfiguration().matches(key)) {
+ continue;
+ }
// If the type has been pruned from the program then don't issue a diagnostic.
if (DexProgramClass.asProgramClassOrNull(
appViewWithLiveness.appInfo().definitionForWithoutExistenceAssert(key))
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
index 7874b5c..7a82e3f 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/LegacyResourceShrinker.java
@@ -37,6 +37,8 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -48,8 +50,8 @@
public class LegacyResourceShrinker {
private final Map<String, byte[]> dexInputs;
- private final List<PathAndBytes> resFolderInputs;
- private final List<PathAndBytes> xmlInputs;
+ private final Collection<PathAndBytes> resFolderInputs;
+ private final Collection<PathAndBytes> xmlInputs;
private List<String> proguardMapStrings;
private final List<PathAndBytes> manifest;
private final Map<PathAndBytes, FeatureSplit> resourceTables;
@@ -57,8 +59,8 @@
public static class Builder {
private final Map<String, byte[]> dexInputs = new HashMap<>();
- private final List<PathAndBytes> resFolderInputs = new ArrayList<>();
- private final List<PathAndBytes> xmlInputs = new ArrayList<>();
+ private final Map<Path, PathAndBytes> resFolderInputs = new HashMap<>();
+ private final Map<Path, PathAndBytes> xmlInputs = new HashMap<>();
private final List<PathAndBytes> manifests = new ArrayList<>();
private final Map<PathAndBytes, FeatureSplit> resourceTables = new HashMap<>();
@@ -88,19 +90,34 @@
}
public Builder addResFolderInput(Path path, byte[] bytes) {
- resFolderInputs.add(new PathAndBytes(bytes, path));
+ PathAndBytes existing = resFolderInputs.get(path);
+ if (existing != null) {
+ assert Arrays.equals(existing.getBytes(), bytes);
+ } else {
+ resFolderInputs.put(path, new PathAndBytes(bytes, path));
+ }
return this;
}
public Builder addXmlInput(Path path, byte[] bytes) {
- xmlInputs.add(new PathAndBytes(bytes, path));
+ PathAndBytes existing = xmlInputs.get(path);
+ if (existing != null) {
+ assert Arrays.equals(existing.getBytes(), bytes);
+ } else {
+ xmlInputs.put(path, new PathAndBytes(bytes, path));
+ }
return this;
}
public LegacyResourceShrinker build() {
assert manifests != null && resourceTables != null;
return new LegacyResourceShrinker(
- dexInputs, resFolderInputs, manifests, resourceTables, xmlInputs, proguardMapStrings);
+ dexInputs,
+ resFolderInputs.values(),
+ manifests,
+ resourceTables,
+ xmlInputs.values(),
+ proguardMapStrings);
}
public void setProguardMapStrings(List<String> proguardMapStrings) {
@@ -110,10 +127,10 @@
private LegacyResourceShrinker(
Map<String, byte[]> dexInputs,
- List<PathAndBytes> resFolderInputs,
+ Collection<PathAndBytes> resFolderInputs,
List<PathAndBytes> manifests,
Map<PathAndBytes, FeatureSplit> resourceTables,
- List<PathAndBytes> xmlInputs,
+ Collection<PathAndBytes> xmlInputs,
List<String> proguardMapStrings) {
this.dexInputs = dexInputs;
this.resFolderInputs = resFolderInputs;
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
index f289319..49f3546 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
@@ -59,7 +59,7 @@
private final Map<String, Supplier<InputStream>> resfileProviders = new HashMap<>();
private final Map<ResourceTable, FeatureSplit> resourceTables = new HashMap<>();
private ClassReferenceCallback enqueuerCallback;
- private Map<Integer, String> resourceIdToXmlFiles;
+ private Map<Integer, List<String>> resourceIdToXmlFiles;
private Set<String> packageNames;
private final Set<String> seenNoneClassValues = new HashSet<>();
private final Set<Integer> seenResourceIds = new HashSet<>();
@@ -208,10 +208,12 @@
}
private void traceXmlForResourceId(int id) {
- String xmlFile = getResourceIdToXmlFiles().get(id);
- if (xmlFile != null) {
- InputStream inputStream = xmlFileProviders.get(xmlFile).get();
- traceXml(xmlFile, inputStream);
+ List<String> xmlFiles = getResourceIdToXmlFiles().get(id);
+ if (xmlFiles != null) {
+ for (String xmlFile : xmlFiles) {
+ InputStream inputStream = xmlFileProviders.get(xmlFile).get();
+ traceXml(xmlFile, inputStream);
+ }
}
}
@@ -255,7 +257,7 @@
element.getChildList().forEach(e -> visitNode(e, xmlName));
}
- public Map<Integer, String> getResourceIdToXmlFiles() {
+ public Map<Integer, List<String>> getResourceIdToXmlFiles() {
if (resourceIdToXmlFiles == null) {
resourceIdToXmlFiles = new HashMap<>();
for (ResourceTable resourceTable : resourceTables.keySet()) {
@@ -271,7 +273,9 @@
FileReference file = item.getFile();
if (file.getType() == FileReference.Type.PROTO_XML) {
int id = ResourceTableUtilKt.toIdentifier(packageEntry, type, entry);
- resourceIdToXmlFiles.put(id, file.getPath());
+ resourceIdToXmlFiles
+ .computeIfAbsent(id, unused -> new ArrayList<>())
+ .add(file.getPath());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/androidresources/MultiConfigXmlFilesTest.java b/src/test/java/com/android/tools/r8/androidresources/MultiConfigXmlFilesTest.java
new file mode 100644
index 0000000..c8ebee4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/MultiConfigXmlFilesTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2024, 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.androidresources;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+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 MultiConfigXmlFilesTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withDefaultDexRuntime().withAllApiLevels().build();
+ }
+
+ public static String VIEW_WITH_CLASS_ATTRIBUTE_REFERENCE =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" class=\"%s\"/>\n";
+
+ public AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+ return new AndroidTestResourceBuilder()
+ .withSimpleManifestAndAppNameString()
+ .addRClassInitializeWithDefaultValues(R.xml.class)
+ // We add two configurations of the xml file, one for default and one for -v24.
+ // They reference different classes, both of which we should keep.
+ .addXml(
+ "xml_with_class_reference.xml",
+ String.format(VIEW_WITH_CLASS_ATTRIBUTE_REFERENCE, Foo.class.getTypeName()))
+ .addApiSpecificXml(
+ "xml_with_class_reference.xml",
+ String.format(VIEW_WITH_CLASS_ATTRIBUTE_REFERENCE, Bar.class.getTypeName()))
+ .build(temp);
+ }
+
+ @Test
+ public void testClassReferences() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(TestClass.class, Foo.class, Bar.class)
+ .addAndroidResources(getTestResources(temp))
+ .addKeepMainRule(TestClass.class)
+ .enableOptimizedShrinking()
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName(
+ "xml", "xml_with_class_reference");
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Foo.class), isPresent());
+ assertThat(codeInspector.clazz(Bar.class), isPresent());
+ })
+ .assertSuccess();
+ }
+
+ public static class TestClass {
+ public static void main(String[] args) {
+ System.out.println(R.xml.xml_with_class_reference);
+ }
+ }
+
+ // Only referenced from XML file
+ public static class Bar {}
+
+ // Only referenced from XML file
+ public static class Foo {}
+
+ public static class R {
+ public static class xml {
+ public static int xml_with_class_reference;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkingMultiApkAsFeaturesplits.java b/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkingMultiApkAsFeaturesplits.java
new file mode 100644
index 0000000..97534c6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkingMultiApkAsFeaturesplits.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2024, 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.androidresources;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+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 ResourceShrinkingMultiApkAsFeaturesplits extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean optimized;
+
+ public static String VIEW =
+ "<view xmlns:android=\"http://schemas.android.com/apk/res/android\"/>\n";
+
+ @Parameters(name = "{0}, optimized: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ BooleanUtils.values());
+ }
+
+ public static AndroidTestResource getTestResources(TemporaryFolder temp, boolean base, String xml)
+ throws Exception {
+ AndroidTestResourceBuilder androidTestResourceBuilder =
+ new AndroidTestResourceBuilder()
+ .withSimpleManifestAndAppNameString()
+ .addRClassInitializeWithDefaultValues(base, R.string.class, R.xml.class)
+ .addXml("both_used.xml", VIEW)
+ .addXml("both_unused.xml", VIEW);
+ if (base) {
+ androidTestResourceBuilder.addXml("only_in_base.xml", VIEW);
+ }
+ return androidTestResourceBuilder.build(temp);
+ }
+
+ @Test
+ public void test() throws Exception {
+ TemporaryFolder featureSplitTemp = ToolHelper.getTemporaryFolderForTest();
+ featureSplitTemp.create();
+ String featureSplitName = "featuresplit";
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(Base.class)
+ .addAndroidResources(getTestResources(temp, true, VIEW))
+ .addFeatureSplitAndroidResources(
+ // For the feature, we don't add the R class (we already have it in the base)
+ // and to test we add one less xml file.
+ getTestResources(featureSplitTemp, false, VIEW), featureSplitName)
+ .applyIf(optimized, R8FullTestBuilder::enableOptimizedShrinking)
+ .addKeepMainRule(Base.class)
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("string", "used");
+ resourceTableInspector.assertDoesNotContainResourceWithName("string", "unused");
+ resourceTableInspector.assertContainsResourceWithName("xml", "both_used");
+ resourceTableInspector.assertDoesNotContainResourceWithName("xml", "both_unused");
+ resourceTableInspector.assertContainsResourceWithName("xml", "only_in_base");
+ })
+ .inspectShrunkenResourcesForFeature(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("string", "used");
+ resourceTableInspector.assertDoesNotContainResourceWithName("string", "unused");
+ resourceTableInspector.assertContainsResourceWithName("xml", "both_used");
+ resourceTableInspector.assertDoesNotContainResourceWithName("xml", "both_unused");
+ resourceTableInspector.assertDoesNotContainResourceWithName("xml", "only_in_base");
+ },
+ featureSplitName)
+ .assertResourceFile("res/xml/both_used.xml", true)
+ .assertResourceFile("res/xml/only_in_base.xml", true)
+ .assertResourceFile("res/xml/both_unused.xml", false)
+ .assertFeatureResourceFile("res/xml/both_used.xml", true, featureSplitName)
+ .assertFeatureResourceFile("res/xml/both_unused.xml", false, featureSplitName)
+ .assertFeatureResourceFile("res/xml/only_in_base.xml", false, featureSplitName)
+ .run(parameters.getRuntime(), Base.class)
+ .assertSuccess();
+ }
+
+ public static class Base {
+
+ public static void main(String[] args) {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println(R.string.used);
+ System.out.println(R.xml.both_used);
+ System.out.println(R.xml.only_in_base);
+ }
+ }
+ }
+
+ public static class R {
+
+ public static class string {
+ public static int used;
+ public static int unused;
+ }
+
+ public static class xml {
+ public static int both_used;
+ public static int both_unused;
+ public static int only_in_base;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java b/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java
index 56573a5..597cca6 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.analysis.path.state.PathConstraintAnalysisState;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameterFactory;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeUnopCompareNode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -50,7 +51,8 @@
CodeInspector inspector = new CodeInspector(app);
IRCode code =
inspector.clazz(Main.class).uniqueMethodWithOriginalName("greet").buildIR(appView);
- PathConstraintAnalysis analysis = new PathConstraintAnalysis(appView, code);
+ PathConstraintAnalysis analysis =
+ new PathConstraintAnalysis(appView, code, new MethodParameterFactory());
DataflowAnalysisResult result = analysis.run(code.entryBlock());
assertTrue(result.isSuccessfulAnalysisResult());
SuccessfulDataflowAnalysisResult<BasicBlock, PathConstraintAnalysisState> successfulResult =
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 f0cd1ab..448acc4 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
@@ -4,8 +4,8 @@
package com.android.tools.r8.profile.art.completeness;
-import static com.android.tools.r8.ir.desugar.records.RecordDesugaring.EQUALS_RECORD_METHOD_NAME;
-import static com.android.tools.r8.ir.desugar.records.RecordDesugaring.GET_FIELDS_AS_OBJECTS_METHOD_NAME;
+import static com.android.tools.r8.ir.desugar.records.RecordFullInstructionDesugaring.EQUALS_RECORD_METHOD_NAME;
+import static com.android.tools.r8.ir.desugar.records.RecordFullInstructionDesugaring.GET_FIELDS_AS_OBJECTS_METHOD_NAME;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.ifThen;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
index 942cc92..172fb2b 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
@@ -195,6 +195,20 @@
return self();
}
+ public <E extends Throwable> R8TestCompileResult assertResourceFile(String name, boolean present)
+ throws IOException {
+ assertNotNull(resourceShrinkerOutput);
+ assertEquals(ZipUtils.containsEntry(resourceShrinkerOutput, name), present);
+ return self();
+ }
+
+ public <E extends Throwable> R8TestCompileResult assertFeatureResourceFile(
+ String name, boolean present, String featureName) throws IOException {
+ Path path = resourceShrinkerOutputForFeatures.get(featureName);
+ assertEquals(ZipUtils.containsEntry(path, name), present);
+ return self();
+ }
+
public String dumpResources() throws IOException {
ProcessResult processResult = AndroidResourceTestingUtils.dumpWithAapt2(resourceShrinkerOutput);
assert processResult.exitCode == 0;
diff --git a/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java b/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
index eb5b8e2..80333c3 100644
--- a/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
@@ -293,6 +293,8 @@
private final Map<String, Integer> styleables = new TreeMap<>();
private final Map<String, byte[]> drawables = new TreeMap<>();
private final Map<String, String> xmlFiles = new TreeMap<>();
+ // Used for xml files that we hard code to -v24
+ private final Map<String, String> apiSpecificXmlFiles = new TreeMap<>();
private final Map<String, String> rawXmlFiles = new TreeMap<>();
private final List<Class<?>> classesToRemap = new ArrayList<>();
private int packageId = 0x7f;
@@ -304,8 +306,15 @@
// These R classes will be used to rewrite the namespace and class names on the aapt2
// generated names.
public AndroidTestResourceBuilder addRClassInitializeWithDefaultValues(Class<?>... rClasses) {
+ return addRClassInitializeWithDefaultValues(true, rClasses);
+ }
+
+ public AndroidTestResourceBuilder addRClassInitializeWithDefaultValues(
+ boolean addRClass, Class<?>... rClasses) {
for (Class<?> rClass : rClasses) {
- classesToRemap.add(rClass);
+ if (addRClass) {
+ classesToRemap.add(rClass);
+ }
RClassType rClassType = RClassType.fromClass(rClass);
for (Field declaredField : rClass.getDeclaredFields()) {
String name = declaredField.getName();
@@ -364,6 +373,11 @@
return this;
}
+ public AndroidTestResourceBuilder addApiSpecificXml(String name, String content) {
+ apiSpecificXmlFiles.put(name, content);
+ return this;
+ }
+
public AndroidTestResourceBuilder addKeepXmlFor(String... resourceReferences) {
addRawXml("keep.xml", String.format(KEEP_XML, String.join(",", resourceReferences)));
return this;
@@ -435,6 +449,13 @@
}
}
+ if (apiSpecificXmlFiles.size() > 0) {
+ File xmlFolder = temp.newFolder("res", "xml-v24");
+ for (Entry<String, String> entry : apiSpecificXmlFiles.entrySet()) {
+ FileUtils.writeTextFile(xmlFolder.toPath().resolve(entry.getKey()), entry.getValue());
+ }
+ }
+
if (rawXmlFiles.size() > 0) {
File rawXmlFolder = temp.newFolder("res", "raw");
for (Entry<String, String> entry : rawXmlFiles.entrySet()) {