Merge commit '1f51f11ab8070005bb33d693e0e94565f57fb88a' into dev-release
diff --git a/build.gradle b/build.gradle
index 921a270..2045d5a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -45,8 +45,8 @@
mockitoVersion = '2.10.0'
// The kotlin version is only here to specify the kotlin language level,
// all kotlin compilations are done in tests.
- kotlinVersion = '1.5.0'
- kotlinExtMetadataJVMVersion = '0.3.0'
+ kotlinVersion = '1.6.0'
+ kotlinExtMetadataJVMVersion = '0.4.1'
smaliVersion = '2.2b4'
errorproneVersion = '2.3.2'
testngVersion = '6.10'
@@ -2201,6 +2201,13 @@
systemProperty 'test_git_HEAD_sha1', project.property('HEAD_sha1')
}
+ if (project.hasProperty('kotlin_compiler_dev')) {
+ systemProperty 'com.android.tools.r8.kotlincompilerdev', '1';
+ }
+
+ if (project.hasProperty('kotlin_compiler_old')) {
+ systemProperty 'com.android.tools.r8.kotlincompilerold', '1';
+ }
if (!useTestingState) {
testLogging.exceptionFormat = 'full'
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 4623314..31f7a57 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -1078,7 +1078,7 @@
cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
cipd_version: "refs/heads/master"
properties_j: "builder_group:\"internal.client.r8\""
- properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-dev\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\"]"
+ properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-dev\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\",\"*debug*\"]"
}
priority: 26
execution_timeout_secs: 43200
@@ -1107,7 +1107,7 @@
cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
cipd_version: "refs/heads/master"
properties_j: "builder_group:\"internal.client.r8\""
- properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-old\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\"]"
+ properties_j: "test_options:[\"--runtimes=dex-default:jdk11\",\"--kotlin-compiler-old\",\"--one_line_per_test\",\"--archive_failures\",\"--no-internal\",\"*kotlin*\",\"*debug*\"]"
}
priority: 26
execution_timeout_secs: 43200
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 3aa3fe2..6d178eb 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -332,7 +332,7 @@
expiration_timeout = time.hour * 35,
properties = {
"builder_group" : "internal.client.r8",
- "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-dev", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*"]
+ "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-dev", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*", "*debug*"]
}
)
@@ -343,7 +343,7 @@
expiration_timeout = time.hour * 35,
properties = {
"builder_group" : "internal.client.r8",
- "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-old", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*"]
+ "test_options" : ["--runtimes=dex-default:jdk11", "--kotlin-compiler-old", "--one_line_per_test", "--archive_failures", "--no-internal", "*kotlin*", "*debug*"]
}
)
diff --git a/src/main/java/com/android/tools/r8/DumpOptions.java b/src/main/java/com/android/tools/r8/DumpOptions.java
index e8c6a49..14ae9c6 100644
--- a/src/main/java/com/android/tools/r8/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/DumpOptions.java
@@ -5,6 +5,7 @@
package com.android.tools.r8;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -147,6 +148,11 @@
return featureSplitConfiguration;
}
+ public StartupConfiguration getStartupConfiguration() {
+ // The startup configuration is not included in dumps.
+ return null;
+ }
+
public String getParsedProguardConfiguration() {
return proguardConfiguration == null ? null : proguardConfiguration.getParsedConfiguration();
}
diff --git a/src/main/java/com/android/tools/r8/FeatureSplit.java b/src/main/java/com/android/tools/r8/FeatureSplit.java
index d0eafdb..9b55bde 100644
--- a/src/main/java/com/android/tools/r8/FeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/FeatureSplit.java
@@ -38,6 +38,14 @@
}
};
+ public static final FeatureSplit BASE_STARTUP =
+ new FeatureSplit(null, null) {
+ @Override
+ public boolean isBase() {
+ return true;
+ }
+ };
+
private final ProgramConsumer programConsumer;
private final List<ProgramResourceProvider> programResourceProviders;
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 9946047..b0eec1a 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.origin.CommandLineOrigin;
-import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.L8TreePruner;
import com.android.tools.r8.synthesis.SyntheticFinalization;
import com.android.tools.r8.utils.AndroidApp;
@@ -135,10 +134,6 @@
AppView<AppInfo> appView = readApp(inputApp, options, executor, timing);
- if (!options.testing.disableL8AnnotationRemoval) {
- AnnotationRemover.clearAnnotations(appView);
- }
-
new IRConverter(appView, timing).convert(appView, executor);
SyntheticFinalization.finalize(appView, executor);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index cec2d83..4b53393 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.inspector.Inspector;
@@ -819,6 +820,10 @@
skipDump = false;
}
+ public DexItemFactory getDexItemFactory() {
+ return proguardConfiguration.getDexItemFactory();
+ }
+
/** Get the enable-tree-shaking state. */
public boolean getEnableTreeShaking() {
return enableTreeShaking;
@@ -905,6 +910,9 @@
internal.featureSplitConfiguration = featureSplitConfiguration;
+ internal.startupConfiguration =
+ StartupConfiguration.createStartupConfiguration(getDexItemFactory(), getReporter());
+
internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index e402ce5..990d545 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.dex;
import static com.android.tools.r8.graph.DexCode.TryHandler.NO_HANDLER;
+import static com.android.tools.r8.graph.DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.ConstStringJumbo;
@@ -233,7 +234,7 @@
Instruction target = debugEventTargets.get(lastOriginalOffset);
int lineDelta = defaultEvent.getLineDelta();
int pcDelta = target.getOffset() - lastNewOffset;
- addDefaultEvent(lineDelta, pcDelta, events);
+ addDefaultEventWithAdvancePcIfNecessary(lineDelta, pcDelta, events, factory);
lastNewOffset = target.getOffset();
} else {
events.add(event);
@@ -247,27 +248,6 @@
return code.getDebugInfo();
}
- // Add a default event. If the lineDelta and pcDelta can be encoded in one default event
- // that will be done. Otherwise, this can output an advance line and/or advance pc event
- // followed by a default event. A default event is always emitted as that is what will
- // materialize an entry in the line table.
- private void addDefaultEvent(int lineDelta, int pcDelta, List<DexDebugEvent> events) {
- if (lineDelta < Constants.DBG_LINE_BASE
- || lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
- events.add(factory.createAdvanceLine(lineDelta));
- lineDelta = 0;
- }
- if (pcDelta >= Constants.DBG_ADDRESS_RANGE) {
- events.add(factory.createAdvancePC(pcDelta));
- pcDelta = 0;
- }
- int specialOpcode =
- 0x0a + (lineDelta - Constants.DBG_LINE_BASE) + Constants.DBG_LINE_RANGE * pcDelta;
- assert specialOpcode >= 0x0a;
- assert specialOpcode <= 0xff;
- events.add(factory.createDefault(specialOpcode));
- }
-
private List<Instruction> expandCode() {
LinkedList<Instruction> instructions = new LinkedList<>();
Collections.addAll(instructions, method.getCode().asDexCode().instructions);
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
new file mode 100644
index 0000000..c87d584
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
@@ -0,0 +1,69 @@
+// 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.experimental.startup;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+public class StartupConfiguration {
+
+ List<DexType> startupClasses;
+
+ public StartupConfiguration(List<DexType> startupClasses) {
+ this.startupClasses = startupClasses;
+ }
+
+ public static StartupConfiguration createStartupConfiguration(
+ DexItemFactory dexItemFactory, Reporter reporter) {
+ String propertyValue = System.getProperty("com.android.tools.r8.startupclassdescriptors");
+ if (propertyValue == null) {
+ return null;
+ }
+
+ List<String> startupClassDescriptors;
+ try {
+ startupClassDescriptors = FileUtils.readAllLines(Paths.get(propertyValue));
+ } catch (IOException e) {
+ throw reporter.fatalError(new ExceptionDiagnostic(e));
+ }
+
+ if (startupClassDescriptors.isEmpty()) {
+ return null;
+ }
+
+ List<DexType> startupClasses = new ArrayList<>(startupClassDescriptors.size());
+ for (String startupClassDescriptor : startupClassDescriptors) {
+ if (startupClassDescriptor.trim().isEmpty()) {
+ continue;
+ }
+ if (!DescriptorUtils.isClassDescriptor(startupClassDescriptor)) {
+ reporter.warning(
+ new StringDiagnostic(
+ "Invalid class descriptor for startup class: " + startupClassDescriptor));
+ continue;
+ }
+ DexType startupClass = dexItemFactory.createType(startupClassDescriptor);
+ startupClasses.add(startupClass);
+ }
+ return new StartupConfiguration(startupClasses);
+ }
+
+ public boolean hasStartupClasses() {
+ return !startupClasses.isEmpty();
+ }
+
+ public List<DexType> getStartupClasses() {
+ return startupClasses;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index f460b50..215b25a 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -22,78 +22,96 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.Sets;
-import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
public class ClassToFeatureSplitMap {
- private final Map<DexType, FeatureSplit> classToFeatureSplitMap = new IdentityHashMap<>();
+ private final FeatureSplit baseStartup;
+ private final Map<DexType, FeatureSplit> classToFeatureSplitMap;
private final Map<FeatureSplit, String> representativeStringsForFeatureSplit;
- private ClassToFeatureSplitMap() {
- this(new HashMap<>());
- }
-
- private ClassToFeatureSplitMap(Map<FeatureSplit, String> representativeStringsForFeatureSplit) {
+ private ClassToFeatureSplitMap(
+ FeatureSplit baseStartup,
+ Map<DexType, FeatureSplit> classToFeatureSplitMap,
+ Map<FeatureSplit, String> representativeStringsForFeatureSplit) {
+ this.baseStartup = baseStartup;
+ this.classToFeatureSplitMap = classToFeatureSplitMap;
this.representativeStringsForFeatureSplit = representativeStringsForFeatureSplit;
}
public static ClassToFeatureSplitMap createEmptyClassToFeatureSplitMap() {
- return new ClassToFeatureSplitMap(null);
- }
-
- public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
- AppView<? extends AppInfoWithClassHierarchy> appView) {
- return createInitialClassToFeatureSplitMap(appView.options());
+ return new ClassToFeatureSplitMap(FeatureSplit.BASE, new IdentityHashMap<>(), null);
}
public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
InternalOptions options) {
return createInitialClassToFeatureSplitMap(
- options.dexItemFactory(), options.featureSplitConfiguration, options.reporter);
+ options.dexItemFactory(),
+ options.featureSplitConfiguration,
+ options.startupConfiguration,
+ options.reporter);
}
public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
DexItemFactory dexItemFactory,
FeatureSplitConfiguration featureSplitConfiguration,
+ StartupConfiguration startupConfiguration,
Reporter reporter) {
-
- ClassToFeatureSplitMap result = new ClassToFeatureSplitMap();
- if (featureSplitConfiguration == null) {
- return result;
+ if (featureSplitConfiguration == null && startupConfiguration == null) {
+ return createEmptyClassToFeatureSplitMap();
}
- for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
- String representativeType = null;
- for (ProgramResourceProvider programResourceProvider :
- featureSplit.getProgramResourceProviders()) {
- try {
- for (ProgramResource programResource : programResourceProvider.getProgramResources()) {
- for (String classDescriptor : programResource.getClassDescriptors()) {
- DexType type = dexItemFactory.createType(classDescriptor);
- result.classToFeatureSplitMap.put(type, featureSplit);
- if (representativeType == null || classDescriptor.compareTo(representativeType) > 0) {
- representativeType = classDescriptor;
+ Map<DexType, FeatureSplit> classToFeatureSplitMap = new IdentityHashMap<>();
+ Map<FeatureSplit, String> representativeStringsForFeatureSplit = new IdentityHashMap<>();
+ if (featureSplitConfiguration != null) {
+ for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
+ String representativeType = null;
+ for (ProgramResourceProvider programResourceProvider :
+ featureSplit.getProgramResourceProviders()) {
+ try {
+ for (ProgramResource programResource : programResourceProvider.getProgramResources()) {
+ for (String classDescriptor : programResource.getClassDescriptors()) {
+ DexType type = dexItemFactory.createType(classDescriptor);
+ classToFeatureSplitMap.put(type, featureSplit);
+ if (representativeType == null
+ || classDescriptor.compareTo(representativeType) > 0) {
+ representativeType = classDescriptor;
+ }
}
}
+ } catch (ResourceException e) {
+ throw reporter.fatalError(e.getMessage());
}
- } catch (ResourceException e) {
- throw reporter.fatalError(e.getMessage());
+ }
+ if (representativeType != null) {
+ representativeStringsForFeatureSplit.put(featureSplit, representativeType);
}
}
- if (representativeType != null) {
- result.representativeStringsForFeatureSplit.put(featureSplit, representativeType);
- }
}
- return result;
- }
- public int compareFeatureSplitsForDexTypes(DexType a, DexType b, SyntheticItems syntheticItems) {
- FeatureSplit featureSplitA = getFeatureSplit(a, syntheticItems);
- FeatureSplit featureSplitB = getFeatureSplit(b, syntheticItems);
- return compareFeatureSplits(featureSplitA, featureSplitB);
+ FeatureSplit baseStartup;
+ if (startupConfiguration != null && startupConfiguration.hasStartupClasses()) {
+ DexType representativeType = null;
+ for (DexType startupClass : startupConfiguration.getStartupClasses()) {
+ if (classToFeatureSplitMap.containsKey(startupClass)) {
+ continue;
+ }
+ classToFeatureSplitMap.put(startupClass, FeatureSplit.BASE_STARTUP);
+ if (representativeType == null
+ || startupClass.getDescriptor().compareTo(representativeType.getDescriptor()) > 0) {
+ representativeType = startupClass;
+ }
+ }
+ baseStartup = FeatureSplit.BASE_STARTUP;
+ representativeStringsForFeatureSplit.put(
+ baseStartup, representativeType.toDescriptorString());
+ } else {
+ baseStartup = FeatureSplit.BASE;
+ }
+ return new ClassToFeatureSplitMap(
+ baseStartup, classToFeatureSplitMap, representativeStringsForFeatureSplit);
}
public int compareFeatureSplits(FeatureSplit featureSplitA, FeatureSplit featureSplitB) {
@@ -114,6 +132,14 @@
.compareTo(representativeStringsForFeatureSplit.get(featureSplitB));
}
+ /**
+ * Returns the base startup if there are any startup classes given on input. Otherwise returns
+ * base.
+ */
+ public FeatureSplit getBaseStartup() {
+ return baseStartup;
+ }
+
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
Set<DexProgramClass> classes, SyntheticItems syntheticItems) {
Map<FeatureSplit, Set<DexProgramClass>> result = new IdentityHashMap<>();
@@ -135,7 +161,7 @@
if (feature != null) {
return feature;
}
- feature = syntheticItems.getContextualFeatureSplit(type);
+ feature = syntheticItems.getContextualFeatureSplit(type, this);
if (feature != null) {
return feature;
}
@@ -168,23 +194,18 @@
return !isInBase(clazz, syntheticItems);
}
- public boolean isInSameFeatureOrBothInBase(
+ public boolean isInSameFeatureOrBothInSameBase(
ProgramMethod a, ProgramMethod b, SyntheticItems syntheticItems) {
- return isInSameFeatureOrBothInBase(a.getHolder(), b.getHolder(), syntheticItems);
+ return isInSameFeatureOrBothInSameBase(a.getHolder(), b.getHolder(), syntheticItems);
}
- public boolean isInSameFeatureOrBothInBase(
+ public boolean isInSameFeatureOrBothInSameBase(
DexProgramClass a, DexProgramClass b, SyntheticItems syntheticItems) {
return getFeatureSplit(a, syntheticItems) == getFeatureSplit(b, syntheticItems);
}
- public boolean isInSameFeatureOrBothInBase(DexType a, DexType b, SyntheticItems syntheticItems) {
- return getFeatureSplit(a, syntheticItems) == getFeatureSplit(b, syntheticItems);
- }
-
public ClassToFeatureSplitMap rewrittenWithLens(GraphLens lens) {
- ClassToFeatureSplitMap rewrittenClassToFeatureSplitMap =
- new ClassToFeatureSplitMap(representativeStringsForFeatureSplit);
+ Map<DexType, FeatureSplit> rewrittenClassToFeatureSplitMap = new IdentityHashMap<>();
classToFeatureSplitMap.forEach(
(type, featureSplit) -> {
DexType rewrittenType = lens.lookupType(type);
@@ -192,25 +213,24 @@
// The type was removed by enum unboxing.
return;
}
- FeatureSplit existing =
- rewrittenClassToFeatureSplitMap.classToFeatureSplitMap.put(
- rewrittenType, featureSplit);
+ FeatureSplit existing = rewrittenClassToFeatureSplitMap.put(rewrittenType, featureSplit);
// If we map two classes to the same class then they must be from the same feature split.
assert existing == null || existing == featureSplit;
});
- return rewrittenClassToFeatureSplitMap;
+ return new ClassToFeatureSplitMap(
+ baseStartup, rewrittenClassToFeatureSplitMap, representativeStringsForFeatureSplit);
}
public ClassToFeatureSplitMap withoutPrunedItems(PrunedItems prunedItems) {
- ClassToFeatureSplitMap classToFeatureSplitMapAfterPruning =
- new ClassToFeatureSplitMap(representativeStringsForFeatureSplit);
+ Map<DexType, FeatureSplit> rewrittenClassToFeatureSplitMap = new IdentityHashMap<>();
classToFeatureSplitMap.forEach(
(type, featureSplit) -> {
if (!prunedItems.getRemovedClasses().contains(type)) {
- classToFeatureSplitMapAfterPruning.classToFeatureSplitMap.put(type, featureSplit);
+ rewrittenClassToFeatureSplitMap.put(type, featureSplit);
}
});
- return classToFeatureSplitMapAfterPruning;
+ return new ClassToFeatureSplitMap(
+ baseStartup, rewrittenClassToFeatureSplitMap, representativeStringsForFeatureSplit);
}
// Static helpers to avoid verbose predicates.
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index e5a744b..1cf6ae7 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.origin.Origin;
@@ -13,7 +12,6 @@
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.IterableUtils;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -125,26 +123,6 @@
return syntheticItems;
}
- public void addSynthesizedClassForLibraryDesugaring(DexProgramClass clazz) {
- assert checkIfObsolete();
- assert options().desugaredLibraryConfiguration != null;
- syntheticItems.addLegacySyntheticClassForLibraryDesugaring(clazz);
- }
-
- public void addSynthesizedClass(DexProgramClass clazz, ProgramDefinition context) {
- assert checkIfObsolete();
- assert context != null;
- syntheticItems.addLegacySyntheticClass(clazz, context, FeatureSplit.BASE);
- }
-
- public void addSynthesizedClassToBase(DexProgramClass clazz, Iterable<DexProgramClass> contexts) {
- assert checkIfObsolete();
- assert !IterableUtils.isEmpty(contexts);
- SyntheticItems syntheticItems = getSyntheticItems();
- contexts.forEach(
- context -> syntheticItems.addLegacySyntheticClass(clazz, context, FeatureSplit.BASE));
- }
-
public List<DexProgramClass> classes() {
assert checkIfObsolete();
return app.classes();
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 4eaea9f..c928847 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
-import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.ArrayCloneMethodResult;
@@ -161,15 +160,6 @@
return this;
}
- @Override
- public void addSynthesizedClass(DexProgramClass clazz, ProgramDefinition context) {
- assert checkIfObsolete();
- assert context != null;
- FeatureSplit featureSplit =
- classToFeatureSplitMap.getFeatureSplit(context, getSyntheticItems());
- getSyntheticItems().addLegacySyntheticClass(clazz, context, featureSplit);
- }
-
/** Primitive traversal over all (non-interface) superclasses of a given type. */
public TraversalContinuation traverseSuperClasses(
DexClass clazz, TriFunction<DexType, DexClass, DexClass, TraversalContinuation> fn) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index 7fedd7c..b5b0c60 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -103,21 +103,28 @@
+ "`";
return true;
}
- if (featureImplementations.size() > 1
- || !featureImplementations.containsKey(FeatureSplit.BASE)) {
+ if (featureImplementations.keySet().stream().anyMatch(feature -> !feature.isBase())) {
return true;
}
+ // All service implementations are in one of the base splits.
+ assert featureImplementations.size() <= 2;
// Check if service is defined feature
DexProgramClass serviceClass = appView.definitionForProgramType(serviceType);
if (serviceClass != null
&& classToFeatureSplitMap.isInFeature(serviceClass, appView.getSyntheticItems())) {
return true;
}
- for (DexType implementationType : featureImplementations.get(FeatureSplit.BASE)) {
- DexProgramClass implementationClass = appView.definitionForProgramType(implementationType);
- if (implementationClass != null
- && classToFeatureSplitMap.isInFeature(implementationClass, appView.getSyntheticItems())) {
- return true;
+ for (Entry<FeatureSplit, List<DexType>> entry : featureImplementations.entrySet()) {
+ FeatureSplit feature = entry.getKey();
+ assert feature.isBase();
+ List<DexType> implementationTypes = entry.getValue();
+ for (DexType implementationType : implementationTypes) {
+ DexProgramClass implementationClass = appView.definitionForProgramType(implementationType);
+ if (implementationClass != null
+ && classToFeatureSplitMap.isInFeature(
+ implementationClass, appView.getSyntheticItems())) {
+ return true;
+ }
}
}
return false;
@@ -216,6 +223,7 @@
public AppServices build() {
for (DataResourceProvider provider : appView.appInfo().app().dataResourceProviders) {
+ // TODO(b/208677025): This should use BASE_STARTUP for startup classes.
readServices(provider, FeatureSplit.BASE);
}
if (options.featureSplitConfiguration != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index e21fbf4..303eaf8e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepClassInfo;
+import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
@@ -213,6 +214,10 @@
return simpleInliningConstraintFactory;
}
+ public DexApplication app() {
+ return appInfo().app();
+ }
+
public T appInfo() {
assert !appInfo.hasClassHierarchy() || enableWholeProgramOptimizations();
return appInfo;
@@ -533,6 +538,10 @@
return getKeepInfo().getClassInfo(clazz);
}
+ public KeepFieldInfo getKeepInfo(ProgramField field) {
+ return getKeepInfo().getFieldInfo(field);
+ }
+
public KeepMethodInfo getKeepInfo(ProgramMethod method) {
return getKeepInfo().getMethodInfo(method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 309a768..d0a336a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -22,7 +22,7 @@
import java.util.List;
import java.util.function.Predicate;
-public abstract class DexApplication {
+public abstract class DexApplication implements DexDefinitionSupplier {
public final ImmutableList<DataResourceProvider> dataResourceProviders;
@@ -56,6 +56,11 @@
public abstract Builder<?> builder();
+ @Override
+ public DexItemFactory dexItemFactory() {
+ return dexItemFactory;
+ }
+
public DexDefinitionSupplier getDefinitionsSupplier(
SyntheticDefinitionsProvider syntheticDefinitionsProvider) {
DexApplication self = this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index e359150..6cef0f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -318,7 +318,7 @@
}
public void forEachFieldMatching(
- Predicate<DexEncodedField> predicate, Consumer<DexEncodedField> consumer) {
+ Predicate<? super DexEncodedField> predicate, Consumer<? super DexEncodedField> consumer) {
fields(predicate).forEach(consumer);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index 8091db7..71822f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -261,6 +261,11 @@
factory.createSetOutlineCallerFrame(
nextPosition.getOutlineCallee(), nextPosition.getOutlinePositions()));
}
+ addDefaultEventWithAdvancePcIfNecessary(lineDelta, pcDelta, events, factory);
+ }
+
+ public static void addDefaultEventWithAdvancePcIfNecessary(
+ int lineDelta, int pcDelta, List<DexDebugEvent> events, DexItemFactory factory) {
if (lineDelta < Constants.DBG_LINE_BASE
|| lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
events.add(factory.createAdvanceLine(lineDelta));
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 2bbb51b..2e09eec 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -196,7 +196,12 @@
}
public void forEachProgramField(Consumer<? super ProgramField> consumer) {
- forEachField(field -> consumer.accept(new ProgramField(this, field)));
+ forEachProgramFieldMatching(alwaysTrue(), consumer);
+ }
+
+ public void forEachProgramFieldMatching(
+ Predicate<? super DexEncodedField> predicate, Consumer<? super ProgramField> consumer) {
+ forEachFieldMatching(predicate, field -> consumer.accept(new ProgramField(this, field)));
}
public void forEachProgramInstanceField(Consumer<? super ProgramField> consumer) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index b3e8f74..8315b20 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.references.ClassReference;
@@ -54,8 +56,21 @@
return Reference.classFromDescriptor(toDescriptorString());
}
+ public DynamicTypeWithUpperBound toDynamicType(AppView<AppInfoWithLiveness> appView) {
+ return toDynamicType(appView, Nullability.maybeNull());
+ }
+
+ public DynamicTypeWithUpperBound toDynamicType(
+ AppView<AppInfoWithLiveness> appView, Nullability nullability) {
+ return DynamicType.create(appView, toTypeElement(appView, nullability));
+ }
+
public TypeElement toTypeElement(AppView<?> appView) {
- return TypeElement.fromDexType(this, Nullability.maybeNull(), appView);
+ return toTypeElement(appView, Nullability.maybeNull());
+ }
+
+ public TypeElement toTypeElement(AppView<?> appView, Nullability nullability) {
+ return TypeElement.fromDexType(this, nullability, appView);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 5328885..0a60b01 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.utils.IteratorUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
@@ -49,6 +50,12 @@
}
@Override
+ public ArgumentInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ return this;
+ }
+
+ @Override
public boolean equals(Object obj) {
return obj == this;
}
@@ -91,6 +98,9 @@
// ArgumentInfo are combined with `this` first, and the `info` argument second.
public abstract ArgumentInfo combine(ArgumentInfo info);
+ public abstract ArgumentInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens);
+
@Override
public abstract boolean equals(Object obj);
@@ -166,6 +176,18 @@
}
@Override
+ public RemovedArgumentInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ SingleValue rewrittenSingleValue =
+ hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens) : null;
+ DexType rewrittenType = graphLens.lookupType(type);
+ if (rewrittenSingleValue != singleValue || rewrittenType != type) {
+ return new RemovedArgumentInfo(rewrittenSingleValue, rewrittenType);
+ }
+ return this;
+ }
+
+ @Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
@@ -253,6 +275,19 @@
}
@Override
+ public RewrittenTypeInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ DexType rewrittenNewType = graphLens.lookupType(newType);
+ SingleValue rewrittenSingleValue =
+ hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens) : null;
+ if (rewrittenNewType != newType || rewrittenSingleValue != singleValue) {
+ // The old type is intentionally not rewritten.
+ return new RewrittenTypeInfo(oldType, rewrittenNewType, rewrittenSingleValue);
+ }
+ return this;
+ }
+
+ @Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
@@ -368,6 +403,28 @@
return argumentInfos.size();
}
+ public ArgumentInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ Int2ObjectSortedMap<ArgumentInfo> rewrittenArgumentInfos = new Int2ObjectRBTreeMap<>();
+ for (Int2ObjectMap.Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
+ ArgumentInfo argumentInfo = entry.getValue();
+ ArgumentInfo rewrittenArgumentInfo = argumentInfo.rewrittenWithLens(appView, graphLens);
+ if (rewrittenArgumentInfo != argumentInfo) {
+ rewrittenArgumentInfos.put(entry.getIntKey(), rewrittenArgumentInfo);
+ }
+ }
+ if (!rewrittenArgumentInfos.isEmpty()) {
+ for (Int2ObjectMap.Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
+ int key = entry.getIntKey();
+ if (!rewrittenArgumentInfos.containsKey(key)) {
+ rewrittenArgumentInfos.put(key, entry.getValue());
+ }
+ }
+ return new ArgumentInfoCollection(rewrittenArgumentInfos);
+ }
+ return this;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
@@ -694,6 +751,20 @@
return dexItemFactory.createProto(newReturnType, newParameters);
}
+ public RewrittenPrototypeDescription rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ ArgumentInfoCollection newArgumentInfoCollection =
+ argumentInfoCollection.rewrittenWithLens(appView, graphLens);
+ RewrittenTypeInfo newRewrittenReturnInfo =
+ hasRewrittenReturnInfo() ? rewrittenReturnInfo.rewrittenWithLens(appView, graphLens) : null;
+ if (newArgumentInfoCollection != argumentInfoCollection
+ || newRewrittenReturnInfo != rewrittenReturnInfo) {
+ return new RewrittenPrototypeDescription(
+ extraParameters, newRewrittenReturnInfo, newArgumentInfoCollection);
+ }
+ return this;
+ }
+
public RewrittenPrototypeDescription withRewrittenReturnInfo(
RewrittenTypeInfo newRewrittenReturnInfo) {
if (Objects.equals(rewrittenReturnInfo, newRewrittenReturnInfo)) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 150f206..e5a1aec 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -85,8 +85,7 @@
resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
if (field != null) {
if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordFieldAccess(
- fieldInstruction, field.getDefinition(), code.context());
+ fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
}
if (fieldBitAccessAnalysis != null) {
fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 70c8519..714535c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -4,19 +4,31 @@
package com.android.tools.r8.ir.analysis.fieldaccess;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
+import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteArrayTypeFieldState;
+import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteClassTypeFieldState;
+import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcretePrimitiveTypeFieldState;
+import com.android.tools.r8.ir.analysis.fieldaccess.state.FieldState;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.BottomValue;
import com.android.tools.r8.ir.analysis.value.NonConstantNumberValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
@@ -30,9 +42,10 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldArgumentInitializationInfo;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
@@ -40,13 +53,14 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class FieldAssignmentTracker {
+ private final AbstractValueFactory abstractValueFactory;
private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory dexItemFactory;
// A field access graph with edges from methods to the fields that they access. Edges are removed
// from the graph as we process methods, such that we can conclude that all field writes have been
@@ -58,14 +72,17 @@
// sites have been seen when a class no longer has any incoming edges.
private final ObjectAllocationGraph objectAllocationGraph;
- // The set of fields that may store a non-zero value.
- private final Set<DexEncodedField> nonZeroFields = Sets.newConcurrentHashSet();
+ // Information about the fields in the program. If a field is not a key in the map then no writes
+ // has been seen to the field.
+ private final Map<DexEncodedField, FieldState> fieldStates = new ConcurrentHashMap<>();
private final Map<DexProgramClass, Map<DexEncodedField, AbstractValue>>
abstractInstanceFieldValues = new ConcurrentHashMap<>();
FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
+ this.abstractValueFactory = appView.abstractValueFactory();
this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
this.fieldAccessGraph = new FieldAccessGraph();
this.objectAllocationGraph = new ObjectAllocationGraph();
}
@@ -109,33 +126,114 @@
}
abstractInstanceFieldValues.put(clazz, abstractInstanceFieldValuesForClass);
});
- }
-
- private boolean isAlwaysZero(DexEncodedField field) {
- return !appView.appInfo().isPinned(field.getReference()) && !nonZeroFields.contains(field);
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachProgramField(
+ field -> {
+ FieldAccessInfo accessInfo = fieldAccessInfos.get(field.getReference());
+ KeepFieldInfo keepInfo = appView.getKeepInfo(field);
+ if (keepInfo.isPinned(appView.options()) || accessInfo.isWrittenFromMethodHandle()) {
+ fieldStates.put(field.getDefinition(), FieldState.unknown());
+ }
+ });
+ }
}
void acceptClassInitializerDefaultsResult(
ClassInitializerDefaultsResult classInitializerDefaultsResult) {
classInitializerDefaultsResult.forEachOptimizedField(
(field, value) -> {
- if (!value.isDefault(field.getReference().type)) {
- nonZeroFields.add(field);
+ DexType fieldType = field.getType();
+ if (value.isDefault(field.getType())) {
+ return;
}
+ assert fieldType.isClassType() || fieldType.isPrimitiveType();
+ fieldStates.compute(
+ field,
+ (f, fieldState) -> {
+ if (fieldState == null) {
+ AbstractValue abstractValue = value.toAbstractValue(abstractValueFactory);
+ if (fieldType.isClassType()) {
+ assert abstractValue.isSingleStringValue()
+ || abstractValue.isSingleDexItemBasedStringValue();
+ if (fieldType == dexItemFactory.stringType) {
+ return ConcreteClassTypeFieldState.create(
+ abstractValue, DynamicType.definitelyNotNull());
+ } else {
+ ClassTypeElement nonNullableStringType =
+ dexItemFactory
+ .stringType
+ .toTypeElement(appView, definitelyNotNull())
+ .asClassType();
+ return ConcreteClassTypeFieldState.create(
+ abstractValue, DynamicType.createExact(nonNullableStringType));
+ }
+ } else {
+ assert fieldType.isPrimitiveType();
+ return ConcretePrimitiveTypeFieldState.create(abstractValue);
+ }
+ }
+ // If the field is already assigned outside the class initializer then just give up.
+ return FieldState.unknown();
+ });
});
}
- void recordFieldAccess(
- FieldInstruction instruction, DexEncodedField field, ProgramMethod context) {
+ void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) {
if (instruction.isFieldPut()) {
recordFieldPut(field, instruction.value(), context);
}
}
- private void recordFieldPut(DexEncodedField field, Value value, ProgramMethod context) {
- if (!value.isZero()) {
- nonZeroFields.add(field);
- }
+ private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) {
+ // For now only attempt to prove that fields are definitely null. In order to prove a single
+ // value for fields that are not definitely null, we need to prove that the given field is never
+ // read before it is written.
+ AbstractValue abstractValue =
+ value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown();
+ fieldStates.compute(
+ field.getDefinition(),
+ (f, fieldState) -> {
+ if (fieldState == null || fieldState.isBottom()) {
+ DexType fieldType = field.getType();
+ if (fieldType.isArrayType()) {
+ return ConcreteArrayTypeFieldState.create(abstractValue);
+ }
+ if (fieldType.isPrimitiveType()) {
+ return ConcretePrimitiveTypeFieldState.create(abstractValue);
+ }
+ assert fieldType.isClassType();
+ DynamicType dynamicType =
+ fieldType.isArrayType()
+ ? DynamicType.unknown()
+ : WideningUtils.widenDynamicNonReceiverType(
+ appView,
+ value.getDynamicType(appView).withNullability(Nullability.maybeNull()),
+ field.getType());
+ return ConcreteClassTypeFieldState.create(abstractValue, dynamicType);
+ }
+
+ if (fieldState.isUnknown()) {
+ return fieldState;
+ }
+
+ assert fieldState.isConcrete();
+
+ if (fieldState.isArray()) {
+ ConcreteArrayTypeFieldState arrayFieldState = fieldState.asArray();
+ return arrayFieldState.mutableJoin(appView, abstractValue);
+ }
+
+ if (fieldState.isPrimitive()) {
+ ConcretePrimitiveTypeFieldState primitiveFieldState = fieldState.asPrimitive();
+ return primitiveFieldState.mutableJoin(abstractValue, abstractValueFactory);
+ }
+
+ assert fieldState.isClass();
+
+ ConcreteClassTypeFieldState classFieldState = fieldState.asClass();
+ return classFieldState.mutableJoin(
+ appView, abstractValue, value.getDynamicType(appView), field);
+ });
}
void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
@@ -146,7 +244,7 @@
return;
}
- InvokeDirect invoke = instruction.getUniqueConstructorInvoke(appView.dexItemFactory());
+ InvokeDirect invoke = instruction.getUniqueConstructorInvoke(dexItemFactory);
if (invoke == null) {
// We just lost track.
abstractInstanceFieldValues.remove(clazz);
@@ -238,27 +336,40 @@
}
private void recordAllFieldPutsProcessed(
- DexEncodedField field, ProgramMethod context, OptimizationFeedbackDelayed feedback) {
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionForHolder(field, context));
- if (clazz == null) {
- assert false;
- return;
+ ProgramField field, ProgramMethod context, OptimizationFeedbackDelayed feedback) {
+ FieldState fieldState = fieldStates.getOrDefault(field.getDefinition(), FieldState.bottom());
+ AbstractValue abstractValue = fieldState.getAbstractValue(appView.abstractValueFactory());
+ if (abstractValue.isNonTrivial()) {
+ feedback.recordFieldHasAbstractValue(field.getDefinition(), appView, abstractValue);
}
- if (isAlwaysZero(field)) {
- feedback.recordFieldHasAbstractValue(
- field, appView, appView.abstractValueFactory().createSingleNumberValue(0));
+ if (fieldState.isClass() && field.getOptimizationInfo().getDynamicType().isUnknown()) {
+ ConcreteClassTypeFieldState classFieldState = fieldState.asClass();
+ DynamicType dynamicType = classFieldState.getDynamicType();
+ if (!dynamicType.isUnknown()) {
+ assert WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType())
+ == dynamicType;
+ if (dynamicType.isNotNullType()) {
+ feedback.markFieldHasDynamicType(field.getDefinition(), dynamicType);
+ } else {
+ DynamicTypeWithUpperBound staticType = field.getType().toDynamicType(appView);
+ if (dynamicType.asDynamicTypeWithUpperBound().strictlyLessThan(staticType, appView)) {
+ feedback.markFieldHasDynamicType(field.getDefinition(), dynamicType);
+ }
+ }
+ }
}
- if (!field.isStatic()) {
- recordAllInstanceFieldPutsProcessed(clazz, field, feedback);
+ if (!field.getAccessFlags().isStatic()) {
+ recordAllInstanceFieldPutsProcessed(field, feedback);
}
}
private void recordAllInstanceFieldPutsProcessed(
- DexProgramClass clazz, DexEncodedField field, OptimizationFeedbackDelayed feedback) {
+ ProgramField field, OptimizationFeedbackDelayed feedback) {
if (appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
AbstractValue abstractValue = BottomValue.getInstance();
+ DexProgramClass clazz = field.getHolder();
for (DexEncodedMethod method : clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
InstanceFieldInitializationInfo fieldInitializationInfo =
method
@@ -290,7 +401,7 @@
assert !abstractValue.isBottom();
if (!abstractValue.isUnknown()) {
- feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
+ feedback.recordFieldHasAbstractValue(field.getDefinition(), appView, abstractValue);
}
}
}
@@ -334,8 +445,7 @@
static class FieldAccessGraph {
// The fields written by each method.
- private final Map<DexEncodedMethod, List<DexEncodedField>> fieldWrites =
- new IdentityHashMap<>();
+ private final Map<DexEncodedMethod, List<ProgramField>> fieldWrites = new IdentityHashMap<>();
// The number of writes that have not yet been processed per field.
private final Reference2IntMap<DexEncodedField> pendingFieldWrites =
@@ -348,8 +458,7 @@
appView.appInfo().getFieldAccessInfoCollection();
fieldAccessInfoCollection.forEach(
info -> {
- DexEncodedField field =
- appView.appInfo().resolveField(info.getField()).getResolvedField();
+ ProgramField field = appView.appInfo().resolveField(info.getField()).getProgramField();
if (field == null) {
return;
}
@@ -359,18 +468,18 @@
fieldWrites
.computeIfAbsent(context.getDefinition(), ignore -> new ArrayList<>())
.add(field));
- pendingFieldWrites.put(field, info.getNumberOfWriteContexts());
+ pendingFieldWrites.put(field.getDefinition(), info.getNumberOfWriteContexts());
}
});
}
- void markProcessed(ProgramMethod method, Consumer<DexEncodedField> allWritesSeenConsumer) {
- List<DexEncodedField> fieldWritesInMethod = fieldWrites.get(method.getDefinition());
+ void markProcessed(ProgramMethod method, Consumer<ProgramField> allWritesSeenConsumer) {
+ List<ProgramField> fieldWritesInMethod = fieldWrites.get(method.getDefinition());
if (fieldWritesInMethod != null) {
- for (DexEncodedField field : fieldWritesInMethod) {
- int numberOfPendingFieldWrites = pendingFieldWrites.removeInt(field) - 1;
+ for (ProgramField field : fieldWritesInMethod) {
+ int numberOfPendingFieldWrites = pendingFieldWrites.removeInt(field.getDefinition()) - 1;
if (numberOfPendingFieldWrites > 0) {
- pendingFieldWrites.put(field, numberOfPendingFieldWrites);
+ pendingFieldWrites.put(field.getDefinition(), numberOfPendingFieldWrites);
} else {
allWritesSeenConsumer.accept(field);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java
new file mode 100644
index 0000000..c3474af
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/BottomFieldState.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+
+/** Used to represent the state for fields that have never been assigned in the program. */
+public class BottomFieldState extends FieldState {
+
+ private static final BottomFieldState INSTANCE = new BottomFieldState();
+
+ private BottomFieldState() {}
+
+ public static BottomFieldState getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) {
+ return abstractValueFactory.createNullValue();
+ }
+
+ @Override
+ public boolean isBottom() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java
new file mode 100644
index 0000000..1b82558
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+/**
+ * The information that we track for fields with an array type.
+ *
+ * <p>Since we don't gain much from tracking the dynamic types of arrays, this is only tracking the
+ * abstract value.
+ */
+public class ConcreteArrayTypeFieldState extends ConcreteReferenceTypeFieldState {
+
+ ConcreteArrayTypeFieldState(AbstractValue abstractValue) {
+ super(abstractValue);
+ }
+
+ public static FieldState create(AbstractValue abstractValue) {
+ return abstractValue.isUnknown()
+ ? FieldState.unknown()
+ : new ConcreteArrayTypeFieldState(abstractValue);
+ }
+
+ @Override
+ public boolean isArray() {
+ return true;
+ }
+
+ @Override
+ public ConcreteArrayTypeFieldState asArray() {
+ return this;
+ }
+
+ public FieldState mutableJoin(AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
+ this.abstractValue =
+ this.abstractValue.joinReference(abstractValue, appView.abstractValueFactory());
+ return isEffectivelyUnknown() ? unknown() : this;
+ }
+
+ private boolean isEffectivelyUnknown() {
+ return abstractValue.isUnknown();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java
new file mode 100644
index 0000000..7cd6f2e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+/** The information that we track for fields whose type is a class type. */
+public class ConcreteClassTypeFieldState extends ConcreteReferenceTypeFieldState {
+
+ private DynamicType dynamicType;
+
+ ConcreteClassTypeFieldState(AbstractValue abstractValue, DynamicType dynamicType) {
+ super(abstractValue);
+ this.dynamicType = dynamicType;
+ }
+
+ public static FieldState create(AbstractValue abstractValue, DynamicType dynamicType) {
+ return abstractValue.isUnknown() && dynamicType.isUnknown()
+ ? FieldState.unknown()
+ : new ConcreteClassTypeFieldState(abstractValue, dynamicType);
+ }
+
+ public DynamicType getDynamicType() {
+ return dynamicType;
+ }
+
+ @Override
+ public boolean isClass() {
+ return true;
+ }
+
+ @Override
+ public ConcreteClassTypeFieldState asClass() {
+ return this;
+ }
+
+ public FieldState mutableJoin(
+ AppView<AppInfoWithLiveness> appView,
+ AbstractValue abstractValue,
+ DynamicType dynamicType,
+ ProgramField field) {
+ this.abstractValue =
+ this.abstractValue.joinReference(abstractValue, appView.abstractValueFactory());
+ this.dynamicType =
+ field.getType().isArrayType()
+ ? DynamicType.unknown()
+ : WideningUtils.widenDynamicNonReceiverType(
+ appView, this.dynamicType.join(appView, dynamicType), field.getType());
+ return isEffectivelyUnknown() ? unknown() : this;
+ }
+
+ private boolean isEffectivelyUnknown() {
+ return abstractValue.isUnknown() && dynamicType.isUnknown();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteFieldState.java
new file mode 100644
index 0000000..25ddbb9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteFieldState.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+/** A shared base class for non-trivial field information (neither bottom nor top). */
+public abstract class ConcreteFieldState extends FieldState {
+
+ @Override
+ public boolean isConcrete() {
+ return true;
+ }
+
+ @Override
+ public ConcreteFieldState asConcrete() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java
new file mode 100644
index 0000000..c2eb778
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+
+/** The information that we track for fields whose type is a primitive type. */
+public class ConcretePrimitiveTypeFieldState extends ConcreteFieldState {
+
+ private AbstractValue abstractValue;
+
+ ConcretePrimitiveTypeFieldState(AbstractValue abstractValue) {
+ this.abstractValue = abstractValue;
+ }
+
+ public static FieldState create(AbstractValue abstractValue) {
+ return abstractValue.isUnknown()
+ ? FieldState.unknown()
+ : new ConcretePrimitiveTypeFieldState(abstractValue);
+ }
+
+ @Override
+ public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) {
+ return abstractValue;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return true;
+ }
+
+ @Override
+ public ConcretePrimitiveTypeFieldState asPrimitive() {
+ return this;
+ }
+
+ public FieldState mutableJoin(
+ AbstractValue abstractValue, AbstractValueFactory abstractValueFactory) {
+ if (abstractValue.isUnknown()) {
+ return FieldState.unknown();
+ }
+ this.abstractValue = this.abstractValue.joinPrimitive(abstractValue, abstractValueFactory);
+ return isEffectivelyUnknown() ? unknown() : this;
+ }
+
+ private boolean isEffectivelyUnknown() {
+ return abstractValue.isUnknown();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java
new file mode 100644
index 0000000..64d0674
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteReferenceTypeFieldState.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+
+/** The information that we track for fields whose type is a reference type. */
+public abstract class ConcreteReferenceTypeFieldState extends ConcreteFieldState {
+
+ protected AbstractValue abstractValue;
+
+ ConcreteReferenceTypeFieldState(AbstractValue abstractValue) {
+ this.abstractValue = abstractValue;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) {
+ return abstractValue;
+ }
+
+ @Override
+ public boolean isReference() {
+ return true;
+ }
+
+ @Override
+ public ConcreteReferenceTypeFieldState asReference() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java
new file mode 100644
index 0000000..6e0b203
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/FieldState.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+
+/** An abstraction of the runtime values that may flow into each field. */
+public abstract class FieldState {
+
+ public static BottomFieldState bottom() {
+ return BottomFieldState.getInstance();
+ }
+
+ public static UnknownFieldState unknown() {
+ return UnknownFieldState.getInstance();
+ }
+
+ public abstract AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory);
+
+ public boolean isArray() {
+ return false;
+ }
+
+ public ConcreteArrayTypeFieldState asArray() {
+ return null;
+ }
+
+ public boolean isBottom() {
+ return false;
+ }
+
+ public boolean isClass() {
+ return false;
+ }
+
+ public ConcreteClassTypeFieldState asClass() {
+ return null;
+ }
+
+ public boolean isConcrete() {
+ return false;
+ }
+
+ public ConcreteFieldState asConcrete() {
+ return null;
+ }
+
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ public ConcretePrimitiveTypeFieldState asPrimitive() {
+ return null;
+ }
+
+ public boolean isReference() {
+ return false;
+ }
+
+ public ConcreteReferenceTypeFieldState asReference() {
+ return null;
+ }
+
+ public boolean isUnknown() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java
new file mode 100644
index 0000000..e408f09
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/UnknownFieldState.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldaccess.state;
+
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+
+/** Represents that nothing is known about the values that may flow into a given field. */
+public class UnknownFieldState extends FieldState {
+
+ private static final UnknownFieldState INSTANCE = new UnknownFieldState();
+
+ private UnknownFieldState() {}
+
+ public static UnknownFieldState getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue(AbstractValueFactory abstractValueFactory) {
+ return AbstractValue.unknown();
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 0303ba7..30c3b67 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -14,9 +14,8 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueNull;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
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.NullOrAbstractValue;
@@ -147,26 +146,17 @@
// Abstract value.
feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
- // Dynamic upper bound type.
- TypeElement fieldType =
- TypeElement.fromDexType(field.getReference().type, Nullability.maybeNull(), appView);
- TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
- if (dynamicUpperBoundType.strictlyLessThan(fieldType, appView)) {
- if (maybeNull && dynamicUpperBoundType.isDefinitelyNotNull()) {
- assert dynamicUpperBoundType.isReferenceType();
- dynamicUpperBoundType = dynamicUpperBoundType.asReferenceType().asMaybeNull();
+ // Dynamic type.
+ if (field.getType().isReferenceType()) {
+ DynamicTypeWithUpperBound staticType = field.getType().toDynamicType(appView);
+ DynamicTypeWithUpperBound dynamicType = value.getDynamicType(appView);
+ if (dynamicType.strictlyLessThan(staticType, appView)) {
+ if (maybeNull && dynamicType.getNullability().isDefinitelyNotNull()) {
+ assert dynamicType.getDynamicUpperBoundType().isReferenceType();
+ dynamicType = dynamicType.withNullability(Nullability.maybeNull());
+ }
+ feedback.markFieldHasDynamicType(field, dynamicType);
}
- feedback.markFieldHasDynamicUpperBoundType(field, dynamicUpperBoundType);
- }
-
- // Dynamic lower bound type.
- ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
- if (dynamicLowerBoundType != null) {
- assert dynamicLowerBoundType.lessThanOrEqual(dynamicUpperBoundType, appView);
- if (maybeNull && dynamicLowerBoundType.isDefinitelyNotNull()) {
- dynamicLowerBoundType = dynamicLowerBoundType.asMaybeNull().asClassType();
- }
- feedback.markFieldHasDynamicLowerBoundType(field, dynamicLowerBoundType);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
index 1a32390..1b9bb1e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
@@ -6,9 +6,11 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Objects;
+import java.util.Set;
/**
* Represents the runtime type of a reference value. This type may be more precise than the value's
@@ -17,20 +19,9 @@
* <p>If a lower bound is known on the runtime type (e.g., {@code new A()}), then {@link
* DynamicTypeWithLowerBound} is used.
*/
-public class DynamicType {
+public abstract class DynamicType {
- private static final DynamicType BOTTOM = new DynamicType(TypeElement.getBottom());
- private static final DynamicType NULL_TYPE = new DynamicType(TypeElement.getNull());
- private static final DynamicType UNKNOWN = new DynamicType(TypeElement.getTop());
-
- private final TypeElement dynamicUpperBoundType;
-
- DynamicType(TypeElement dynamicUpperBoundType) {
- assert dynamicUpperBoundType != null;
- this.dynamicUpperBoundType = dynamicUpperBoundType;
- }
-
- public static DynamicType create(
+ public static DynamicTypeWithUpperBound create(
AppView<AppInfoWithLiveness> appView, TypeElement dynamicUpperBoundType) {
ClassTypeElement dynamicLowerBoundType = null;
if (dynamicUpperBoundType.isClassType()) {
@@ -44,7 +35,7 @@
return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
}
- public static DynamicType create(
+ public static DynamicTypeWithUpperBound create(
AppView<AppInfoWithLiveness> appView,
TypeElement dynamicUpperBoundType,
ClassTypeElement dynamicLowerBoundType) {
@@ -67,14 +58,15 @@
appView, dynamicUpperBoundType.asClassType(), dynamicLowerBoundType);
}
assert verifyNotEffectivelyFinalClassType(appView, dynamicUpperBoundType);
- return new DynamicType(dynamicUpperBoundType);
+ return new DynamicTypeWithUpperBound(dynamicUpperBoundType);
}
- public static DynamicType createExact(ClassTypeElement exactDynamicType) {
+ public static ExactDynamicType createExact(ClassTypeElement exactDynamicType) {
return new ExactDynamicType(exactDynamicType);
}
- public static DynamicType create(AppView<AppInfoWithLiveness> appView, Value value) {
+ public static DynamicTypeWithUpperBound create(
+ AppView<AppInfoWithLiveness> appView, Value value) {
assert value.getType().isReferenceType();
TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
ClassTypeElement dynamicLowerBoundType =
@@ -83,22 +75,46 @@
return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
}
- public static DynamicType bottom() {
- return BOTTOM;
+ public static DynamicTypeWithUpperBound bottom() {
+ return DynamicTypeWithUpperBound.BOTTOM;
}
- public static DynamicType definitelyNull() {
- return NULL_TYPE;
+ public static DynamicTypeWithUpperBound definitelyNull() {
+ return DynamicTypeWithUpperBound.NULL_TYPE;
}
- public static DynamicType unknown() {
- return UNKNOWN;
+ public static NotNullDynamicType definitelyNotNull() {
+ return NotNullDynamicType.get();
}
- public TypeElement getDynamicUpperBoundType() {
- return dynamicUpperBoundType;
+ public static DynamicTypeWithUpperBound unknown() {
+ return DynamicTypeWithUpperBound.UNKNOWN;
}
+ public static DynamicType join(
+ AppView<AppInfoWithLiveness> appView, Iterable<DynamicType> dynamicTypes) {
+ DynamicType result = bottom();
+ for (DynamicType dynamicType : dynamicTypes) {
+ result = result.join(appView, dynamicType);
+ }
+ return result;
+ }
+
+ public boolean hasDynamicUpperBoundType() {
+ return false;
+ }
+
+ /**
+ * Returns the dynamic upper bound type if this is an instance of {@link
+ * DynamicTypeWithUpperBound}.
+ *
+ * <p>The {@link NotNullDynamicType} does not have an upper bound type. This therefore takes the
+ * static type corresponding to the dynamic type as an argument, and returns the given static type
+ * with non null information attached to it when this dynamic type is the {@link
+ * NotNullDynamicType}.
+ */
+ public abstract TypeElement getDynamicUpperBoundType(TypeElement staticType);
+
public boolean hasDynamicLowerBoundType() {
return false;
}
@@ -107,20 +123,36 @@
return null;
}
- public Nullability getNullability() {
- return getDynamicUpperBoundType().nullability();
- }
+ public abstract ClassTypeElement getExactClassType();
+
+ public abstract Nullability getNullability();
public boolean isBottom() {
- return getDynamicUpperBoundType().isBottom();
+ return false;
+ }
+
+ public boolean isDynamicTypeWithUpperBound() {
+ return false;
+ }
+
+ public DynamicTypeWithUpperBound asDynamicTypeWithUpperBound() {
+ return null;
+ }
+
+ public boolean isExactClassType() {
+ return getExactClassType() != null;
}
public boolean isNullType() {
- return getDynamicUpperBoundType().isNullType();
+ return false;
+ }
+
+ public boolean isNotNullType() {
+ return false;
}
public boolean isUnknown() {
- return getDynamicUpperBoundType().isTop();
+ return false;
}
public DynamicType join(AppView<AppInfoWithLiveness> appView, DynamicType dynamicType) {
@@ -133,57 +165,27 @@
if (isUnknown() || dynamicType.isUnknown()) {
return unknown();
}
- TypeElement upperBoundType =
- getDynamicUpperBoundType().join(dynamicType.getDynamicUpperBoundType(), appView);
- ClassTypeElement lowerBoundType = meetDynamicLowerBound(appView, dynamicType);
- if (upperBoundType.equals(getDynamicUpperBoundType())
- && Objects.equals(lowerBoundType, getDynamicLowerBoundType())) {
- return this;
+ if (isNotNullType() || dynamicType.isNotNullType()) {
+ if (getNullability().isNullable() || dynamicType.getNullability().isNullable()) {
+ return unknown();
+ }
+ return definitelyNotNull();
}
- return create(appView, upperBoundType, lowerBoundType);
+ assert isDynamicTypeWithUpperBound();
+ assert dynamicType.isDynamicTypeWithUpperBound();
+ return asDynamicTypeWithUpperBound().join(appView, dynamicType.asDynamicTypeWithUpperBound());
}
- private ClassTypeElement meetDynamicLowerBound(
- AppView<AppInfoWithLiveness> appView, DynamicType dynamicType) {
- if (isNullType()) {
- if (dynamicType.hasDynamicLowerBoundType()) {
- return dynamicType.getDynamicLowerBoundType().joinNullability(Nullability.definitelyNull());
- }
- return null;
- }
- if (dynamicType.isNullType()) {
- if (hasDynamicLowerBoundType()) {
- return getDynamicLowerBoundType().joinNullability(Nullability.definitelyNull());
- }
- return null;
- }
- if (!hasDynamicLowerBoundType() || !dynamicType.hasDynamicLowerBoundType()) {
- return null;
- }
- ClassTypeElement lowerBoundType = getDynamicLowerBoundType();
- ClassTypeElement otherLowerBoundType = dynamicType.getDynamicLowerBoundType();
- if (lowerBoundType.lessThanOrEqualUpToNullability(otherLowerBoundType, appView)) {
- return lowerBoundType.joinNullability(otherLowerBoundType.nullability());
- }
- if (otherLowerBoundType.lessThanOrEqualUpToNullability(lowerBoundType, appView)) {
- return otherLowerBoundType.joinNullability(lowerBoundType.nullability());
- }
- return null;
- }
+ public abstract DynamicType rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, Set<DexType> prunedTypes);
+
+ public abstract DynamicType withNullability(Nullability nullability);
@Override
- public boolean equals(Object other) {
- if (other == null || getClass() != other.getClass()) {
- return false;
- }
- DynamicType dynamicType = (DynamicType) other;
- return dynamicUpperBoundType.equals(dynamicType.dynamicUpperBoundType);
- }
+ public abstract boolean equals(Object other);
@Override
- public int hashCode() {
- return dynamicUpperBoundType.hashCode();
- }
+ public abstract int hashCode();
private static boolean verifyNotEffectivelyFinalClassType(
AppView<AppInfoWithLiveness> appView, TypeElement type) {
@@ -194,17 +196,4 @@
}
return true;
}
-
- public DynamicType withNullability(Nullability nullability) {
- assert !hasDynamicLowerBoundType();
- if (!getDynamicUpperBoundType().isReferenceType()) {
- return this;
- }
- ReferenceTypeElement dynamicUpperBoundReferenceType =
- getDynamicUpperBoundType().asReferenceType();
- if (dynamicUpperBoundReferenceType.nullability() == nullability) {
- return this;
- }
- return new DynamicType(dynamicUpperBoundReferenceType.getOrCreateVariant(nullability));
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
index e4190bc..9448ebb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Objects;
-public class DynamicTypeWithLowerBound extends DynamicType {
+public class DynamicTypeWithLowerBound extends DynamicTypeWithUpperBound {
private final ClassTypeElement dynamicLowerBoundType;
@@ -16,6 +16,7 @@
ClassTypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
super(dynamicUpperBoundType);
assert !dynamicUpperBoundType.equals(dynamicLowerBoundType);
+ assert dynamicUpperBoundType.nullability() == dynamicLowerBoundType.nullability();
this.dynamicLowerBoundType = dynamicLowerBoundType;
}
@@ -64,7 +65,7 @@
}
@Override
- public DynamicType withNullability(Nullability nullability) {
+ public DynamicTypeWithLowerBound withNullability(Nullability nullability) {
if (getDynamicUpperBoundType().nullability() == nullability) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java
new file mode 100644
index 0000000..fa985fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithUpperBound.java
@@ -0,0 +1,283 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.type;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents the runtime type of a reference value. This type may be more precise than the value's
+ * statically declared type.
+ *
+ * <p>If a lower bound is known on the runtime type (e.g., {@code new A()}), then {@link
+ * DynamicTypeWithLowerBound} is used.
+ */
+public class DynamicTypeWithUpperBound extends DynamicType {
+
+ static final DynamicTypeWithUpperBound BOTTOM =
+ new DynamicTypeWithUpperBound(TypeElement.getBottom());
+ static final DynamicTypeWithUpperBound NULL_TYPE =
+ new DynamicTypeWithUpperBound(TypeElement.getNull());
+ static final DynamicTypeWithUpperBound UNKNOWN =
+ new DynamicTypeWithUpperBound(TypeElement.getTop());
+
+ private final TypeElement dynamicUpperBoundType;
+
+ DynamicTypeWithUpperBound(TypeElement dynamicUpperBoundType) {
+ assert dynamicUpperBoundType != null;
+ this.dynamicUpperBoundType = dynamicUpperBoundType;
+ }
+
+ public static DynamicTypeWithUpperBound create(
+ AppView<AppInfoWithLiveness> appView, TypeElement dynamicUpperBoundType) {
+ ClassTypeElement dynamicLowerBoundType =
+ isEffectivelyFinal(appView, dynamicUpperBoundType)
+ ? dynamicUpperBoundType.asClassType()
+ : null;
+ return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
+ }
+
+ public static DynamicTypeWithUpperBound create(
+ AppView<AppInfoWithLiveness> appView,
+ TypeElement dynamicUpperBoundType,
+ ClassTypeElement dynamicLowerBoundType) {
+ if (dynamicUpperBoundType.isBottom()) {
+ return bottom();
+ }
+ if (dynamicUpperBoundType.isNullType()) {
+ return definitelyNull();
+ }
+ if (dynamicUpperBoundType.isTop()) {
+ return unknown();
+ }
+ if (dynamicLowerBoundType != null) {
+ assert dynamicUpperBoundType.isClassType();
+ assert dynamicUpperBoundType.nullability() == dynamicLowerBoundType.nullability();
+ if (dynamicUpperBoundType.equals(dynamicLowerBoundType)) {
+ return createExact(dynamicLowerBoundType);
+ }
+ return DynamicTypeWithLowerBound.create(
+ appView, dynamicUpperBoundType.asClassType(), dynamicLowerBoundType);
+ }
+ assert verifyNotEffectivelyFinalClassType(appView, dynamicUpperBoundType);
+ return new DynamicTypeWithUpperBound(dynamicUpperBoundType);
+ }
+
+ public static DynamicTypeWithUpperBound create(
+ AppView<AppInfoWithLiveness> appView, Value value) {
+ assert value.getType().isReferenceType();
+ TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
+ ClassTypeElement dynamicLowerBoundType =
+ value.getDynamicLowerBoundType(
+ appView, dynamicUpperBoundType, dynamicUpperBoundType.nullability());
+ return create(appView, dynamicUpperBoundType, dynamicLowerBoundType);
+ }
+
+ private static boolean isEffectivelyFinal(AppView<?> appView, TypeElement type) {
+ if (type.isClassType()) {
+ ClassTypeElement classType = type.asClassType();
+ DexClass clazz = appView.definitionFor(classType.getClassType());
+ return clazz != null && clazz.isEffectivelyFinal(appView);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasDynamicUpperBoundType() {
+ return true;
+ }
+
+ @Override
+ public TypeElement getDynamicUpperBoundType(TypeElement staticType) {
+ return getDynamicUpperBoundType();
+ }
+
+ public TypeElement getDynamicUpperBoundType() {
+ return dynamicUpperBoundType;
+ }
+
+ @Override
+ public boolean hasDynamicLowerBoundType() {
+ return false;
+ }
+
+ @Override
+ public ClassTypeElement getDynamicLowerBoundType() {
+ return null;
+ }
+
+ @Override
+ public boolean isExactClassType() {
+ return getExactClassType() != null;
+ }
+
+ @Override
+ public ClassTypeElement getExactClassType() {
+ return hasDynamicLowerBoundType()
+ && getDynamicLowerBoundType().equalUpToNullability(getDynamicUpperBoundType())
+ ? getDynamicLowerBoundType()
+ : null;
+ }
+
+ @Override
+ public Nullability getNullability() {
+ return dynamicUpperBoundType.nullability();
+ }
+
+ @Override
+ public boolean isBottom() {
+ return dynamicUpperBoundType.isBottom();
+ }
+
+ @Override
+ public boolean isDynamicTypeWithUpperBound() {
+ return true;
+ }
+
+ @Override
+ public DynamicTypeWithUpperBound asDynamicTypeWithUpperBound() {
+ return this;
+ }
+
+ @Override
+ public boolean isNullType() {
+ return dynamicUpperBoundType.isNullType();
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return dynamicUpperBoundType.isTop();
+ }
+
+ public DynamicType join(
+ AppView<AppInfoWithLiveness> appView, DynamicTypeWithUpperBound dynamicType) {
+ TypeElement upperBoundType =
+ getDynamicUpperBoundType().join(dynamicType.getDynamicUpperBoundType(), appView);
+ ClassTypeElement lowerBoundType =
+ isEffectivelyFinal(appView, upperBoundType)
+ ? upperBoundType.asClassType()
+ : meetDynamicLowerBound(appView, dynamicType);
+ if (upperBoundType.equals(getDynamicUpperBoundType())
+ && Objects.equals(lowerBoundType, getDynamicLowerBoundType())) {
+ return this;
+ }
+ return create(appView, upperBoundType, lowerBoundType);
+ }
+
+ private ClassTypeElement meetDynamicLowerBound(
+ AppView<AppInfoWithLiveness> appView, DynamicType dynamicType) {
+ if (isNullType()) {
+ if (dynamicType.hasDynamicLowerBoundType()) {
+ return dynamicType.getDynamicLowerBoundType().joinNullability(Nullability.definitelyNull());
+ }
+ return null;
+ }
+ if (dynamicType.isNullType()) {
+ if (hasDynamicLowerBoundType()) {
+ return getDynamicLowerBoundType().joinNullability(Nullability.definitelyNull());
+ }
+ return null;
+ }
+ if (!hasDynamicLowerBoundType() || !dynamicType.hasDynamicLowerBoundType()) {
+ return null;
+ }
+ ClassTypeElement lowerBoundType = getDynamicLowerBoundType();
+ ClassTypeElement otherLowerBoundType = dynamicType.getDynamicLowerBoundType();
+ if (lowerBoundType.lessThanOrEqualUpToNullability(otherLowerBoundType, appView)) {
+ return lowerBoundType.joinNullability(otherLowerBoundType.nullability());
+ }
+ if (otherLowerBoundType.lessThanOrEqualUpToNullability(lowerBoundType, appView)) {
+ return otherLowerBoundType.joinNullability(lowerBoundType.nullability());
+ }
+ return null;
+ }
+
+ @Override
+ public DynamicType rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, Set<DexType> prunedTypes) {
+ if (isBottom() || isNullType() || isUnknown()) {
+ return this;
+ }
+ TypeElement rewrittenDynamicUpperBoundType =
+ dynamicUpperBoundType.rewrittenWithLens(appView, graphLens, prunedTypes);
+ ClassTypeElement rewrittenDynamicLowerBoundClassType = null;
+ if (hasDynamicLowerBoundType()) {
+ TypeElement rewrittenDynamicLowerBoundType =
+ getDynamicLowerBoundType().rewrittenWithLens(appView, graphLens, prunedTypes);
+ if (rewrittenDynamicLowerBoundType.isClassType()) {
+ rewrittenDynamicLowerBoundClassType = rewrittenDynamicLowerBoundType.asClassType();
+ }
+ }
+ return rewrittenDynamicLowerBoundClassType != null
+ ? create(appView, rewrittenDynamicUpperBoundType, rewrittenDynamicLowerBoundClassType)
+ : create(appView, rewrittenDynamicUpperBoundType);
+ }
+
+ public boolean strictlyLessThan(TypeElement type, AppView<AppInfoWithLiveness> appView) {
+ DynamicTypeWithUpperBound dynamicType = create(appView, type);
+ return strictlyLessThan(dynamicType, appView);
+ }
+
+ public boolean strictlyLessThan(DynamicTypeWithUpperBound dynamicType, AppView<?> appView) {
+ if (equals(dynamicType)) {
+ return false;
+ }
+ if (getDynamicUpperBoundType().equals(dynamicType.getDynamicUpperBoundType())) {
+ if (!dynamicType.hasDynamicLowerBoundType()) {
+ return hasDynamicLowerBoundType();
+ }
+ return hasDynamicLowerBoundType()
+ && getDynamicLowerBoundType()
+ .strictlyLessThan(dynamicType.getDynamicLowerBoundType(), appView);
+ }
+ return getDynamicUpperBoundType()
+ .strictlyLessThan(dynamicType.getDynamicUpperBoundType(), appView);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ DynamicTypeWithUpperBound dynamicType = (DynamicTypeWithUpperBound) other;
+ return dynamicUpperBoundType.equals(dynamicType.dynamicUpperBoundType);
+ }
+
+ @Override
+ public int hashCode() {
+ return dynamicUpperBoundType.hashCode();
+ }
+
+ private static boolean verifyNotEffectivelyFinalClassType(
+ AppView<AppInfoWithLiveness> appView, TypeElement type) {
+ if (type.isClassType()) {
+ ClassTypeElement classType = type.asClassType();
+ DexClass clazz = appView.definitionFor(classType.getClassType());
+ assert clazz == null || !clazz.isEffectivelyFinal(appView);
+ }
+ return true;
+ }
+
+ @Override
+ public DynamicTypeWithUpperBound withNullability(Nullability nullability) {
+ assert !hasDynamicLowerBoundType();
+ if (!getDynamicUpperBoundType().isReferenceType()) {
+ return this;
+ }
+ ReferenceTypeElement dynamicUpperBoundReferenceType =
+ getDynamicUpperBoundType().asReferenceType();
+ if (dynamicUpperBoundReferenceType.nullability() == nullability) {
+ return this;
+ }
+ return new DynamicTypeWithUpperBound(
+ dynamicUpperBoundReferenceType.getOrCreateVariant(nullability));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
index d66cfcc..1ab6c53 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
@@ -4,25 +4,60 @@
package com.android.tools.r8.ir.analysis.type;
-public class ExactDynamicType extends DynamicType {
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Set;
+
+public class ExactDynamicType extends DynamicTypeWithUpperBound {
ExactDynamicType(ClassTypeElement exactDynamicType) {
super(exactDynamicType);
}
@Override
+ public ClassTypeElement getDynamicUpperBoundType() {
+ return getExactClassType();
+ }
+
+ @Override
+ public ClassTypeElement getDynamicLowerBoundType() {
+ return getExactClassType();
+ }
+
+ @Override
+ public ClassTypeElement getExactClassType() {
+ return super.getDynamicUpperBoundType().asClassType();
+ }
+
+ @Override
public boolean hasDynamicLowerBoundType() {
return true;
}
@Override
- public ClassTypeElement getDynamicUpperBoundType() {
- return super.getDynamicUpperBoundType().asClassType();
+ public boolean isExactClassType() {
+ return true;
}
@Override
- public ClassTypeElement getDynamicLowerBoundType() {
- return getDynamicUpperBoundType();
+ public DynamicType rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, Set<DexType> prunedTypes) {
+ TypeElement rewrittenType =
+ getExactClassType().rewrittenWithLens(appView, graphLens, prunedTypes);
+ assert rewrittenType.isClassType() || rewrittenType.isPrimitiveType();
+ return rewrittenType.isClassType()
+ ? new ExactDynamicType(rewrittenType.asClassType())
+ : unknown();
+ }
+
+ @Override
+ public ExactDynamicType withNullability(Nullability nullability) {
+ if (getNullability() == nullability) {
+ return this;
+ }
+ return new ExactDynamicType(getExactClassType().getOrCreateVariant(nullability));
}
@Override
@@ -31,19 +66,11 @@
return false;
}
ExactDynamicType dynamicType = (ExactDynamicType) other;
- return getDynamicUpperBoundType().equals(dynamicType.getDynamicUpperBoundType());
+ return getExactClassType().equals(dynamicType.getExactClassType());
}
@Override
public int hashCode() {
- return getDynamicLowerBoundType().hashCode();
- }
-
- @Override
- public DynamicType withNullability(Nullability nullability) {
- if (getDynamicUpperBoundType().nullability() == nullability) {
- return this;
- }
- return new ExactDynamicType(getDynamicUpperBoundType().getOrCreateVariant(nullability));
+ return getExactClassType().hashCode();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java
new file mode 100644
index 0000000..37ed14a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.type;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Set;
+
+/**
+ * A dynamic type that encodes that a given value is guaranteed to be non null.
+ *
+ * <p>This dynamic type is a singleton and does not have an upper bound type. If this dynamic type
+ * is used in a context where there is a corresponding static type, then the dynamic upper bound
+ * type is the static type with non null information attached. See also {@link
+ * #getDynamicUpperBoundType(TypeElement)}.
+ */
+public class NotNullDynamicType extends DynamicType {
+
+ private static final NotNullDynamicType INSTANCE = new NotNullDynamicType();
+
+ private NotNullDynamicType() {}
+
+ public static NotNullDynamicType get() {
+ return INSTANCE;
+ }
+
+ @Override
+ public ReferenceTypeElement getDynamicUpperBoundType(TypeElement staticType) {
+ assert staticType.isReferenceType();
+ return staticType.asReferenceType().getOrCreateVariant(Nullability.definitelyNotNull());
+ }
+
+ @Override
+ public ClassTypeElement getExactClassType() {
+ return null;
+ }
+
+ @Override
+ public Nullability getNullability() {
+ return Nullability.definitelyNotNull();
+ }
+
+ @Override
+ public boolean isNotNullType() {
+ return true;
+ }
+
+ @Override
+ public NotNullDynamicType rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens graphLens, Set<DexType> prunedTypes) {
+ return this;
+ }
+
+ @Override
+ public DynamicType withNullability(Nullability nullability) {
+ assert !nullability.isBottom();
+ return nullability.isDefinitelyNotNull() ? this : unknown();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return this == other;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+}
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 854ea6d..850b669 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
@@ -180,6 +180,14 @@
return join(other, factory, type.isReferenceType(), false);
}
+ public AbstractValue joinPrimitive(AbstractValue other, AbstractValueFactory factory) {
+ return join(other, factory, false, false);
+ }
+
+ public AbstractValue joinReference(AbstractValue other, AbstractValueFactory factory) {
+ return join(other, factory, true, false);
+ }
+
// TODO(b/196321452): Clean this up, in particular, replace the "allow" parameters by a
// configuration object.
public AbstractValue join(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index b499b81..0e746d4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -143,7 +143,11 @@
return factory.createSingleNumberValue(enumDataMap.getUnboxedValue(field));
}
}
- return factory.createSingleFieldValue(
- lens.lookupField(field), getObjectState().rewrittenWithLens(appView, lens));
+ DexField rewrittenField = lens.lookupField(field);
+ ObjectState rewrittenObjectState = getObjectState().rewrittenWithLens(appView, lens);
+ if (rewrittenField != field || rewrittenObjectState != getObjectState()) {
+ return factory.createSingleFieldValue(rewrittenField, rewrittenObjectState);
+ }
+ return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index f25c73b..e0a6238 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -53,19 +53,12 @@
}
public static Assume createAssumeDynamicTypeInstruction(
- TypeElement dynamicUpperBoundType,
- ClassTypeElement dynamicLowerBoundType,
+ DynamicTypeWithUpperBound dynamicType,
Value dest,
Value src,
Instruction origin,
AppView<?> appView) {
- return new Assume(
- new DynamicTypeAssumption(dynamicUpperBoundType, dynamicLowerBoundType),
- null,
- dest,
- src,
- origin,
- appView);
+ return new Assume(new DynamicTypeAssumption(dynamicType), null, dest, src, origin, appView);
}
@Override
@@ -142,7 +135,7 @@
return false;
}
if (hasDynamicTypeAssumption()) {
- outType = dynamicTypeAssumption.getDynamicUpperBoundType();
+ outType = dynamicTypeAssumption.getDynamicType().getDynamicUpperBoundType();
}
if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
@@ -261,6 +254,8 @@
assert inType.isNullType() || outType.equals(inType.asReferenceType().asMeetWithNotNull())
: "At " + this + System.lineSeparator() + outType + " != " + inType;
} else {
+ assert hasDynamicTypeAssumption();
+ assert !src().isConstNumber();
assert outType.equals(inType)
: "At " + this + System.lineSeparator() + outType + " != " + inType;
}
@@ -279,13 +274,14 @@
builder.append("; not null");
}
if (hasDynamicTypeAssumption()) {
+ DynamicTypeWithUpperBound dynamicType = dynamicTypeAssumption.getDynamicType();
if (hasOutValue()) {
- if (!dynamicTypeAssumption.dynamicUpperBoundType.equalUpToNullability(outValue.getType())) {
- builder.append("; upper bound: ").append(dynamicTypeAssumption.dynamicUpperBoundType);
+ if (!dynamicType.getDynamicUpperBoundType().equalUpToNullability(outValue.getType())) {
+ builder.append("; upper bound: ").append(dynamicType.getDynamicUpperBoundType());
}
}
- if (dynamicTypeAssumption.dynamicLowerBoundType != null) {
- builder.append("; lower bound: ").append(dynamicTypeAssumption.dynamicLowerBoundType);
+ if (dynamicType.hasDynamicLowerBoundType()) {
+ builder.append("; lower bound: ").append(dynamicType.getDynamicLowerBoundType());
}
}
return builder.toString();
@@ -293,27 +289,23 @@
public static class DynamicTypeAssumption {
- private final TypeElement dynamicUpperBoundType;
- private final ClassTypeElement dynamicLowerBoundType;
+ private final DynamicTypeWithUpperBound dynamicType;
- public DynamicTypeAssumption(
- TypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
- this.dynamicUpperBoundType = dynamicUpperBoundType;
- this.dynamicLowerBoundType = dynamicLowerBoundType;
+ public DynamicTypeAssumption(DynamicTypeWithUpperBound dynamicType) {
+ assert dynamicType != null;
+ this.dynamicType = dynamicType;
}
- public TypeElement getDynamicUpperBoundType() {
- return dynamicUpperBoundType;
- }
-
- public ClassTypeElement getDynamicLowerBoundType() {
- return dynamicLowerBoundType;
+ public DynamicTypeWithUpperBound getDynamicType() {
+ return dynamicType;
}
public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
- assert !dynamicUpperBoundType.isBottom();
- assert !dynamicUpperBoundType.isTop();
- assert dynamicUpperBoundType.lessThanOrEqualUpToNullability(src.getType(), appView);
+ assert !dynamicType.isBottom();
+ assert !dynamicType.isUnknown();
+ assert dynamicType
+ .getDynamicUpperBoundType()
+ .lessThanOrEqualUpToNullability(src.getType(), appView);
return true;
}
@@ -326,13 +318,12 @@
return false;
}
DynamicTypeAssumption assumption = (DynamicTypeAssumption) other;
- return dynamicUpperBoundType == assumption.dynamicUpperBoundType
- && dynamicLowerBoundType == assumption.dynamicLowerBoundType;
+ return dynamicType.equals(assumption.dynamicType);
}
@Override
public int hashCode() {
- return Objects.hash(dynamicUpperBoundType, dynamicLowerBoundType);
+ return dynamicType.hashCode();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 38e86e1..36ec641 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -132,7 +132,9 @@
if (receiverLowerBoundType != null) {
DexType refinedReceiverType =
TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this);
- assert receiverLowerBoundType.getClassType() == refinedReceiverType
+ assert appViewWithLiveness
+ .appInfo()
+ .isSubtype(receiverLowerBoundType.getClassType(), refinedReceiverType)
|| appView.options().testing.allowTypeErrors
|| receiver.getDynamicUpperBoundType(appViewWithLiveness).isNullType()
|| receiverLowerBoundType.isBasedOnMissingClass(appViewWithLiveness)
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 27713d8..cc59793 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -1093,7 +1094,7 @@
return type;
}
- public DynamicType getDynamicType(AppView<AppInfoWithLiveness> appView) {
+ public DynamicTypeWithUpperBound getDynamicType(AppView<AppInfoWithLiveness> appView) {
return DynamicType.create(appView, this);
}
@@ -1124,7 +1125,12 @@
// If there is an alias of the receiver, which is defined by an Assume instruction that
// carries a dynamic type, then use the dynamic type as the refined receiver type.
lattice =
- aliasedValue.definition.asAssume().getDynamicTypeAssumption().getDynamicUpperBoundType();
+ aliasedValue
+ .definition
+ .asAssume()
+ .getDynamicTypeAssumption()
+ .getDynamicType()
+ .getDynamicUpperBoundType();
// For precision, verify that the dynamic type is at least as precise as the static type.
assert lattice.lessThanOrEqualUpToNullability(type, appView) : type + " < " + lattice;
@@ -1190,6 +1196,7 @@
.getDefinition()
.asAssume()
.getDynamicTypeAssumption()
+ .getDynamicType()
.getDynamicLowerBoundType();
if (aliasedValueType != null) {
aliasedValueType = aliasedValueType.meetNullability(getType().nullability());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
index c6070c5..b5e3b1e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
@@ -6,8 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -19,9 +18,7 @@
void markFieldAsPropagated(DexEncodedField field);
- void markFieldHasDynamicLowerBoundType(DexEncodedField field, ClassTypeElement type);
-
- void markFieldHasDynamicUpperBoundType(DexEncodedField field, TypeElement type);
+ void markFieldHasDynamicType(DexEncodedField field, DynamicType dynamicType);
void markFieldBitsRead(DexEncodedField field, int bitsRead);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 37ce381..047894d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -114,7 +114,6 @@
import com.google.common.base.Suppliers;
import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -819,20 +818,9 @@
// Assure that no more optimization feedback left after post processing.
assert feedback.noUpdatesLeft();
- assert checkLegacySyntheticsAreInBuilder(appView, builder);
return builder.build();
}
- private boolean checkLegacySyntheticsAreInBuilder(
- AppView<AppInfoWithLiveness> appView, Builder<?> builder) {
- Collection<DexProgramClass> inAppInfo =
- appView.appInfo().getSyntheticItems().getLegacyPendingClasses();
- Collection<DexProgramClass> inBuilder = builder.getSynthesizedClasses();
- assert inAppInfo.containsAll(inBuilder);
- assert inBuilder.containsAll(inAppInfo);
- return true;
- }
-
private void waveStart(ProgramMethodSet wave) {
onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index dafd559..5794f8d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -479,7 +479,8 @@
InstancePut instancePut = current.asInstancePut();
DexField field = instancePut.getField();
FieldLookupResult lookup = graphLens.lookupFieldResult(field);
- insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, instancePut, lookup);
+ iterator =
+ insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, instancePut, lookup);
DexField rewrittenField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
@@ -550,7 +551,8 @@
StaticPut staticPut = current.asStaticPut();
DexField field = staticPut.getField();
FieldLookupResult lookup = graphLens.lookupFieldResult(field);
- insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, staticPut, lookup);
+ iterator =
+ insertCastForFieldAssignmentIfNeeded(code, blocks, iterator, staticPut, lookup);
DexField actualField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
@@ -752,7 +754,7 @@
assert code.hasNoMergedClasses(appView);
}
- private void insertCastForFieldAssignmentIfNeeded(
+ private InstructionListIterator insertCastForFieldAssignmentIfNeeded(
IRCode code,
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
@@ -774,6 +776,7 @@
Instruction next = iterator.next();
assert next == fieldPut;
}
+ return iterator;
}
private DexField rewriteFieldReference(FieldLookupResult lookup, ProgramMethod context) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 6cf694a..8b229f9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -9,8 +9,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
@@ -38,10 +37,12 @@
void methodReturnsAbstractValue(
DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue);
- void methodReturnsObjectWithUpperBoundType(
- DexEncodedMethod method, AppView<?> appView, TypeElement type);
+ default void setDynamicReturnType(
+ ProgramMethod method, AppView<?> appView, DynamicType dynamicType) {
+ setDynamicReturnType(method.getDefinition(), appView, dynamicType);
+ }
- void methodReturnsObjectWithLowerBoundType(DexEncodedMethod method, ClassTypeElement type);
+ void setDynamicReturnType(DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType);
void methodMayNotHaveSideEffects(DexEncodedMethod method);
@@ -92,9 +93,7 @@
void unsetClassInlinerMethodConstraint(ProgramMethod method);
- void unsetDynamicLowerBoundReturnType(ProgramMethod method);
-
- void unsetDynamicUpperBoundReturnType(ProgramMethod method);
+ void unsetDynamicReturnType(ProgramMethod method);
void unsetEnumUnboxerMethodClassification(ProgramMethod method);
@@ -135,8 +134,7 @@
unsetCheckNullReceiverBeforeAnySideEffect(method);
unsetClassInitializerMayBePostponed(method);
unsetClassInlinerMethodConstraint(method);
- unsetDynamicLowerBoundReturnType(method);
- unsetDynamicUpperBoundReturnType(method);
+ unsetDynamicReturnType(method);
unsetEnumUnboxerMethodClassification(method);
unsetForceInline(method);
unsetInitializedClassesOnNormalExit(method);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index efc31d3..30ea9f8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -12,7 +12,9 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
@@ -217,10 +219,9 @@
return false;
}
- TypeElement dynamicUpperBoundType =
- TypeElement.fromDexType(invoke.getInvokedMethod().holder, definitelyNotNull(), appView);
- assumedValuesBuilder.addAssumedValueKnownToDominateAllUsers(
- invoke, outValue, dynamicUpperBoundType, null);
+ DynamicTypeWithUpperBound dynamicType =
+ invoke.getInvokedMethod().getHolderType().toDynamicType(appView, definitelyNotNull());
+ assumedValuesBuilder.addAssumedValueKnownToDominateAllUsers(invoke, outValue, dynamicType);
return true;
}
@@ -257,10 +258,7 @@
if (invoke.hasUsedOutValue()) {
needsAssumeInstruction =
computeAssumedValuesForOutValue(
- invoke,
- optimizationInfo.getDynamicUpperBoundTypeOrElse(invoke.getOutType()),
- optimizationInfo.getDynamicLowerBoundType(),
- assumedValuesBuilder);
+ invoke, optimizationInfo.getDynamicType(), assumedValuesBuilder);
}
// Case (3), parameters that are not null after the invocation.
@@ -306,35 +304,38 @@
FieldOptimizationInfo optimizationInfo = field.getDefinition().getOptimizationInfo();
return computeAssumedValuesForOutValue(
- fieldGet,
- optimizationInfo.getDynamicUpperBoundTypeOrElse(fieldGet.getOutType()),
- optimizationInfo.getDynamicLowerBoundType(),
- assumedValuesBuilder);
+ fieldGet, optimizationInfo.getDynamicType(), assumedValuesBuilder);
}
private boolean computeAssumedValuesForOutValue(
Instruction instruction,
- TypeElement dynamicUpperBoundType,
- ClassTypeElement dynamicLowerBoundType,
+ DynamicType dynamicType,
AssumedValues.Builder assumedValuesBuilder) {
Value outValue = instruction.outValue();
-
// Do not insert dynamic type information if it does not refine the static type.
- boolean isRedundant =
- !dynamicUpperBoundType.strictlyLessThan(outValue.getType(), appView)
- && dynamicLowerBoundType == null;
- if (isRedundant) {
+ if (dynamicType.isUnknown()) {
return false;
}
- // Do not insert dynamic type information if the dynamic type only refines the nullability.
- if (dynamicUpperBoundType.equalUpToNullability(outValue.getType())
- && dynamicLowerBoundType == null) {
- assert dynamicUpperBoundType.isDefinitelyNotNull();
+ // Insert an assume-not-null instruction if the dynamic type only refines the nullability.
+ if (dynamicType.isNotNullType()) {
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(instruction, outValue);
+ return true;
+ }
+
+ DynamicTypeWithUpperBound dynamicTypeWithUpperBound = dynamicType.asDynamicTypeWithUpperBound();
+ DynamicTypeWithUpperBound staticType = DynamicType.create(appView, outValue.getType());
+ if (!dynamicTypeWithUpperBound.strictlyLessThan(staticType, appView)) {
+ return false;
+ }
+
+ if (!dynamicTypeWithUpperBound.getNullability().isMaybeNull()
+ && dynamicTypeWithUpperBound.withNullability(Nullability.maybeNull()).equals(staticType)) {
+ assert dynamicTypeWithUpperBound.getNullability().isDefinitelyNotNull();
assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(instruction, outValue);
} else {
assumedValuesBuilder.addAssumedValueKnownToDominateAllUsers(
- instruction, outValue, dynamicUpperBoundType, dynamicLowerBoundType);
+ instruction, outValue, dynamicTypeWithUpperBound);
}
return true;
}
@@ -707,21 +708,17 @@
this.dynamicTypeAssumption = dynamicTypeAssumption;
}
- void setDynamicTypeAssumption(
- TypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
- dynamicTypeAssumption =
- new DynamicTypeAssumption(dynamicUpperBoundType, dynamicLowerBoundType);
- if (dynamicUpperBoundType.isDefinitelyNotNull()) {
- setNotNull();
- }
- if (dynamicLowerBoundType != null && dynamicLowerBoundType.isDefinitelyNotNull()) {
+ void setDynamicTypeAssumption(DynamicTypeWithUpperBound dynamicType) {
+ assert dynamicType != null;
+ dynamicTypeAssumption = new DynamicTypeAssumption(dynamicType);
+ if (dynamicType.getDynamicUpperBoundType().isDefinitelyNotNull()) {
setNotNull();
}
}
boolean isNull() {
return dynamicTypeAssumption != null
- && dynamicTypeAssumption.getDynamicUpperBoundType().isDefinitelyNull();
+ && dynamicTypeAssumption.getDynamicType().getNullability().isDefinitelyNull();
}
boolean isNonNull() {
@@ -874,17 +871,12 @@
}
void addAssumedValueKnownToDominateAllUsers(
- Instruction instruction,
- Value assumedValue,
- TypeElement dynamicUpperBoundType,
- ClassTypeElement dynamicLowerBoundType) {
+ Instruction instruction, Value assumedValue, DynamicTypeWithUpperBound dynamicType) {
updateAssumedValueInfo(
instruction,
assumedValue,
AssumedDominance.everything(),
- assumedValueInfo ->
- assumedValueInfo.setDynamicTypeAssumption(
- dynamicUpperBoundType, dynamicLowerBoundType));
+ assumedValueInfo -> assumedValueInfo.setDynamicTypeAssumption(dynamicType));
}
void addNonNullValueKnownToDominateAllUsers(Instruction instruction, Value nonNullValue) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
index 85ac5b8..70057fc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
@@ -33,14 +33,19 @@
private final AppView<?> appView;
private final IRCode code;
- private final Set<Value> affectedValues = Sets.newIdentityHashSet();
+ private final Set<Value> affectedValues;
private final Set<Assume> assumeInstructionsToRemove = Sets.newIdentityHashSet();
private boolean mayHaveIntroducedTrivialPhi = false;
public AssumeRemover(AppView<?> appView, IRCode code) {
+ this(appView, code, Sets.newIdentityHashSet());
+ }
+
+ public AssumeRemover(AppView<?> appView, IRCode code, Set<Value> affectedValues) {
this.appView = appView;
this.code = code;
+ this.affectedValues = affectedValues;
}
public Set<Value> getAffectedValues() {
@@ -57,7 +62,7 @@
Assume assumeInstruction = user.asAssume();
assumeInstruction.unsetDynamicTypeAssumption();
if (!assumeInstruction.hasNonNullAssumption()) {
- assumeInstruction.unsetDynamicTypeAssumption();
+ markForRemoval(assumeInstruction);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index dae6f04..a10c4d7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -30,6 +30,8 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
import com.android.tools.r8.ir.analysis.equivalence.BasicBlockBehavioralSubsumption;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -1655,16 +1657,13 @@
value.isDefinedByInstructionSatisfying(
Instruction::isAssumeWithDynamicTypeAssumption));
if (aliasedValue != null) {
- TypeElement dynamicType =
- aliasedValue
- .definition
- .asAssume()
- .getDynamicTypeAssumption()
- .getDynamicUpperBoundType();
- if (dynamicType.isDefinitelyNull()) {
+ DynamicTypeWithUpperBound dynamicType =
+ aliasedValue.getDefinition().asAssume().getDynamicTypeAssumption().getDynamicType();
+ Nullability nullability = dynamicType.getNullability();
+ if (nullability.isDefinitelyNull()) {
result = InstanceOfResult.FALSE;
- } else if (dynamicType.lessThanOrEqual(instanceOfType, appView)
- && (!inType.isNullable() || !dynamicType.isNullable())) {
+ } else if (dynamicType.getDynamicUpperBoundType().lessThanOrEqual(instanceOfType, appView)
+ && (!inType.isNullable() || !nullability.isNullable())) {
result = InstanceOfResult.TRUE;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 8007b17..d612c5f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -161,7 +161,8 @@
SyntheticItems syntheticItems = appView.getSyntheticItems();
ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase(singleTarget, method, syntheticItems)) {
+ if (!classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(
+ singleTarget, method, syntheticItems)) {
// Still allow inlining if we inline from the base into a feature.
if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder(), syntheticItems)) {
whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index 9b738b6..10988dc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -5,9 +5,8 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.JumpInstruction;
@@ -29,41 +28,16 @@
*
* <p>If the method has no normal exits, then null is returned.
*/
- public TypeElement computeDynamicReturnType(DexEncodedMethod method, IRCode code) {
- assert method.getReference().proto.returnType.isReferenceType();
- List<TypeElement> returnedTypes = new ArrayList<>();
- for (BasicBlock block : code.blocks) {
+ public DynamicType computeDynamicReturnType(ProgramMethod method, IRCode code) {
+ assert method.getReturnType().isReferenceType();
+ List<DynamicType> returnedTypes = new ArrayList<>();
+ for (BasicBlock block : code.getBlocks()) {
JumpInstruction exitInstruction = block.exit();
if (exitInstruction.isReturn()) {
Value returnValue = exitInstruction.asReturn().returnValue();
- returnedTypes.add(returnValue.getDynamicUpperBoundType(appView));
+ returnedTypes.add(returnValue.getDynamicType(appView));
}
}
- return returnedTypes.isEmpty() ? null : TypeElement.join(returnedTypes, appView);
- }
-
- public ClassTypeElement computeDynamicLowerBoundType(DexEncodedMethod method, IRCode code) {
- assert method.getReference().proto.returnType.isReferenceType();
- ClassTypeElement result = null;
- for (BasicBlock block : code.blocks) {
- JumpInstruction exitInstruction = block.exit();
- if (exitInstruction.isReturn()) {
- Value returnValue = exitInstruction.asReturn().returnValue();
- ClassTypeElement dynamicLowerBoundType = returnValue.getDynamicLowerBoundType(appView);
- if (dynamicLowerBoundType == null) {
- return null;
- }
- if (result == null) {
- result = dynamicLowerBoundType;
- } else if (dynamicLowerBoundType.equalUpToNullability(result)) {
- if (dynamicLowerBoundType.nullability() != result.nullability()) {
- result = dynamicLowerBoundType.join(result, appView).asClassType();
- }
- } else {
- return null;
- }
- }
- }
- return result;
+ return DynamicType.join(appView, returnedTypes);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 510331b..b4a067e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1108,8 +1108,7 @@
}
}
assert inlineeStack.isEmpty();
- assumeRemover.removeMarkedInstructions(blocksToRemove);
- assumeRemover.finish();
+ assumeRemover.removeMarkedInstructions(blocksToRemove).finish();
classInitializationAnalysis.finish();
code.removeBlocks(blocksToRemove);
code.removeAllDeadAndTrivialPhis();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 206c455..755f334 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -411,7 +411,7 @@
// Verify that the optimization info is consistent with the static value.
assert definition.getOptimizationInfo().getAbstractValue().isUnknown()
|| !definition.hasExplicitStaticValue()
- || abstractValue == definition.getOptimizationInfo().getAbstractValue();
+ || abstractValue.equals(definition.getOptimizationInfo().getAbstractValue());
} else {
// This is guaranteed to read the default value of the field.
abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index afb7003..b3f74c5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -189,6 +188,7 @@
}
}
+ AssumeRemover assumeRemover = new AssumeRemover(appView, code, affectedValues);
for (BasicBlock head : code.topologicallySortedBlocks()) {
if (head.hasUniquePredecessor() && head.getUniquePredecessor().hasUniqueNormalSuccessor()) {
// Already visited.
@@ -211,14 +211,16 @@
}
if (instruction.isInstanceGet()) {
- handleInstanceGet(it, instruction.asInstanceGet(), field);
+ handleInstanceGet(it, instruction.asInstanceGet(), field, assumeRemover);
} else if (instruction.isInstancePut()) {
handleInstancePut(instruction.asInstancePut(), field);
} else if (instruction.isStaticGet()) {
- handleStaticGet(it, instruction.asStaticGet(), field);
+ handleStaticGet(it, instruction.asStaticGet(), field, assumeRemover);
} else if (instruction.isStaticPut()) {
handleStaticPut(instruction.asStaticPut(), field);
}
+ } else if (instruction.isAssume()) {
+ assumeRemover.removeIfMarked(instruction.asAssume(), it);
} else if (instruction.isInitClass()) {
handleInitClass(it, instruction.asInitClass());
} else if (instruction.isMonitor()) {
@@ -288,9 +290,7 @@
activeStates.recordActiveStateOnBlockExit(end, activeState);
}
processInstructionsToRemove();
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ assumeRemover.removeMarkedInstructions().finish();
assert code.isConsistentSSA();
}
@@ -398,7 +398,10 @@
}
private void handleInstanceGet(
- InstructionListIterator it, InstanceGet instanceGet, DexClassAndField field) {
+ InstructionListIterator it,
+ InstanceGet instanceGet,
+ DexClassAndField field,
+ AssumeRemover assumeRemover) {
if (instanceGet.outValue().hasLocalInfo()) {
clearMostRecentInstanceFieldWrite(instanceGet, field);
return;
@@ -408,6 +411,7 @@
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
if (replacement != null) {
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(instanceGet.outValue());
replacement.eliminateRedundantRead(it, instanceGet);
return;
}
@@ -464,7 +468,10 @@
}
private void handleStaticGet(
- InstructionListIterator instructionIterator, StaticGet staticGet, DexClassAndField field) {
+ InstructionListIterator instructionIterator,
+ StaticGet staticGet,
+ DexClassAndField field,
+ AssumeRemover assumeRemover) {
if (staticGet.outValue().hasLocalInfo()) {
killNonFinalActiveFields(staticGet);
clearMostRecentStaticFieldWrite(staticGet, field);
@@ -473,6 +480,7 @@
FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
if (replacement != null) {
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(staticGet.outValue());
replacement.eliminateRedundantRead(instructionIterator, staticGet);
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index c3c2307..442cb96 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -14,12 +14,12 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.AssumeRemover;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.InliningOracle;
@@ -209,8 +209,10 @@
// Inline the class instance.
Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AssumeRemover assumeRemover = new AssumeRemover(appView, code, affectedValues);
try {
- anyInlinedMethods |= processor.processInlining(code, affectedValues, inliningIRProvider);
+ anyInlinedMethods |=
+ processor.processInlining(code, affectedValues, assumeRemover, inliningIRProvider);
} catch (IllegalClassInlinerStateException e) {
// We introduced a user that we cannot handle in the class inliner as a result of force
// inlining. Abort gracefully from class inlining without removing the instance.
@@ -224,10 +226,9 @@
}
// Restore normality.
+ assumeRemover.removeMarkedInstructions();
code.removeAllDeadAndTrivialPhis(affectedValues);
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ assumeRemover.finish();
assert code.isConsistentSSA();
rootsIterator.remove();
repeat = true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 82322c7..aede4f7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -48,6 +49,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.AssumeRemover;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -178,13 +180,12 @@
}
DexEncodedField field = fieldResolutionResult.getResolvedField();
FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
- ClassTypeElement dynamicLowerBoundType = optimizationInfo.getDynamicLowerBoundType();
- if (dynamicLowerBoundType == null
- || !dynamicLowerBoundType.equals(optimizationInfo.getDynamicUpperBoundType())) {
+ DynamicType dynamicType = optimizationInfo.getDynamicType();
+ if (!dynamicType.isExactClassType()) {
return EligibilityStatus.NOT_ELIGIBLE;
}
eligibleClass =
- asProgramClassOrNull(appView.definitionFor(dynamicLowerBoundType.getClassType()));
+ asProgramClassOrNull(appView.definitionFor(dynamicType.getExactClassType().getClassType()));
if (eligibleClass == null) {
return EligibilityStatus.NOT_ELIGIBLE;
}
@@ -376,6 +377,7 @@
boolean processInlining(
IRCode code,
Set<Value> affectedValues,
+ AssumeRemover assumeRemover,
InliningIRProvider inliningIRProvider)
throws IllegalClassInlinerStateException {
// Verify that `eligibleInstance` is not aliased.
@@ -386,7 +388,7 @@
rebindIndirectEligibleInstanceUsersFromPhis();
removeMiscUsages(code, affectedValues);
- removeFieldReads(code);
+ removeFieldReads(code, assumeRemover);
removeFieldWrites();
removeInstruction(root);
return anyInlinedMethods;
@@ -666,24 +668,26 @@
}
// Replace field reads with appropriate values, insert phis when needed.
- private void removeFieldReads(IRCode code) {
+ private void removeFieldReads(IRCode code, AssumeRemover assumeRemover) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
if (root.isNewInstance()) {
- removeFieldReadsFromNewInstance(code, affectedValues);
+ removeFieldReadsFromNewInstance(code, affectedValues, assumeRemover);
} else {
assert root.isStaticGet();
- removeFieldReadsFromStaticGet(code, affectedValues);
+ removeFieldReadsFromStaticGet(code, affectedValues, assumeRemover);
}
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
}
- private void removeFieldReadsFromNewInstance(IRCode code, Set<Value> affectedValues) {
+ private void removeFieldReadsFromNewInstance(
+ IRCode code, Set<Value> affectedValues, AssumeRemover assumeRemover) {
TreeSet<InstanceGet> uniqueInstanceGetUsersWithDeterministicOrder =
new TreeSet<>(Comparator.comparingInt(x -> x.outValue().getNumber()));
for (Instruction user : eligibleInstance.uniqueUsers()) {
if (user.isInstanceGet()) {
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(user.outValue());
if (user.hasUsedOutValue()) {
uniqueInstanceGetUsersWithDeterministicOrder.add(user.asInstanceGet());
} else {
@@ -738,7 +742,8 @@
removeInstruction(fieldRead);
}
- private void removeFieldReadsFromStaticGet(IRCode code, Set<Value> affectedValues) {
+ private void removeFieldReadsFromStaticGet(
+ IRCode code, Set<Value> affectedValues, AssumeRemover assumeRemover) {
Set<BasicBlock> seen = Sets.newIdentityHashSet();
Set<Instruction> users = eligibleInstance.uniqueUsers();
for (Instruction user : users) {
@@ -759,6 +764,7 @@
}
if (instruction.isInstanceGet()) {
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(instruction.outValue());
if (instruction.hasUsedOutValue()) {
replaceFieldReadFromStaticGet(
code, instructionIterator, user.asInstanceGet(), affectedValues);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index ff79200..95a4b63 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -140,7 +140,7 @@
// Since the value is a single field value, the type should be exact.
assert abstractValue.isSingleFieldValue();
- ClassTypeElement enumFieldType = optimizationInfo.getExactClassType(appView);
+ ClassTypeElement enumFieldType = optimizationInfo.getDynamicType().getExactClassType();
if (enumFieldType == null) {
assert false : "Expected to have an exact dynamic type for enum instance";
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index 3a9c9c0..6356bbf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -3,24 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.info;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-// A flat lattice structure:
-// BOTTOM, TOP, and a lattice element that holds accumulated argument info.
+// A flat lattice structure: TOP and a lattice element that holds accumulated argument info.
public abstract class CallSiteOptimizationInfo {
public static TopCallSiteOptimizationInfo top() {
return TopCallSiteOptimizationInfo.getInstance();
}
- public boolean isTop() {
- return false;
- }
-
public boolean isConcreteCallSiteOptimizationInfo() {
return false;
}
@@ -29,33 +22,11 @@
return null;
}
- public CallSiteOptimizationInfo join(
- CallSiteOptimizationInfo other, AppView<?> appView, DexEncodedMethod method) {
- if (other.isTop()) {
- return other;
- }
- if (isTop()) {
- return this;
- }
- assert isConcreteCallSiteOptimizationInfo() && other.isConcreteCallSiteOptimizationInfo();
- return asConcreteCallSiteOptimizationInfo()
- .join(other.asConcreteCallSiteOptimizationInfo(), appView, method);
- }
-
- public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod method) {
- return false;
- }
-
-
// The index exactly matches with in values of invocation, i.e., even including receiver.
- public TypeElement getDynamicUpperBoundType(int argIndex) {
+ public DynamicType getDynamicType(int argIndex) {
return null;
}
- // TODO(b/139246447): dynamic lower bound type?
-
- // TODO(b/69963623): we need to re-run unused argument removal?
-
// The index exactly matches with in values of invocation, i.e., even including receiver.
public AbstractValue getAbstractArgumentValue(int argIndex) {
return UnknownValue.getInstance();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index 230b15b..2ee5a6e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -3,31 +3,25 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.info;
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.utils.MapUtils.canonicalizeEmptyMap;
import static java.util.Objects.requireNonNull;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import java.util.List;
// Accumulated optimization info from call sites.
public class ConcreteCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
@@ -35,7 +29,7 @@
// inValues() size == DexMethod.arity + (isStatic ? 0 : 1) // receiver
// That is, this information takes into account the receiver as well.
private final int size;
- private final Int2ReferenceMap<TypeElement> dynamicUpperBoundTypes;
+ private final Int2ReferenceMap<DynamicType> dynamicTypes;
private final Int2ReferenceMap<AbstractValue> constants;
private ConcreteCallSiteOptimizationInfo(int size) {
@@ -44,29 +38,24 @@
private ConcreteCallSiteOptimizationInfo(
int size,
- Int2ReferenceMap<TypeElement> dynamicUpperBoundTypes,
+ Int2ReferenceMap<DynamicType> dynamicTypes,
Int2ReferenceMap<AbstractValue> constants) {
assert size > 0;
+ assert constants.values().stream().noneMatch(AbstractValue::isUnknown);
+ assert dynamicTypes.values().stream().noneMatch(DynamicType::isUnknown);
this.size = size;
- this.dynamicUpperBoundTypes = requireNonNull(dynamicUpperBoundTypes);
+ this.dynamicTypes = requireNonNull(dynamicTypes);
this.constants = requireNonNull(constants);
}
private static CallSiteOptimizationInfo create(
int size,
- Int2ReferenceMap<TypeElement> dynamicUpperBoundTypes,
+ Int2ReferenceMap<DynamicType> dynamicTypes,
Int2ReferenceMap<AbstractValue> constants) {
- return constants.isEmpty() && dynamicUpperBoundTypes.isEmpty()
+ return constants.isEmpty() && dynamicTypes.isEmpty()
? top()
: new ConcreteCallSiteOptimizationInfo(
- size, canonicalizeEmptyMap(dynamicUpperBoundTypes), canonicalizeEmptyMap(constants));
- }
-
- public CallSiteOptimizationInfo fixupAfterExtraNullParameters(int extraNullParameters) {
- return extraNullParameters > 0
- ? new ConcreteCallSiteOptimizationInfo(
- size + extraNullParameters, dynamicUpperBoundTypes, constants)
- : this;
+ size, canonicalizeEmptyMap(dynamicTypes), canonicalizeEmptyMap(constants));
}
public CallSiteOptimizationInfo fixupAfterParametersChanged(
@@ -79,7 +68,7 @@
if (parameterChanges.isEmpty()) {
if (prototypeChanges.hasExtraParameters()) {
return new ConcreteCallSiteOptimizationInfo(
- size + prototypeChanges.numberOfExtraParameters(), dynamicUpperBoundTypes, constants);
+ size + prototypeChanges.numberOfExtraParameters(), dynamicTypes, constants);
}
return this;
}
@@ -94,7 +83,7 @@
Int2ReferenceMap<AbstractValue> rewrittenConstants =
new Int2ReferenceArrayMap<>(newSizeAfterParameterRemoval);
- Int2ReferenceMap<TypeElement> rewrittenDynamicUpperBoundTypes =
+ Int2ReferenceMap<DynamicType> rewrittenDynamicTypes =
new Int2ReferenceArrayMap<>(newSizeAfterParameterRemoval);
for (int parameterIndex = 0, rewrittenParameterIndex = 0;
parameterIndex < size;
@@ -105,107 +94,23 @@
if (!abstractValue.isUnknown()) {
rewrittenConstants.put(rewrittenParameterIndex, abstractValue);
}
- TypeElement dynamicUpperBoundType = dynamicUpperBoundTypes.get(parameterIndex);
- if (dynamicUpperBoundType != null) {
- rewrittenDynamicUpperBoundTypes.put(rewrittenParameterIndex, dynamicUpperBoundType);
+ DynamicType dynamicType = dynamicTypes.get(parameterIndex);
+ if (dynamicType != null) {
+ rewrittenDynamicTypes.put(rewrittenParameterIndex, dynamicType);
}
rewrittenParameterIndex++;
}
}
return ConcreteCallSiteOptimizationInfo.create(
newSizeAfterParameterRemoval + prototypeChanges.numberOfExtraParameters(),
- rewrittenDynamicUpperBoundTypes,
+ rewrittenDynamicTypes,
rewrittenConstants);
}
- CallSiteOptimizationInfo join(
- ConcreteCallSiteOptimizationInfo other, AppView<?> appView, DexEncodedMethod method) {
- assert size == other.size;
- assert size == method.getNumberOfArguments();
- ConcreteCallSiteOptimizationInfo result = new ConcreteCallSiteOptimizationInfo(size);
- for (int i = 0; i < result.size; i++) {
- AbstractValue abstractValue =
- getAbstractArgumentValue(i)
- .join(
- other.getAbstractArgumentValue(i),
- appView.abstractValueFactory(),
- method.getArgumentType(i));
- if (abstractValue.isNonTrivial()) {
- result.constants.put(i, abstractValue);
- }
-
- TypeElement thisUpperBoundType = getDynamicUpperBoundType(i);
- if (thisUpperBoundType == null) {
- // This means the corresponding argument is primitive. The counterpart should be too.
- assert other.getDynamicUpperBoundType(i) == null;
- continue;
- }
- assert thisUpperBoundType.isReferenceType();
- TypeElement otherUpperBoundType = other.getDynamicUpperBoundType(i);
- assert otherUpperBoundType != null && otherUpperBoundType.isReferenceType();
- result.dynamicUpperBoundTypes.put(
- i, thisUpperBoundType.join(otherUpperBoundType, appView));
- }
- if (result.hasUsefulOptimizationInfo(appView, method)) {
- return result;
- }
- // As soon as we know the argument collection so far does not have any useful optimization info,
- // move to TOP so that further collection can be simply skipped.
- return top();
- }
-
- private TypeElement[] getStaticTypes(AppView<?> appView, DexEncodedMethod method) {
- int argOffset = method.getFirstNonReceiverArgumentIndex();
- int size = method.getReference().getArity() + argOffset;
- TypeElement[] staticTypes = new TypeElement[size];
- if (!method.isStatic()) {
- staticTypes[0] =
- TypeElement.fromDexType(method.getHolderType(), definitelyNotNull(), appView);
- }
- for (int i = 0; i < method.getReference().getArity(); i++) {
- staticTypes[i + argOffset] =
- TypeElement.fromDexType(method.getParameter(i), maybeNull(), appView);
- }
- return staticTypes;
- }
-
@Override
- public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod method) {
- TypeElement[] staticTypes = getStaticTypes(appView, method);
- for (int i = 0; i < size; i++) {
- AbstractValue abstractValue = getAbstractArgumentValue(i);
- if (abstractValue.isNonTrivial()) {
- return true;
- }
-
- if (!staticTypes[i].isReferenceType()) {
- continue;
- }
- TypeElement dynamicUpperBoundType = getDynamicUpperBoundType(i);
- if (dynamicUpperBoundType == null) {
- continue;
- }
- // To avoid the full join of type lattices below, separately check if the nullability of
- // arguments is improved, and if so, we can eagerly conclude that we've collected useful
- // call site information for this method.
- Nullability nullability = dynamicUpperBoundType.nullability();
- if (nullability.isDefinitelyNull()) {
- return true;
- }
- // TODO(b/139246447): Similar to nullability, if dynamic lower bound type is available,
- // we stop here and regard that call sites of this method have useful info.
- // In general, though, we're looking for (strictly) better dynamic types for arguments.
- if (dynamicUpperBoundType.strictlyLessThan(staticTypes[i], appView)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public TypeElement getDynamicUpperBoundType(int argIndex) {
+ public DynamicType getDynamicType(int argIndex) {
assert 0 <= argIndex && argIndex < size;
- return dynamicUpperBoundTypes.getOrDefault(argIndex, null);
+ return dynamicTypes.getOrDefault(argIndex, DynamicType.unknown());
}
@Override
@@ -214,47 +119,6 @@
return constants.getOrDefault(argIndex, UnknownValue.getInstance());
}
- public static CallSiteOptimizationInfo fromArguments(
- AppView<AppInfoWithLiveness> appView,
- DexMethod invokedMethod,
- List<Value> arguments,
- ProgramMethod context) {
- ConcreteCallSiteOptimizationInfo newCallSiteInfo =
- new ConcreteCallSiteOptimizationInfo(arguments.size());
- boolean hasReceiver = arguments.size() > invokedMethod.getArity();
- boolean isTop = true;
- for (int i = 0; i < newCallSiteInfo.size; i++) {
- Value arg = arguments.get(i);
-
- // Constant propagation.
- Value aliasedValue = arg.getAliasedValue();
- if (!aliasedValue.isPhi()) {
- AbstractValue abstractValue = aliasedValue.definition.getAbstractValue(appView, context);
- if (abstractValue.isNonTrivial()) {
- newCallSiteInfo.constants.put(i, abstractValue);
- isTop = false;
- }
- }
-
- // Type propagation.
- if (arg.getType().isReferenceType()) {
- TypeElement staticType =
- TypeElement.fromDexType(
- hasReceiver ? invokedMethod.holder : invokedMethod.proto.getParameter(i),
- maybeNull(),
- appView);
- TypeElement dynamicUpperBoundType = arg.getDynamicUpperBoundType(appView);
- if (dynamicUpperBoundType != staticType) {
- newCallSiteInfo.dynamicUpperBoundTypes.put(i, dynamicUpperBoundType);
- isTop = false;
- } else {
- newCallSiteInfo.dynamicUpperBoundTypes.put(i, staticType);
- }
- }
- }
- return isTop ? CallSiteOptimizationInfo.top() : newCallSiteInfo;
- }
-
public static CallSiteOptimizationInfo fromMethodState(
AppView<AppInfoWithLiveness> appView,
ProgramMethod method,
@@ -280,7 +144,7 @@
// Type propagation.
DexType staticType = method.getDefinition().getArgumentType(argumentIndex);
if (staticType.isReferenceType()) {
- TypeElement staticTypeElement = staticType.toTypeElement(appView);
+ DynamicTypeWithUpperBound staticTypeElement = staticType.toDynamicType(appView);
if (staticType.isArrayType()) {
Nullability nullability = concreteParameterState.asArrayParameter().getNullability();
if (nullability.isDefinitelyNull()) {
@@ -288,8 +152,8 @@
argumentIndex, appView.abstractValueFactory().createNullValue());
isTop = false;
} else if (nullability.isDefinitelyNotNull()) {
- newCallSiteInfo.dynamicUpperBoundTypes.put(
- argumentIndex, staticTypeElement.asArrayType().asDefinitelyNotNull());
+ newCallSiteInfo.dynamicTypes.put(
+ argumentIndex, staticTypeElement.withNullability(Nullability.definitelyNotNull()));
isTop = false;
} else {
// The nullability should never be unknown, since we should use the unknown method state
@@ -300,8 +164,7 @@
} else if (staticType.isClassType()) {
DynamicType dynamicType = concreteParameterState.asReferenceParameter().getDynamicType();
if (!dynamicType.isUnknown()) {
- newCallSiteInfo.dynamicUpperBoundTypes.put(
- argumentIndex, dynamicType.getDynamicUpperBoundType());
+ newCallSiteInfo.dynamicTypes.put(argumentIndex, dynamicType);
isTop = false;
}
}
@@ -326,18 +189,17 @@
return false;
}
ConcreteCallSiteOptimizationInfo otherInfo = (ConcreteCallSiteOptimizationInfo) other;
- return dynamicUpperBoundTypes.equals(otherInfo.dynamicUpperBoundTypes)
- && constants.equals(otherInfo.constants);
+ return dynamicTypes.equals(otherInfo.dynamicTypes) && constants.equals(otherInfo.constants);
}
@Override
public int hashCode() {
- return System.identityHashCode(dynamicUpperBoundTypes) * 7 + System.identityHashCode(constants);
+ return System.identityHashCode(dynamicTypes) * 7 + System.identityHashCode(constants);
}
@Override
public String toString() {
- return dynamicUpperBoundTypes.toString()
+ return dynamicTypes.toString()
+ (constants == null ? "" : (System.lineSeparator() + constants));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
index c790a02..2e3d1ce 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
@@ -4,8 +4,7 @@
package com.android.tools.r8.ir.optimize.info;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -35,13 +34,8 @@
}
@Override
- public ClassTypeElement getDynamicLowerBoundType() {
- return null;
- }
-
- @Override
- public TypeElement getDynamicUpperBoundType() {
- return null;
+ public DynamicType getDynamicType() {
+ return DynamicType.unknown();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 7bc6c1c..1ce6568 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -7,8 +7,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.InvokeDirect;
@@ -32,8 +31,6 @@
static int UNKNOWN_RETURNED_ARGUMENT = -1;
static boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
static AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
- static TypeElement UNKNOWN_TYPE = null;
- static ClassTypeElement UNKNOWN_CLASS_TYPE = null;
static boolean UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT = false;
static boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
static boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
@@ -74,13 +71,8 @@
}
@Override
- public TypeElement getDynamicUpperBoundType() {
- return UNKNOWN_TYPE;
- }
-
- @Override
- public ClassTypeElement getDynamicLowerBoundType() {
- return UNKNOWN_CLASS_TYPE;
+ public DynamicType getDynamicType() {
+ return DynamicType.unknown();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
index 8f42838..6493a7d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
@@ -4,13 +4,8 @@
package com.android.tools.r8.ir.optimize.info;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class FieldOptimizationInfo
implements MemberOptimizationInfo<MutableFieldOptimizationInfo> {
@@ -25,32 +20,7 @@
*/
public abstract int getReadBits();
- public abstract ClassTypeElement getDynamicLowerBoundType();
-
- public abstract TypeElement getDynamicUpperBoundType();
-
- public ClassTypeElement getExactClassType(AppView<AppInfoWithLiveness> appView) {
- ClassTypeElement dynamicLowerBoundType = getDynamicLowerBoundType();
- TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
- if (dynamicUpperBoundType == null || !dynamicUpperBoundType.isClassType()) {
- return null;
- }
- DexType upperType = dynamicUpperBoundType.asClassType().getClassType();
- if (dynamicLowerBoundType != null && upperType == dynamicLowerBoundType.getClassType()) {
- return dynamicLowerBoundType;
- }
- DexClass upperClass = appView.definitionFor(upperType);
- if (upperClass != null && upperClass.isEffectivelyFinal(appView)) {
- assert dynamicLowerBoundType == null;
- return ClassTypeElement.create(upperType, dynamicUpperBoundType.nullability(), appView);
- }
- return null;
- }
-
- public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
- TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
- return dynamicUpperBoundType != null ? dynamicUpperBoundType : orElse;
- }
+ public abstract DynamicType getDynamicType();
public abstract boolean isDead();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 3de6a7a..b2d7be0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -6,8 +6,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
@@ -38,14 +37,7 @@
public abstract EnumUnboxerMethodClassification getEnumUnboxerMethodClassification();
- public abstract TypeElement getDynamicUpperBoundType();
-
- public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
- TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
- return dynamicUpperBoundType != null ? dynamicUpperBoundType : orElse;
- }
-
- public abstract ClassTypeElement getDynamicLowerBoundType();
+ public abstract DynamicType getDynamicType();
public final boolean hasNonNullParamOrThrow() {
return getNonNullParamOrThrow() != null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index d994add..a0c4eb2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -61,9 +61,8 @@
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis.ClassInitializerSideEffect;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.StatefulObjectValue;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -151,7 +150,7 @@
}
computeEnumUnboxerMethodClassification(method, code, feedback, methodProcessor, timing);
computeSimpleInliningConstraint(method, code, feedback, timing);
- computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
+ computeDynamicReturnType(dynamicTypeOptimization, feedback, method, code, timing);
if (options.enableInitializedClassesAnalysis) {
computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
}
@@ -874,7 +873,7 @@
private void computeDynamicReturnType(
DynamicTypeOptimization dynamicTypeOptimization,
OptimizationFeedback feedback,
- DexEncodedMethod method,
+ ProgramMethod method,
IRCode code,
Timing timing) {
timing.begin("Compute dynamic return type");
@@ -885,46 +884,39 @@
private void computeDynamicReturnType(
DynamicTypeOptimization dynamicTypeOptimization,
OptimizationFeedback feedback,
- DexEncodedMethod method,
+ ProgramMethod method,
IRCode code) {
- if (dynamicTypeOptimization != null) {
- DexType staticReturnTypeRaw = method.getReference().proto.returnType;
- if (!staticReturnTypeRaw.isReferenceType()) {
- return;
- }
- TypeElement dynamicUpperBoundReturnType =
- dynamicTypeOptimization.computeDynamicReturnType(method, code);
- if (dynamicUpperBoundReturnType != null) {
- if (dynamicUpperBoundReturnType.isReferenceType()
- && dynamicUpperBoundReturnType.isDefinitelyNull()) {
- feedback.methodReturnsAbstractValue(
- method, appView, appView.abstractValueFactory().createSingleNumberValue(0));
- feedback.methodReturnsObjectWithUpperBoundType(method, appView, TypeElement.getNull());
- } else {
- TypeElement staticReturnType =
- TypeElement.fromDexType(staticReturnTypeRaw, Nullability.maybeNull(), appView);
- // If the dynamic return type is not more precise than the static return type there is no
- // need to record it.
- if (dynamicUpperBoundReturnType.strictlyLessThan(staticReturnType, appView)) {
- feedback.methodReturnsObjectWithUpperBoundType(
- method, appView, dynamicUpperBoundReturnType);
- }
- }
- }
+ if (dynamicTypeOptimization == null) {
+ return;
+ }
+ if (!method.getReturnType().isReferenceType()) {
+ return;
+ }
- if (dynamicUpperBoundReturnType != null && dynamicUpperBoundReturnType.isNullType()) {
- return;
- }
+ DynamicType dynamicReturnType = dynamicTypeOptimization.computeDynamicReturnType(method, code);
+ if (dynamicReturnType.isBottom() || dynamicReturnType.isUnknown()) {
+ return;
+ }
- ClassTypeElement dynamicLowerBoundReturnType =
- dynamicTypeOptimization.computeDynamicLowerBoundType(method, code);
- if (dynamicLowerBoundReturnType != null) {
- assert dynamicUpperBoundReturnType == null
- || dynamicUpperBoundReturnType
- .nullability()
- .lessThanOrEqual(dynamicLowerBoundReturnType.nullability());
- feedback.methodReturnsObjectWithLowerBoundType(method, dynamicLowerBoundReturnType);
- }
+ if (dynamicReturnType.isNullType()) {
+ feedback.methodReturnsAbstractValue(
+ method.getDefinition(), appView, appView.abstractValueFactory().createNullValue());
+ feedback.setDynamicReturnType(method, appView, dynamicReturnType);
+ return;
+ }
+
+ if (dynamicReturnType.isNotNullType()) {
+ feedback.setDynamicReturnType(method, appView, dynamicReturnType);
+ return;
+ }
+
+ // If the dynamic return type is not more precise than the static return type there is no
+ // need to record it.
+ DynamicTypeWithUpperBound staticReturnType = method.getReturnType().toDynamicType(appView);
+ if (dynamicReturnType
+ .asDynamicTypeWithUpperBound()
+ .strictlyLessThan(staticReturnType, appView)) {
+ feedback.setDynamicReturnType(method, appView, dynamicReturnType);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index 9150ea0..c03d170 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -6,12 +6,10 @@
import static java.util.Collections.emptySet;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -34,38 +32,25 @@
private AbstractValue abstractValue = UnknownValue.getInstance();
private int flags;
private int readBits = 0;
- private ClassTypeElement dynamicLowerBoundType = null;
- private TypeElement dynamicUpperBoundType = null;
+ private DynamicType dynamicType = DynamicType.unknown();
public MutableFieldOptimizationInfo fixupClassTypeReferences(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens) {
return fixupClassTypeReferences(appView, lens, emptySet());
}
public MutableFieldOptimizationInfo fixupClassTypeReferences(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- GraphLens lens,
- Set<DexType> prunedTypes) {
- if (dynamicUpperBoundType != null) {
- dynamicUpperBoundType = dynamicUpperBoundType.rewrittenWithLens(appView, lens, prunedTypes);
- }
- if (dynamicLowerBoundType != null) {
- TypeElement dynamicLowerBoundType =
- this.dynamicLowerBoundType.rewrittenWithLens(appView, lens);
- if (dynamicLowerBoundType.isClassType()) {
- this.dynamicLowerBoundType = dynamicLowerBoundType.asClassType();
- } else {
- assert dynamicLowerBoundType.isPrimitiveType();
- this.dynamicLowerBoundType = null;
- this.dynamicUpperBoundType = null;
- }
- }
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, Set<DexType> prunedTypes) {
+ dynamicType = dynamicType.rewrittenWithLens(appView, lens, prunedTypes);
return this;
}
public MutableFieldOptimizationInfo mutableCopy() {
MutableFieldOptimizationInfo copy = new MutableFieldOptimizationInfo();
+ copy.abstractValue = abstractValue;
copy.flags = flags;
+ copy.readBits = readBits;
+ copy.dynamicType = dynamicType;
return copy;
}
@@ -75,11 +60,12 @@
}
void setAbstractValue(AbstractValue abstractValue) {
+ assert getAbstractValue().isUnknown() || abstractValue.isNonTrivial();
this.abstractValue = abstractValue;
}
public void fixupAbstractValue(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- abstractValue = abstractValue.rewrittenWithLens(appView, lens);
+ setAbstractValue(abstractValue.rewrittenWithLens(appView, lens));
}
@Override
@@ -101,21 +87,12 @@
}
@Override
- public ClassTypeElement getDynamicLowerBoundType() {
- return dynamicLowerBoundType;
+ public DynamicType getDynamicType() {
+ return dynamicType;
}
- void setDynamicLowerBoundType(ClassTypeElement type) {
- dynamicLowerBoundType = type;
- }
-
- @Override
- public TypeElement getDynamicUpperBoundType() {
- return dynamicUpperBoundType;
- }
-
- void setDynamicUpperBoundType(TypeElement type) {
- dynamicUpperBoundType = type;
+ void setDynamicType(DynamicType dynamicType) {
+ this.dynamicType = dynamicType;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 7bda54d..968cc66 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -6,14 +6,14 @@
import static java.util.Collections.emptySet;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -45,9 +45,7 @@
ClassInlinerMethodConstraint.alwaysFalse();
private EnumUnboxerMethodClassification enumUnboxerMethodClassification =
EnumUnboxerMethodClassification.unknown();
- private TypeElement returnsObjectWithUpperBoundType = DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
- private ClassTypeElement returnsObjectWithLowerBoundType =
- DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
+ private DynamicType dynamicType = DynamicType.unknown();
private InlinePreference inlining = InlinePreference.Default;
// Stores information about instance methods and constructors for
// class inliner, null value indicates that the method is not eligible.
@@ -150,8 +148,7 @@
initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
returnedArgument = template.returnedArgument;
abstractReturnValue = template.abstractReturnValue;
- returnsObjectWithUpperBoundType = template.returnsObjectWithUpperBoundType;
- returnsObjectWithLowerBoundType = template.returnsObjectWithLowerBoundType;
+ dynamicType = template.dynamicType;
inlining = template.inlining;
simpleInliningConstraint = template.simpleInliningConstraint;
bridgeInfo = template.bridgeInfo;
@@ -177,29 +174,13 @@
}
public MutableMethodOptimizationInfo fixupClassTypeReferences(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens) {
return fixupClassTypeReferences(appView, lens, emptySet());
}
public MutableMethodOptimizationInfo fixupClassTypeReferences(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- GraphLens lens,
- Set<DexType> prunedTypes) {
- if (returnsObjectWithUpperBoundType != null) {
- returnsObjectWithUpperBoundType =
- returnsObjectWithUpperBoundType.rewrittenWithLens(appView, lens, prunedTypes);
- }
- if (returnsObjectWithLowerBoundType != null) {
- TypeElement returnsObjectWithLowerBoundType =
- this.returnsObjectWithLowerBoundType.rewrittenWithLens(appView, lens, prunedTypes);
- if (returnsObjectWithLowerBoundType.isClassType()) {
- this.returnsObjectWithLowerBoundType = returnsObjectWithLowerBoundType.asClassType();
- } else {
- assert returnsObjectWithLowerBoundType.isPrimitiveType();
- this.returnsObjectWithUpperBoundType = DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
- this.returnsObjectWithLowerBoundType = DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
- }
- }
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, Set<DexType> prunedTypes) {
+ dynamicType = dynamicType.rewrittenWithLens(appView, lens, prunedTypes);
return this;
}
@@ -321,13 +302,8 @@
}
@Override
- public TypeElement getDynamicUpperBoundType() {
- return returnsObjectWithUpperBoundType;
- }
-
- @Override
- public ClassTypeElement getDynamicLowerBoundType() {
- return returnsObjectWithLowerBoundType;
+ public DynamicType getDynamicType() {
+ return dynamicType;
}
@Override
@@ -626,8 +602,8 @@
abstractReturnValue = UnknownValue.getInstance();
}
- void markReturnsObjectWithUpperBoundType(AppView<?> appView, TypeElement type) {
- assert type != null;
+ void setDynamicType(AppView<?> appView, DynamicType newDynamicType, DexEncodedMethod method) {
+ assert newDynamicType != null;
// We may get more precise type information if the method is reprocessed (e.g., due to
// optimization info collected from all call sites), and hence the
// `returnsObjectWithUpperBoundType` is allowed to become more precise.
@@ -635,31 +611,30 @@
// Nullability could be less precise, though. For example, suppose a value is known to be
// non-null after a safe invocation, hence recorded with the non-null variant. If that call is
// inlined and the method is reprocessed, such non-null assumption cannot be made again.
- assert returnsObjectWithUpperBoundType == DefaultMethodOptimizationInfo.UNKNOWN_TYPE
- || type.lessThanOrEqualUpToNullability(returnsObjectWithUpperBoundType, appView)
- : "upper bound type changed from " + returnsObjectWithUpperBoundType + " to " + type;
- returnsObjectWithUpperBoundType = type;
- if (type.isNullType()) {
- returnsObjectWithLowerBoundType = null;
+ assert verifyDynamicType(appView, newDynamicType, method);
+ dynamicType = newDynamicType;
+ }
+
+ private boolean verifyDynamicType(
+ AppView<?> appView, DynamicType newDynamicType, DexEncodedMethod method) {
+ if (appView.enableWholeProgramOptimizations()) {
+ TypeElement staticReturnType = method.getReturnType().toTypeElement(appView);
+ TypeElement previousDynamicUpperBoundType =
+ dynamicType.getDynamicUpperBoundType(staticReturnType);
+ TypeElement newDynamicUpperBoundType =
+ newDynamicType.getDynamicUpperBoundType(staticReturnType);
+ assert newDynamicUpperBoundType.lessThanOrEqualUpToNullability(
+ previousDynamicUpperBoundType, appView)
+ : "upper bound type changed from "
+ + previousDynamicUpperBoundType
+ + " to "
+ + newDynamicUpperBoundType;
}
+ return true;
}
- void unsetDynamicUpperBoundReturnType() {
- returnsObjectWithUpperBoundType = null;
- }
-
- void markReturnsObjectWithLowerBoundType(ClassTypeElement type) {
- assert type != null;
- // Currently, we only have a lower bound type when we have _exact_ runtime type information.
- // Thus, the type should never become more precise (although the nullability could).
- assert returnsObjectWithLowerBoundType == DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE
- || type.equalUpToNullability(returnsObjectWithLowerBoundType)
- : "lower bound type changed from " + returnsObjectWithLowerBoundType + " to " + type;
- returnsObjectWithLowerBoundType = type;
- }
-
- void unsetDynamicLowerBoundReturnType() {
- returnsObjectWithLowerBoundType = null;
+ void unsetDynamicType() {
+ dynamicType = DynamicType.unknown();
}
// TODO(b/140214568): Should be package-private.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 23bedd0..5e9386c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -8,10 +8,10 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
@@ -41,6 +41,10 @@
new IdentityHashMap<>();
private final Map<DexEncodedMethod, ConstraintWithTarget> processed = new IdentityHashMap<>();
+ private MutableFieldOptimizationInfo getFieldOptimizationInfoForUpdating(ProgramField field) {
+ return getFieldOptimizationInfoForUpdating(field.getDefinition());
+ }
+
private synchronized MutableFieldOptimizationInfo getFieldOptimizationInfoForUpdating(
DexEncodedField field) {
MutableFieldOptimizationInfo info = fieldOptimizationInfos.get(field);
@@ -133,13 +137,8 @@
}
@Override
- public void markFieldHasDynamicLowerBoundType(DexEncodedField field, ClassTypeElement type) {
- getFieldOptimizationInfoForUpdating(field).setDynamicLowerBoundType(type);
- }
-
- @Override
- public void markFieldHasDynamicUpperBoundType(DexEncodedField field, TypeElement type) {
- getFieldOptimizationInfoForUpdating(field).setDynamicUpperBoundType(type);
+ public void markFieldHasDynamicType(DexEncodedField field, DynamicType dynamicType) {
+ getFieldOptimizationInfoForUpdating(field).setDynamicType(dynamicType);
}
@Override
@@ -199,15 +198,9 @@
}
@Override
- public synchronized void methodReturnsObjectWithUpperBoundType(
- DexEncodedMethod method, AppView<?> appView, TypeElement type) {
- getMethodOptimizationInfoForUpdating(method).markReturnsObjectWithUpperBoundType(appView, type);
- }
-
- @Override
- public synchronized void methodReturnsObjectWithLowerBoundType(
- DexEncodedMethod method, ClassTypeElement type) {
- getMethodOptimizationInfoForUpdating(method).markReturnsObjectWithLowerBoundType(type);
+ public synchronized void setDynamicReturnType(
+ DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType) {
+ getMethodOptimizationInfoForUpdating(method).setDynamicType(appView, dynamicType, method);
}
@Override
@@ -333,13 +326,8 @@
}
@Override
- public synchronized void unsetDynamicLowerBoundReturnType(ProgramMethod method) {
- getMethodOptimizationInfoForUpdating(method).unsetDynamicLowerBoundReturnType();
- }
-
- @Override
- public synchronized void unsetDynamicUpperBoundReturnType(ProgramMethod method) {
- getMethodOptimizationInfoForUpdating(method).unsetDynamicUpperBoundReturnType();
+ public synchronized void unsetDynamicReturnType(ProgramMethod method) {
+ getMethodOptimizationInfoForUpdating(method).unsetDynamicType();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 3bcd3fc..fd6d27e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -10,8 +10,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
@@ -44,10 +43,7 @@
public void markFieldAsPropagated(DexEncodedField field) {}
@Override
- public void markFieldHasDynamicLowerBoundType(DexEncodedField field, ClassTypeElement type) {}
-
- @Override
- public void markFieldHasDynamicUpperBoundType(DexEncodedField field, TypeElement type) {}
+ public void markFieldHasDynamicType(DexEncodedField field, DynamicType dynamicType) {}
@Override
public void markFieldBitsRead(DexEncodedField field, int bitsRead) {}
@@ -79,12 +75,8 @@
DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) {}
@Override
- public void methodReturnsObjectWithUpperBoundType(
- DexEncodedMethod method, AppView<?> appView, TypeElement type) {}
-
- @Override
- public void methodReturnsObjectWithLowerBoundType(
- DexEncodedMethod method, ClassTypeElement type) {}
+ public void setDynamicReturnType(
+ DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType) {}
@Override
public void methodMayNotHaveSideEffects(DexEncodedMethod method) {}
@@ -160,10 +152,7 @@
public void unsetClassInlinerMethodConstraint(ProgramMethod method) {}
@Override
- public void unsetDynamicLowerBoundReturnType(ProgramMethod method) {}
-
- @Override
- public void unsetDynamicUpperBoundReturnType(ProgramMethod method) {}
+ public void unsetDynamicReturnType(ProgramMethod method) {}
@Override
public void unsetEnumUnboxerMethodClassification(ProgramMethod method) {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 40a6cdd..5fd4800 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -10,8 +10,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
@@ -51,12 +50,7 @@
}
@Override
- public void markFieldHasDynamicLowerBoundType(DexEncodedField field, ClassTypeElement type) {
- // Ignored.
- }
-
- @Override
- public void markFieldHasDynamicUpperBoundType(DexEncodedField field, TypeElement type) {
+ public void markFieldHasDynamicType(DexEncodedField field, DynamicType dynamicType) {
// Ignored.
}
@@ -108,15 +102,9 @@
}
@Override
- public void methodReturnsObjectWithUpperBoundType(
- DexEncodedMethod method, AppView<?> appView, TypeElement type) {
- method.getMutableOptimizationInfo().markReturnsObjectWithUpperBoundType(appView, type);
- }
-
- @Override
- public void methodReturnsObjectWithLowerBoundType(
- DexEncodedMethod method, ClassTypeElement type) {
- // Ignored.
+ public void setDynamicReturnType(
+ DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType) {
+ method.getMutableOptimizationInfo().setDynamicType(appView, dynamicType, method);
}
@Override
@@ -250,15 +238,8 @@
}
@Override
- public void unsetDynamicLowerBoundReturnType(ProgramMethod method) {
- withMutableMethodOptimizationInfo(
- method, MutableMethodOptimizationInfo::unsetDynamicLowerBoundReturnType);
- }
-
- @Override
- public void unsetDynamicUpperBoundReturnType(ProgramMethod method) {
- withMutableMethodOptimizationInfo(
- method, MutableMethodOptimizationInfo::unsetDynamicUpperBoundReturnType);
+ public void unsetDynamicReturnType(ProgramMethod method) {
+ withMutableMethodOptimizationInfo(method, MutableMethodOptimizationInfo::unsetDynamicType);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java
index 241b934..aa2ef1f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java
@@ -13,9 +13,4 @@
static TopCallSiteOptimizationInfo getInstance() {
return INSTANCE;
}
-
- @Override
- public boolean isTop() {
- return true;
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index ef40285..c5d846e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.info.field;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -37,6 +38,10 @@
public abstract InstanceFieldInitializationInfo get(DexEncodedField field);
+ public final InstanceFieldInitializationInfo get(DexClassAndField field) {
+ return get(field.getDefinition());
+ }
+
public abstract boolean isEmpty();
public abstract InstanceFieldInitializationInfoCollection fixupAfterParametersChanged(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
index 167a528..ee5dc08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Set;
public class EnumMethodOptimizer extends StatelessLibraryMethodModelCollection {
@@ -43,14 +44,18 @@
DexClassAndMethod singleTarget,
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove) {
- if (singleTarget.getReference() == appView.dexItemFactory().enumMembers.valueOf
+ if (appView.hasLiveness()
+ && singleTarget.getReference() == appView.dexItemFactory().enumMembers.valueOf
&& invoke.inValues().get(0).isConstClass()) {
- insertAssumeDynamicType(code, instructionIterator, invoke);
+ insertAssumeDynamicType(appView.withLiveness(), code, instructionIterator, invoke);
}
}
private void insertAssumeDynamicType(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke) {
// TODO(b/152516470): Support unboxing enums with Enum#valueOf in try-catch.
if (invoke.getBlock().hasCatchHandlers()) {
return;
@@ -62,8 +67,6 @@
|| enumClass.superType != appView.dexItemFactory().enumType) {
return;
}
- TypeElement dynamicUpperBoundType =
- TypeElement.fromDexType(enumType, definitelyNotNull(), appView);
Value outValue = invoke.outValue();
if (outValue == null) {
return;
@@ -73,9 +76,10 @@
outValue.replaceUsers(specializedOutValue);
// Insert AssumeDynamicType instruction.
+ DynamicTypeWithUpperBound dynamicType = enumType.toDynamicType(appView, definitelyNotNull());
Assume assumeInstruction =
Assume.createAssumeDynamicTypeInstruction(
- dynamicUpperBoundType, null, specializedOutValue, outValue, invoke, appView);
+ dynamicType, specializedOutValue, outValue, invoke, appView);
assumeInstruction.setPosition(appView.options().debug ? invoke.getPosition() : Position.none());
instructionIterator.add(assumeInstruction);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index ca99313..0b41d07 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.optimize.library;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -14,7 +12,7 @@
import com.android.tools.r8.graph.DexItemFactory.EnumMembers;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
@@ -128,16 +126,9 @@
for (DexMethod method : dexItemFactory.libraryMethodsReturningNonNull) {
DexEncodedMethod definition = lookupMethod(method);
if (definition != null) {
- TypeElement staticType =
- TypeElement.fromDexType(method.proto.returnType, maybeNull(), appView);
- feedback.methodReturnsObjectWithUpperBoundType(
- definition,
- appView,
- definition
- .getOptimizationInfo()
- .getDynamicUpperBoundTypeOrElse(staticType)
- .asReferenceType()
- .asDefinitelyNotNull());
+ assert definition.getOptimizationInfo().getDynamicType().isUnknown()
+ || definition.getOptimizationInfo().getDynamicType().isNotNullType();
+ feedback.setDynamicReturnType(definition, appView, DynamicType.definitelyNotNull());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 9e7d703..bd8696b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -54,6 +54,7 @@
private final int[] metadataVersion;
private final String inlineClassUnderlyingPropertyName;
private final KotlinTypeInfo inlineClassUnderlyingType;
+ private final int jvmFlags;
// List of tracked assignments of kotlin metadata.
private final KotlinMetadataMembersTracker originalMembersWithKotlinInfo;
@@ -77,7 +78,8 @@
int[] metadataVersion,
String inlineClassUnderlyingPropertyName,
KotlinTypeInfo inlineClassUnderlyingType,
- KotlinMetadataMembersTracker originalMembersWithKotlinInfo) {
+ KotlinMetadataMembersTracker originalMembersWithKotlinInfo,
+ int jvmFlags) {
this.flags = flags;
this.name = name;
this.nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin =
@@ -98,6 +100,7 @@
this.inlineClassUnderlyingPropertyName = inlineClassUnderlyingPropertyName;
this.inlineClassUnderlyingType = inlineClassUnderlyingType;
this.originalMembersWithKotlinInfo = originalMembersWithKotlinInfo;
+ this.jvmFlags = jvmFlags;
}
public static KotlinClassInfo create(
@@ -180,7 +183,8 @@
metadataVersion,
kmClass.getInlineClassUnderlyingPropertyName(),
KotlinTypeInfo.create(kmClass.getInlineClassUnderlyingType(), factory, reporter),
- originalMembersWithKotlinInfo);
+ originalMembersWithKotlinInfo,
+ JvmExtensionsKt.getJvmFlags(kmClass));
}
private static KotlinTypeReference getAnonymousObjectOrigin(
@@ -371,6 +375,7 @@
}
JvmClassExtensionVisitor extensionVisitor =
(JvmClassExtensionVisitor) kmClass.visitExtensions(JvmClassExtensionVisitor.TYPE);
+ extensionVisitor.visitJvmFlags(jvmFlags);
extensionVisitor.visitModuleName(moduleName);
if (anonymousObjectOrigin != null) {
rewritten |=
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
index ee9ea2c..1e4d633 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
@@ -303,6 +303,7 @@
public static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) {
appendKeyValue(indent, "flags", sb, kmClass.getFlags() + "");
+ appendKeyValue(indent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmClass) + "");
appendKeyValue(indent, "name", sb, kmClass.getName());
appendKeyValue(
indent,
@@ -518,13 +519,22 @@
"setterSignature",
sb,
setterSignature != null ? setterSignature.asString() : "null");
- JvmMethodSignature syntheticMethod =
+ JvmMethodSignature syntheticMethodForAnnotations =
JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty);
appendKeyValue(
newIndent,
"syntheticMethodForAnnotations",
sb,
- syntheticMethod != null ? syntheticMethod.asString() : "null");
+ syntheticMethodForAnnotations != null
+ ? syntheticMethodForAnnotations.asString()
+ : "null");
+ JvmMethodSignature syntheticMethodForDelegate =
+ JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty);
+ appendKeyValue(
+ newIndent,
+ "syntheticMethodForDelegate",
+ sb,
+ syntheticMethodForDelegate != null ? syntheticMethodForDelegate.asString() : "null");
});
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
index 9d0597d..39ab90c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -58,6 +58,8 @@
private final KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations;
+ private final KotlinJvmMethodSignatureInfo syntheticMethodForDelegate;
+
private KotlinPropertyInfo(
int flags,
int getterFlags,
@@ -72,7 +74,8 @@
KotlinJvmFieldSignatureInfo fieldSignature,
KotlinJvmMethodSignatureInfo getterSignature,
KotlinJvmMethodSignatureInfo setterSignature,
- KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations) {
+ KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations,
+ KotlinJvmMethodSignatureInfo syntheticMethodForDelegate) {
this.flags = flags;
this.getterFlags = getterFlags;
this.setterFlags = setterFlags;
@@ -87,6 +90,7 @@
this.getterSignature = getterSignature;
this.setterSignature = setterSignature;
this.syntheticMethodForAnnotations = syntheticMethodForAnnotations;
+ this.syntheticMethodForDelegate = syntheticMethodForDelegate;
}
public static KotlinPropertyInfo create(
@@ -108,7 +112,9 @@
KotlinJvmMethodSignatureInfo.create(
JvmExtensionsKt.getSetterSignature(kmProperty), factory),
KotlinJvmMethodSignatureInfo.create(
- JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty), factory));
+ JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty), factory),
+ KotlinJvmMethodSignatureInfo.create(
+ JvmExtensionsKt.getSyntheticMethodForDelegate(kmProperty), factory));
}
@Override
@@ -187,6 +193,11 @@
syntheticMethodForAnnotations.rewrite(
extensionVisitor::visitSyntheticMethodForAnnotations, null, appView, namingLens);
}
+ if (syntheticMethodForDelegate != null) {
+ rewritten |=
+ syntheticMethodForDelegate.rewrite(
+ extensionVisitor::visitSyntheticMethodForDelegate, null, appView, namingLens);
+ }
}
return rewritten;
}
@@ -215,5 +226,8 @@
if (syntheticMethodForAnnotations != null) {
syntheticMethodForAnnotations.trace(definitionSupplier);
}
+ if (syntheticMethodForDelegate != null) {
+ syntheticMethodForDelegate.trace(definitionSupplier);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 62bf247..e17606f 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -137,7 +137,8 @@
FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
nonReboundFieldReferenceToDefinitionMap.forEach(
(nonReboundFieldReference, reboundFieldReference) -> {
- DexField rewrittenReboundFieldReference = lens.lookupField(reboundFieldReference);
+ DexField rewrittenReboundFieldReference =
+ lens.getRenamedFieldSignature(reboundFieldReference);
DexField rewrittenNonReboundFieldReference =
rewrittenReboundFieldReference.withHolder(
lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 890cb48..897aaa8 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -4,11 +4,24 @@
package com.android.tools.r8.optimize.argumentpropagation;
+import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
+
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
+import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -20,13 +33,14 @@
* Takes as input a mapping from old method signatures to new method signatures (with parameters
* removed), and rewrites all method definitions in the application to their new method signatures.
*/
-public class ArgumentPropagatorApplicationFixer {
+public class ArgumentPropagatorApplicationFixer extends TreeFixerBase {
private final AppView<AppInfoWithLiveness> appView;
private final ArgumentPropagatorGraphLens graphLens;
public ArgumentPropagatorApplicationFixer(
AppView<AppInfoWithLiveness> appView, ArgumentPropagatorGraphLens graphLens) {
+ super(appView);
this.appView = appView;
this.graphLens = graphLens;
}
@@ -47,12 +61,25 @@
ThreadUtils.processItems(affectedClasses, this::fixupClass, executorService);
timing.end();
+ // Fixup optimization info.
+ timing.time("Fixup optimization info", () -> fixupOptimizationInfos(executorService));
+
timing.begin("Rewrite AppView");
appView.rewriteWithLens(graphLens);
timing.end();
}
private void fixupClass(DexProgramClass clazz) {
+ fixupFields(clazz);
+ fixupMethods(clazz);
+ }
+
+ private void fixupFields(DexProgramClass clazz) {
+ clazz.setInstanceFields(fixupFields(clazz.instanceFields()));
+ clazz.setStaticFields(fixupFields(clazz.staticFields()));
+ }
+
+ private void fixupMethods(DexProgramClass clazz) {
MethodCollection methodCollection = clazz.getMethodCollection();
methodCollection.replaceMethods(
method -> {
@@ -68,11 +95,77 @@
builder -> {
RewrittenPrototypeDescription prototypeChanges =
graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
- builder
- .apply(prototypeChanges.createParameterAnnotationsRemover(method))
- .fixupOptimizationInfo(
- appView, prototypeChanges.createMethodOptimizationInfoFixer());
+ builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
});
});
}
+
+ private void fixupOptimizationInfos(ExecutorService executorService) throws ExecutionException {
+ PrunedItems prunedItems = PrunedItems.empty(appView.app());
+ getSimpleFeedback()
+ .fixupOptimizationInfos(
+ appView,
+ executorService,
+ new OptimizationInfoFixer() {
+ @Override
+ public void fixup(
+ DexEncodedField field, MutableFieldOptimizationInfo optimizationInfo) {
+ optimizationInfo.fixupAbstractValue(appView, graphLens);
+ }
+
+ @Override
+ public void fixup(
+ DexEncodedMethod method, MutableMethodOptimizationInfo optimizationInfo) {
+ // Fixup the return value in case the method returns a field that had its signature
+ // changed.
+ optimizationInfo
+ .fixupAbstractReturnValue(appView, graphLens)
+ .fixupInstanceInitializerInfo(appView, graphLens, prunedItems);
+
+ // Rewrite the optimization info to account for method signature changes.
+ if (graphLens.hasPrototypeChanges(method.getReference())) {
+ RewrittenPrototypeDescription prototypeChanges =
+ graphLens.getPrototypeChanges(method.getReference());
+ MethodOptimizationInfoFixer fixer =
+ prototypeChanges.createMethodOptimizationInfoFixer();
+ optimizationInfo.fixup(appView, fixer);
+ }
+ }
+ });
+ }
+
+ @Override
+ public DexField fixupFieldReference(DexField field) {
+ return graphLens.internalGetNextFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod fixupMethodReference(DexMethod method) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public DexType fixupType(DexType type) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public DexType mapClassType(DexType type) {
+ return type;
+ }
+
+ @Override
+ public void recordFieldChange(DexField from, DexField to) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void recordMethodChange(DexMethod from, DexMethod to) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void recordClassChange(DexType from, DexType to) {
+ throw new Unreachable();
+ }
}
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 96e08b6..eef092d 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
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -285,11 +286,11 @@
ProgramMethod resolvedMethod,
ProgramMethod context,
ConcretePolymorphicMethodStateOrBottom existingMethodState) {
- DynamicType dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
+ DynamicTypeWithUpperBound dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
assert !dynamicReceiverType.getDynamicUpperBoundType().nullability().isDefinitelyNull();
ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
- DynamicType bounds =
+ DynamicTypeWithUpperBound bounds =
computeBoundsForPolymorphicMethodState(
invoke, resolvedMethod, singleTarget, context, dynamicReceiverType);
MethodState existingMethodStateForBounds =
@@ -319,13 +320,13 @@
return ConcretePolymorphicMethodState.create(bounds, methodStateForBounds);
}
- private DynamicType computeBoundsForPolymorphicMethodState(
+ private DynamicTypeWithUpperBound computeBoundsForPolymorphicMethodState(
InvokeMethodWithReceiver invoke,
ProgramMethod resolvedMethod,
ProgramMethod singleTarget,
ProgramMethod context,
- DynamicType dynamicReceiverType) {
- DynamicType bounds =
+ DynamicTypeWithUpperBound dynamicReceiverType) {
+ DynamicTypeWithUpperBound bounds =
singleTarget != null
? DynamicType.createExact(
singleTarget.getHolderType().toTypeElement(appView).asClassType())
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index a0f67e0..3ed1334 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.optimize.argumentpropagation;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
@@ -14,6 +15,7 @@
import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Map.Entry;
public class ArgumentPropagatorGraphLens extends NestedGraphLens {
@@ -21,9 +23,10 @@
ArgumentPropagatorGraphLens(
AppView<AppInfoWithLiveness> appView,
+ BidirectionalOneToOneMap<DexField, DexField> fieldMap,
BidirectionalOneToOneMap<DexMethod, DexMethod> methodMap,
Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges) {
- super(appView, EMPTY_FIELD_MAP, methodMap, EMPTY_TYPE_MAP);
+ super(appView, fieldMap, methodMap, EMPTY_TYPE_MAP);
this.prototypeChanges = prototypeChanges;
}
@@ -31,12 +34,30 @@
return new Builder(appView);
}
+ public boolean hasPrototypeChanges(DexMethod method) {
+ return method != internalGetPreviousMethodSignature(method);
+ }
+
public RewrittenPrototypeDescription getPrototypeChanges(DexMethod method) {
- assert method != internalGetPreviousMethodSignature(method);
+ assert hasPrototypeChanges(method);
return prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none());
}
@Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ FieldLookupResult lookupResult = super.internalDescribeLookupField(previous);
+ if (lookupResult.getReference().getType() != previous.getReference().getType()) {
+ return FieldLookupResult.builder(this)
+ .setReboundReference(lookupResult.getReboundReference())
+ .setReference(lookupResult.getReference())
+ .setReadCastType(lookupResult.getReadCastType())
+ .setWriteCastType(lookupResult.getReference().getType())
+ .build();
+ }
+ return lookupResult;
+ }
+
+ @Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
DexMethod previous = internalGetPreviousMethodSignature(method);
@@ -58,6 +79,11 @@
}
@Override
+ public DexField internalGetNextFieldSignature(DexField field) {
+ return super.internalGetNextFieldSignature(field);
+ }
+
+ @Override
public DexMethod internalGetNextMethodSignature(DexMethod method) {
return super.internalGetNextMethodSignature(method);
}
@@ -65,6 +91,8 @@
public static class Builder {
private final AppView<AppInfoWithLiveness> appView;
+ private final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
+ new BidirectionalOneToOneHashMap<>();
private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures =
new BidirectionalOneToOneHashMap<>();
private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges =
@@ -75,16 +103,23 @@
}
public boolean isEmpty() {
- return newMethodSignatures.isEmpty();
+ return newFieldSignatures.isEmpty() && newMethodSignatures.isEmpty();
}
public ArgumentPropagatorGraphLens.Builder mergeDisjoint(
ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
+ newFieldSignatures.putAll(partialGraphLensBuilder.newFieldSignatures);
newMethodSignatures.putAll(partialGraphLensBuilder.newMethodSignatures);
prototypeChanges.putAll(partialGraphLensBuilder.prototypeChanges);
return this;
}
+ public Builder recordMove(DexField from, DexField to) {
+ assert from != to;
+ newFieldSignatures.put(from, to);
+ return this;
+ }
+
public Builder recordMove(
DexMethod from, DexMethod to, RewrittenPrototypeDescription prototypeChangesForMethod) {
assert from != to;
@@ -99,9 +134,26 @@
}
public ArgumentPropagatorGraphLens build() {
- return isEmpty()
- ? null
- : new ArgumentPropagatorGraphLens(appView, newMethodSignatures, prototypeChanges);
+ if (isEmpty()) {
+ return null;
+ }
+ ArgumentPropagatorGraphLens argumentPropagatorGraphLens =
+ new ArgumentPropagatorGraphLens(
+ appView, newFieldSignatures, newMethodSignatures, prototypeChanges);
+ fixupPrototypeChangesAfterFieldSignatureChanges(argumentPropagatorGraphLens);
+ return argumentPropagatorGraphLens;
+ }
+
+ private void fixupPrototypeChangesAfterFieldSignatureChanges(
+ ArgumentPropagatorGraphLens argumentPropagatorGraphLens) {
+ for (Entry<DexMethod, RewrittenPrototypeDescription> entry : prototypeChanges.entrySet()) {
+ RewrittenPrototypeDescription prototypeChangesForMethod = entry.getValue();
+ RewrittenPrototypeDescription rewrittenPrototypeChangesForMethod =
+ prototypeChangesForMethod.rewrittenWithLens(appView, argumentPropagatorGraphLens);
+ if (rewrittenPrototypeChangesForMethod != prototypeChangesForMethod) {
+ entry.setValue(rewrittenPrototypeChangesForMethod);
+ }
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
index 892c282..f92af83 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.optimize.argumentpropagation;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Argument;
@@ -75,16 +76,15 @@
// TODO(b/190154391): This should also materialize dynamic lower bound information.
// TODO(b/190154391) This should also materialize the nullability of array arguments.
if (argumentValue.getType().isReferenceType()) {
- TypeElement dynamicUpperBoundType =
- optimizationInfo.getDynamicUpperBoundType(argument.getIndex());
- if (dynamicUpperBoundType == null || dynamicUpperBoundType.isTop()) {
+ DynamicType dynamicType = optimizationInfo.getDynamicType(argument.getIndex());
+ if (dynamicType.isUnknown()) {
continue;
}
- if (dynamicUpperBoundType.isBottom()) {
+ if (dynamicType.isBottom()) {
assert false;
continue;
}
- if (dynamicUpperBoundType.isDefinitelyNull()) {
+ if (dynamicType.getNullability().isDefinitelyNull()) {
ConstNumber nullInstruction = code.createConstNull();
nullInstruction.setPosition(argument.getPosition());
affectedValues.addAll(argumentValue.affectedValues());
@@ -92,21 +92,36 @@
instructionsToAdd.add(nullInstruction);
continue;
}
+ if (dynamicType.isNotNullType()) {
+ if (!argumentValue.getType().isDefinitelyNotNull()) {
+ Value nonNullValue =
+ code.createValue(argumentValue.getType().asReferenceType().asMeetWithNotNull());
+ argumentValue.replaceUsers(nonNullValue, affectedValues);
+ Assume assumeNotNull =
+ Assume.createAssumeNonNullInstruction(
+ nonNullValue, argumentValue, argument, appView);
+ assumeNotNull.setPosition(argument.getPosition());
+ assumeInstructions.add(assumeNotNull);
+ }
+ continue;
+ }
+ DynamicTypeWithUpperBound dynamicTypeWithUpperBound =
+ dynamicType.asDynamicTypeWithUpperBound();
Value specializedArg;
- if (dynamicUpperBoundType.strictlyLessThan(argumentValue.getType(), appView)) {
+ if (dynamicTypeWithUpperBound.strictlyLessThan(argumentValue.getType(), appView)) {
specializedArg = code.createValue(argumentValue.getType());
affectedValues.addAll(argumentValue.affectedValues());
argumentValue.replaceUsers(specializedArg);
Assume assumeType =
Assume.createAssumeDynamicTypeInstruction(
- dynamicUpperBoundType, null, specializedArg, argumentValue, argument, appView);
+ dynamicTypeWithUpperBound, specializedArg, argumentValue, argument, appView);
assumeType.setPosition(argument.getPosition());
assumeInstructions.add(assumeType);
} else {
specializedArg = argumentValue;
}
assert specializedArg != null && specializedArg.getType().isReferenceType();
- if (dynamicUpperBoundType.isDefinitelyNotNull()) {
+ if (dynamicType.getNullability().isDefinitelyNotNull()) {
// If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
if (!specializedArg.getType().isDefinitelyNotNull()) {
Value nonNullArg =
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index 772a404..f3e9000 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistryWithResult;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -187,21 +189,43 @@
}
@Override
+ public void registerInstanceFieldRead(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ private void registerFieldAccess(DexField field) {
+ FieldResolutionResult resolutionResult = appView.appInfo().resolveField(field);
+ if (resolutionResult.getProgramField() == null) {
+ return;
+ }
+
+ ProgramField resolvedField = resolutionResult.getProgramField();
+ DexField rewrittenFieldReference =
+ graphLens.internalGetNextFieldSignature(resolvedField.getReference());
+ if (rewrittenFieldReference != resolvedField.getReference()) {
+ markAffected();
+ }
+ }
+
+ @Override
public void registerInitClass(DexType type) {}
@Override
- public void registerInstanceFieldRead(DexField field) {}
-
- @Override
- public void registerInstanceFieldWrite(DexField field) {}
-
- @Override
- public void registerStaticFieldRead(DexField field) {}
-
- @Override
- public void registerStaticFieldWrite(DexField field) {}
-
- @Override
public void registerTypeReference(DexType type) {}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 81df33e..94da691 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -212,8 +212,9 @@
.asClassParameter()
.getDynamicType();
DexType staticType = method.getArgumentType(index);
- assert dynamicType
- == WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType);
+ assert dynamicType.isUnknown()
+ || !WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType)
+ .isUnknown();
return true;
});
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 8e7380d..6b94973 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -9,23 +9,32 @@
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.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorGraphLens.Builder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepFieldInfo;
+import com.android.tools.r8.utils.AccessUtils;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.IntBox;
@@ -197,10 +206,7 @@
this.options = appView.options();
}
- // TODO(b/190154391): Remove unused parameters by simulating they are constant.
// TODO(b/190154391): Strengthen the static type of parameters.
- // TODO(b/190154391): If we learn that a method returns a constant, then consider changing its
- // return type to void.
// TODO(b/69963623): If we optimize a method to be unconditionally throwing (because it has a
// bottom parameter), then for each caller that becomes unconditionally throwing, we could
// also enqueue the caller's callers for reprocessing. This would propagate the throwing
@@ -435,6 +441,15 @@
DexMethodSignatureSet interfaceDispatchOutsideProgram,
ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
BooleanBox affected = new BooleanBox();
+ clazz.forEachProgramFieldMatching(
+ field -> field.getType().isClassType(),
+ field -> {
+ DexField newFieldSignature = getNewFieldSignature(field);
+ if (newFieldSignature != field.getReference()) {
+ partialGraphLensBuilder.recordMove(field.getReference(), newFieldSignature);
+ affected.set();
+ }
+ });
DexMethodSignatureSet instanceInitializerSignatures = DexMethodSignatureSet.create();
clazz.forEachProgramInstanceInitializer(instanceInitializerSignatures::add);
clazz.forEachProgramMethod(
@@ -454,6 +469,71 @@
return affected.get();
}
+ private DexField getNewFieldSignature(ProgramField field) {
+ DynamicType dynamicType = field.getOptimizationInfo().getDynamicType();
+ if (dynamicType.isUnknown()) {
+ return field.getReference();
+ }
+
+ KeepFieldInfo keepInfo = appView.getKeepInfo(field);
+
+ // We don't have dynamic type information for fields that are kept.
+ assert !keepInfo.isPinned(options);
+
+ if (!keepInfo.isFieldTypeStrengtheningAllowed(options)) {
+ return field.getReference();
+ }
+
+ if (dynamicType.isNullType()) {
+ // Don't optimize always null fields; these will be optimized anyway.
+ return field.getReference();
+ }
+
+ if (dynamicType.isNotNullType()) {
+ // We don't have a more specific type.
+ return field.getReference();
+ }
+
+ DynamicTypeWithUpperBound dynamicTypeWithUpperBound =
+ dynamicType.asDynamicTypeWithUpperBound();
+ TypeElement dynamicUpperBoundType = dynamicTypeWithUpperBound.getDynamicUpperBoundType();
+ assert dynamicUpperBoundType.isReferenceType();
+
+ ClassTypeElement staticFieldType = field.getType().toTypeElement(appView).asClassType();
+ if (dynamicUpperBoundType.equalUpToNullability(staticFieldType)) {
+ // We don't have more precise type information.
+ return field.getReference();
+ }
+
+ if (!dynamicUpperBoundType.strictlyLessThan(staticFieldType, appView)) {
+ assert options.testing.allowTypeErrors;
+ return field.getReference();
+ }
+
+ DexType newStaticFieldType;
+ if (dynamicUpperBoundType.isClassType()) {
+ ClassTypeElement dynamicUpperBoundClassType = dynamicUpperBoundType.asClassType();
+ if (dynamicUpperBoundClassType.getClassType() == dexItemFactory.objectType) {
+ if (dynamicUpperBoundClassType.getInterfaces().hasSingleKnownInterface()) {
+ newStaticFieldType =
+ dynamicUpperBoundClassType.getInterfaces().getSingleKnownInterface();
+ } else {
+ return field.getReference();
+ }
+ } else {
+ newStaticFieldType = dynamicUpperBoundClassType.getClassType();
+ }
+ } else {
+ newStaticFieldType = dynamicUpperBoundType.asArrayType().toDexType(dexItemFactory);
+ }
+
+ if (!AccessUtils.isAccessibleInSameContextsAs(newStaticFieldType, field.getType(), appView)) {
+ return field.getReference();
+ }
+
+ return field.getReference().withType(newStaticFieldType, dexItemFactory);
+ }
+
private DexMethod getNewMethodSignature(
ProgramMethod method, RewrittenPrototypeDescription prototypeChanges) {
DexMethodSignature methodSignatureWithoutPrototypeChanges = method.getMethodSignature();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
index 9af5fb3..fab9f6f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -67,7 +67,7 @@
@Override
public Nullability getNullability() {
- return getDynamicType().getDynamicUpperBoundType().nullability();
+ return getDynamicType().getNullability();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
index d17ff72..d5fbf2b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
import java.util.HashMap;
@@ -18,24 +19,28 @@
public class ConcretePolymorphicMethodState extends ConcreteMethodState
implements ConcretePolymorphicMethodStateOrBottom, ConcretePolymorphicMethodStateOrUnknown {
- private final Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState;
+ private final Map<DynamicTypeWithUpperBound, ConcreteMonomorphicMethodStateOrUnknown>
+ receiverBoundsToState;
private ConcretePolymorphicMethodState(
- Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState) {
+ Map<DynamicTypeWithUpperBound, ConcreteMonomorphicMethodStateOrUnknown>
+ receiverBoundsToState) {
this.receiverBoundsToState = receiverBoundsToState;
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
}
private ConcretePolymorphicMethodState(
- DynamicType receiverBounds, ConcreteMonomorphicMethodStateOrUnknown methodState) {
+ DynamicTypeWithUpperBound receiverBounds,
+ ConcreteMonomorphicMethodStateOrUnknown methodState) {
this.receiverBoundsToState = new HashMap<>(1);
receiverBoundsToState.put(receiverBounds, methodState);
assert !isEffectivelyUnknown();
}
public static ConcretePolymorphicMethodStateOrUnknown create(
- DynamicType receiverBounds, ConcreteMonomorphicMethodStateOrUnknown methodState) {
+ DynamicTypeWithUpperBound receiverBounds,
+ ConcreteMonomorphicMethodStateOrUnknown methodState) {
return receiverBounds.isUnknown() && methodState.isUnknown()
? MethodState.unknown()
: new ConcretePolymorphicMethodState(receiverBounds, methodState);
@@ -44,7 +49,7 @@
private ConcretePolymorphicMethodStateOrUnknown add(
AppView<AppInfoWithLiveness> appView,
DexMethodSignature methodSignature,
- DynamicType bounds,
+ DynamicTypeWithUpperBound bounds,
ConcreteMonomorphicMethodStateOrUnknown methodState,
StateCloner cloner) {
assert !isEffectivelyBottom();
@@ -90,11 +95,12 @@
}
public void forEach(
- BiConsumer<? super DynamicType, ? super ConcreteMonomorphicMethodStateOrUnknown> consumer) {
+ BiConsumer<? super DynamicTypeWithUpperBound, ? super ConcreteMonomorphicMethodStateOrUnknown>
+ consumer) {
receiverBoundsToState.forEach(consumer);
}
- public MethodState getMethodStateForBounds(DynamicType dynamicType) {
+ public MethodState getMethodStateForBounds(DynamicTypeWithUpperBound dynamicType) {
ConcreteMonomorphicMethodStateOrUnknown methodStateForBounds =
receiverBoundsToState.get(dynamicType);
if (methodStateForBounds != null) {
@@ -115,7 +121,7 @@
public MethodState mutableCopy() {
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
- Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState =
+ Map<DynamicTypeWithUpperBound, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState =
new HashMap<>();
forEach((bounds, methodState) -> receiverBoundsToState.put(bounds, methodState.mutableCopy()));
return new ConcretePolymorphicMethodState(receiverBoundsToState);
@@ -123,16 +129,16 @@
public MethodState mutableCopyWithRewrittenBounds(
AppView<AppInfoWithLiveness> appView,
- Function<DynamicType, DynamicType> boundsRewriter,
+ Function<DynamicTypeWithUpperBound, DynamicTypeWithUpperBound> boundsRewriter,
DexMethodSignature methodSignature,
StateCloner cloner) {
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
- Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> rewrittenReceiverBoundsToState =
- new HashMap<>();
- for (Entry<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> entry :
+ Map<DynamicTypeWithUpperBound, ConcreteMonomorphicMethodStateOrUnknown>
+ rewrittenReceiverBoundsToState = new HashMap<>();
+ for (Entry<DynamicTypeWithUpperBound, ConcreteMonomorphicMethodStateOrUnknown> entry :
receiverBoundsToState.entrySet()) {
- DynamicType rewrittenBounds = boundsRewriter.apply(entry.getKey());
+ DynamicTypeWithUpperBound rewrittenBounds = boundsRewriter.apply(entry.getKey());
if (rewrittenBounds == null) {
continue;
}
@@ -160,7 +166,7 @@
assert !isEffectivelyUnknown();
assert !methodState.isEffectivelyBottom();
assert !methodState.isEffectivelyUnknown();
- for (Entry<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> entry :
+ for (Entry<DynamicTypeWithUpperBound, ConcreteMonomorphicMethodStateOrUnknown> entry :
methodState.receiverBoundsToState.entrySet()) {
ConcretePolymorphicMethodStateOrUnknown result =
add(appView, methodSignature, entry.getKey(), entry.getValue(), cloner);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
index 74c9107..4da32d6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
@@ -54,7 +54,7 @@
@Override
public Nullability getNullability() {
- return getDynamicType().getDynamicUpperBoundType().nullability();
+ return getDynamicType().getNullability();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
index 36525f5..085d4a0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
@@ -16,7 +16,7 @@
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePolymorphicMethodState;
@@ -46,7 +46,7 @@
// Argument information for virtual methods that is currently inactive, but should be propagated
// to all overrides below a given upper bound.
- final Map<DynamicType, MethodStateCollectionBySignature> inactiveUntilUpperBound =
+ final Map<DynamicTypeWithUpperBound, MethodStateCollectionBySignature> inactiveUntilUpperBound =
new HashMap<>();
PropagationState(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java
index f30aa8e..8e657dd 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java
@@ -11,7 +11,9 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class WideningUtils {
@@ -20,13 +22,11 @@
AppView<AppInfoWithLiveness> appView,
ProgramMethod resolvedMethod,
DynamicType dynamicReceiverType) {
- return shouldWidenDynamicType(
- appView,
- dynamicReceiverType,
- resolvedMethod.getHolderType(),
- Nullability.definitelyNotNull())
- ? DynamicType.unknown()
- : dynamicReceiverType;
+ return internalWidenDynamicClassType(
+ appView,
+ dynamicReceiverType,
+ resolvedMethod.getHolderType(),
+ Nullability.definitelyNotNull());
}
public static DynamicType widenDynamicNonReceiverType(
@@ -39,39 +39,38 @@
DynamicType dynamicType,
DexType staticType,
Nullability staticNullability) {
- return shouldWidenDynamicType(appView, dynamicType, staticType, staticNullability)
- ? DynamicType.unknown()
- : dynamicType;
+ return internalWidenDynamicClassType(appView, dynamicType, staticType, staticNullability);
}
- private static boolean shouldWidenDynamicType(
+ private static DynamicType internalWidenDynamicClassType(
AppView<AppInfoWithLiveness> appView,
DynamicType dynamicType,
DexType staticType,
Nullability staticNullability) {
assert staticType.isClassType();
- if (dynamicType.isUnknown()) {
- return true;
- }
if (dynamicType.isBottom()
|| dynamicType.isNullType()
- || dynamicType.getNullability().strictlyLessThan(staticNullability)) {
- return false;
+ || dynamicType.isNotNullType()
+ || dynamicType.isUnknown()) {
+ return dynamicType;
}
+ DynamicTypeWithUpperBound dynamicTypeWithUpperBound = dynamicType.asDynamicTypeWithUpperBound();
+ TypeElement dynamicUpperBoundType = dynamicTypeWithUpperBound.getDynamicUpperBoundType();
ClassTypeElement staticTypeElement =
staticType.toTypeElement(appView).asClassType().getOrCreateVariant(staticNullability);
- if (!dynamicType.getDynamicUpperBoundType().equals(staticTypeElement)) {
- return false;
+ if (dynamicType.getNullability().strictlyLessThan(staticNullability)) {
+ if (dynamicType.getNullability().isDefinitelyNotNull()
+ && dynamicUpperBoundType.equalUpToNullability(staticTypeElement)
+ && hasTrivialLowerBound(appView, dynamicType, staticType)) {
+ return DynamicType.definitelyNotNull();
+ }
+ return dynamicType;
+ }
+ if (!dynamicUpperBoundType.equals(staticTypeElement)) {
+ return dynamicType;
}
if (!dynamicType.hasDynamicLowerBoundType()) {
- return true;
- }
-
- DexClass staticTypeClass = appView.definitionFor(staticType);
- if (staticTypeClass == null || !staticTypeClass.isProgramClass()) {
- // TODO(b/190154391): If this is a library class with no program subtypes, then we might as
- // well widen to 'unknown'.
- return false;
+ return DynamicType.unknown();
}
// If the static type does not have any program subtypes, then widen the dynamic type to
@@ -80,6 +79,23 @@
// Note that if the static type is pinned, it could have subtypes outside the set of program
// classes, but in this case it is still unlikely that we can use the dynamic lower bound type
// information for anything, so we intentionally also widen to 'unknown' in this case.
+ return isEffectivelyFinal(appView, staticType) ? DynamicType.unknown() : dynamicType;
+ }
+
+ private static boolean hasTrivialLowerBound(
+ AppView<AppInfoWithLiveness> appView, DynamicType dynamicType, DexType staticType) {
+ return !dynamicType.hasDynamicLowerBoundType() || isEffectivelyFinal(appView, staticType);
+ }
+
+ private static boolean isEffectivelyFinal(
+ AppView<AppInfoWithLiveness> appView, DexType staticType) {
+ DexClass staticTypeClass = appView.definitionFor(staticType);
+ if (staticTypeClass == null) {
+ return false;
+ }
+ if (!staticTypeClass.isProgramClass()) {
+ return staticTypeClass.isFinal();
+ }
ObjectAllocationInfoCollection objectAllocationInfoCollection =
appView.appInfo().getObjectAllocationInfoCollection();
return !objectAllocationInfoCollection.hasInstantiatedStrictSubtype(
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 32654be..244257a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -385,13 +384,6 @@
}
}
- public static void clearAnnotations(AppView<?> appView) {
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- clazz.clearAnnotations();
- clazz.members().forEach(DexDefinition::clearAnnotations);
- }
- }
-
public static class Builder {
/**
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 61c9077..c34f7f3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
@@ -978,10 +979,7 @@
private boolean isInstantiatedDirectly(DexProgramClass clazz) {
assert checkIfObsolete();
DexType type = clazz.type;
- return
- // TODO(b/165224388): Synthetic classes should be represented in the allocation info.
- getSyntheticItems().isLegacySyntheticClass(clazz)
- || (!clazz.isInterface() && objectAllocationInfoCollection.isInstantiatedDirectly(clazz))
+ return (!clazz.isInterface() && objectAllocationInfoCollection.isInstantiatedDirectly(clazz))
// TODO(b/145344105): Model annotations in the object allocation info.
|| (clazz.isAnnotation() && liveTypes.contains(type));
}
@@ -1060,6 +1058,10 @@
&& !fieldAccessInfo.isWrittenOutside(method);
}
+ public boolean isInstanceFieldWrittenOnlyInInstanceInitializers(DexClassAndField field) {
+ return isInstanceFieldWrittenOnlyInInstanceInitializers(field.getDefinition());
+ }
+
public boolean isInstanceFieldWrittenOnlyInInstanceInitializers(DexEncodedField field) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
@@ -1269,7 +1271,7 @@
getClassToFeatureSplitMap().rewrittenWithLens(lens),
getMainDexInfo().rewrittenWithLens(getSyntheticItems(), lens),
deadProtoTypes,
- getMissingClasses().commitSyntheticItems(committedItems),
+ getMissingClasses(),
lens.rewriteReferences(liveTypes),
lens.rewriteReferences(targetedMethods),
lens.rewriteReferences(failedMethodResolutionTargets),
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index a5b539b..597a07d 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -181,6 +181,7 @@
@Override
public void registerExceptionGuard(DexType guard) {
+ setMaxApiReferenceLevel(guard);
enqueuer.traceExceptionGuard(guard, getContext());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 140b86b..312b61e 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -74,6 +74,16 @@
allowFieldTypeStrengthening = original.internalIsFieldTypeStrengtheningAllowed();
}
+ @Override
+ public Builder makeTop() {
+ return super.makeTop().disallowFieldTypeStrengthening();
+ }
+
+ @Override
+ public Builder makeBottom() {
+ return super.makeBottom().allowFieldTypeStrengthening();
+ }
+
public boolean isFieldTypeStrengtheningAllowed() {
return allowFieldTypeStrengthening;
}
@@ -112,6 +122,12 @@
}
@Override
+ boolean internalIsEqualTo(KeepFieldInfo other) {
+ return super.internalIsEqualTo(other)
+ && isFieldTypeStrengtheningAllowed() == other.internalIsFieldTypeStrengtheningAllowed();
+ }
+
+ @Override
public KeepFieldInfo doBuild() {
return new KeepFieldInfo(this);
}
@@ -136,7 +152,10 @@
@Override
public Joiner merge(Joiner joiner) {
// Should be extended to merge the fields of this class in case any are added.
- return super.merge(joiner);
+ return super.merge(joiner)
+ .applyIf(
+ !joiner.builder.isFieldTypeStrengtheningAllowed(),
+ KeepFieldInfo.Joiner::disallowFieldTypeStrengthening);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index ac8f1fb..5e33712 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramDerivedContext;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.ImmutableSet;
@@ -51,13 +50,6 @@
return new MissingClasses(Sets.newIdentityHashSet());
}
- public MissingClasses commitSyntheticItems(CommittedItems committedItems) {
- return builder()
- // TODO(b/175542052): Synthetic types should not be reported as missing in the first place.
- .removeAlreadyMissingClasses(committedItems.getLegacySyntheticTypes())
- .build();
- }
-
public void forEach(Consumer<DexType> missingClassConsumer) {
missingClasses.forEach(missingClassConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 9822a54..3155b54 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -387,7 +387,7 @@
if (!appInfo
.getClassToFeatureSplitMap()
- .isInSameFeatureOrBothInBase(sourceClass, targetClass, appView.getSyntheticItems())) {
+ .isInSameFeatureOrBothInSameBase(sourceClass, targetClass, appView.getSyntheticItems())) {
return false;
}
if (appView.appServices().allServiceTypes().contains(sourceClass.type)
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index f71c94e..1e4071f 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -54,11 +54,6 @@
return committedProgramTypes;
}
- @Deprecated
- public Collection<DexType> getLegacySyntheticTypes() {
- return committed.getLegacyTypes().keySet();
- }
-
@Override
public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
// All synthetic types are committed to the application so lookup is just the base lookup.
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
index fd7437e..6961fb0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -34,7 +34,6 @@
private final CommittedSyntheticsCollection parent;
private Map<DexType, List<SyntheticProgramClassReference>> newNonLegacyClasses = null;
private Map<DexType, List<SyntheticMethodReference>> newNonLegacyMethods = null;
- private Map<DexType, List<LegacySyntheticReference>> newLegacyClasses = null;
private ImmutableSet.Builder<DexType> newSyntheticInputs = null;
public Builder(CommittedSyntheticsCollection parent) {
@@ -76,28 +75,6 @@
return this;
}
- public Builder addLegacyClasses(Map<DexType, LegacySyntheticDefinition> classes) {
- if (newLegacyClasses == null) {
- newLegacyClasses = new IdentityHashMap<>();
- }
- classes.forEach(
- (type, item) ->
- newLegacyClasses
- .computeIfAbsent(type, ignore -> new ArrayList<>())
- .add(item.toReference()));
- return this;
- }
-
- public Builder addLegacyClass(LegacySyntheticReference reference) {
- if (newLegacyClasses == null) {
- newLegacyClasses = new IdentityHashMap<>();
- }
- newLegacyClasses
- .computeIfAbsent(reference.getHolder(), ignore -> new ArrayList<>())
- .add(reference);
- return this;
- }
-
public Builder addSyntheticInput(DexType syntheticInput) {
if (newSyntheticInputs == null) {
newSyntheticInputs = ImmutableSet.builder();
@@ -116,26 +93,21 @@
if (newNonLegacyMethods != null) {
newSyntheticInputs.addAll(newNonLegacyMethods.keySet());
}
- if (newLegacyClasses != null) {
- newSyntheticInputs.addAll(newLegacyClasses.keySet());
- }
return this;
}
public CommittedSyntheticsCollection build() {
- if (newNonLegacyClasses == null && newNonLegacyMethods == null && newLegacyClasses == null) {
+ if (newNonLegacyClasses == null && newNonLegacyMethods == null) {
return parent;
}
ImmutableMap<DexType, List<SyntheticProgramClassReference>> allNonLegacyClasses =
merge(newNonLegacyClasses, parent.nonLegacyClasses);
ImmutableMap<DexType, List<SyntheticMethodReference>> allNonLegacyMethods =
merge(newNonLegacyMethods, parent.nonLegacyMethods);
- ImmutableMap<DexType, List<LegacySyntheticReference>> allLegacyClasses =
- merge(newLegacyClasses, parent.legacyTypes);
ImmutableSet<DexType> allSyntheticInputs =
newSyntheticInputs == null ? parent.syntheticInputs : newSyntheticInputs.build();
return new CommittedSyntheticsCollection(
- allLegacyClasses, allNonLegacyMethods, allNonLegacyClasses, allSyntheticInputs);
+ allNonLegacyMethods, allNonLegacyClasses, allSyntheticInputs);
}
}
@@ -151,15 +123,7 @@
}
private static final CommittedSyntheticsCollection EMPTY =
- new CommittedSyntheticsCollection(
- ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of());
-
- /**
- * Immutable set of synthetic types in the application (eg, committed).
- *
- * <p>TODO(b/158159959): Remove legacy support.
- */
- private final ImmutableMap<DexType, List<LegacySyntheticReference>> legacyTypes;
+ new CommittedSyntheticsCollection(ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of());
/** Mapping from synthetic type to its synthetic method item description. */
private final ImmutableMap<DexType, List<SyntheticMethodReference>> nonLegacyMethods;
@@ -171,11 +135,9 @@
public final ImmutableSet<DexType> syntheticInputs;
public CommittedSyntheticsCollection(
- ImmutableMap<DexType, List<LegacySyntheticReference>> legacyTypes,
ImmutableMap<DexType, List<SyntheticMethodReference>> nonLegacyMethods,
ImmutableMap<DexType, List<SyntheticProgramClassReference>> nonLegacyClasses,
ImmutableSet<DexType> syntheticInputs) {
- this.legacyTypes = legacyTypes;
this.nonLegacyMethods = nonLegacyMethods;
this.nonLegacyClasses = nonLegacyClasses;
this.syntheticInputs = syntheticInputs;
@@ -185,7 +147,6 @@
private boolean verifySyntheticInputsSubsetOfSynthetics() {
Set<DexType> synthetics =
ImmutableSet.<DexType>builder()
- .addAll(legacyTypes.keySet())
.addAll(nonLegacyMethods.keySet())
.addAll(nonLegacyClasses.keySet())
.build();
@@ -206,14 +167,13 @@
}
boolean isEmpty() {
- boolean empty =
- legacyTypes.isEmpty() && nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
+ boolean empty = nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
assert !empty || syntheticInputs.isEmpty();
return empty;
}
boolean containsType(DexType type) {
- return containsLegacyType(type) || containsNonLegacyType(type);
+ return containsNonLegacyType(type);
}
boolean containsTypeOfKind(DexType type, SyntheticKind kind) {
@@ -229,10 +189,6 @@
return false;
}
- public boolean containsLegacyType(DexType type) {
- return legacyTypes.containsKey(type);
- }
-
public boolean containsNonLegacyType(DexType type) {
return nonLegacyMethods.containsKey(type) || nonLegacyClasses.containsKey(type);
}
@@ -241,14 +197,6 @@
return syntheticInputs.contains(type);
}
- public ImmutableMap<DexType, List<LegacySyntheticReference>> getLegacyTypes() {
- return legacyTypes;
- }
-
- public List<LegacySyntheticReference> getLegacyTypes(DexType type) {
- return legacyTypes.getOrDefault(type, emptyList());
- }
-
public ImmutableMap<DexType, List<SyntheticMethodReference>> getNonLegacyMethods() {
return nonLegacyMethods;
}
@@ -279,13 +227,6 @@
}
Builder builder = CommittedSyntheticsCollection.empty().builder();
boolean changed = false;
- for (LegacySyntheticReference reference : IterableUtils.flatten(legacyTypes.values())) {
- if (removed.contains(reference.getHolder())) {
- changed = true;
- } else {
- builder.addLegacyClass(reference);
- }
- }
for (SyntheticMethodReference reference : IterableUtils.flatten(nonLegacyMethods.values())) {
if (removed.contains(reference.getHolder())) {
changed = true;
@@ -314,7 +255,6 @@
CommittedSyntheticsCollection rewriteWithLens(NonIdentityGraphLens lens) {
ImmutableSet.Builder<DexType> syntheticInputsBuilder = ImmutableSet.builder();
return new CommittedSyntheticsCollection(
- rewriteItems(legacyTypes, lens, syntheticInputsBuilder),
rewriteItems(nonLegacyMethods, lens, syntheticInputsBuilder),
rewriteItems(nonLegacyClasses, lens, syntheticInputsBuilder),
syntheticInputsBuilder.build());
@@ -340,7 +280,6 @@
}
boolean verifyTypesAreInApp(DexApplication application) {
- assert verifyTypesAreInApp(application, legacyTypes.keySet());
assert verifyTypesAreInApp(application, nonLegacyMethods.keySet());
assert verifyTypesAreInApp(application, nonLegacyClasses.keySet());
assert verifyTypesAreInApp(application, syntheticInputs);
diff --git a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java
deleted file mode 100644
index 3e50bf5..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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.synthesis;
-
-import com.android.tools.r8.FeatureSplit;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.utils.IterableUtils;
-import com.google.common.collect.ImmutableSet;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class LegacySyntheticDefinition {
- private final DexProgramClass clazz;
- private final Map<DexType, FeatureSplit> contexts = new ConcurrentHashMap<>();
-
- public LegacySyntheticDefinition(DexProgramClass clazz) {
- this.clazz = clazz;
- }
-
- public void addContext(ProgramDefinition clazz, FeatureSplit featureSplit) {
- DexType type = clazz.getContextType();
- contexts.put(type, featureSplit);
- }
-
- public Set<DexType> getContexts() {
- return contexts.keySet();
- }
-
- public LegacySyntheticReference toReference() {
- return new LegacySyntheticReference(
- clazz.getType(), ImmutableSet.copyOf(contexts.keySet()), getFeatureSplit());
- }
-
- public FeatureSplit getFeatureSplit() {
- assert verifyConsistentFeatures();
- if (contexts.isEmpty()) {
- return FeatureSplit.BASE;
- }
- return IterableUtils.first(contexts.values());
- }
-
- private boolean verifyConsistentFeatures() {
- HashSet<FeatureSplit> features = new HashSet<>(contexts.values());
- assert features.size() < 2;
- return true;
- }
-
- public DexProgramClass getDefinition() {
- return clazz;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java
deleted file mode 100644
index 715e790..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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.synthesis;
-
-import com.android.tools.r8.FeatureSplit;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import java.util.Set;
-
-public class LegacySyntheticReference implements Rewritable<LegacySyntheticReference> {
- private final DexType type;
- private final Set<DexType> contexts;
- private final FeatureSplit featureSplit;
-
- public LegacySyntheticReference(DexType type, Set<DexType> contexts, FeatureSplit featureSplit) {
- this.type = type;
- this.contexts = contexts;
- this.featureSplit = featureSplit;
- }
-
- @Override
- public DexType getHolder() {
- return type;
- }
-
- public Set<DexType> getContexts() {
- return contexts;
- }
-
- public FeatureSplit getFeatureSplit() {
- return featureSplit;
- }
-
- @Override
- public LegacySyntheticReference rewrite(NonIdentityGraphLens lens) {
- DexType rewrittenType = lens.lookupType(type);
- Set<DexType> rewrittenContexts = lens.rewriteTypes(getContexts());
- if (type == rewrittenType && contexts.equals(rewrittenContexts)) {
- return this;
- }
- return new LegacySyntheticReference(rewrittenType, rewrittenContexts, featureSplit);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 3bf8883..9fe1fb9 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -155,7 +155,7 @@
public String toString() {
return "SynthesizingContext{"
+ getSynthesizingContextType()
- + (featureSplit != FeatureSplit.BASE ? ", feature:" + featureSplit : "")
+ + (!featureSplit.isBase() ? ", feature:" + featureSplit : "")
+ "}";
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 0ce464d..9548f78 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -269,8 +269,7 @@
new CommittedItems(
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
application,
- new CommittedSyntheticsCollection(
- committed.getLegacyTypes(), finalMethods, finalClasses, finalInputSynthetics),
+ new CommittedSyntheticsCollection(finalMethods, finalClasses, finalInputSynthetics),
ImmutableList.of()),
syntheticFinalizationGraphLens,
PrunedItems.builder().setPrunedApp(application).addRemovedClasses(prunedSynthetics).build(),
@@ -330,13 +329,6 @@
private boolean verifyOneSyntheticPerSyntheticClass() {
Set<DexType> seen = Sets.newIdentityHashSet();
committed
- .getLegacyTypes()
- .forEach(
- (type, references) -> {
- assert seen.add(type);
- assert references.size() == 1;
- });
- committed
.getNonLegacyClasses()
.forEach(
(type, references) -> {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index caf0f42..f8e241b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -41,7 +41,6 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -59,23 +58,17 @@
/** Collection of pending items. */
private static class PendingSynthetics {
- /**
- * Thread safe collection of synthesized classes that are not yet committed to the application.
- *
- * <p>TODO(b/158159959): Remove legacy support.
- */
- private final Map<DexType, LegacySyntheticDefinition> legacyClasses = new ConcurrentHashMap<>();
/** Thread safe collection of synthetic items not yet committed to the application. */
private final ConcurrentHashMap<DexType, SyntheticDefinition<?, ?, ?>> nonLegacyDefinitions =
new ConcurrentHashMap<>();
boolean isEmpty() {
- return legacyClasses.isEmpty() && nonLegacyDefinitions.isEmpty();
+ return nonLegacyDefinitions.isEmpty();
}
boolean containsType(DexType type) {
- return legacyClasses.containsKey(type) || nonLegacyDefinitions.containsKey(type);
+ return nonLegacyDefinitions.containsKey(type);
}
boolean containsTypeOfKind(DexType type, SyntheticKind kind) {
@@ -84,22 +77,17 @@
}
boolean verifyNotRewritten(NonIdentityGraphLens lens) {
- assert legacyClasses.keySet().equals(lens.rewriteTypes(legacyClasses.keySet()));
assert nonLegacyDefinitions.keySet().equals(lens.rewriteTypes(nonLegacyDefinitions.keySet()));
return true;
}
Collection<DexProgramClass> getAllProgramClasses() {
- List<DexProgramClass> allPending =
- new ArrayList<>(nonLegacyDefinitions.size() + legacyClasses.size());
+ List<DexProgramClass> allPending = new ArrayList<>(nonLegacyDefinitions.size());
for (SyntheticDefinition<?, ?, ?> item : nonLegacyDefinitions.values()) {
if (item.isProgramDefinition()) {
allPending.add(item.asProgramDefinition().getHolder());
}
}
- for (LegacySyntheticDefinition legacy : legacyClasses.values()) {
- allPending.add(legacy.getDefinition());
- }
return Collections.unmodifiableList(allPending);
}
}
@@ -189,22 +177,16 @@
public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
DexClass clazz = null;
SyntheticKind kind = null;
- LegacySyntheticDefinition legacyItem = pending.legacyClasses.get(type);
- if (legacyItem != null) {
- clazz = legacyItem.getDefinition();
- } else {
- SyntheticDefinition<?, ?, ?> item = pending.nonLegacyDefinitions.get(type);
- if (item != null) {
- clazz = item.getHolder();
- kind = item.getKind();
- assert clazz.isProgramClass() == item.isProgramDefinition();
- assert clazz.isClasspathClass() == item.isClasspathDefinition();
- }
+ SyntheticDefinition<?, ?, ?> item = pending.nonLegacyDefinitions.get(type);
+ if (item != null) {
+ clazz = item.getHolder();
+ kind = item.getKind();
+ assert clazz.isProgramClass() == item.isProgramDefinition();
+ assert clazz.isClasspathClass() == item.isClasspathDefinition();
}
if (clazz != null) {
- assert legacyItem != null || kind != null;
- assert baseDefinitionFor.apply(type) == null
- || (kind != null && kind.mayOverridesNonProgramType)
+ assert kind != null;
+ assert baseDefinitionFor.apply(type) == null || kind.mayOverridesNonProgramType
: "Pending synthetic definition also present in the active program: " + type;
return clazz;
}
@@ -227,10 +209,6 @@
return committed.containsType(type);
}
- private boolean isLegacyCommittedSynthetic(DexType type) {
- return committed.containsLegacyType(type);
- }
-
private boolean isNonLegacyCommittedSynthetic(DexType type) {
return committed.containsNonLegacyType(type);
}
@@ -239,22 +217,10 @@
return pending.containsType(type);
}
- private boolean isLegacyPendingSynthetic(DexType type) {
- return pending.legacyClasses.containsKey(type);
- }
-
private boolean isNonLegacyPendingSynthetic(DexType type) {
return pending.nonLegacyDefinitions.containsKey(type);
}
- public boolean isLegacySyntheticClass(DexType type) {
- return isLegacyCommittedSynthetic(type) || isLegacyPendingSynthetic(type);
- }
-
- public boolean isLegacySyntheticClass(DexProgramClass clazz) {
- return isLegacySyntheticClass(clazz.getType());
- }
-
public boolean isNonLegacySynthetic(DexProgramClass clazz) {
return isNonLegacySynthetic(clazz.type);
}
@@ -291,7 +257,7 @@
}
public boolean isSyntheticClass(DexType type) {
- return isLegacySyntheticClass(type) || isNonLegacySynthetic(type);
+ return isNonLegacySynthetic(type);
}
public boolean isSyntheticClass(DexProgramClass clazz) {
@@ -306,21 +272,14 @@
return committed.containsSyntheticInput(clazz.getType());
}
- public FeatureSplit getContextualFeatureSplit(DexType type) {
- if (pending.legacyClasses.containsKey(type)) {
- LegacySyntheticDefinition definition = pending.legacyClasses.get(type);
- return definition.getFeatureSplit();
- }
- if (committed.containsLegacyType(type)) {
- List<LegacySyntheticReference> types = committed.getLegacyTypes(type);
- if (types.isEmpty()) {
- return null;
- }
- assert verifyAllHaveSameFeature(types, LegacySyntheticReference::getFeatureSplit);
- return types.get(0).getFeatureSplit();
- }
+ public FeatureSplit getContextualFeatureSplit(
+ DexType type, ClassToFeatureSplitMap classToFeatureSplitMap) {
if (isSyntheticOfKind(type, SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS)) {
- return FeatureSplit.BASE;
+ // Use the startup base if there is one, such that we don't merge non-startup classes with the
+ // shared utility class in case it is used during startup. The use of base startup allows for
+ // merging startup classes with the shared utility class, however, which could be bad for
+ // startup if the shared utility class is not used during startup.
+ return classToFeatureSplitMap.getBaseStartup();
}
List<SynthesizingContext> contexts = getSynthesizingContexts(type);
if (contexts.isEmpty()) {
@@ -358,13 +317,6 @@
ImmutableList.Builder<DexType> builder = ImmutableList.builder();
forEachSynthesizingContext(
type, synthesizingContext -> builder.add(synthesizingContext.getSynthesizingContextType()));
- for (LegacySyntheticReference legacyReference : committed.getLegacyTypes(type)) {
- builder.addAll(legacyReference.getContexts());
- }
- LegacySyntheticDefinition legacyDefinition = pending.legacyClasses.get(type);
- if (legacyDefinition != null) {
- builder.addAll(legacyDefinition.getContexts());
- }
return builder.build();
}
@@ -416,10 +368,6 @@
return true;
}
- public Collection<DexProgramClass> getLegacyPendingClasses() {
- return ListUtils.map(pending.legacyClasses.values(), LegacySyntheticDefinition::getDefinition);
- }
-
private SynthesizingContext getSynthesizingContext(
ProgramDefinition context, AppView<?> appView) {
return getSynthesizingContext(
@@ -451,29 +399,6 @@
// Addition and creation of synthetic items.
- public void addLegacySyntheticClassForLibraryDesugaring(DexProgramClass clazz) {
- internalAddLegacySyntheticClass(clazz);
- // No context information is added for library context.
- // This is intended only to support desugared-library compilation.
- }
-
- // TODO(b/158159959): Remove the usage of this direct class addition.
- public void addLegacySyntheticClass(
- DexProgramClass clazz, ProgramDefinition context, FeatureSplit featureSplit) {
- LegacySyntheticDefinition legacyItem = internalAddLegacySyntheticClass(clazz);
- legacyItem.addContext(context, featureSplit);
- }
-
- private LegacySyntheticDefinition internalAddLegacySyntheticClass(DexProgramClass clazz) {
- assert !isCommittedSynthetic(clazz.type);
- assert !pending.nonLegacyDefinitions.containsKey(clazz.type);
- LegacySyntheticDefinition legacyItem =
- pending.legacyClasses.computeIfAbsent(
- clazz.getType(), type -> new LegacySyntheticDefinition(clazz));
- assert legacyItem.getDefinition() == clazz;
- return legacyItem;
- }
-
private DexProgramClass internalEnsureDexProgramClass(
SyntheticKind kind,
Consumer<SyntheticProgramClassBuilder> classConsumer,
@@ -824,9 +749,6 @@
DexApplication application = prunedItems.getPrunedApp();
Set<DexType> removedClasses = prunedItems.getNoLongerSyntheticItems();
CommittedSyntheticsCollection.Builder builder = committed.builder();
- // Legacy synthetics must already have been committed to the app.
- assert verifyClassesAreInApp(application, pending.legacyClasses.values());
- builder.addLegacyClasses(pending.legacyClasses);
// Compute the synthetic additions and add them to the application.
ImmutableList<DexType> committedProgramTypes;
DexApplication amendedApplication;
@@ -863,15 +785,6 @@
committedProgramTypes);
}
- private static boolean verifyClassesAreInApp(
- DexApplication app, Collection<LegacySyntheticDefinition> classes) {
- for (LegacySyntheticDefinition item : classes) {
- DexProgramClass clazz = item.getDefinition();
- assert app.programDefinitionFor(clazz.type) != null : "Missing synthetic: " + clazz.type;
- }
- return true;
- }
-
public void writeAttributeIfIntermediateSyntheticClass(
ClassWriter writer, DexProgramClass clazz, AppView<?> appView) {
if (!appView.options().intermediate || !appView.options().isGeneratingClassFiles()) {
diff --git a/src/main/java/com/android/tools/r8/utils/AccessUtils.java b/src/main/java/com/android/tools/r8/utils/AccessUtils.java
new file mode 100644
index 0000000..0498949
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/AccessUtils.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+
+public class AccessUtils {
+
+ public static boolean isAccessibleInSameContextsAs(
+ DexType newType, DexType oldType, DexDefinitionSupplier definitions) {
+ DexItemFactory dexItemFactory = definitions.dexItemFactory();
+ DexType newBaseType = newType.toBaseType(dexItemFactory);
+ if (!newBaseType.isClassType()) {
+ return true;
+ }
+ DexClass newBaseClass = definitions.definitionFor(newBaseType);
+ if (newBaseClass == null) {
+ return false;
+ }
+ if (newBaseClass.isPublic()) {
+ return true;
+ }
+ DexType oldBaseType = oldType.toBaseType(dexItemFactory);
+ assert oldBaseType.isClassType();
+ DexClass oldBaseClass = definitions.definitionFor(oldBaseType);
+ if (oldBaseClass == null || oldBaseClass.isPublic()) {
+ return false;
+ }
+ return newBaseType.isSamePackage(oldBaseType);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index b1a2757..959238e 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.DexItemFactory;
@@ -528,6 +529,7 @@
dumpProgramResources(
dumpProgramFileName,
options.getFeatureSplitConfiguration(),
+ options.getStartupConfiguration(),
nextDexIndex,
out,
reporter,
@@ -581,6 +583,7 @@
private int dumpProgramResources(
String archiveName,
FeatureSplitConfiguration featureSplitConfiguration,
+ StartupConfiguration startupConfiguration,
int nextDexIndex,
ZipOutputStream out,
Reporter reporter,
@@ -595,7 +598,7 @@
try {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(
- dexItemFactory, featureSplitConfiguration, reporter);
+ dexItemFactory, featureSplitConfiguration, startupConfiguration, reporter);
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream();
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 de70549..fadaaa0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -159,6 +160,7 @@
public DataResourceConsumer dataResourceConsumer;
public FeatureSplitConfiguration featureSplitConfiguration;
+ public StartupConfiguration startupConfiguration;
public List<Consumer<InspectorImpl>> outputInspections = Collections.emptyList();
@@ -1637,7 +1639,6 @@
public boolean allowInvokeErrors = false;
public boolean allowUnnecessaryDontWarnWildcards = true;
public boolean allowUnusedDontWarnRules = true;
- public boolean disableL8AnnotationRemoval = false;
public boolean reportUnusedProguardConfigurationRules = false;
public boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding = true;
public boolean alwaysUsePessimisticRegisterAllocation = false;
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
index c1046e9..5367c26 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -371,6 +371,15 @@
stack.push(child);
}
+ public <E extends Exception> void time(String title, ThrowingAction<E> action) throws E {
+ begin(title);
+ try {
+ action.execute();
+ } finally {
+ end();
+ }
+ }
+
public <T> T time(String title, Supplier<T> supplier) {
begin(title);
try {
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 3cd4941..9b81396 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MAX_SUPPORTED_VERSION;
import static com.android.tools.r8.ToolHelper.isWindows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -303,9 +304,17 @@
/** Compile asserting success and return the output path. */
public Path compile() throws IOException {
+ return compile(false);
+ }
+
+ public Path compile(boolean expectingFailure) throws IOException {
Path output = getOrCreateOutputPath();
ProcessResult result = compileInternal(output);
- assertEquals(result.toString(), result.exitCode, 0);
+ if (expectingFailure) {
+ assertNotEquals(result.toString(), result.exitCode, 0);
+ } else {
+ assertEquals(result.toString(), result.exitCode, 0);
+ }
return output;
}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index 2ac3f87..0ddd80e 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -52,6 +52,10 @@
return !isNewerThanOrEqualTo(otherVersion);
}
+ public boolean isOlderThanMinSupported() {
+ return isOlderThan(KotlinCompilerVersion.MIN_SUPPORTED_VERSION);
+ }
+
public boolean isFirst() {
return index == 0;
}
@@ -68,6 +72,7 @@
public static class Builder {
private Predicate<KotlinCompilerVersion> compilerFilter = c -> false;
+ private Predicate<KotlinCompilerVersion> oldCompilerFilter = c -> true;
private Predicate<KotlinTargetVersion> targetVersionFilter = t -> false;
private boolean withDevCompiler =
System.getProperty("com.android.tools.r8.kotlincompilerdev") != null;
@@ -105,11 +110,21 @@
return this;
}
+ public Builder withOldCompilers() {
+ this.withOldCompilers = true;
+ return this;
+ }
+
public Builder withOldCompilersIfSet() {
assumeTrue(withOldCompilers);
return this;
}
+ public Builder withOldCompilersStartingFrom(KotlinCompilerVersion minOldVersion) {
+ oldCompilerFilter = oldCompilerFilter.and(v -> v.isGreaterThanOrEqualTo(minOldVersion));
+ return this;
+ }
+
public Builder withAllTargetVersions() {
withTargetVersionFilter(t -> t != KotlinTargetVersion.NONE);
return this;
@@ -139,8 +154,8 @@
compilerVersions =
Arrays.stream(KotlinCompilerVersion.values())
.filter(c -> c.isLessThan(KotlinCompilerVersion.MIN_SUPPORTED_VERSION))
+ .filter(c -> oldCompilerFilter.test(c))
.collect(Collectors.toList());
-
} else {
compilerVersions =
KotlinCompilerVersion.getSupported().stream()
@@ -162,7 +177,7 @@
}
}
}
- assert !testParameters.isEmpty();
+ assert !testParameters.isEmpty() || withOldCompilers;
return new KotlinTestParametersCollection(testParameters);
}
}
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 072da5a..e672f87 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -139,11 +139,6 @@
return this;
}
- public L8TestBuilder setDisableL8AnnotationRemoval(boolean disableL8AnnotationRemoval) {
- return addOptionsModifier(
- options -> options.testing.disableL8AnnotationRemoval = disableL8AnnotationRemoval);
- }
-
public L8TestCompileResult compile()
throws IOException, CompilationFailedException, ExecutionException {
// We wrap exceptions in a RuntimeException to call this from a lambda.
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index f0c648b..ccdd0ca 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -2237,7 +2237,7 @@
builder.appendArtOption("-Xnoimage-dex2oat");
}
for (String s : ToolHelper.getBootLibs(dexVm)) {
- builder.appendBootClassPath(new File(s).getCanonicalPath());
+ builder.appendBootClasspath(new File(s).getCanonicalPath());
}
builder.setMainClass(JUNIT_TEST_RUNNER);
builder.appendProgramArgument(fullClassName);
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 0480694..b96cef4 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -179,7 +179,7 @@
for (int i = 2; i < args.length; i++) {
args[i] = featureDependencies[i - 2].toString();
}
- return runArt(runtime, additionalRunClassPath, mainClassSubject.getFinalName(), args);
+ return runArt(runtime, mainClassSubject.getFinalName(), args);
}
public String getProguardMap() {
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 9ab97e7..af079d9 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ObjectArrays;
+import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
@@ -56,6 +57,7 @@
public final int minApiLevel;
private final OutputMode outputMode;
final List<Path> additionalRunClassPath = new ArrayList<>();
+ final List<Path> additionalBootClasspath = new ArrayList<>();
final List<String> vmArguments = new ArrayList<>();
private boolean withArt6Plus64BitsLib = false;
private boolean withArtFrameworks = true;
@@ -164,12 +166,10 @@
case DEX:
return runArt(
new DexRuntime(ToolHelper.getDexVm()),
- additionalRunClassPath,
mainClassSubject.getFinalName());
case CF:
return runJava(
TestRuntime.getDefaultJavaRuntime(),
- additionalRunClassPath,
mainClassSubject.getFinalName());
default:
throw new Unreachable();
@@ -207,12 +207,11 @@
}
assertThat("Did you forget a keep rule for the main method?", mainClassSubject, isPresent());
if (runtime.isDex()) {
- return runArt(runtime, additionalRunClassPath, mainClassSubject.getFinalName(), args);
+ return runArt(runtime, mainClassSubject.getFinalName(), args);
}
assert runtime.isCf();
return runJava(
runtime,
- additionalRunClassPath,
ObjectArrays.concat(mainClassSubject.getFinalName(), args));
}
@@ -256,6 +255,38 @@
}
}
+ public CR addBootClasspathClasses(Class<?>... classes) throws Exception {
+ return addBootClasspathClasses(Arrays.asList(classes));
+ }
+
+ public CR addBootClasspathClasses(List<Class<?>> classes) throws Exception {
+ if (getBackend() == Backend.DEX) {
+ additionalBootClasspath.add(
+ testForD8(state.getTempFolder())
+ .addProgramClasses(classes)
+ .setMinApi(minApiLevel)
+ .compile()
+ .writeToZip());
+ return self();
+ }
+ assert getBackend() == Backend.CF;
+ try {
+ Path path = state.getNewTempFolder().resolve("runtime-classes.jar");
+ ArchiveConsumer consumer = new ArchiveConsumer(path);
+ for (Class<?> clazz : classes) {
+ consumer.accept(
+ ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
+ DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
+ null);
+ }
+ consumer.finished(null);
+ additionalBootClasspath.add(path);
+ return self();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
public CR addDesugaredCoreLibraryRunClassPath(
Function<AndroidApiLevel, Path> classPathSupplier, AndroidApiLevel minAPILevel) {
addRunClasspathFiles(classPathSupplier.apply(minAPILevel));
@@ -523,30 +554,29 @@
}
}
- private RR runJava(TestRuntime runtime, List<Path> additionalClassPath, String... arguments)
- throws IOException {
+ private RR runJava(TestRuntime runtime, String... arguments) throws IOException {
assert runtime != null;
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, OutputMode.ClassFile);
- List<Path> classPath = ImmutableList.<Path>builder()
- .addAll(additionalClassPath)
- .add(out)
- .build();
- ProcessResult result = ToolHelper.runJava(runtime.asCf(), vmArguments, classPath, arguments);
+ List<Path> classPath =
+ ImmutableList.<Path>builder().addAll(additionalRunClassPath).add(out).build();
+ ProcessResult result =
+ ToolHelper.runJava(
+ runtime.asCf(), vmArguments, additionalBootClasspath, classPath, arguments);
return createRunResult(runtime, result);
}
- RR runArt(
- TestRuntime runtime, List<Path> additionalClassPath, String mainClass, String... arguments)
- throws IOException {
+ RR runArt(TestRuntime runtime, String mainClass, String... arguments) throws IOException {
DexVm vm = runtime.asDex().getVm();
// TODO(b/127785410): Always assume a non-null runtime.
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, OutputMode.DexIndexed);
- List<String> classPath = ImmutableList.<String>builder()
- .addAll(additionalClassPath.stream().map(Path::toString).collect(Collectors.toList()))
- .add(out.toString())
- .build();
+ List<String> classPath =
+ ImmutableList.<String>builder()
+ .addAll(
+ additionalRunClassPath.stream().map(Path::toString).collect(Collectors.toList()))
+ .add(out.toString())
+ .build();
Consumer<ArtCommandBuilder> commandConsumer =
withArt6Plus64BitsLib && vm.getVersion().isNewerThanOrEqual(DexVm.Version.V6_0_1)
? builder -> builder.appendArtOption("--64")
@@ -554,6 +584,22 @@
commandConsumer =
commandConsumer.andThen(
builder -> {
+ if (!additionalBootClasspath.isEmpty()) {
+ DexVm dexVm = runtime.asDex().getVm();
+ if (dexVm.isNewerThan(DexVm.ART_4_4_4_HOST)) {
+ builder.appendArtOption("-Ximage:/system/non/existent/image.art");
+ builder.appendArtOption("-Xnoimage-dex2oat");
+ }
+ try {
+ for (String s : ToolHelper.getBootLibs(dexVm)) {
+ builder.appendBootClasspath(new File(s).getCanonicalPath());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException();
+ }
+ additionalBootClasspath.forEach(
+ path -> builder.appendBootClasspath(path.toString()));
+ }
for (String vmArgument : vmArguments) {
builder.appendArtOption(vmArgument);
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2d0cc12..57fabd1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -422,7 +422,7 @@
protected List<String> classpaths = new ArrayList<>();
protected String mainClass;
protected List<String> programArguments = new ArrayList<>();
- protected List<String> bootClassPaths = new ArrayList<>();
+ protected List<String> bootClasspaths = new ArrayList<>();
protected String executionDirectory;
public CommandBuilder appendArtOption(String option) {
@@ -450,8 +450,8 @@
return this;
}
- public CommandBuilder appendBootClassPath(String lib) {
- bootClassPaths.add(lib);
+ public CommandBuilder appendBootClasspath(String lib) {
+ bootClasspaths.add(lib);
return this;
}
@@ -475,13 +475,13 @@
builder.append(entry.getValue());
result.add(builder.toString());
}
+ if (!bootClasspaths.isEmpty()) {
+ result.add("-Xbootclasspath:" + String.join(":", bootClasspaths));
+ }
if (!classpaths.isEmpty()) {
result.add("-cp");
result.add(String.join(":", classpaths));
}
- if (!bootClassPaths.isEmpty()) {
- result.add("-Xbootclasspath:" + String.join(":", bootClassPaths));
- }
if (mainClass != null) {
result.add(mainClass);
}
@@ -555,7 +555,7 @@
.setVmOptions(options)
.setSystemProperties(systemProperties)
.setClasspath(toFileList(classpaths))
- .setBootClasspath(toFileList(bootClassPaths))
+ .setBootClasspath(toFileList(bootClasspaths))
.setMainClass(mainClass)
.setProgramArguments(programArguments);
}
@@ -1416,12 +1416,28 @@
public static ProcessResult runJava(
CfRuntime runtime, List<String> vmArgs, List<Path> classpath, String... args)
throws IOException {
- String cp =
- classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR));
+ return runJava(runtime, vmArgs, ImmutableList.of(), classpath, args);
+ }
+
+ public static ProcessResult runJava(
+ CfRuntime runtime,
+ List<String> vmArgs,
+ List<Path> bootClasspaths,
+ List<Path> classpath,
+ String... args)
+ throws IOException {
List<String> cmdline = new ArrayList<>(Arrays.asList(runtime.getJavaExecutable().toString()));
cmdline.addAll(vmArgs);
+ if (!bootClasspaths.isEmpty()) {
+ cmdline.add(
+ "-Xbootclasspath/a:"
+ + bootClasspaths.stream()
+ .map(Path::toString)
+ .collect(Collectors.joining(CLASSPATH_SEPARATOR)));
+ }
cmdline.add("-cp");
- cmdline.add(cp);
+ cmdline.add(
+ classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR)));
cmdline.addAll(Arrays.asList(args));
ProcessBuilder builder = new ProcessBuilder(cmdline);
return runProcess(builder);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingInterfaceTest.java
new file mode 100644
index 0000000..4f89220
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingInterfaceTest.java
@@ -0,0 +1,93 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelInlineMissingInterfaceTest extends TestBase {
+
+ private final AndroidApiLevel libraryAdditionApiLevel = AndroidApiLevel.M;
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ boolean isApiLevel =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryAdditionApiLevel);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ProgramClass.class, VerificationError.class, Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, libraryAdditionApiLevel))
+ .apply(
+ setMockApiLevelForDefaultInstanceInitializer(
+ LibraryClass.class, libraryAdditionApiLevel))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // Dex2Oat do not complain about missing super interfaces since these are not verified
+ // the same way as super types. We therefore expect the class to be absent.
+ assertThat(inspector.clazz(VerificationError.class), Matchers.isAbsent());
+ })
+ .applyIf(isApiLevel, b -> b.addRunClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(isApiLevel, "Hello World")
+ .assertSuccessWithOutputLinesIf(!isApiLevel, "Lower Api Level");
+ }
+
+ public interface LibraryClass {}
+
+ public static class ProgramClass implements LibraryClass {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("Hello World");
+ }
+ }
+
+ public static class VerificationError {
+
+ public static ProgramClass create() {
+ return new ProgramClass();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ VerificationError.create().foo();
+ } else {
+ System.out.println("Lower Api Level");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java
new file mode 100644
index 0000000..4e05ffb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java
@@ -0,0 +1,92 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelInlineMissingSuperTypeTest extends TestBase {
+
+ private final AndroidApiLevel libraryAdditionApiLevel = AndroidApiLevel.M;
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ boolean isMockedApiLevel =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryAdditionApiLevel);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ProgramClass.class, VerificationError.class, Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, libraryAdditionApiLevel))
+ .apply(
+ setMockApiLevelForDefaultInstanceInitializer(
+ LibraryClass.class, libraryAdditionApiLevel))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/207832084): Should be present and only inlined from M.
+ assertThat(inspector.clazz(VerificationError.class), Matchers.isAbsent());
+ })
+ .applyIf(isMockedApiLevel, b -> b.addRunClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(isMockedApiLevel, "Hello World")
+ .assertSuccessWithOutputLinesIf(!isMockedApiLevel, "Lower Api Level");
+ }
+
+ public static class LibraryClass {}
+
+ public static class ProgramClass extends LibraryClass {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("Hello World");
+ }
+ }
+
+ public static class VerificationError {
+
+ public static ProgramClass create() {
+ return new ProgramClass();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ VerificationError.create().foo();
+ } else {
+ System.out.println("Lower Api Level");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
new file mode 100644
index 0000000..4125cc2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -0,0 +1,85 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelMockClassTest extends TestBase {
+
+ private final AndroidApiLevel mockLevel = AndroidApiLevel.M;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12.
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean isMockApiLevel =
+ parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
+ .compile()
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel),
+ b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(isMockApiLevel, "LibraryClass::foo")
+ .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
+ .inspect(
+ inspector ->
+ // TODO(b/204982782): These should be stubbed for api-level 1-23.
+ assertThat(inspector.clazz(LibraryClass.class), Matchers.isAbsent()));
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ public void foo() {
+ System.out.println("LibraryClass::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ new LibraryClass().foo();
+ } else {
+ System.out.println("Hello World");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
new file mode 100644
index 0000000..46a9fe8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -0,0 +1,88 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelMockInheritedClassTest extends TestBase {
+
+ private final AndroidApiLevel mockLevel = AndroidApiLevel.M;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12.
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean isMockApiLevel =
+ parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, ProgramClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(ProgramClass.class)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
+ .compile()
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel),
+ b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
+ .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
+ .inspect(
+ inspector ->
+ // TODO(b/204982782): These should be stubbed for api-level 1-23.
+ assertThat(inspector.clazz(LibraryClass.class), Matchers.isAbsent()));
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {}
+
+ public static class ProgramClass extends LibraryClass {
+
+ public void foo() {
+ System.out.println("ProgramClass::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ new ProgramClass().foo();
+ } else {
+ System.out.println("Hello World");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java
new file mode 100644
index 0000000..6b91898
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMethodMissingClassTest.java
@@ -0,0 +1,128 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelMockMethodMissingClassTest extends TestBase {
+
+ private final AndroidApiLevel initialLibraryMockLevel = AndroidApiLevel.M;
+ private final AndroidApiLevel finalLibraryMethodLevel = AndroidApiLevel.O_MR1;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12.
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean preMockApis =
+ parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(initialLibraryMockLevel);
+ boolean postMockApis =
+ !preMockApis && parameters.getApiLevel().isGreaterThanOrEqualTo(finalLibraryMethodLevel);
+ boolean betweenMockApis = !preMockApis && !postMockApis;
+ Method addedOn23 = LibraryClass.class.getMethod("addedOn23");
+ Method adeddOn27 = LibraryClass.class.getMethod("addedOn27");
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, initialLibraryMockLevel))
+ .apply(
+ setMockApiLevelForDefaultInstanceInitializer(
+ LibraryClass.class, initialLibraryMockLevel))
+ .apply(setMockApiLevelForMethod(addedOn23, initialLibraryMockLevel))
+ .apply(setMockApiLevelForMethod(adeddOn27, finalLibraryMethodLevel))
+ .compile()
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(initialLibraryMockLevel),
+ b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(preMockApis, "Hello World")
+ .assertSuccessWithOutputLinesIf(
+ betweenMockApis,
+ "LibraryClass::addedOn23",
+ "LibraryClass::missingAndReferenced",
+ "Hello World")
+ .assertSuccessWithOutputLinesIf(
+ postMockApis,
+ "LibraryClass::addedOn23",
+ "LibraryClass::missingAndReferenced",
+ "LibraryCLass::addedOn27",
+ "Hello World")
+ .inspect(
+ inspector -> {
+ // TODO(b/204982782): Should be stubbed for api-level 1-23 with methods.
+ assertThat(
+ inspector.clazz(ApiModelMockClassTest.LibraryClass.class), Matchers.isAbsent());
+ });
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ public void addedOn23() {
+ System.out.println("LibraryClass::addedOn23");
+ }
+
+ public void addedOn27() {
+ System.out.println("LibraryCLass::addedOn27");
+ }
+
+ public void missingAndReferenced() {
+ System.out.println("LibraryClass::missingAndReferenced");
+ }
+
+ public void missingNotReferenced() {
+ System.out.println("LibraryClass::missingNotReferenced");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ LibraryClass libraryClass = new LibraryClass();
+ libraryClass.addedOn23();
+ libraryClass.missingAndReferenced();
+ if (AndroidBuildVersion.VERSION >= 27) {
+ libraryClass.addedOn27();
+ }
+ }
+ System.out.println("Hello World");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
new file mode 100644
index 0000000..285f3d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -0,0 +1,113 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelMockSuperChainClassTest extends TestBase {
+
+ private final AndroidApiLevel mockApiLevel = AndroidApiLevel.N;
+ private final AndroidApiLevel lowerMockApiLevel = AndroidApiLevel.M;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12.
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ boolean isMockApiLevel =
+ parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, ProgramClass.class)
+ .addLibraryClasses(LibraryClass.class, OtherLibraryClass.class, LibraryInterface.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(ProgramClass.class)
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, lowerMockApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, lowerMockApiLevel))
+ .apply(setMockApiLevelForClass(OtherLibraryClass.class, mockApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(OtherLibraryClass.class, mockApiLevel))
+ .apply(setMockApiLevelForClass(LibraryInterface.class, lowerMockApiLevel))
+ .compile()
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(lowerMockApiLevel),
+ b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(mockApiLevel),
+ b -> b.addBootClasspathClasses(OtherLibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
+ .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
+ .inspect(
+ inspector -> {
+ // TODO(b/204982782): These should be stubbed out for api-level 1-23.
+ assertThat(inspector.clazz(LibraryClass.class), not(Matchers.isPresent()));
+ assertThat(inspector.clazz(LibraryInterface.class), not(Matchers.isPresent()));
+ // TODO(b/204982782): This should be stubbed out for api-level 1-24.
+ assertThat(inspector.clazz(OtherLibraryClass.class), not(Matchers.isPresent()));
+ });
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {}
+
+ // Only present from api level 23
+ public interface LibraryInterface {}
+
+ // Only present from api level 24
+ public static class OtherLibraryClass extends LibraryClass {}
+
+ public static class ProgramClass extends OtherLibraryClass implements LibraryInterface {
+
+ public void foo() {
+ System.out.println("ProgramClass::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 24) {
+ new ProgramClass().foo();
+ } else {
+ System.out.println("Hello World");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
new file mode 100644
index 0000000..9565973
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
@@ -0,0 +1,122 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelNoInliningOfTryCatchReferenceTest extends TestBase {
+
+ private final AndroidApiLevel exceptionApiLevel = AndroidApiLevel.L_MR1;
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Method tryCatch = TestClass.class.getDeclaredMethod("testTryCatch");
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, TestClass.class, Caller.class, KeptClass.class)
+ .addLibraryClasses(ApiException.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMethodRules(
+ Reference.methodFromMethod(KeptClass.class.getDeclaredMethod("keptMethodThatMayThrow")))
+ .addKeepMainRule(Main.class)
+ .apply(setMockApiLevelForClass(ApiException.class, exceptionApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(ApiException.class, exceptionApiLevel))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .enableInliningAnnotations()
+ .addHorizontallyMergedClassesInspector(
+ horizontallyMergedClassesInspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(exceptionApiLevel)) {
+ horizontallyMergedClassesInspector.assertClassesMerged(
+ TestClass.class, Caller.class);
+ } else {
+ horizontallyMergedClassesInspector.assertNoClassesMerged();
+ }
+ })
+ .apply(
+ ApiModelingTestHelper.addTracedApiReferenceLevelCallBack(
+ (reference, apiLevel) -> {
+ if (reference.equals(Reference.methodFromMethod(tryCatch))) {
+ assertEquals(
+ exceptionApiLevel.max(
+ parameters.isCfRuntime()
+ ? AndroidApiLevel.B
+ : parameters.getApiLevel()),
+ apiLevel);
+ }
+ }))
+ .compile()
+ .applyIf(
+ parameters.isCfRuntime()
+ || parameters
+ .asDexRuntime()
+ .getMinApiLevel()
+ .isGreaterThanOrEqualTo(exceptionApiLevel),
+ b -> b.addRunClasspathClasses(ApiException.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World");
+ }
+
+ public static class ApiException extends RuntimeException {}
+
+ public static class TestClass {
+
+ public static void testTryCatch() {
+ try {
+ KeptClass.keptMethodThatMayThrow();
+ } catch (ApiException e) {
+ System.out.println("Caught ApiException");
+ }
+ }
+ }
+
+ public static class KeptClass {
+
+ public static void keptMethodThatMayThrow() {
+ if (System.currentTimeMillis() == 0) {
+ throw new ApiException();
+ }
+ System.out.println("Hello World");
+ }
+ }
+
+ public static class Caller {
+
+ @NeverInline
+ public static void callTestClass() {
+ TestClass.testTryCatch();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Caller.callTestClass();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
index 0e373e9..f1498b6 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.AlwaysInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -51,6 +52,11 @@
" synthetic void registerObserver(...);",
"}")
.allowAccessModification()
+ .addAlwaysInliningAnnotations()
+ .addKeepRules(
+ "-alwaysinline class * { @"
+ + AlwaysInline.class.getTypeName()
+ + " !synthetic <methods>; }")
.enableNeverClassInliningAnnotations()
// TODO(b/120764902): MemberSubject.getOriginalName() is not working without the @NeverMerge
// annotation on DataAdapter.Observer.
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/SimpleObservableList.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/SimpleObservableList.java
index e6eceb7..b99ee02 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/SimpleObservableList.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/SimpleObservableList.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.bridgeremoval.bridgestokeep;
+import com.android.tools.r8.AlwaysInline;
import com.android.tools.r8.bridgeremoval.bridgestokeep.ObservableList.Observer;
import java.util.ArrayList;
import java.util.List;
@@ -13,6 +14,7 @@
private List<O> observers = new ArrayList<>();
+ @AlwaysInline
@Override
public void registerObserver(O observer) {
if (observer != null && observers != null && !observers.contains(observer)) {
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index cdecb76..12bbdf3 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -50,7 +50,10 @@
private static final String HELLO_NAME = "hello.Hello";
private static final String[] KEEP_HELLO = {
- "-keep class " + HELLO_NAME + " {", " public static void main(...);", "}",
+ "-keep class " + HELLO_NAME + " {",
+ " public static void main(...);",
+ "}",
+ "-allowaccessmodification"
};
private static Pair<Path, Path> r8R8Debug;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
new file mode 100644
index 0000000..d594603
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -0,0 +1,121 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.experimental.startup.StartupConfiguration;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.google.common.collect.ImmutableList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class HorizontalClassMergingWithStartupClassesTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public List<Class<?>> startupClasses;
+
+ @Parameters(name = "{0}, startup classes: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ ImmutableList.of(
+ Collections.emptyList(), ImmutableList.of(StartupA.class, StartupB.class)));
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addOptionsModification(
+ options -> {
+ DexItemFactory dexItemFactory = options.dexItemFactory();
+ options.startupConfiguration =
+ new StartupConfiguration(
+ startupClasses.stream()
+ .map(clazz -> toDexType(clazz, dexItemFactory))
+ .collect(Collectors.toList()));
+ })
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .applyIf(
+ startupClasses.isEmpty(),
+ i ->
+ i.assertIsCompleteMergeGroup(
+ StartupA.class,
+ StartupB.class,
+ OnClickHandlerA.class,
+ OnClickHandlerB.class),
+ i ->
+ i.assertIsCompleteMergeGroup(StartupA.class, StartupB.class)
+ .assertIsCompleteMergeGroup(
+ OnClickHandlerA.class, OnClickHandlerB.class))
+ .assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("StartupA", "StartupB");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ StartupA.foo();
+ StartupB.bar();
+ }
+
+ // @Keep
+ public void onClick() {
+ OnClickHandlerA.baz();
+ OnClickHandlerB.qux();
+ }
+ }
+
+ static class StartupA {
+
+ @NeverInline
+ static void foo() {
+ System.out.println("StartupA");
+ }
+ }
+
+ static class StartupB {
+
+ @NeverInline
+ static void bar() {
+ System.out.println("StartupB");
+ }
+ }
+
+ static class OnClickHandlerA {
+
+ @NeverInline
+ static void baz() {
+ System.out.println("IdleA");
+ }
+ }
+
+ static class OnClickHandlerB {
+
+ @NeverInline
+ static void qux() {
+ System.out.println("IdleB");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 645588c..e57aaa9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -163,10 +163,6 @@
L8TestBuilder::setDebug)
.addOptionsModifier(optionsModifier)
.setDesugarJDKLibsConfiguration(ToolHelper.DESUGAR_LIB_CONVERSIONS)
- // If we compile extended library here, it means we use TestNG. TestNG requires
- // annotations, hence we disable annotation removal. This implies that extra warnings are
- // generated.
- .setDisableL8AnnotationRemoval(!additionalProgramFiles.isEmpty())
.compile()
.applyIf(
additionalProgramFiles.isEmpty(),
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
index d07e819..e3afc1c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
@@ -68,9 +68,9 @@
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
if (method.getHolderType().toSourceString().endsWith("$C")) {
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
} else {
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNull();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
index eb665f8..847bf55 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -63,9 +63,9 @@
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
if (method.getHolderType().toSourceString().endsWith("$C")) {
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
} else {
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNull();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
index ee612f8..f55c70b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
@@ -58,7 +58,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
index fed41d3..ca24f1f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
@@ -64,7 +64,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
index 042ca95..39f33af 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -64,7 +64,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
assert abstractValue.isSingleStringValue()
&& abstractValue.asSingleStringValue().getDexString().toString().equals("nul");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
index 4c1b255..5c80990 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
@@ -56,8 +56,8 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
- assert callSiteOptimizationInfo.getAbstractArgumentValue(0).isUnknown();
+ assertTrue(callSiteOptimizationInfo.getDynamicType(0).isNotNullType());
+ assertTrue(callSiteOptimizationInfo.getAbstractArgumentValue(0).isUnknown());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
index ba515db5..51593ba 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
@@ -62,11 +62,15 @@
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
if (methodName.equals("m")) {
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
} else {
assert methodName.equals("test");
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo
+ .getDynamicType(0)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType()
+ .isDefinitelyNotNull();
assert callSiteOptimizationInfo.getAbstractArgumentValue(0).isUnknown();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
index bbe45f0..1c5689d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -60,7 +60,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull();
AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
if (method.getHolderType().toSourceString().endsWith("$A")) {
assert abstractValue.isSingleStringValue()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
index 86a89ed..76daffe 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -54,14 +53,12 @@
}
private void callSiteOptimizationInfoInspect(ProgramMethod method) {
- assert method.getReference().name.toString().equals("test")
- : "Unexpected revisit: " + method.toSourceString();
+ assertTrue(
+ "Unexpected revisit: " + method.toSourceString(),
+ method.getReference().name.toString().equals("test"));
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
- assert upperBoundType.isDefinitelyNotNull();
- assert upperBoundType.isClassType()
- && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Base");
+ assertTrue(callSiteOptimizationInfo.getDynamicType(1).isNotNullType());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 5b7fa84..5811471 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -63,12 +63,20 @@
method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType;
if (methodName.equals("test")) {
- upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
+ upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(1)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
} else {
// TODO(b/139246447): should avoid visiting <init>, which is trivial, default init!
// For testing purpose, `Base` is not merged and kept. The system correctly caught that, when
// the default initializer is invoked, the receiver had a refined type, `Sub1`.
- upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0);
+ upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(0)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
}
assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
index 4c72b52..9fe54b4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -64,10 +63,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
- assert upperBoundType.isDefinitelyNotNull();
- assert upperBoundType.isClassType()
- && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Base");
+ assertTrue(callSiteOptimizationInfo.getDynamicType(1).isNotNullType());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
index 0c4b99a..fd6b29e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -66,14 +67,18 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
- assert upperBoundType.isDefinitelyNotNull();
+ DynamicType dynamicType = callSiteOptimizationInfo.getDynamicType(1);
if (method.getHolderType().toSourceString().endsWith("$A")) {
+ TypeElement upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(1)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
+ assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Sub1");
} else {
- assert upperBoundType.isClassType()
- && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Base");
+ assertTrue(dynamicType.isNotNullType());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
index 0b46131..2055210 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -52,14 +51,12 @@
}
private void callSiteOptimizationInfoInspect(ProgramMethod method) {
- assert method.getReference().name.toString().equals("test")
- : "Unexpected revisit: " + method.toSourceString();
+ assertTrue(
+ "Unexpected revisit: " + method.toSourceString(),
+ method.getReference().name.toString().equals("test"));
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0);
- assert upperBoundType.isDefinitelyNotNull();
- assert upperBoundType.isClassType()
- && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Base");
+ assertTrue(callSiteOptimizationInfo.getDynamicType(0).isNotNullType());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index dae8e30..a32daad 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -63,7 +63,11 @@
// TODO(b/139246447): should avoid visiting <init>, which is trivial, default init!
// For testing purpose, `Base` is not merged and kept. The system correctly caught that, when
// the default initializer is invoked, the receiver had a refined type, `Sub1`.
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0);
+ TypeElement upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(0)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Sub1");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
index 1952e51..b419ad2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -62,13 +61,15 @@
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
if (methodName.equals("m")) {
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
- assert upperBoundType.isDefinitelyNotNull();
- assert upperBoundType.isClassType()
- && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Base");
+ assertTrue(callSiteOptimizationInfo.getDynamicType(1).isNotNullType());
} else {
- assert methodName.equals("test");
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
+ assertTrue(methodName.equals("test"));
+ assertTrue(
+ callSiteOptimizationInfo
+ .getDynamicType(0)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType()
+ .isDefinitelyNotNull());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index e863304..99a3ddf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -63,12 +63,20 @@
method.getOptimizationInfo().getArgumentInfos();
TypeElement upperBoundType;
if (methodName.equals("m")) {
- upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
+ upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(1)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
} else {
// TODO(b/139246447): should avoid visiting <init>, which is trivial, default init!
// For testing purpose, `Base` is not merged and kept. The system correctly caught that, when
// the default initializer is invoked, the receiver had a refined type, `Sub1`.
- upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0);
+ upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(0)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
}
assert upperBoundType.isDefinitelyNotNull();
assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index dc8820b..fd605ac 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -57,7 +57,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(1).isNotNullType();
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
index f989c9f..a80798a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
@@ -64,7 +64,11 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
+ TypeElement upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(1)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
assert upperBoundType.isNullable();
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
index 0c38b8f..b3988ee 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -63,7 +63,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
+ assertTrue(callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index d386c2f..cf5cbe1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -55,7 +55,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo.getDynamicType(0).isNotNullType();
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
index e473e4a..08bd75c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -62,13 +62,21 @@
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
if (methodName.equals("m")) {
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
+ TypeElement upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(1)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
assert upperBoundType.isNullable();
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().equals(method.getHolderType());
} else {
assert methodName.equals("test");
- assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
+ assert callSiteOptimizationInfo
+ .getDynamicType(0)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType()
+ .isDefinitelyNotNull();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
index 0ee8eca..c1c88a2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -60,7 +60,11 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
+ TypeElement upperBoundType =
+ callSiteOptimizationInfo
+ .getDynamicType(1)
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType();
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A");
if (method.getHolderType().toSourceString().endsWith("$A")) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
index 5874af4..e5635d0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
@@ -93,11 +93,6 @@
|| parameters.getApiLevel().getLevel() < EXCEPTIONS.get(exception);
}
- private boolean compileTargetHasVerificationBug() {
- // A CF target could target any API in the end.
- return parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.L);
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -131,7 +126,7 @@
boolean mainHasInlinedCatchHandler =
Streams.stream(classSubject.mainMethod().iterateTryCatches())
.anyMatch(tryCatch -> tryCatch.isCatching(exception));
- if (compileTargetHasVerificationBug() && compilationTargetIsMissingExceptionType()) {
+ if (compilationTargetIsMissingExceptionType()) {
assertFalse(mainHasInlinedCatchHandler);
} else {
assertTrue(mainHasInlinedCatchHandler);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 68991bd..84b6917 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.inliner;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
@@ -72,9 +73,9 @@
result.inspector().clazz(TestClassCallingMethodWithNonExisting.class);
boolean hasCatchHandler =
Streams.stream(classSubject.mainMethod().iterateTryCatches()).count() > 0;
- int runtimeLevel = parameters.getApiLevel().getLevel();
- assertEquals(runtimeLevel >= AndroidApiLevel.L.getLevel(), hasCatchHandler);
-
+ // The catch handler does not exist in ClassWithCatchNonExisting.methodWithCatch thus we assign
+ // UNKNOWN api level. As a result we do not inline.
+ assertFalse(hasCatchHandler);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningTest.java
new file mode 100644
index 0000000..bf1ec2e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FieldTypeStrengtheningTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ FieldSubject fieldSubject = mainClassSubject.uniqueFieldWithName("f");
+ assertEquals(
+ aClassSubject.getFinalName(), fieldSubject.getField().getType().getTypeName());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class Main {
+
+ static Object f;
+
+ public static void main(String[] args) {
+ setField();
+ printField();
+ }
+
+ @NeverInline
+ static void setField() {
+ f = new A();
+ }
+
+ @NeverInline
+ static void printField() {
+ System.out.println(f);
+ }
+ }
+
+ public static class A {
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 2d2611f..05758fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLIN_DEV;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
@@ -36,6 +37,7 @@
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -62,6 +64,8 @@
// SAM interfaces lambdas are implemented by invoke dynamic in kotlin 1.5 unlike 1.4 where a
// class is generated for each. In CF we leave invokeDynamic but for DEX we desugar the classes
// and merge them.
+ // TODO(b/208816049): Fix test.
+ Assume.assumeTrue(kotlinParameters.getCompiler().isNot(KOTLIN_DEV));
boolean hasKotlinCGeneratedLambdaClasses = kotlinParameters.isOlderThan(KOTLINC_1_5_0);
String mainClassName = "class_inliner_lambda_j_style.MainKt";
runTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
index 03d1aa2..d426170 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -27,10 +27,7 @@
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
- getKotlinTestParameters()
- .withAllCompilers()
- .withTargetVersion(KotlinTargetVersion.JAVA_6)
- .build());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public KotlinLambdaMergingDebugTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
index 1dae299..39d2473 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
@@ -99,13 +99,17 @@
.assertAllWarningMessagesMatch(
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.writeToZip();
+ boolean expectingCompilationError = kotlinParameters.isOlderThanMinSupported() && !keepUnit;
Path output =
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(expectingCompilationError);
+ if (expectingCompilationError) {
+ return;
+ }
final JvmTestRunResult runResult =
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index 440a8d5..5739e90 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -135,7 +135,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
.addProgramFiles(output)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
index 5437d0d..b2e2631 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
@@ -84,7 +84,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/anonymous_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
.addClasspath(main)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index 994a011..d3e2677 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -1,17 +1,28 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// 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.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
import java.util.Collection;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -19,21 +30,25 @@
@RunWith(Parameterized.class)
public class MetadataRewriteDelegatedPropertyTest extends KotlinMetadataTestBase {
+ private static final String PKG_LIB = PKG + ".delegated_property_lib";
private static final String PKG_APP = PKG + ".delegated_property_app";
- private static final String EXPECTED_MAIN =
+ private static final String EXPECTED =
StringUtils.lines(
- "Initial string has been read in CustomDelegate from 'x'",
- "Initial string has been read in CustomDelegate from 'x'",
- "New value has been read in CustomDelegate from 'x'",
- "New value has been read in CustomDelegate from 'x'",
- "null",
- "New value has been read in CustomDelegate from 'x'");
+ "foobar",
+ "var com.android.tools.r8.kotlin.metadata.delegated_property_lib.MyDelegatedProperty.oldName:"
+ + " kotlin.String");
+
+ private static final KotlinCompilerVersion MIN_SUPPORTED_KOTLIN_VERSION = KOTLINC_1_4_20;
@Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+ getKotlinTestParameters()
+ .withOldCompilersStartingFrom(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withCompilersStartingFromIncluding(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withAllTargetVersions()
+ .build());
}
public MetadataRewriteDelegatedPropertyTest(
@@ -43,17 +58,26 @@
}
private final TestParameters parameters;
- private static final KotlinCompileMemoizer jars =
+
+ private static final KotlinCompileMemoizer libJars =
getCompileMemoizer(
- getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"));
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
@Test
public void smokeTest() throws Exception {
+ Path libJar = libJars.getForConfiguration(kotlinc, targetVersion);
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
testForJvm()
- .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
- .addClasspath(jars.getForConfiguration(kotlinc, targetVersion))
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
+ .addClasspath(output)
.run(parameters.getRuntime(), PKG_APP + ".MainKt")
- .assertSuccessWithOutput(EXPECTED_MAIN);
+ .assertSuccessWithOutput(EXPECTED);
}
@Test
@@ -64,21 +88,60 @@
kotlinc.getKotlinStdlibJar(),
kotlinc.getKotlinReflectJar(),
kotlinc.getKotlinAnnotationJar())
- .addProgramFiles(jars.getForConfiguration(kotlinc, targetVersion))
- .addKeepAllClassesRule()
+ .addKeepClassAndMembersRules(PKG_LIB + ".MyDelegatedProperty")
+ .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.compile()
- .inspect(
- inspector ->
- assertEqualMetadata(
- new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)),
- inspector,
- (addedStrings, addedNonInitStrings) -> {}))
+ .inspect(this::inspectMetadata)
.writeToZip();
+ Path main =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(outputJar)
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
testForJvm()
- .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
- .addClasspath(outputJar)
+ .addRunClasspathFiles(
+ kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), outputJar)
+ .addClasspath(main)
.run(parameters.getRuntime(), PKG_APP + ".MainKt")
- .assertSuccessWithOutput(EXPECTED_MAIN);
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testInsufficientMetadataForLib() throws Exception {
+ Path outputJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(
+ kotlinc.getKotlinStdlibJar(),
+ kotlinc.getKotlinReflectJar(),
+ kotlinc.getKotlinAnnotationJar())
+ .addKeepClassAndMembersRules(PKG_LIB + ".MyDelegatedProperty")
+ .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .compile()
+ .writeToZip();
+ ProcessResult compileResult =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(outputJar)
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compileRaw();
+ Assert.assertEquals(1, compileResult.exitCode);
+ assertThat(
+ compileResult.stderr,
+ containsString(
+ "unsupported [reference to the synthetic extension property for a Java get/set"
+ + " method]"));
+ }
+
+ private void inspectMetadata(CodeInspector inspector) {
+ ClassSubject clazz = inspector.clazz(PKG_LIB + ".MyDelegatedProperty");
+ assertThat(clazz, isPresent());
+ KotlinClassMetadata kotlinClassMetadata = clazz.getKotlinClassMetadata();
+ Assert.assertNotNull(kotlinClassMetadata);
+ String metadataAsString = KotlinMetadataWriter.kotlinMetadataToString("", kotlinClassMetadata);
+ assertThat(metadataAsString, containsString("syntheticMethodForDelegate:"));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
index 15a3391..e45b633 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
@@ -21,11 +21,9 @@
import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import kotlinx.metadata.KmFlexibleTypeUpperBound;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -96,7 +94,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/flexible_upper_bound_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
.addClasspath(main)
@@ -104,7 +105,7 @@
.assertSuccessWithOutput(EXPECTED);
}
- private void inspect(CodeInspector inspector) throws IOException, ExecutionException {
+ private void inspect(CodeInspector inspector) {
// We are checking that A is renamed, and that the flexible upper bound information is
// reflecting that.
ClassSubject a = inspector.clazz(PKG_LIB + ".A");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index 8b9e4c1..2b316c9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -104,7 +104,10 @@
.addClasspathFiles(baseLibJar, libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), baseLibJar, libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index dc4535c..eae75ac 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -144,7 +144,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index b1001e1..a03354e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -157,7 +157,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index 7f0fc70..67401c2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -162,7 +162,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index 001f000..10afff3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -158,7 +158,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index 2707bc2..fb2e952 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -98,7 +98,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index 97ec9f0..cbbbbb0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -93,7 +93,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/nested_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index 6d69d26..557562c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -87,7 +87,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index a29d2ec..7e12a9f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -95,7 +95,10 @@
.addSourceFiles(
getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
@@ -192,7 +195,10 @@
.addSourceFiles(
getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index 06ed6a6..2a69c5a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -85,7 +85,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index 59d626b..84ee3ec 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -87,7 +87,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index 7aa3952..afe617a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -88,7 +88,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
@@ -135,7 +138,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index 8482191..cfc0ea8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -138,7 +138,10 @@
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main"))
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index 18fce95..880bb14 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -140,7 +140,10 @@
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
.addClasspath(mainJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassIncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassIncludeDescriptorClassesTest.java
new file mode 100644
index 0000000..ac394af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassIncludeDescriptorClassesTest.java
@@ -0,0 +1,112 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.*;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInlineClassIncludeDescriptorClassesTest extends KotlinMetadataTestBase {
+
+ private final String EXPECTED = StringUtils.lines("Hello World!");
+ private static final KotlinCompilerVersion MIN_SUPPORTED_KOTLIN_VERSION = KOTLINC_1_6_0;
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ getKotlinTestParameters()
+ .withOldCompilersStartingFrom(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withCompilersStartingFromIncluding(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withAllTargetVersions()
+ .build());
+ }
+
+ public MetadataRewriteInlineClassIncludeDescriptorClassesTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ private static final KotlinCompileMemoizer libJars =
+ getCompileMemoizer(
+ getKotlinFileInTest(PKG_PREFIX + "/inline_class_fun_descriptor_classes_lib", "lib"),
+ getKotlinFileInTest(
+ PKG_PREFIX + "/inline_class_fun_descriptor_classes_lib", "keepForApi"));
+ private final TestParameters parameters;
+
+ @Test
+ public void smokeTest() throws Exception {
+ Path libJar = libJars.getForConfiguration(kotlinc, targetVersion);
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(
+ PKG_PREFIX + "/inline_class_fun_descriptor_classes_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".inline_class_fun_descriptor_classes_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testMetadataForLib() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+ .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ // kotlinc will generate a method for the unboxed type on the form login-XXXXX(String).
+ // We define an annotation that specify we keep the method and descriptor classes.
+ .addKeepRules(
+ "-keepclasseswithmembers class * { @"
+ + PKG
+ + ".inline_class_fun_descriptor_classes_lib.KeepForApi *; }")
+ .addKeepRules(
+ "-keepclassmembers,includedescriptorclasses class * { @"
+ + PKG
+ + ".inline_class_fun_descriptor_classes_lib.KeepForApi *; }")
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/208209210): Perhaps this should be kept.
+ assertThat(
+ inspector.clazz(
+ PKG + ".inline_class_fun_descriptor_classes_lib.KeepForApi.Password"),
+ not(Matchers.isPresent()));
+ })
+ .writeToZip();
+ ProcessResult kotlinCompileAppResult =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(
+ PKG_PREFIX + "/inline_class_fun_descriptor_classes_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compileRaw();
+ assertEquals(1, kotlinCompileAppResult.exitCode);
+ assertThat(kotlinCompileAppResult.stderr, containsString("unresolved reference: Password"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java
new file mode 100644
index 0000000..af22ae1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java
@@ -0,0 +1,121 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_6_0;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteInlineClassTest extends KotlinMetadataTestBase {
+
+ private final String EXPECTED = StringUtils.lines("Password(s=Hello World!)");
+ private final String passwordTypeName = PKG + ".inline_class_lib.Password";
+ private static final KotlinCompilerVersion MIN_SUPPORTED_KOTLIN_VERSION = KOTLINC_1_6_0;
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ getKotlinTestParameters()
+ .withOldCompilersStartingFrom(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withCompilersStartingFromIncluding(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withAllTargetVersions()
+ .build());
+ }
+
+ public MetadataRewriteInlineClassTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ private static final KotlinCompileMemoizer libJars =
+ getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/inline_class_lib", "lib"));
+ private final TestParameters parameters;
+
+ @Test
+ public void smokeTest() throws Exception {
+ Path libJar = libJars.getForConfiguration(kotlinc, targetVersion);
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/inline_class_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".inline_class_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testMetadataForLib() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+ .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addKeepClassAndMembersRules(passwordTypeName)
+ // kotlinc will generate a method for the unboxed type on the form login-XXXXX(String).
+ // Ideally, this should be targeted by annotation instead.
+ .addKeepRules(
+ "-keep class " + PKG + ".inline_class_lib.LibKt { *** login-*(java.lang.String); }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+ Path main =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/inline_class_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
+ .addClasspath(main)
+ .run(parameters.getRuntime(), PKG + ".inline_class_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ private void inspect(CodeInspector inspector) throws IOException {
+ CodeInspector stdLibInspector =
+ new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion));
+ ClassSubject clazzSubject = stdLibInspector.clazz(passwordTypeName);
+ ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+ assertThat(r8Clazz, isPresent());
+ KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+ KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+ TestCase.assertNotNull(rewrittenMetadata);
+ KotlinClassHeader originalHeader = originalMetadata.getHeader();
+ KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+ TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+ TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+ Assert.assertArrayEquals(originalHeader.getData1(), rewrittenHeader.getData1());
+ Assert.assertArrayEquals(originalHeader.getData2(), rewrittenHeader.getData2());
+ String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+ String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+ TestCase.assertEquals(expected, actual);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
new file mode 100644
index 0000000..2a7ee54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2020, 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.kotlin.metadata;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteLocalDelegatedPropertyTest extends KotlinMetadataTestBase {
+
+ private static final String PKG_APP = PKG + ".local_delegated_property_app";
+ private static final String EXPECTED_MAIN =
+ StringUtils.lines(
+ "Initial string has been read in CustomDelegate from 'x'",
+ "Initial string has been read in CustomDelegate from 'x'",
+ "New value has been read in CustomDelegate from 'x'",
+ "New value has been read in CustomDelegate from 'x'",
+ "null",
+ "New value has been read in CustomDelegate from 'x'");
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+ }
+
+ public MetadataRewriteLocalDelegatedPropertyTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ private final TestParameters parameters;
+ private static final KotlinCompileMemoizer jars =
+ getCompileMemoizer(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"));
+
+ @Test
+ public void smokeTest() throws Exception {
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
+ .addClasspath(jars.getForConfiguration(kotlinc, targetVersion))
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED_MAIN);
+ }
+
+ @Test
+ public void testMetadataForLib() throws Exception {
+ Path outputJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(
+ kotlinc.getKotlinStdlibJar(),
+ kotlinc.getKotlinReflectJar(),
+ kotlinc.getKotlinAnnotationJar())
+ .addProgramFiles(jars.getForConfiguration(kotlinc, targetVersion))
+ .addKeepAllClassesRule()
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(
+ inspector ->
+ assertEqualMetadata(
+ new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)),
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {}))
+ .writeToZip();
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar())
+ .addClasspath(outputJar)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED_MAIN);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 0cf8903..b46dc08 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -89,7 +89,10 @@
.addClasspathFiles(libJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
- .compile();
+ .compile(kotlinParameters.isOlderThanMinSupported());
+ if (kotlinParameters.isOlderThanMinSupported()) {
+ return;
+ }
testForJvm()
.addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
.addProgramFiles(output)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
index 29fa8ec..6aecc85 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
@@ -11,6 +11,7 @@
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertNull;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
@@ -37,13 +38,15 @@
private static final String PKG_LIB = PKG + ".value_class_lib";
private static final String PKG_APP = PKG + ".value_class_app";
private final TestParameters parameters;
+ private static final KotlinCompilerVersion MIN_SUPPORTED_KOTLIN_VERSION = KOTLINC_1_5_0;
@Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
getKotlinTestParameters()
- .withCompilersStartingFromIncluding(KOTLINC_1_5_0)
+ .withOldCompilersStartingFrom(MIN_SUPPORTED_KOTLIN_VERSION)
+ .withCompilersStartingFromIncluding(MIN_SUPPORTED_KOTLIN_VERSION)
.withTargetVersion(JAVA_8)
.build());
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt
index a35754f..43a9e2e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt
@@ -1,59 +1,14 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// 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.kotlin.metadata.delegated_property_app
-import kotlin.reflect.KMutableProperty1
-import kotlin.reflect.KProperty
-import kotlin.reflect.full.declaredMemberProperties
-import kotlin.reflect.jvm.isAccessible
-
-class Resource(private var s : String = "Initial string") {
-
- override fun toString(): String {
- return s;
- }
-}
-
-object CustomDelegate {
-
- private var resource : Resource = Resource()
-
- operator fun getValue(thisRef: Any?, property: KProperty<*>): Resource {
- println("$resource has been read in CustomDelegate from '${property.name}'")
- return resource;
- }
-
- operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Resource) {
- println("$value has been assigned to '${property.name}'")
- this.resource = resource;
- }
-}
-
-open class Base {
-
- fun doSomethingOnBarRef() : Resource {
- var x by CustomDelegate
- val propRef = x.javaClass.kotlin.declaredMemberProperties.first() as KMutableProperty1<Resource, String>
- propRef.isAccessible = true
- propRef.set(x, "New value")
- propRef.get(x)
- // Getting the delegate is not yet supported and will return null. We are printing the value
- // allowing us to observe if the behavior changes.
- println(propRef.getDelegate(x))
- return x
- }
-}
-
-object Impl : Base() {
- operator fun invoke(): Impl {
- return this
- }
-}
-
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.MyDelegatedProperty
fun main() {
- val impl = Impl()
- impl.doSomethingOnBarRef()
+ val myDelegatedProperty = MyDelegatedProperty()
+ myDelegatedProperty.oldName = "foobar";
+ println(myDelegatedProperty.newName)
+ println(myDelegatedProperty::oldName.toString())
}
-
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_lib/lib.kt
new file mode 100644
index 0000000..da40ce5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_lib/lib.kt
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.delegated_property_lib
+
+class MyDelegatedProperty {
+ var newName: String = "Hello World!"
+ var oldName: String by this::newName
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_app/main.kt
new file mode 100644
index 0000000..28cecd5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_app/main.kt
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.inline_class_app
+
+import com.android.tools.r8.kotlin.metadata.inline_class_lib.Password
+import com.android.tools.r8.kotlin.metadata.inline_class_lib.login
+
+fun main() {
+ login(Password("Hello World!"))
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_app/main.kt
new file mode 100644
index 0000000..a9d5023
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_app/main.kt
@@ -0,0 +1,13 @@
+// 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.kotlin.metadata.inline_class_fun_descriptor_classes_app
+
+import com.android.tools.r8.kotlin.metadata.inline_class_fun_descriptor_classes_lib.create
+import com.android.tools.r8.kotlin.metadata.inline_class_fun_descriptor_classes_lib.Password
+import com.android.tools.r8.kotlin.metadata.inline_class_fun_descriptor_classes_lib.login
+
+fun main() {
+ login(create("Hello World!"))
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_lib/keepForApi.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_lib/keepForApi.kt
new file mode 100644
index 0000000..1e0ec5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_lib/keepForApi.kt
@@ -0,0 +1,11 @@
+// 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.kotlin.metadata.inline_class_fun_descriptor_classes_lib
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeepForApi {
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_lib/lib.kt
new file mode 100644
index 0000000..8c902e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_fun_descriptor_classes_lib/lib.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.metadata.inline_class_fun_descriptor_classes_lib
+
+@JvmInline
+value class Password(val s: String)
+
+@KeepForApi
+fun create(pw : String) : Password {
+ return Password(pw)
+}
+
+@KeepForApi
+fun login(pw : Password) {
+ println(pw.s)
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_lib/lib.kt
new file mode 100644
index 0000000..56bc18f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/inline_class_lib/lib.kt
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.metadata.inline_class_lib
+
+@JvmInline
+value class Password(private val s: String)
+
+fun login(pw : Password) {
+ println(pw)
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/local_delegated_property_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/local_delegated_property_app/main.kt
new file mode 100644
index 0000000..b8959e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/local_delegated_property_app/main.kt
@@ -0,0 +1,59 @@
+// Copyright (c) 2020, 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.kotlin.metadata.local_delegated_property_app
+
+import kotlin.reflect.KMutableProperty1
+import kotlin.reflect.KProperty
+import kotlin.reflect.full.declaredMemberProperties
+import kotlin.reflect.jvm.isAccessible
+
+class Resource(private var s : String = "Initial string") {
+
+ override fun toString(): String {
+ return s;
+ }
+}
+
+object CustomDelegate {
+
+ private var resource : Resource = Resource()
+
+ operator fun getValue(thisRef: Any?, property: KProperty<*>): Resource {
+ println("$resource has been read in CustomDelegate from '${property.name}'")
+ return resource;
+ }
+
+ operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Resource) {
+ println("$value has been assigned to '${property.name}'")
+ this.resource = resource;
+ }
+}
+
+open class Base {
+
+ fun doSomethingOnBarRef() : Resource {
+ var x by CustomDelegate
+ val propRef = x.javaClass.kotlin.declaredMemberProperties.first() as KMutableProperty1<Resource, String>
+ propRef.isAccessible = true
+ propRef.set(x, "New value")
+ propRef.get(x)
+ // Getting the delegate is not yet supported and will return null. We are printing the value
+ // allowing us to observe if the behavior changes.
+ println(propRef.getDelegate(x))
+ return x
+ }
+}
+
+object Impl : Base() {
+ operator fun invoke(): Impl {
+ return this
+ }
+}
+
+
+fun main() {
+ val impl = Impl()
+ impl.doSomethingOnBarRef()
+}
+
diff --git a/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java b/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java
index 2e211ad..cda6322 100644
--- a/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NoFieldTypeStrengthening;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.Reference;
@@ -30,7 +31,8 @@
@Parameterized.Parameters(name = "{1}, overload aggressively: {0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public OverloadedReservedFieldNamingTest(
@@ -47,7 +49,8 @@
.addKeepRules(
"-keep class " + A.class.getTypeName() + " { boolean a; }",
overloadAggressively ? "-overloadaggressively" : "")
- .setMinApi(parameters.getRuntime())
+ .enableNoFieldTypeStrengtheningAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyAggressiveOverloading)
.run(parameters.getRuntime(), TestClass.class)
@@ -82,6 +85,8 @@
static final boolean a = System.currentTimeMillis() >= 0;
static final String hello = System.currentTimeMillis() >= 0 ? "Hello" : null;
+
+ @NoFieldTypeStrengthening
static final Object world = System.currentTimeMillis() >= 0 ? " world!" : null;
@Override
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
index 543dbf8..6faa4f7 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
@@ -9,7 +9,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoFieldTypeStrengthening;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -33,6 +33,7 @@
.addKeepClassRules(NonPublicKeptClass.class)
.apply(this::configureRepackaging)
.enableInliningAnnotations()
+ .enableNoFieldTypeStrengtheningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -63,6 +64,7 @@
public static class IneligibleForRepackaging {
+ @NoFieldTypeStrengthening
private static NonPublicKeptClass FIELD =
System.currentTimeMillis() > 0 ? new PublicSubClass() : null;
diff --git a/src/test/java/com/android/tools/r8/softverification/FoundClass.java b/src/test/java/com/android/tools/r8/softverification/FoundClass.java
index cc25389..2165828 100644
--- a/src/test/java/com/android/tools/r8/softverification/FoundClass.java
+++ b/src/test/java/com/android/tools/r8/softverification/FoundClass.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.softverification;
-public class FoundClass {
+public class FoundClass extends RuntimeException {
public static int staticField = 42;
diff --git a/src/test/java/com/android/tools/r8/softverification/MissingClass.java b/src/test/java/com/android/tools/r8/softverification/MissingClass.java
index d653af7..189ba2b 100644
--- a/src/test/java/com/android/tools/r8/softverification/MissingClass.java
+++ b/src/test/java/com/android/tools/r8/softverification/MissingClass.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.softverification;
-public class MissingClass {
+public class MissingClass extends RuntimeException {
public static int staticField = 42;
diff --git a/src/test/java/com/android/tools/r8/softverification/MissingMember.java b/src/test/java/com/android/tools/r8/softverification/MissingMember.java
index 9fee087..8d9a2de 100644
--- a/src/test/java/com/android/tools/r8/softverification/MissingMember.java
+++ b/src/test/java/com/android/tools/r8/softverification/MissingMember.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.softverification;
-public class MissingMember {
+public class MissingMember extends RuntimeException {
public static int staticField = 42;
diff --git a/src/test/java/com/android/tools/r8/softverification/MissingSuperType.java b/src/test/java/com/android/tools/r8/softverification/MissingSuperType.java
new file mode 100644
index 0000000..e77ea01
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/softverification/MissingSuperType.java
@@ -0,0 +1,21 @@
+// 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.softverification;
+
+public class MissingSuperType extends MissingClass {
+
+ public static int staticField = 42;
+
+ public int instanceField = 42;
+
+ public static void staticMethod() {
+ System.out.println("MissingSuperType::staticMethod");
+ }
+
+ @Override
+ public void instanceMethod() {
+ System.out.println("MissingSuperType::instanceMethod");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/softverification/TestHashCode.java b/src/test/java/com/android/tools/r8/softverification/TestHashCode.java
new file mode 100644
index 0000000..d856b71
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/softverification/TestHashCode.java
@@ -0,0 +1,26 @@
+// 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.softverification;
+
+public class TestHashCode {
+
+ public static String run() {
+ return run(null);
+ }
+
+ public static String run(MissingClass missingClass) {
+ if (System.currentTimeMillis() == 0) {
+ missingClass.hashCode();
+ }
+ if (System.currentTimeMillis() == 0) {
+ missingClass.hashCode();
+ }
+ String currentString = "foobar";
+ for (int i = 0; i < 10; i++) {
+ currentString = "foobar" + (i + currentString.length());
+ }
+ return currentString;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/softverification/TestRunner.java b/src/test/java/com/android/tools/r8/softverification/TestRunner.java
index 6baef2a..dbe5ab1 100644
--- a/src/test/java/com/android/tools/r8/softverification/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/softverification/TestRunner.java
@@ -47,9 +47,15 @@
measure.start("InstanceField");
TestInstanceField.run();
sb.append(measure.stop());
+ measure.start("HashCode");
+ TestHashCode.run();
+ sb.append(measure.stop());
measure.start("InstanceMethod");
TestInstanceMethod.run();
sb.append(measure.stop());
+ measure.start("TryCatch");
+ TestTryCatch.run();
+ sb.append(measure.stop());
return sb.toString();
}
diff --git a/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java b/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java
index 375f03f..c3f91db 100644
--- a/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java
+++ b/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java
@@ -74,8 +74,11 @@
private static final Path ANDROID_STUDIO_LIB_PATH = Paths.get("PATH_TO_PROJECT/libs/library.jar");
- private static final int COUNT = 1100;
+ private static final int COUNT = 800;
+ private static final List<Class<?>> referenceClasses =
+ ImmutableList.of(
+ MissingClass.class, MissingMember.class, FoundClass.class, MissingSuperType.class);
private static final List<Class<?>> testClasses =
ImmutableList.of(
TestCheckCast.class,
@@ -85,7 +88,9 @@
TestStaticField.class,
TestStaticMethod.class,
TestInstanceField.class,
- TestInstanceMethod.class);
+ TestInstanceMethod.class,
+ TestHashCode.class,
+ TestTryCatch.class);
private static final Collection<String> testClassBinaryNames =
ImmutableSet.copyOf(ListUtils.map(testClasses, TestBase::binaryName));
@@ -93,8 +98,7 @@
ZipBuilder builder = ZipBuilder.builder(path);
builder.addFilesRelative(
ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(Measure.class));
- for (Class<?> clazz :
- ImmutableList.of(MissingClass.class, MissingMember.class, FoundClass.class)) {
+ for (Class<?> clazz : referenceClasses) {
String postFix = clazz.getSimpleName();
int classCounter = 0;
for (int i = 0; i < COUNT; i++) {
@@ -168,6 +172,11 @@
.replaceClassDescriptorInMethodInstructions(
descriptor(MissingClass.class),
getDescriptorFromClassBinaryName(referenceBinaryName))
+ .transformTryCatchBlock(
+ "run",
+ (start, end, handler, type, visitor) -> {
+ visitor.visitTryCatchBlock(start, end, handler, referenceBinaryName);
+ })
.transform());
}
diff --git a/src/test/java/com/android/tools/r8/softverification/TestTryCatch.java b/src/test/java/com/android/tools/r8/softverification/TestTryCatch.java
new file mode 100644
index 0000000..4be92a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/softverification/TestTryCatch.java
@@ -0,0 +1,25 @@
+// 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.softverification;
+
+public class TestTryCatch {
+
+ public static Object getObject() {
+ return new Object();
+ }
+
+ public static String run() {
+ try {
+ getObject();
+ } catch (MissingClass e) {
+ throw new RuntimeException("Foo");
+ }
+ String currentString = "foobar";
+ for (int i = 0; i < 10; i++) {
+ currentString = "foobar" + (i + currentString.length());
+ }
+ return currentString;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 689fce0..69b8232 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -71,8 +71,17 @@
public HorizontallyMergedClassesInspector applyIf(
boolean condition, ThrowableConsumer<HorizontallyMergedClassesInspector> consumer) {
+ return applyIf(condition, consumer, ThrowableConsumer.empty());
+ }
+
+ public HorizontallyMergedClassesInspector applyIf(
+ boolean condition,
+ ThrowableConsumer<HorizontallyMergedClassesInspector> thenConsumer,
+ ThrowableConsumer<HorizontallyMergedClassesInspector> elseConsumer) {
if (condition) {
- consumer.acceptWithRuntimeException(this);
+ thenConsumer.acceptWithRuntimeException(this);
+ } else {
+ elseConsumer.acceptWithRuntimeException(this);
}
return this;
}
diff --git a/tools/test.py b/tools/test.py
index f5ce159..041f17b 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -287,9 +287,9 @@
if options.print_obfuscated_stacktraces:
gradle_args.append('-Pprint_obfuscated_stacktraces')
if options.kotlin_compiler_old:
- gradle_args.append('-Dcom.android.tools.r8.kotlincompilerold=1')
+ gradle_args.append('-Pkotlin_compiler_old')
if options.kotlin_compiler_dev:
- gradle_args.append('-Dcom.android.tools.r8.kotlincompilerdev=1')
+ gradle_args.append('-Pkotlin_compiler_dev')
download_kotlin_dev.download_newest()
if os.name == 'nt':
# temporary hack