Merge commit 'f360ce2486bd287908762b33c335c9b4f8a8802a' into dev-release
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
index 7199b77..12e7b48 100644
--- a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -4,12 +4,22 @@
package com.android.tools.r8;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Reporter;
@Keep
public class AssertionsConfiguration {
- /** The possible transformations of the javac generated assertion code during compilation. */
+ /**
+ * The simple transformations of the javac generated assertion code during compilation (see {@link
+ * AssertionsConfiguration.Builder#setTransformation(AssertionTransformation)}). For configuring
+ * the transformation to invoke an assertion handler use {@link
+ * AssertionsConfiguration.Builder#setAssertionHandler(MethodReference)}.
+ *
+ * @deprecated As of version 3.3 this enum should not be used.
+ */
+ @Deprecated
@Keep
public enum AssertionTransformation {
/** Unconditionally enable the javac generated assertion code. */
@@ -30,20 +40,51 @@
}
private final AssertionTransformation transformation;
+ private final MethodReference assertionHandler;
private final AssertionTransformationScope scope;
private final String value;
AssertionsConfiguration(
- AssertionTransformation transformation, AssertionTransformationScope scope, String value) {
+ AssertionTransformation transformation,
+ MethodReference assertionHandler,
+ AssertionTransformationScope scope,
+ String value) {
this.transformation = transformation;
+ this.assertionHandler = assertionHandler;
this.scope = scope;
this.value = value;
+ assert BooleanUtils.xor(transformation != null, assertionHandler != null);
}
+ public boolean isCompileTimeEnabled() {
+ return transformation == AssertionTransformation.ENABLE;
+ }
+
+ public boolean isCompileTimeDisabled() {
+ return transformation == AssertionTransformation.DISABLE;
+ }
+
+ public boolean isPassthrough() {
+ return transformation == AssertionTransformation.PASSTHROUGH;
+ }
+
+ public boolean isAssertionHandler() {
+ return assertionHandler != null;
+ }
+
+ /**
+ * @deprecated As of version 3.3, use one of {@link #isCompileTimeEnabled()} ()}, {@link
+ * #isCompileTimeDisabled()} ()} or {@link #isPassthrough()} ()}.
+ */
+ @Deprecated
public AssertionTransformation getTransformation() {
return transformation;
}
+ public MethodReference getAssertionHandler() {
+ return assertionHandler;
+ }
+
public AssertionTransformationScope getScope() {
return scope;
}
@@ -66,6 +107,7 @@
public static class Builder {
Reporter reporter;
private AssertionTransformation transformation;
+ private MethodReference assertionHandler;
private AssertionTransformationScope scope;
private String value;
@@ -73,10 +115,17 @@
this.reporter = reporter;
}
- /** Set how to handle javac generated assertion code. */
+ /**
+ * Set how to handle javac generated assertion code.
+ *
+ * @deprecated As of version 3.3, use one of {@link #setCompileTimeDisable()}, {@link
+ * #setCompileTimeDisable()} or {@link #setPassthrough()} ()}.
+ */
+ @Deprecated
public AssertionsConfiguration.Builder setTransformation(
AssertionTransformation transformation) {
this.transformation = transformation;
+ this.assertionHandler = null;
return this;
}
@@ -84,6 +133,13 @@
* Unconditionally enable javac generated assertion code in all packages and classes. This
* corresponds to passing <code>-enableassertions</code> or <code>-ea</code> to the java CLI.
*/
+ public AssertionsConfiguration.Builder setCompileTimeEnable() {
+ setTransformation(AssertionTransformation.ENABLE);
+ return this;
+ }
+
+ /** @deprecated As of version 3.3, replaced by {@link #setCompileTimeEnable()} ()} */
+ @Deprecated
public AssertionsConfiguration.Builder setEnable() {
setTransformation(AssertionTransformation.ENABLE);
return this;
@@ -93,6 +149,13 @@
* Disable the javac generated assertion code in all packages and classes. This corresponds to
* passing <code>-disableassertions</code> or <code>-da</code> to the java CLI.
*/
+ public AssertionsConfiguration.Builder setCompileTimeDisable() {
+ setTransformation(AssertionTransformation.DISABLE);
+ return this;
+ }
+
+ /** @deprecated As of version 3.3, replaced by {@link #setCompileTimeDisable()} */
+ @Deprecated
public AssertionsConfiguration.Builder setDisable() {
setTransformation(AssertionTransformation.DISABLE);
return this;
@@ -104,6 +167,18 @@
return this;
}
+ /**
+ * Rewrite the throwing of <code>java.lang.AssertionError</code> to call the supplied method
+ * <code>assertionHandler</code>. The method must be a reference to a static method taking one
+ * argument of type <code>java.lang.AssertionError</code>. After the assertion handler as been
+ * called, the code continues as if assertions where disabled.
+ */
+ public AssertionsConfiguration.Builder setAssertionHandler(MethodReference assertionHandler) {
+ this.transformation = null;
+ this.assertionHandler = assertionHandler;
+ return this;
+ }
+
public AssertionsConfiguration.Builder setScopeAll() {
this.scope = AssertionTransformationScope.ALL;
this.value = null;
@@ -144,19 +219,20 @@
/** Build and return the {@link AssertionsConfiguration}. */
public AssertionsConfiguration build() {
- if (transformation == null) {
- reporter.error("No transformation specified for building AccertionConfiguration");
+ if (transformation == null && assertionHandler == null) {
+ reporter.error(
+ "No transformation or assertion handler specified for building AssertionConfiguration");
}
if (scope == null) {
- reporter.error("No scope specified for building AccertionConfiguration");
+ reporter.error("No scope specified for building AssertionConfiguration");
}
if (scope == AssertionTransformationScope.PACKAGE && value == null) {
- reporter.error("No package name specified for building AccertionConfiguration");
+ reporter.error("No package name specified for building AssertionConfiguration");
}
if (scope == AssertionTransformationScope.CLASS && value == null) {
- reporter.error("No class name specified for building AccertionConfiguration");
+ reporter.error("No class name specified for building AssertionConfiguration");
}
- return new AssertionsConfiguration(transformation, scope, value);
+ return new AssertionsConfiguration(transformation, assertionHandler, scope, value);
}
/**
@@ -174,14 +250,25 @@
*
* <pre>
* D8Command command = D8Command.builder()
- * .addAssertionsConfiguration(builder -> builder.setEnabled().setScopeAll().build())
+ * .addAssertionsConfiguration(
+ * builder -> builder.setCompileTimeEnable().setScopeAll().build())
* ...
* .build();
* </pre>
*/
+ public static AssertionsConfiguration compileTimeEnableAllAssertions(
+ AssertionsConfiguration.Builder builder) {
+ return builder.setCompileTimeEnable().setScopeAll().build();
+ }
+
+ /**
+ * @deprecated As of version 3.3, replaced by {@link #compileTimeEnableAllAssertions(Builder)}
+ * ()}
+ */
+ @Deprecated
public static AssertionsConfiguration enableAllAssertions(
AssertionsConfiguration.Builder builder) {
- return builder.setEnable().setScopeAll().build();
+ return compileTimeEnableAllAssertions(builder);
}
/**
@@ -190,7 +277,8 @@
*
* <pre>
* D8Command command = D8Command.builder()
- * .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions)
+ * .addAssertionsConfiguration(
+ * AssertionsConfiguration.Builder::compileTimeDisableAllAssertions)
* ...
* .build();
* </pre>
@@ -199,14 +287,25 @@
*
* <pre>
* D8Command command = D8Command.builder()
- * .addAssertionsConfiguration(builder -> builder.setDisabled().setScopeAll().build())
+ * .addAssertionsConfiguration(
+ * builder -> builder.setCompileTimeDisabled().setScopeAll().build())
* ...
* .build();
* </pre>
*/
+ public static AssertionsConfiguration compileTimeDisableAllAssertions(
+ AssertionsConfiguration.Builder builder) {
+ return builder.setCompileTimeDisable().setScopeAll().build();
+ }
+
+ /**
+ * @deprecated As of version 3.3, replaced by {@link #compileTimeDisableAllAssertions(Builder)}
+ * ()}
+ */
+ @Deprecated
public static AssertionsConfiguration disableAllAssertions(
AssertionsConfiguration.Builder builder) {
- return builder.setDisable().setScopeAll().build();
+ return compileTimeDisableAllAssertions(builder);
}
/**
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index ad3e92b..cd0bc2f 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -210,7 +210,7 @@
clazz -> {
ProgramMethod classInitializer = clazz.getProgramClassInitializer();
if (classInitializer != null) {
- analysis.processNewlyLiveMethod(classInitializer, clazz);
+ analysis.processNewlyLiveMethod(classInitializer, clazz, null);
}
},
executor);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index a12020f..8dd34a8 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -507,7 +507,11 @@
assert internal.assertionsConfiguration == null;
internal.assertionsConfiguration =
new AssertionConfigurationWithDefault(
- AssertionTransformation.DISABLE, getAssertionsConfiguration());
+ AssertionsConfiguration.builder(getReporter())
+ .setTransformation(AssertionTransformation.DISABLE)
+ .setScopeAll()
+ .build(),
+ getAssertionsConfiguration());
internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 41f4030..26c99ce 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -202,7 +202,11 @@
assert internal.assertionsConfiguration == null;
internal.assertionsConfiguration =
new AssertionConfigurationWithDefault(
- AssertionTransformation.DISABLE, getAssertionsConfiguration());
+ AssertionsConfiguration.builder(getReporter())
+ .setTransformation(AssertionTransformation.DISABLE)
+ .setScopeAll()
+ .build(),
+ getAssertionsConfiguration());
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b51ee2e..bf04680 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -485,9 +485,6 @@
}
ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility);
- if (InternalOptions.shouldEnableKeepRuleSynthesisForRecompilation()) {
- configurationBuilder.enableKeepRuleSynthesisForRecompilation();
- }
if (proguardConfigurationConsumerForTesting != null) {
proguardConfigurationConsumerForTesting.accept(configurationBuilder);
@@ -921,9 +918,13 @@
assert internal.assertionsConfiguration == null;
internal.assertionsConfiguration =
new AssertionConfigurationWithDefault(
- getProgramConsumer() instanceof ClassFileConsumer
- ? AssertionTransformation.PASSTHROUGH
- : AssertionTransformation.DISABLE,
+ AssertionsConfiguration.builder(getReporter())
+ .setTransformation(
+ getProgramConsumer() instanceof ClassFileConsumer
+ ? AssertionTransformation.PASSTHROUGH
+ : AssertionTransformation.DISABLE)
+ .setScopeAll()
+ .build(),
getAssertionsConfiguration());
// TODO(b/171552739): Enable class merging for CF. When compiling libraries, we need to be
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
deleted file mode 100644
index 058201f..0000000
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
+++ /dev/null
@@ -1,26 +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.androidapi;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.function.BiConsumer;
-
-/**
- * This is an interface for all generated classes from api-versions.xml for building a database from
- * a serialized hashed format.
- */
-public interface AndroidApiForHashingClass {
-
- DexType getType();
-
- AndroidApiLevel getApiLevel();
-
- void visitMethodsWithApiLevels(BiConsumer<DexMethod, AndroidApiLevel> consumer);
-
- void visitFieldsWithApiLevels(BiConsumer<DexField, AndroidApiLevel> consumer);
-}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java
new file mode 100644
index 0000000..726b1a0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, 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.androidapi;
+
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+/** This interface is used to add additional known references to the api database. */
+class AndroidApiForHashingReference {
+
+ private final DexReference reference;
+
+ private final AndroidApiLevel apiLevel;
+
+ private AndroidApiForHashingReference(DexReference reference, AndroidApiLevel apiLevel) {
+ this.reference = reference;
+ this.apiLevel = apiLevel;
+ }
+
+ static AndroidApiForHashingReference create(DexReference reference, AndroidApiLevel apiLevel) {
+ return new AndroidApiForHashingReference(reference, apiLevel);
+ }
+
+ DexReference getReference() {
+ return reference;
+ }
+
+ AndroidApiLevel getApiLevel() {
+ return apiLevel;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
new file mode 100644
index 0000000..03727d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2022, 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.androidapi;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.function.BiConsumer;
+
+class AndroidApiLevelDatabaseHelper {
+
+ static void visitAdditionalKnownApiReferences(
+ DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) {
+ // StringBuilder and StringBuffer lack api definitions for the exact same methods in
+ // api-versions.xml. See b/216587554 for related error.
+ for (DexType type : new DexType[] {factory.stringBuilderType, factory.stringBufferType}) {
+ apiLevelConsumer.accept(
+ factory.createMethod(type, factory.createProto(factory.intType), "capacity"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.intType, factory.intType), "codePointAt"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.intType, factory.intType), "codePointBefore"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(factory.intType, factory.intType, factory.intType),
+ "codePointCount"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.voidType, factory.intType), "ensureCapacity"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(
+ factory.voidType,
+ factory.intType,
+ factory.intType,
+ factory.charArrayType,
+ factory.intType),
+ "getChars"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.intType, factory.stringType), "indexOf"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(factory.intType, factory.stringType, factory.intType),
+ "indexOf"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.intType, factory.stringType), "lastIndexOf"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(factory.intType, factory.stringType, factory.intType),
+ "lastIndexOf"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(factory.intType, factory.intType, factory.intType),
+ "offsetByCodePoints"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(factory.voidType, factory.intType, factory.charType),
+ "setCharAt"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.voidType, factory.intType), "setLength"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type, factory.createProto(factory.stringType, factory.intType), "substring"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(
+ type,
+ factory.createProto(factory.stringType, factory.intType, factory.intType),
+ "substring"),
+ AndroidApiLevel.B);
+ apiLevelConsumer.accept(
+ factory.createMethod(type, factory.createProto(factory.voidType), "trimToSize"),
+ AndroidApiLevel.B);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
index bedc254..f5343da 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -41,24 +41,21 @@
private final Map<DexReference, AndroidApiLevel> ambiguousCache = new IdentityHashMap<>();
public AndroidApiLevelHashingDatabaseImpl(
- List<AndroidApiForHashingClass> predefinedApiTypeLookup) {
+ List<AndroidApiForHashingReference> predefinedApiTypeLookup) {
loadData();
predefinedApiTypeLookup.forEach(
- apiClass -> {
- DexType type = apiClass.getType();
- lookupNonAmbiguousCache.put(type.hashCode(), null);
- ambiguousCache.put(type, apiClass.getApiLevel());
- apiClass.visitMethodsWithApiLevels(
- (method, apiLevel) -> {
- lookupNonAmbiguousCache.put(method.hashCode(), null);
- ambiguousCache.put(method, apiLevel);
- });
- apiClass.visitFieldsWithApiLevels(
- (field, apiLevel) -> {
- lookupNonAmbiguousCache.put(field.hashCode(), null);
- ambiguousCache.put(field, apiLevel);
- });
+ predefinedApiReference -> {
+ int hashCode = predefinedApiReference.getReference().hashCode();
+ // Do not use computeIfAbsent since a return value of null implies the key should not be
+ // inserted.
+ if (!lookupNonAmbiguousCache.containsKey(hashCode)) {
+ lookupNonAmbiguousCache.put(hashCode, null);
+ ambiguousCache.put(
+ predefinedApiReference.getReference(), predefinedApiReference.getApiLevel());
+ }
});
+ assert predefinedApiTypeLookup.stream()
+ .allMatch(added -> added.getApiLevel().isEqualTo(lookupApiLevel(added.getReference())));
}
private void loadData() {
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index b4609ce..2e2aa7a 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -11,8 +11,10 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import java.util.function.BiConsumer;
public class AndroidApiReferenceLevelCache {
@@ -25,7 +27,7 @@
private AndroidApiReferenceLevelCache(
AppView<?> appView,
AndroidApiLevelCompute apiLevelCompute,
- List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
+ List<AndroidApiForHashingReference> predefinedApiTypeLookupForHashing) {
this.appView = appView;
this.apiLevelCompute = apiLevelCompute;
factory = appView.dexItemFactory();
@@ -37,11 +39,15 @@
public static AndroidApiReferenceLevelCache create(
AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
assert appView.options().apiModelingOptions().enableApiCallerIdentification;
- ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder();
+ ImmutableList.Builder<AndroidApiForHashingReference> builder = ImmutableList.builder();
+ BiConsumer<DexReference, AndroidApiLevel> addItemToList =
+ ConsumerUtils.andThen(AndroidApiForHashingReference::create, builder::add);
+ AndroidApiLevelDatabaseHelper.visitAdditionalKnownApiReferences(
+ appView.dexItemFactory(), addItemToList);
appView
.options()
.apiModelingOptions()
- .visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add);
+ .visitMockedApiLevelsForReferences(appView.dexItemFactory(), addItemToList);
return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build());
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index ae4700f..f9b13a4 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -116,7 +116,7 @@
public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
- apiLevelCompute = AndroidApiLevelCompute.create(appView);
+ apiLevelCompute = appView.apiLevelCompute();
desugaredLibraryConfiguration = appView.options().desugaredLibrarySpecification;
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 07ebb29..f35751b 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -19,11 +19,14 @@
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.code.Base5Format;
+import com.android.tools.r8.code.CfOrDexInstruction;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumberGenerator;
@@ -150,6 +153,7 @@
private final List<LocalVariableInfo> localVariables;
private StackMapStatus stackMapStatus = StackMapStatus.NOT_VERIFIED;
private final com.android.tools.r8.position.Position diagnosticPosition;
+ private final BytecodeMetadata<CfInstruction> metadata;
public CfCode(
DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
@@ -187,6 +191,26 @@
List<CfTryCatch> tryCatchRanges,
List<LocalVariableInfo> localVariables,
com.android.tools.r8.position.Position diagnosticPosition) {
+ this(
+ originalHolder,
+ maxStack,
+ maxLocals,
+ instructions,
+ tryCatchRanges,
+ localVariables,
+ diagnosticPosition,
+ BytecodeMetadata.empty());
+ }
+
+ public CfCode(
+ DexType originalHolder,
+ int maxStack,
+ int maxLocals,
+ List<CfInstruction> instructions,
+ List<CfTryCatch> tryCatchRanges,
+ List<LocalVariableInfo> localVariables,
+ com.android.tools.r8.position.Position diagnosticPosition,
+ BytecodeMetadata<CfInstruction> metadata) {
this.originalHolder = originalHolder;
this.maxStack = maxStack;
this.maxLocals = maxLocals;
@@ -194,6 +218,7 @@
this.tryCatchRanges = tryCatchRanges;
this.localVariables = localVariables;
this.diagnosticPosition = diagnosticPosition;
+ this.metadata = metadata;
}
@Override
@@ -207,6 +232,20 @@
}
@Override
+ public BytecodeMetadata<CfInstruction> getMetadata() {
+ return metadata;
+ }
+
+ @Override
+ public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
+ return getMetadata(instruction.asCfInstruction());
+ }
+
+ public BytecodeInstructionMetadata getMetadata(CfInstruction instruction) {
+ return metadata.getMetadata(instruction);
+ }
+
+ @Override
public StructuralMapping<CfCode> getStructuralMapping() {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index e727373..f8d5917 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
@@ -31,6 +32,10 @@
+ getClass().getCanonicalName());
}
+ public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() {
+ return null;
+ }
+
public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 7689694..a44fc1d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -123,6 +123,11 @@
}
@Override
+ public BytecodeMetadata<Instruction> getMetadata() {
+ return metadata;
+ }
+
+ @Override
public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
return getMetadata(instruction.asDexInstruction());
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 0f27bda..0beeb2f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -291,17 +291,16 @@
assert code.getDebugInfo().isPcBasedInfo();
PcBasedDebugInfo pcBasedDebugInfo = code.getDebugInfo().asPcBasedInfo();
// Generate a line event at each throwing instruction.
- List<DexDebugEvent> events = new ArrayList<>(code.instructions.length + 1);
- events.add(factory.zeroChangeDefaultEvent);
+ List<DexDebugEvent> events = new ArrayList<>(code.instructions.length);
int pc = 0;
int delta = 0;
for (Instruction instruction : code.instructions) {
- delta += instruction.getSize();
if (instruction.canThrow()) {
DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary(delta, delta, events, factory);
pc += delta;
delta = 0;
}
+ delta += instruction.getSize();
}
assert pc + delta - ArrayUtils.last(code.instructions).getSize() <= pcBasedDebugInfo.maxPc;
return new EventBasedDebugInfo(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
index 33f6e4d..ccbb86f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -25,22 +25,24 @@
}
@Override
- public void notifyMarkFieldAsReachable(ProgramField field) {
+ public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {
processSignature(field, field.getContext());
}
@Override
- public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {
+ public void processNewlyLiveField(
+ ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {
processSignature(field, context);
}
@Override
- public void notifyMarkMethodAsTargeted(ProgramMethod method) {
+ public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {
processSignature(method, method.getContext());
}
@Override
- public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+ public void processNewlyLiveMethod(
+ ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
processSignature(method, context);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
index 28ab2ca..745c558 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -6,6 +6,7 @@
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* Provides immutable access to {@link ObjectAllocationInfoCollectionImpl}, which stores the set of
@@ -28,6 +29,8 @@
boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass clazz);
+ void forEachInstantiatedLambdaInterfaces(Consumer<DexType> consumer);
+
ObjectAllocationInfoCollection rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLens lens);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index aa5e4c8..bcad963 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -242,6 +242,11 @@
return instantiatedLambdas.keySet();
}
+ @Override
+ public void forEachInstantiatedLambdaInterfaces(Consumer<DexType> consumer) {
+ getInstantiatedLambdaInterfaces().forEach(consumer);
+ }
+
public void removeAllocationsForPrunedItems(PrunedItems prunedItems) {
Set<DexType> removedClasses = prunedItems.getRemovedClasses();
if (removedClasses.isEmpty()) {
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 972c83a..e6a6cba 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -234,11 +234,7 @@
return new Builder();
}
- public RewrittenTypeInfo(DexType oldType, DexType newType) {
- this(oldType, newType, null, null);
- }
-
- public RewrittenTypeInfo(
+ private RewrittenTypeInfo(
DexType oldType, DexType newType, DexType castType, SingleValue singleValue) {
this.castType = castType;
this.oldType = oldType;
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index 0800ede..2382abc 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -33,9 +33,15 @@
private final Map<DexType, TypeInfo> typeInfo;
- private SubtypingInfo(Map<DexType, TypeInfo> typeInfo, Map<DexType, Set<DexType>> subtypeMap) {
+ private final DexItemFactory factory;
+
+ private SubtypingInfo(
+ Map<DexType, TypeInfo> typeInfo,
+ Map<DexType, Set<DexType>> subtypeMap,
+ DexItemFactory factory) {
this.typeInfo = typeInfo;
this.subtypeMap = subtypeMap;
+ this.factory = factory;
}
public static SubtypingInfo create(AppView<? extends AppInfoWithClassHierarchy> appView) {
@@ -46,12 +52,12 @@
return create(appInfo.app().asDirect().allClasses(), appInfo);
}
- private static SubtypingInfo create(
- Collection<DexClass> classes, DexDefinitionSupplier definitions) {
+ public static SubtypingInfo create(
+ Collection<? extends DexClass> classes, DexDefinitionSupplier definitions) {
Map<DexType, TypeInfo> typeInfo = new ConcurrentHashMap<>();
Map<DexType, Set<DexType>> subtypeMap = new IdentityHashMap<>();
populateSubtypeMap(classes, subtypeMap, typeInfo, definitions);
- return new SubtypingInfo(typeInfo, subtypeMap);
+ return new SubtypingInfo(typeInfo, subtypeMap, definitions.dexItemFactory());
}
private static void populateSuperType(
@@ -111,7 +117,7 @@
}
private static void populateSubtypeMap(
- Collection<DexClass> classes,
+ Collection<? extends DexClass> classes,
Map<DexType, Set<DexType>> map,
Map<DexType, TypeInfo> typeInfo,
DexDefinitionSupplier definitionSupplier) {
@@ -225,6 +231,13 @@
return ImmutableList.of();
}
+ public void forAllInterfaceRoots(Consumer<DexType> fn) {
+ Iterables.filter(
+ getTypeInfo(factory.objectType).directSubtypes,
+ subtype -> getTypeInfo(subtype).isInterface())
+ .forEach(fn);
+ }
+
private static class TypeInfo {
private final DexType type;
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index 86c7e2d..6e6088a 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
public class ApiModelAnalysis extends EnqueuerAnalysis {
@@ -28,17 +29,20 @@
}
@Override
- public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {
+ public void processNewlyLiveField(
+ ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {
computeAndSetApiLevelForDefinition(field);
}
@Override
- public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+ public void processNewlyLiveMethod(
+ ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
computeAndSetApiLevelForDefinition(method);
}
@Override
- public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {
+ public void processTracedCode(
+ ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {
assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel);
if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) {
appView
@@ -52,17 +56,18 @@
}
@Override
- public void notifyMarkMethodAsTargeted(ProgramMethod method) {
+ public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {
computeAndSetApiLevelForDefinition(method);
}
@Override
- public void notifyMarkFieldAsReachable(ProgramField field) {
+ public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {
computeAndSetApiLevelForDefinition(field);
}
@Override
- public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) {
+ public void notifyMarkVirtualDispatchTargetAsLive(
+ LookupTarget target, EnqueuerWorklist worklist) {
target.accept(
this::computeAndSetApiLevelForDefinition,
lookupLambdaTarget -> {
@@ -71,7 +76,8 @@
}
@Override
- public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {
+ public void notifyFailedMethodResolutionTarget(
+ DexEncodedMethod method, EnqueuerWorklist worklist) {
// We may not trace into failed resolution targets.
method.setApiLevelForCode(ComputedApiLevel.unknown());
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index 45bff3f..a6045e4 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -39,7 +40,8 @@
}
@Override
- public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+ public void processNewlyLiveMethod(
+ ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
DexEncodedMethod definition = method.getDefinition();
if (definition.isClassInitializer()) {
Code code = definition.getCode();
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index ceb7e92..eef04d0 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -18,27 +18,33 @@
public abstract class EnqueuerAnalysis {
/** Called when a class is found to be instantiated. */
- public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {}
+ public void processNewlyInstantiatedClass(
+ DexProgramClass clazz, ProgramMethod context, EnqueuerWorklist worklist) {}
/** Called when a class is found to be live. */
public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {}
/** Called when a field is found to be live. */
- public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {}
+ public void processNewlyLiveField(
+ ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {}
/** Called when a method is found to be live. */
- public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {}
+ public void processNewlyLiveMethod(
+ ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {}
/** Called when a method's code has been processed by the registry. */
- public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {}
+ public void processTracedCode(
+ ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {}
- public void notifyMarkMethodAsTargeted(ProgramMethod method) {}
+ public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {}
- public void notifyMarkFieldAsReachable(ProgramField field) {}
+ public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {}
- public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) {}
+ public void notifyMarkVirtualDispatchTargetAsLive(
+ LookupTarget target, EnqueuerWorklist worklist) {}
- public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {}
+ public void notifyFailedMethodResolutionTarget(
+ DexEncodedMethod method, EnqueuerWorklist worklist) {}
/**
* Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index cc74d21..0d19daa 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -63,7 +64,8 @@
}
@Override
- public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {
+ public void processNewlyInstantiatedClass(
+ DexProgramClass clazz, ProgramMethod context, EnqueuerWorklist worklist) {
DexType key = clazz.type;
DexType objectType = appView.dexItemFactory().objectType;
if (context == null) {
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
index ad7dee4..f335b4c 100644
--- a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
+++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
@@ -60,5 +60,10 @@
public BytecodeMetadata<I> build() {
return backing.isEmpty() ? empty() : new BytecodeMetadata<>(backing);
}
+
+ public boolean verifyNoMetadata(Instruction irInstruction) {
+ assert bytecodeMetadataProvider.getMetadata(irInstruction) == null;
+ return true;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
index bd5b1da..62dda05 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
@@ -59,6 +59,14 @@
cache.put(type, true);
return true;
}
+ if (clazz.hasClassInitializer()
+ && clazz.getClassInitializer().getOptimizationInfo().mayHaveSideEffects()
+ && clazz.getMethodCollection().hasVirtualMethods(method -> !method.isAbstract())) {
+ // Require interfaces that trigger class initialization side effects to be implement by all
+ // group members.
+ cache.put(type, true);
+ return true;
+ }
for (DexType parentType : clazz.getInterfaces()) {
if (computeInterfaceHasDirectOrIndirectRuntimeTypeCheck(parentType)) {
cache.put(type, true);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index d0cfd52..b0ddb4a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -132,7 +132,8 @@
* ProtoMessageInfo} object, and create a mapping from the holder to it.
*/
@Override
- public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+ public void processNewlyLiveMethod(
+ ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
if (references.isFindLiteExtensionByNumberMethod(method.getReference())) {
findLiteExtensionByNumberMethods.add(method);
return;
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
index 45fa439..0e09c04 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
@@ -42,7 +42,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfNop());
+ builder.add(new CfNop(), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index bf545a0..a4be195 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -165,6 +165,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfArithmeticBinop(getCfOpcode(), type));
+ builder.add(new CfArithmeticBinop(getCfOpcode(), type), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
index 9140e87..eb20267 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
@@ -24,6 +24,12 @@
return inValues.get(INDEX_INDEX);
}
+ public int getIndexOrDefault(int defaultValue) {
+ return index().isConstant()
+ ? index().getConstInstruction().asConstInstruction().asConstNumber().getIntValue()
+ : defaultValue;
+ }
+
@Override
public boolean isArrayAccess() {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index e1bb5bf..d51e318 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -176,7 +176,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfArrayLoad(type));
+ builder.add(new CfArrayLoad(type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 4f0995b..3835b3d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -116,7 +116,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfArrayLength());
+ builder.add(new CfArrayLength(), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 4108175..9994dde 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -213,7 +213,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfArrayStore(type));
+ builder.add(new CfArrayStore(type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index e34ab51..864e37e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -264,7 +264,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfCheckCast(type));
+ builder.add(new CfCheckCast(type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index 2b44f93..a7b25a3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -231,7 +231,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfCmp(bias, type));
+ builder.add(new CfCmp(bias, type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 93cefd2..716396d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -197,7 +197,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfConstClass(clazz));
+ builder.add(new CfConstClass(clazz), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 3973d6e..35cef20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -63,7 +63,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfConstMethodHandle(methodHandle));
+ builder.add(new CfConstMethodHandle(methodHandle), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index 6f153a6..64359c8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -63,7 +63,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfConstMethodType(methodType));
+ builder.add(new CfConstMethodType(methodType), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index b849f75..4111b17 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -173,9 +173,9 @@
@Override
public void buildCf(CfBuilder builder) {
if (outType().isObject()) {
- builder.add(new CfConstNull());
+ builder.add(new CfConstNull(), this);
} else {
- builder.add(new CfConstNumber(value, outType()));
+ builder.add(new CfConstNumber(value, outType()), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index f873fd8..a3efba8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -144,7 +144,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfConstString(value));
+ builder.add(new CfConstString(value), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index 6817315..ad9be48 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -69,7 +69,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStore(outType(), builder.getLocalRegister(outValue())));
+ builder.add(new CfStore(outType(), builder.getLocalRegister(outValue())), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index d4d76da..e5bdb6d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -86,7 +86,7 @@
public void buildCf(CfBuilder builder) {
assert getPosition().isSome() && !getPosition().isSyntheticPosition();
// All redundant debug positions are removed. Remaining ones must force a pc advance.
- builder.add(new CfNop());
+ builder.add(new CfNop(), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 4c8c646..4dc3985 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -139,7 +139,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfDexItemBasedConstString(item, nameComputationInfo));
+ builder.add(new CfDexItemBasedConstString(item, nameComputationInfo), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java
index 3bdc40c..2c9c3be 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -69,9 +69,9 @@
@Override
public void buildCf(CfBuilder builder) {
if (this.inValues.get(0).getType().isWidePrimitive()) {
- builder.add(new CfStackInstruction(Opcode.Dup2));
+ builder.add(new CfStackInstruction(Opcode.Dup2), this);
} else {
- builder.add(new CfStackInstruction(Opcode.Dup));
+ builder.add(new CfStackInstruction(Opcode.Dup), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
index a01b789..7eff1c4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -86,7 +86,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStackInstruction(Opcode.Dup2));
+ builder.add(new CfStackInstruction(Opcode.Dup2), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
index dde7f0d..025dec0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Goto.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -118,7 +118,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfGoto(builder.getLabel(getTarget())));
+ builder.add(new CfGoto(builder.getLabel(getTarget())), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index c562fe7..468c7bf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -65,6 +65,10 @@
return get(Opcodes.AND);
}
+ public boolean mayHaveArrayGet() {
+ return get(Opcodes.ARRAY_GET);
+ }
+
public boolean mayHaveArrayLength() {
return get(Opcodes.ARRAY_LENGTH);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index 62cb5a2..ce75f73 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -268,11 +268,11 @@
public void buildCf(CfBuilder builder) {
ValueType ifType = inValues.get(0).outType();
if (inValues.size() == 1) {
- builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget())));
+ builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget())), this);
return;
}
assert inValues.size() == 2;
assert inValues.get(0).outType() == inValues.get(1).outType();
- builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())));
+ builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Inc.java b/src/main/java/com/android/tools/r8/ir/code/Inc.java
index 3d10cc4..de99253 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Inc.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Inc.java
@@ -83,17 +83,21 @@
@Override
public void buildCf(CfBuilder builder) {
+ // Check that this instruction does not have any metadata attached, as it might not materialize
+ // as an iinc in CfCode.
+ assert builder.verifyNoMetadata(this);
Value inValue = inValues.get(0);
int inRegister = builder.getLocalRegister(inValue);
int outRegister = builder.getLocalRegister(outValue);
if (inRegister == outRegister) {
- builder.add(new CfIinc(inRegister, increment));
+ builder.add(new CfIinc(inRegister, increment), this);
} else {
assert inValue.outType() == ValueType.INT;
- builder.add(new CfLoad(ValueType.INT, inRegister));
- builder.add(new CfConstNumber(increment, ValueType.INT));
- builder.add(new CfArithmeticBinop(Opcode.Add, NumericType.INT));
- builder.add(new CfStore(ValueType.INT, outRegister));
+ builder.add(
+ new CfLoad(ValueType.INT, inRegister),
+ new CfConstNumber(increment, ValueType.INT),
+ new CfArithmeticBinop(Opcode.Add, NumericType.INT),
+ new CfStore(ValueType.INT, outRegister));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index aceeab3..80351a6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -76,7 +76,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInitClass(clazz));
+ builder.add(new CfInitClass(clazz), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 347ca58..0bc0830 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -200,7 +200,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField())));
+ builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField())), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index 0530403..123eb45 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -109,7 +109,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInstanceOf(type));
+ builder.add(new CfInstanceOf(type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 95086dc..13cba81 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -241,7 +241,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField())));
+ builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField())), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
index e0e3cdc..601f67f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
@@ -311,12 +311,12 @@
}
}
assert index == numberOfKeys();
- builder.add(new CfSwitch(Kind.TABLE, fallthroughLabel, new int[] {min}, labels));
+ builder.add(new CfSwitch(Kind.TABLE, fallthroughLabel, new int[] {min}, labels), this);
} else {
for (int index : targetBlockIndices()) {
labels.add(builder.getLabel(successors.get(index)));
}
- builder.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, this.keys, labels));
+ builder.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, this.keys, labels), this);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 508b115..1296742 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -147,7 +147,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvokeDynamic(getCallSite()));
+ builder.add(new CfInvokeDynamic(getCallSite()), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 0f2e98a..062220b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -161,7 +161,8 @@
@Override
public void buildCf(CfBuilder builder) {
builder.add(
- new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface));
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface),
+ this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 6412e17..153ca1b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -128,7 +128,8 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEINTERFACE, getInvokedMethod(), true));
+ builder.add(
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKEINTERFACE, getInvokedMethod(), true), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 7163fdd..63df06a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -104,7 +104,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfMultiANewArray(type, arguments().size()));
+ builder.add(new CfMultiANewArray(type, arguments().size()), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 425262b..ad8c087 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -104,7 +104,7 @@
// To translate InvokePolymorphic back into InvokeVirtual, use the original prototype
// that is stored in getProto().
DexMethod method = factory.createMethod(dexMethod.holder, getProto(), dexMethod.name);
- builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, method, false));
+ builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, method, false), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index f1f86b3..7aa20ad 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -155,7 +155,8 @@
@Override
public void buildCf(CfBuilder builder) {
builder.add(
- new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, getInvokedMethod(), isInterface));
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, getInvokedMethod(), isInterface),
+ this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 0a0461d..630e177 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -84,7 +84,8 @@
@Override
public void buildCf(CfBuilder builder) {
builder.add(
- new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface));
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface),
+ this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 9179014..b5933de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -157,7 +157,8 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false));
+ builder.add(
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index eee3bef..fe565bc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -76,7 +76,7 @@
@Override
public void buildCf(CfBuilder builder) {
Value value = src();
- builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)));
+ builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
index 267db9b..b647ecf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -141,6 +141,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfLogicalBinop(getCfOpcode(), type));
+ builder.add(new CfLogicalBinop(getCfOpcode(), type), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index f779651..648c9fd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -137,7 +137,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfMonitor(type));
+ builder.add(new CfMonitor(type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index ad0886a..459c8c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -108,6 +108,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfNeg(type));
+ builder.add(new CfNeg(type), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 3be3d3d..3d6fb7d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -152,7 +152,7 @@
@Override
public void buildCf(CfBuilder builder) {
assert type.isArrayType();
- builder.add(new CfNewArray(type));
+ builder.add(new CfNewArray(type), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 024f6f2..3aff847 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -125,7 +125,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfNew(clazz));
+ builder.add(new CfNew(clazz), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index 33829cd..b56600f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -132,7 +132,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfNewUnboxedEnum(clazz, ordinal));
+ builder.add(new CfNewUnboxedEnum(clazz, ordinal), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index e6b6f8d..5f3b592 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -159,7 +159,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfNumberConversion(from, to));
+ builder.add(new CfNumberConversion(from, to), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index a2f872e..bf8ee2d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -79,7 +79,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(CfStackInstruction.popType(inValues.get(0).outType()));
+ builder.add(CfStackInstruction.popType(inValues.get(0).outType()), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 5de6390..653e20e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -153,14 +153,14 @@
public Position getOutermostCallerMatchingOrElse(
Predicate<Position> predicate, Position defaultValue) {
- return getOutermostCallerMatchingOrElse(predicate, defaultValue, false);
+ Position outerMostMatching = getOutermostCallerMatching(predicate, false);
+ return outerMostMatching == null ? defaultValue : outerMostMatching;
}
- private Position getOutermostCallerMatchingOrElse(
- Predicate<Position> predicate, Position defaultValue, boolean isCallerPosition) {
+ private Position getOutermostCallerMatching(
+ Predicate<Position> predicate, boolean isCallerPosition) {
if (hasCallerPosition()) {
- Position position =
- getCallerPosition().getOutermostCallerMatchingOrElse(predicate, defaultValue, true);
+ Position position = getCallerPosition().getOutermostCallerMatching(predicate, true);
if (position != null) {
return position;
}
@@ -168,7 +168,7 @@
if (isCallerPosition && predicate.test(this)) {
return this;
}
- return defaultValue;
+ return null;
}
public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
@@ -180,14 +180,15 @@
.build();
}
- public Position replaceOutermostCallerPosition(Position newOutermostCallerPosition) {
- if (!hasCallerPosition()) {
- return newOutermostCallerPosition;
+ public Position replacePosition(Position originalPosition, Position newPosition) {
+ if (this == originalPosition) {
+ return newPosition;
}
- return builderWithCopy()
- .setCallerPosition(
- getCallerPosition().replaceOutermostCallerPosition(newOutermostCallerPosition))
- .build();
+ return hasCallerPosition()
+ ? builderWithCopy()
+ .setCallerPosition(callerPosition.replacePosition(originalPosition, newPosition))
+ .build()
+ : this;
}
@Override
@@ -324,7 +325,8 @@
.setLine(line)
.setFile(file)
.setMethod(method)
- .setCallerPosition(callerPosition);
+ .setCallerPosition(callerPosition)
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
}
@Override
@@ -389,7 +391,11 @@
@Override
public PositionBuilder<?, ?> builderWithCopy() {
- return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition);
+ return builder()
+ .setLine(line)
+ .setMethod(method)
+ .setCallerPosition(callerPosition)
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
}
@Override
@@ -442,7 +448,11 @@
@Override
public PositionBuilder<?, ?> builderWithCopy() {
- return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition);
+ return builder()
+ .setLine(line)
+ .setMethod(method)
+ .setCallerPosition(callerPosition)
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
}
@Override
@@ -516,7 +526,8 @@
.setMethod(method)
.setCallerPosition(callerPosition)
.setOutlineCallee(outlineCallee)
- .setIsOutline(isOutline);
+ .setIsOutline(isOutline)
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
outlinePositions.forEach(outlineCallerPositionBuilder::addOutlinePosition);
return outlineCallerPositionBuilder;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
index 00df561..a2949d0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
@@ -73,7 +73,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfRecordFieldValues(fields));
+ builder.add(new CfRecordFieldValues(fields), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index e28ee89..1ec1a8e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -132,7 +132,8 @@
@Override
public void buildCf(CfBuilder builder) {
builder.add(
- isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType())));
+ isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType())),
+ this);
}
public static class Builder extends BuilderBase<Builder, Return> {
diff --git a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
index 72a62cd..b811f86 100644
--- a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
@@ -22,7 +22,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfSafeCheckCast(getType()));
+ builder.add(new CfSafeCheckCast(getType()), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index c7e076c..c4413d0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -214,7 +214,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField())));
+ builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField())), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 339f16f..2230cc7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -221,7 +221,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField())));
+ builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField())), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 8100111..6714e0d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -77,7 +77,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)));
+ builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java
index 777eec1..87ed900 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Swap.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -67,7 +67,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStackInstruction(Opcode.Swap));
+ builder.add(new CfStackInstruction(Opcode.Swap), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 6f47788..6350255 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -83,7 +83,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfThrow());
+ builder.add(new CfThrow(), this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 34142c2..4f4a7b2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -29,6 +29,8 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -102,6 +104,9 @@
private List<InvokeDirect> thisInitializers;
private Map<NewInstance, CfLabel> newInstanceLabels;
+ // Extra information that should be attached to the bytecode instructions.
+ private final BytecodeMetadata.Builder<CfInstruction> bytecodeMetadataBuilder;
+
// Internal structure maintaining the stack height.
private static class StackHeightTracker {
int maxHeight = 0;
@@ -128,10 +133,15 @@
}
}
- public CfBuilder(AppView<?> appView, DexEncodedMethod method, IRCode code) {
+ public CfBuilder(
+ AppView<?> appView,
+ DexEncodedMethod method,
+ IRCode code,
+ BytecodeMetadataProvider bytecodeMetadataProvider) {
this.appView = appView;
this.method = method;
this.code = code;
+ this.bytecodeMetadataBuilder = BytecodeMetadata.builder(bytecodeMetadataProvider);
}
public CfCode build(DeadCodeRemover deadCodeRemover, MethodConversionOptions conversionOptions) {
@@ -383,7 +393,8 @@
instructions,
tryCatchRanges,
localVariablesTable,
- diagnosticPosition);
+ diagnosticPosition,
+ bytecodeMetadataBuilder.build());
}
private static boolean isNopInstruction(Instruction instruction, BasicBlock nextBlock) {
@@ -684,11 +695,25 @@
return registerAllocator.getRegisterForValue(value);
}
- public void add(CfInstruction instruction) {
+ private void add(CfInstruction instruction) {
instructions.add(instruction);
}
+ public void add(CfInstruction instruction, Instruction origin) {
+ bytecodeMetadataBuilder.setMetadata(origin, instruction);
+ add(instruction);
+ }
+
+ public void add(CfInstruction... instructions) {
+ Collections.addAll(this.instructions, instructions);
+ }
+
public void addArgument(Argument argument) {
// Nothing so far.
}
+
+ public boolean verifyNoMetadata(Instruction instruction) {
+ assert bytecodeMetadataBuilder.verifyNoMetadata(instruction);
+ return true;
+ }
}
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 c19a35b..efd8593 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
@@ -1486,7 +1486,13 @@
printMethod(code, "Optimized IR (SSA)", previous);
timing.begin("Finalize IR");
- finalizeIR(code, feedback, conversionOptions, bytecodeMetadataProviderBuilder.build(), timing);
+ finalizeIR(
+ context,
+ code,
+ feedback,
+ conversionOptions,
+ bytecodeMetadataProviderBuilder.build(),
+ timing);
timing.end();
return timing;
}
@@ -1579,6 +1585,7 @@
}
deadCodeRemover.run(code, timing);
finalizeIR(
+ code.context(),
code,
feedback,
DefaultMethodConversionOptions.getInstance(),
@@ -1587,6 +1594,7 @@
}
public void finalizeIR(
+ ProgramMethod method,
IRCode code,
OptimizationFeedback feedback,
MethodConversionOptions conversionOptions,
@@ -1594,7 +1602,7 @@
Timing timing) {
code.traceBlocks();
if (options.isGeneratingClassFiles()) {
- finalizeToCf(code, feedback, conversionOptions);
+ finalizeToCf(code, feedback, conversionOptions, bytecodeMetadataProvider);
} else {
assert options.isGeneratingDex();
finalizeToDex(code, feedback, conversionOptions, bytecodeMetadataProvider, timing);
@@ -1602,10 +1610,13 @@
}
private void finalizeToCf(
- IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions) {
+ IRCode code,
+ OptimizationFeedback feedback,
+ MethodConversionOptions conversionOptions,
+ BytecodeMetadataProvider bytecodeMetadataProvider) {
DexEncodedMethod method = code.method();
assert !method.getCode().isDexCode();
- CfBuilder builder = new CfBuilder(appView, method, code);
+ CfBuilder builder = new CfBuilder(appView, method, code, bytecodeMetadataProvider);
CfCode result = builder.build(deadCodeRemover, conversionOptions);
method.setCode(result, appView);
markProcessed(code, feedback);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index fc60cb9..cd3a6b1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -103,7 +103,8 @@
apiLevelCompute.computeApiLevelForLibraryReference(
cfInvoke.getMethod(), ComputedApiLevel.unknown());
if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)
- || isApiLevelLessThanOrEqualTo9(methodApiLevel)) {
+ || isApiLevelLessThanOrEqualTo9(methodApiLevel)
+ || methodApiLevel.isUnknownApiLevel()) {
return appView.computedMinApiLevel();
}
// Compute the api level of the holder to see if the method will be stubbed.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index d30f717..6740441 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -693,9 +693,7 @@
DexClassAndMethod companionMethod =
helper.ensureDefaultAsMethodOfCompanionClassStub(method);
acceptCompanionMethod(method, companionMethod, eventConsumer);
- return getInvokeStaticInstructions(
- InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
- amendedMethod, appView.dexItemFactory()));
+ return getInvokeStaticInstructions(companionMethod.getReference());
})
.build();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 3e8b5cb..f712cb4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -227,8 +227,9 @@
.resolveMethod(derivedMethod.getMethod(), true)
.getResolvedProgramMethod();
caseMethod =
- InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
- resolvedProgramMethod.getReference(), appView.dexItemFactory());
+ helper
+ .ensureDefaultAsMethodOfProgramCompanionClassStub(resolvedProgramMethod)
+ .getReference();
}
extraDispatchCases.put(type, caseMethod);
});
@@ -276,10 +277,12 @@
DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
if (result != null && !result.isAbstract()) {
assert result.isDefaultMethod();
- extraDispatchCases.put(
- subInterfaceClass.type,
- InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
- result.getReference(), appView.dexItemFactory()));
+ DexMethod forward =
+ helper
+ .ensureDefaultAsMethodOfProgramCompanionClassStub(
+ new ProgramMethod(subInterfaceClass.asProgramClass(), result))
+ .getReference();
+ extraDispatchCases.put(subInterfaceClass.type, forward);
}
}
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index 8e78a50..ba2c90f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.AssertionsConfiguration;
-import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -32,7 +31,7 @@
private static class ConfigurationEntryWithDexString {
- private AssertionsConfiguration entry;
+ private final AssertionsConfiguration entry;
private final DexString value;
private ConfigurationEntryWithDexString(
@@ -72,13 +71,29 @@
throw new Unreachable();
}
}
+
+ public boolean isEnabled() {
+ return entry.isCompileTimeEnabled();
+ }
+
+ public boolean isDisabled() {
+ return entry.isCompileTimeDisabled();
+ }
+
+ public boolean isPassthrough() {
+ return entry.isPassthrough();
+ }
+
+ public boolean isAssertionHandler() {
+ return entry.isAssertionHandler();
+ }
}
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- private final AssertionTransformation defaultTransformation;
+ private final ConfigurationEntryWithDexString defaultConfiguration;
private final List<ConfigurationEntryWithDexString> configuration;
- private final AssertionsConfiguration.AssertionTransformation kotlinTransformation;
+ private final ConfigurationEntryWithDexString kotlinTransformation;
private final boolean enabled;
public AssertionsRewriter(AppView<?> appView) {
@@ -86,13 +101,15 @@
this.dexItemFactory = appView.dexItemFactory();
this.enabled = isEnabled(appView.options());
if (!enabled) {
- defaultTransformation = null;
+ defaultConfiguration = null;
configuration = null;
kotlinTransformation = null;
return;
}
- // Convert the assertion transformation to the representation used for this rewriter.
- this.defaultTransformation = appView.options().assertionsConfiguration.defautlTransformation;
+ // Convert the assertion configuration to the representation used for this rewriter.
+ this.defaultConfiguration =
+ new ConfigurationEntryWithDexString(
+ appView.options().assertionsConfiguration.defaultConfiguration, dexItemFactory);
this.configuration =
appView.options().assertionsConfiguration.assertionsConfigurations.stream()
.map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory()))
@@ -108,40 +125,40 @@
return configuration != null && !configuration.isPassthroughAll();
}
- private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) {
+ private ConfigurationEntryWithDexString getTransformationForMethod(DexEncodedMethod method) {
return getTransformationForType(method.getHolderType());
}
- private AssertionTransformation getTransformationForType(DexType type) {
- AssertionTransformation transformation = defaultTransformation;
+ private ConfigurationEntryWithDexString getTransformationForType(DexType type) {
+ ConfigurationEntryWithDexString result = defaultConfiguration;
for (ConfigurationEntryWithDexString entry : configuration) {
switch (entry.entry.getScope()) {
case ALL:
- transformation = entry.entry.getTransformation();
+ result = entry;
break;
case PACKAGE:
if (entry.value.size == 0) {
if (!type.descriptor.contains(dexItemFactory.descriptorSeparator)) {
- transformation = entry.entry.getTransformation();
+ result = entry;
}
} else if (type.descriptor.startsWith(entry.value)) {
- transformation = entry.entry.getTransformation();
+ result = entry;
}
break;
case CLASS:
if (type.descriptor.equals(entry.value)) {
- transformation = entry.entry.getTransformation();
+ result = entry;
}
if (isDescriptorForClassOrInnerClass(entry.value, type.descriptor)) {
- transformation = entry.entry.getTransformation();
+ result = entry;
}
break;
default:
throw new Unreachable();
}
}
- assert transformation != null;
- return transformation;
+ assert result != null;
+ return result;
}
private boolean isDescriptorForClassOrInnerClass(
@@ -310,8 +327,8 @@
}
private void runInternal(DexEncodedMethod method, IRCode code) {
- AssertionTransformation transformation = getTransformationForMethod(method);
- if (transformation == AssertionTransformation.PASSTHROUGH) {
+ ConfigurationEntryWithDexString configuration = getTransformationForMethod(method);
+ if (configuration.isPassthrough()) {
return;
}
DexEncodedMethod clinit;
@@ -338,7 +355,7 @@
InvokeMethod invoke = current.asInvokeMethod();
if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
if (method.getHolderType() == dexItemFactory.kotlin.assertions.type) {
- rewriteKotlinAssertionEnable(code, transformation, iterator, invoke);
+ rewriteKotlinAssertionEnable(code, configuration, iterator, invoke);
} else {
iterator.replaceCurrentInstruction(code.createIntConstant(0, current.getLocalInfo()));
}
@@ -355,16 +372,13 @@
if (isInitializerEnablingJavaVmAssertions
&& staticGet.getField().name == dexItemFactory.assertionsDisabled) {
iterator.replaceCurrentInstruction(
- code.createIntConstant(
- transformation == AssertionTransformation.DISABLE ? 1 : 0,
- current.getLocalInfo()));
+ code.createIntConstant(configuration.isDisabled() ? 1 : 0, current.getLocalInfo()));
}
// Rewrite kotlin._Assertions.ENABLED getter.
if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) {
iterator.replaceCurrentInstruction(
code.createIntConstant(
- kotlinTransformation == AssertionTransformation.DISABLE ? 0 : 1,
- current.getLocalInfo()));
+ kotlinTransformation.isDisabled() ? 0 : 1, current.getLocalInfo()));
}
}
}
@@ -372,10 +386,10 @@
private void rewriteKotlinAssertionEnable(
IRCode code,
- AssertionTransformation transformation,
+ ConfigurationEntryWithDexString configuration,
InstructionListIterator iterator,
InvokeMethod invoke) {
- if (iterator.hasNext() && transformation == AssertionTransformation.DISABLE) {
+ if (iterator.hasNext() && configuration.isDisabled()) {
// Check if the invocation of Class.desiredAssertionStatus() is followed by a static
// put to kotlin._Assertions.ENABLED, and if so remove both instructions.
// See comment in ClassInitializerAssertionEnablingAnalysis for the expected instruction
@@ -402,8 +416,7 @@
iterator.replaceCurrentInstruction(code.createIntConstant(0));
}
} else {
- iterator.replaceCurrentInstruction(
- code.createIntConstant(transformation == AssertionTransformation.ENABLE ? 1 : 0));
+ iterator.replaceCurrentInstruction(code.createIntConstant(configuration.isEnabled() ? 1 : 0));
}
}
}
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 a39688a..8182229 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
@@ -130,8 +130,9 @@
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
// Do not inline if the inlinee is greater than the api caller level.
// TODO(b/188498051): We should not force inline lower api method calls.
- if (reason != Reason.FORCE && !isApiSafeForInlining(method, singleTarget, appView.options())) {
- whyAreYouNotInliningReporter.reportInlineeHigherApiCall();
+ if (reason != Reason.FORCE
+ && !isApiSafeForInlining(
+ method, singleTarget, appView.options(), whyAreYouNotInliningReporter)) {
return false;
}
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 834e78d..a658752 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
@@ -577,8 +577,7 @@
&& !isSynthesizingNullCheckForReceiverUsingMonitorEnter) {
SimpleEffectAnalysisResult checksReceiverBeingNull =
canInlineWithoutSynthesizingNullCheckForReceiver(appView, code);
- if (!checksReceiverBeingNull.hasResult()
- || !checksReceiverBeingNull.isPartial()
+ if (checksReceiverBeingNull.isNotSatisfied()
|| (checksReceiverBeingNull.isPartial()
&& checksReceiverBeingNull.topMostNotSatisfiedBlockSize() > 1)) {
synthesizeNullCheckForReceiver(appView, code, invoke, code.entryBlock());
@@ -761,13 +760,11 @@
assert false : "Expected position for inlinee call to receiver";
return;
}
+ Position outermostCaller = position.getOutermostCaller();
Position removeInnerFrame =
- position
- .getOutermostCaller()
- .builderWithCopy()
- .setRemoveInnerFramesIfThrowingNpe(true)
- .build();
- instruction.forceOverwritePosition(position.replaceOutermostCallerPosition(removeInnerFrame));
+ outermostCaller.builderWithCopy().setRemoveInnerFramesIfThrowingNpe(true).build();
+ instruction.forceOverwritePosition(
+ position.replacePosition(outermostCaller, removeInnerFrame));
}
}
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 50bea08..984b6c1 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
@@ -19,6 +19,8 @@
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;
+import com.android.tools.r8.ir.code.ArrayGet;
+import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.FieldGet;
import com.android.tools.r8.ir.code.FieldInstruction;
@@ -30,6 +32,7 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
@@ -47,6 +50,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
/**
@@ -88,7 +92,9 @@
public static boolean shouldRun(AppView<?> appView, IRCode code) {
return appView.options().enableRedundantFieldLoadElimination
- && (code.metadata().mayHaveFieldInstruction() || code.metadata().mayHaveInitClass());
+ && (code.metadata().mayHaveArrayGet()
+ || code.metadata().mayHaveFieldInstruction()
+ || code.metadata().mayHaveInitClass());
}
private interface FieldValue {
@@ -97,7 +103,7 @@
return null;
}
- void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant);
+ void eliminateRedundantRead(InstructionListIterator it, Instruction redundant);
}
private class ExistingValue implements FieldValue {
@@ -114,9 +120,9 @@
}
@Override
- public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) {
- affectedValues.addAll(redundant.value().affectedValues());
- redundant.value().replaceUsers(value);
+ public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) {
+ affectedValues.addAll(redundant.outValue().affectedValues());
+ redundant.outValue().replaceUsers(value);
it.removeOrReplaceByDebugLocalRead();
value.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
}
@@ -141,13 +147,106 @@
}
@Override
- public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) {
- affectedValues.addAll(redundant.value().affectedValues());
+ public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) {
+ affectedValues.addAll(redundant.outValue().affectedValues());
it.replaceCurrentInstruction(
value.createMaterializingInstruction(appView.withClassHierarchy(), code, redundant));
}
}
+ private abstract static class ArraySlot {
+
+ protected final Value array;
+ protected final MemberType memberType;
+
+ private ArraySlot(Value array, MemberType memberType) {
+ this.array = array;
+ this.memberType = memberType;
+ }
+
+ public static ArraySlot create(Value array, Value index, MemberType memberType) {
+ if (index.isDefinedByInstructionSatisfying(Instruction::isConstNumber)) {
+ return new ArraySlotWithConstantIndex(
+ array, index.getDefinition().asConstNumber().getIntValue(), memberType);
+ }
+ return new ArraySlotWithValueIndex(array, index, memberType);
+ }
+
+ public MemberType getMemberType() {
+ return memberType;
+ }
+
+ public abstract boolean maybeHasIndex(int i);
+
+ boolean baseEquals(ArraySlot arraySlot) {
+ return array == arraySlot.array && memberType == arraySlot.memberType;
+ }
+ }
+
+ private static class ArraySlotWithConstantIndex extends ArraySlot {
+
+ private final int index;
+
+ private ArraySlotWithConstantIndex(Value array, int index, MemberType memberType) {
+ super(array, memberType);
+ this.index = index;
+ }
+
+ @Override
+ public boolean maybeHasIndex(int i) {
+ return index == i;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(array, index, memberType);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ ArraySlotWithConstantIndex arraySlot = (ArraySlotWithConstantIndex) other;
+ return index == arraySlot.index && baseEquals(arraySlot);
+ }
+ }
+
+ private static class ArraySlotWithValueIndex extends ArraySlot {
+
+ private final Value index;
+
+ private ArraySlotWithValueIndex(Value array, Value index, MemberType memberType) {
+ super(array, memberType);
+ this.index = index;
+ }
+
+ @Override
+ public boolean maybeHasIndex(int i) {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(array, index, memberType);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ ArraySlotWithValueIndex arraySlot = (ArraySlotWithValueIndex) other;
+ return index == arraySlot.index && baseEquals(arraySlot);
+ }
+ }
+
private static class FieldAndObject {
private final DexField field;
private final Value object;
@@ -217,7 +316,14 @@
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
Instruction instruction = it.next();
- if (instruction.isFieldInstruction()) {
+ if (instruction.isArrayAccess()) {
+ if (instruction.isArrayGet()) {
+ handleArrayGet(it, instruction.asArrayGet());
+ } else {
+ assert instruction.isArrayPut();
+ handleArrayPut(instruction.asArrayPut());
+ }
+ } else if (instruction.isFieldInstruction()) {
DexField reference = instruction.asFieldInstruction().getField();
DexClassAndField field = resolveField(reference);
if (field == null || field.getDefinition().isVolatile()) {
@@ -446,6 +552,43 @@
}
}
+ private void handleArrayGet(InstructionListIterator it, ArrayGet arrayGet) {
+ if (arrayGet.outValue().hasLocalInfo()) {
+ return;
+ }
+
+ Value array = arrayGet.array().getAliasedValue();
+ Value index = arrayGet.index().getAliasedValue();
+ ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType());
+ FieldValue replacement = activeState.getArraySlotValue(arraySlot);
+ if (replacement != null) {
+ replacement.eliminateRedundantRead(it, arrayGet);
+ return;
+ }
+
+ activeState.putArraySlotValue(arraySlot, new ExistingValue(arrayGet.outValue()));
+ }
+
+ private void handleArrayPut(ArrayPut arrayPut) {
+ int index = arrayPut.getIndexOrDefault(-1);
+ MemberType memberType = arrayPut.getMemberType();
+
+ // An array-put instruction can potentially write the given array slot on all arrays because of
+ // aliases.
+ if (index < 0) {
+ activeState.removeArraySlotValues(memberType);
+ } else {
+ activeState.removeArraySlotValues(memberType, index);
+ }
+
+ // Update the value of the field to allow redundant load elimination.
+ Value array = arrayPut.array().getAliasedValue();
+ Value indexValue = arrayPut.index().getAliasedValue();
+ ArraySlot arraySlot = ArraySlot.create(array, indexValue, memberType);
+ ExistingValue value = new ExistingValue(arrayPut.value());
+ activeState.putArraySlotValue(arraySlot, value);
+ }
+
private void handleInstanceGet(
InstructionListIterator it,
InstanceGet instanceGet,
@@ -646,6 +789,7 @@
}
private void killAllNonFinalActiveFields() {
+ activeState.clearArraySlotValues();
activeState.clearNonFinalInstanceFields();
activeState.clearNonFinalStaticFields();
activeState.clearMostRecentFieldWrites();
@@ -801,6 +945,8 @@
static class BlockState {
+ private LinkedHashMap<ArraySlot, FieldValue> arraySlotValues;
+
private LinkedHashMap<FieldAndObject, FieldValue> finalInstanceFieldValues;
private LinkedHashMap<DexField, FieldValue> finalStaticFieldValues;
@@ -826,6 +972,10 @@
public BlockState(int maxCapacity, BlockState state) {
this(maxCapacity);
if (state != null) {
+ if (state.arraySlotValues != null && !state.arraySlotValues.isEmpty()) {
+ arraySlotValues = new LinkedHashMap<>();
+ arraySlotValues.putAll(state.arraySlotValues);
+ }
if (state.finalInstanceFieldValues != null && !state.finalInstanceFieldValues.isEmpty()) {
finalInstanceFieldValues = new LinkedHashMap<>();
finalInstanceFieldValues.putAll(state.finalInstanceFieldValues);
@@ -861,6 +1011,10 @@
}
}
+ public void clearArraySlotValues() {
+ arraySlotValues = null;
+ }
+
public void clearMostRecentFieldWrites() {
clearMostRecentInstanceFieldWrites();
clearMostRecentStaticFieldWrites();
@@ -902,6 +1056,10 @@
}
}
+ public FieldValue getArraySlotValue(ArraySlot arraySlot) {
+ return arraySlotValues != null ? arraySlotValues.get(arraySlot) : null;
+ }
+
public FieldValue getInstanceFieldValue(FieldAndObject field) {
FieldValue value =
nonFinalInstanceFieldValues != null ? nonFinalInstanceFieldValues.get(field) : null;
@@ -921,6 +1079,11 @@
}
public void intersect(BlockState state) {
+ if (arraySlotValues != null && state.arraySlotValues != null) {
+ intersectFieldValues(arraySlotValues, state.arraySlotValues);
+ } else {
+ arraySlotValues = null;
+ }
if (finalInstanceFieldValues != null && state.finalInstanceFieldValues != null) {
intersectFieldValues(finalInstanceFieldValues, state.finalInstanceFieldValues);
} else {
@@ -962,7 +1125,9 @@
}
public boolean isEmpty() {
- return isEmpty(finalInstanceFieldValues)
+ return isEmpty(arraySlotValues)
+ && isEmpty(initializedClasses)
+ && isEmpty(finalInstanceFieldValues)
&& isEmpty(finalStaticFieldValues)
&& isEmpty(initializedClasses)
&& isEmpty(nonFinalInstanceFieldValues)
@@ -1008,6 +1173,7 @@
public void reduceSize(int numberOfItemsToRemove) {
assert numberOfItemsToRemove > 0;
assert numberOfItemsToRemove < size();
+ numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, arraySlotValues);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, initializedClasses);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalInstanceFieldValues);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalStaticFieldValues);
@@ -1035,6 +1201,22 @@
return reduceSize(numberOfItemsToRemove, map != null ? map.keySet() : null);
}
+ public void removeArraySlotValues(MemberType memberType) {
+ if (arraySlotValues != null) {
+ arraySlotValues.keySet().removeIf(arraySlot -> arraySlot.getMemberType() == memberType);
+ }
+ }
+
+ public void removeArraySlotValues(MemberType memberType, int index) {
+ if (arraySlotValues != null) {
+ arraySlotValues
+ .keySet()
+ .removeIf(
+ arraySlot ->
+ arraySlot.getMemberType() == memberType && arraySlot.maybeHasIndex(index));
+ }
+ }
+
public void removeInstanceField(FieldAndObject field) {
removeFinalInstanceField(field);
removeNonFinalInstanceField(field);
@@ -1089,6 +1271,14 @@
}
}
+ public void putArraySlotValue(ArraySlot arraySlot, FieldValue value) {
+ ensureCapacityForNewElement();
+ if (arraySlotValues == null) {
+ arraySlotValues = new LinkedHashMap<>();
+ }
+ arraySlotValues.put(arraySlot, value);
+ }
+
public void putFinalInstanceField(FieldAndObject field, FieldValue value) {
ensureCapacityForNewElement();
if (finalInstanceFieldValues == null) {
@@ -1155,7 +1345,8 @@
}
public int size() {
- return size(finalInstanceFieldValues)
+ return size(arraySlotValues)
+ + size(finalInstanceFieldValues)
+ size(finalStaticFieldValues)
+ size(initializedClasses)
+ size(nonFinalInstanceFieldValues)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
index 8942395..ae95f9e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -166,18 +166,19 @@
public static class SimpleEffectAnalysisResult {
+ private final ResultState result;
private final List<Instruction> satisfyingInstructions;
private final List<BasicBlock> topmostNotSatisfiedBlocks;
private SimpleEffectAnalysisResult(
- List<Instruction> satisfyingInstructions, List<BasicBlock> topmostNotSatisfiedBlocks) {
-
+ ResultState result,
+ List<Instruction> satisfyingInstructions,
+ List<BasicBlock> topmostNotSatisfiedBlocks) {
+ this.result = result;
this.satisfyingInstructions = satisfyingInstructions;
this.topmostNotSatisfiedBlocks = topmostNotSatisfiedBlocks;
- }
-
- public boolean hasResult() {
- return true;
+ assert !result.isPartial()
+ || (!satisfyingInstructions.isEmpty() && !topmostNotSatisfiedBlocks.isEmpty());
}
public void forEachSatisfyingInstruction(Consumer<Instruction> instructionConsumer) {
@@ -196,8 +197,12 @@
return new SimpleEffectAnalysisResultBuilder();
}
+ public boolean isNotSatisfied() {
+ return result.isNotSatisfied();
+ }
+
public boolean isPartial() {
- return !satisfyingInstructions.isEmpty() && !topmostNotSatisfiedBlocks.isEmpty();
+ return result.isPartial();
}
}
@@ -205,10 +210,10 @@
List<Instruction> satisfyingInstructions = new ArrayList<>();
List<BasicBlock> failingBlocksForPartialResults = ImmutableList.of();
- private boolean isFailed;
+ private ResultState result;
public void fail() {
- isFailed = true;
+ result = ResultState.NOT_SATISFIED;
}
public void addSatisfyingInstruction(Instruction instruction) {
@@ -219,20 +224,21 @@
this.failingBlocksForPartialResults = basicBlocks;
}
+ public void setResult(ResultState result) {
+ this.result = result;
+ }
+
public SimpleEffectAnalysisResult build() {
- return isFailed
+ return result.isNotComputed()
? NO_RESULT
- : new SimpleEffectAnalysisResult(satisfyingInstructions, failingBlocksForPartialResults);
+ : new SimpleEffectAnalysisResult(
+ result, satisfyingInstructions, failingBlocksForPartialResults);
}
}
private static final SimpleEffectAnalysisResult NO_RESULT =
- new SimpleEffectAnalysisResult(ImmutableList.of(), ImmutableList.of()) {
- @Override
- public boolean hasResult() {
- return false;
- }
- };
+ new SimpleEffectAnalysisResult(
+ ResultState.NOT_SATISFIED, ImmutableList.of(), ImmutableList.of());
public static SimpleEffectAnalysisResult run(IRCode code, InstructionAnalysis analysis) {
SimpleEffectAnalysisResultBuilder builder = SimpleEffectAnalysisResult.builder();
@@ -288,6 +294,7 @@
}
node.setState(resultState);
if (node.getNode().isEntry()) {
+ builder.setResult(resultState.state);
builder.setFailingBlocksForPartialResults(resultState.failingBlocks);
}
return CONTINUE;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 5e3b23d..8a3d24e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -167,23 +167,27 @@
offsetDiff = 1;
builder.addArgumentInfo(
0,
- new RewrittenPrototypeDescription.RewrittenTypeInfo(
- from.holder, to.proto.parameters.values[0]));
+ RewrittenTypeInfo.builder()
+ .setOldType(from.getHolderType())
+ .setNewType(to.getParameter(0))
+ .build());
}
- for (int i = 0; i < from.proto.parameters.size(); i++) {
- DexType fromType = from.proto.parameters.values[i];
- DexType toType = to.proto.parameters.values[i + offsetDiff];
+ for (int i = 0; i < from.getParameters().size(); i++) {
+ DexType fromType = from.getParameter(i);
+ DexType toType = to.getParameter(i + offsetDiff);
if (fromType != toType) {
builder.addArgumentInfo(
i + offsetDiff + toOffset,
- new RewrittenPrototypeDescription.RewrittenTypeInfo(fromType, toType));
+ RewrittenTypeInfo.builder().setOldType(fromType).setNewType(toType).build());
}
}
- RewrittenPrototypeDescription.RewrittenTypeInfo returnInfo =
- from.proto.returnType == to.proto.returnType
+ RewrittenTypeInfo returnInfo =
+ from.getReturnType() == to.getReturnType()
? null
- : new RewrittenPrototypeDescription.RewrittenTypeInfo(
- from.proto.returnType, to.proto.returnType);
+ : RewrittenTypeInfo.builder()
+ .setOldType(from.getReturnType())
+ .setNewType(to.getReturnType())
+ .build();
RewrittenPrototypeDescription prototypeChanges =
RewrittenPrototypeDescription.createForRewrittenTypes(returnInfo, builder.build())
.withExtraUnusedNullParameters(numberOfExtraNullParameters);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
index cc7d4ec..d3d3735 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.inliner;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
@@ -34,6 +35,9 @@
public void reportCallerNotSubtype() {}
@Override
+ public void reportCallerHasUnknownApiLevel() {}
+
+ @Override
public void reportClasspathMethod() {}
@Override
@@ -55,7 +59,8 @@
public void reportInlineeNotSimple() {}
@Override
- public void reportInlineeHigherApiCall() {}
+ public void reportInlineeHigherApiCall(
+ ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel) {}
@Override
public void reportInlineeRefersToClassesNotInMainDex() {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 66d6108..7c3e5da 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.inliner;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.InstancePut;
@@ -53,6 +54,8 @@
public abstract void reportCallerNotSubtype();
+ public abstract void reportCallerHasUnknownApiLevel();
+
public abstract void reportClasspathMethod();
public abstract void reportInaccessible();
@@ -67,7 +70,8 @@
public abstract void reportInlineeNotSimple();
- public abstract void reportInlineeHigherApiCall();
+ public abstract void reportInlineeHigherApiCall(
+ ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel);
public abstract void reportInlineeRefersToClassesNotInMainDex();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index 94632b6..f5807dd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.inliner;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
@@ -73,6 +74,11 @@
}
@Override
+ public void reportCallerHasUnknownApiLevel() {
+ print("computed API level for caller is unknown");
+ }
+
+ @Override
public void reportClasspathMethod() {
print("inlinee is on the classpath.");
}
@@ -115,8 +121,20 @@
}
@Override
- public void reportInlineeHigherApiCall() {
- print("inlinee having a higher api call than caller context.");
+ public void reportInlineeHigherApiCall(
+ ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel) {
+ assert callerApiLevel.isKnownApiLevel();
+ if (inlineeApiLevel.isUnknownApiLevel()) {
+ print("computed API level for inlinee is unknown");
+ } else {
+ assert inlineeApiLevel.isKnownApiLevel();
+ print(
+ "computed API level for inlinee ("
+ + inlineeApiLevel.asKnownApiLevel().getApiLevel()
+ + ") is higher than caller's ("
+ + callerApiLevel.asKnownApiLevel().getApiLevel()
+ + ")");
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 70e6d04..339f93d 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -174,7 +174,11 @@
clazz,
(superType, superClass, isInterface) -> {
if (isInterface && superClass.isNotProgramClass()) {
- frontierStatesForInterfaces.get(superType).add(reservationState);
+ Set<ReservedFieldNamingState> reservedNamingState =
+ frontierStatesForInterfaces.get(superType);
+ if (reservedNamingState != null) {
+ reservedNamingState.add(reservationState);
+ }
}
return TraversalContinuation.CONTINUE;
});
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 8067817..14f7711 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.naming.MethodNameMinifier.State;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -360,6 +361,7 @@
}
private final AppView<AppInfoWithLiveness> appView;
+ private final SubtypingInfo subtypingInfo;
private final Equivalence<DexMethod> equivalence;
private final Equivalence<DexEncodedMethod> definitionEquivalence;
private final MethodNameMinifier.State minifierState;
@@ -371,9 +373,11 @@
/** A map for caching all interface states. */
private final Map<DexType, InterfaceReservationState> interfaceStateMap = new HashMap<>();
- InterfaceMethodNameMinifier(AppView<AppInfoWithLiveness> appView, State minifierState) {
+ InterfaceMethodNameMinifier(
+ AppView<AppInfoWithLiveness> appView, State minifierState, SubtypingInfo subtypingInfo) {
this.appView = appView;
this.minifierState = minifierState;
+ this.subtypingInfo = subtypingInfo;
this.equivalence =
appView.options().getProguardConfiguration().isOverloadAggressively()
? MethodSignatureEquivalence.get()
@@ -420,7 +424,7 @@
// frontier states of classes that implement them. We add the frontier states so that we can
// reserve the names for later method naming.
timing.begin("Compute map");
- computeReservationFrontiersForAllImplementingClasses();
+ computeReservationFrontiersForAllImplementingClasses(interfaces);
for (DexClass iface : interfaces) {
InterfaceReservationState inheritanceState = interfaceStateMap.get(iface.type);
assert inheritanceState != null;
@@ -454,9 +458,6 @@
// refer to interfaces which has been removed.
Set<DexEncodedMethod> implementedMethods =
appView.appInfo().lookupLambdaImplementedMethods(callSite);
- if (implementedMethods.isEmpty()) {
- return;
- }
for (DexEncodedMethod method : implementedMethods) {
Wrapper<DexEncodedMethod> wrapped = definitionEquivalence.wrap(method);
InterfaceMethodGroupState groupState = globalStateMap.get(wrapped);
@@ -636,34 +637,29 @@
}
}
- private void computeReservationFrontiersForAllImplementingClasses() {
- appView
- .appInfo()
- .forEachTypeInHierarchyOfLiveProgramClasses(
- clazz -> {
- // TODO(b/133091438): Extend the if check to test for !clazz.isLibrary().
- if (!clazz.isInterface()) {
- appView
- .appInfo()
- .implementedInterfaces(clazz.type)
- .forEach(
- (directlyImplemented, ignoreIsKnownAndReserveInAllCases) -> {
- InterfaceReservationState iState =
- interfaceStateMap.get(directlyImplemented);
- if (iState != null) {
- DexType frontierType = minifierState.getFrontier(clazz.type);
- iState.addReservationType(frontierType);
- // The reservation state should already be added, but if a class is
- // extending an interface, we will not visit the class during the
- // sub-type traversel.
- if (minifierState.getReservationState(clazz.type) == null) {
- minifierState.allocateReservationStateAndReserve(
- clazz.type, frontierType);
- }
- }
- });
- }
- });
+ private void computeReservationFrontiersForAllImplementingClasses(Iterable<DexClass> interfaces) {
+ interfaces.forEach(
+ iface ->
+ subtypingInfo
+ .subtypes(iface.getType())
+ .forEach(
+ subType -> {
+ DexClass subClass = appView.contextIndependentDefinitionFor(subType);
+ if (subClass == null || subClass.isInterface()) {
+ return;
+ }
+ DexType frontierType = minifierState.getFrontier(subType);
+ if (minifierState.getReservationState(frontierType) == null) {
+ // The reservation state should already be added. If it does not exist
+ // it is because it is not reachable from the type hierarchy of program
+ // classes and we can therefore disregard this interface.
+ return;
+ }
+ InterfaceReservationState iState = interfaceStateMap.get(iface.getType());
+ if (iState != null) {
+ iState.addReservationType(frontierType);
+ }
+ }));
}
private boolean verifyAllCallSitesAreRepresentedIn(List<Wrapper<DexEncodedMethod>> groups) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index b2b9e9f..ca7fa4e 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -181,7 +182,10 @@
}
MethodRenaming computeRenaming(
- Iterable<DexClass> interfaces, ExecutorService executorService, Timing timing)
+ Iterable<DexClass> interfaces,
+ SubtypingInfo subtypingInfo,
+ ExecutorService executorService,
+ Timing timing)
throws ExecutionException {
// Phase 1: Reserve all the names that need to be kept and allocate linked state in the
// library part.
@@ -193,7 +197,7 @@
// states that may hold an implementation.
timing.begin("Phase 2");
InterfaceMethodNameMinifier interfaceMethodNameMinifier =
- new InterfaceMethodNameMinifier(appView, minifierState);
+ new InterfaceMethodNameMinifier(appView, minifierState, subtypingInfo);
timing.end();
timing.begin("Phase 3");
interfaceMethodNameMinifier.assignNamesToInterfaceMethods(timing, interfaces);
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 738be08..26c8d70 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -47,7 +47,7 @@
assert appView.options().isMinifying();
SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
timing.begin("ComputeInterfaces");
- List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder();
+ List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder(subtypingInfo);
timing.end();
timing.begin("MinifyClasses");
ClassNameMinifier classNameMinifier =
@@ -67,7 +67,7 @@
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
new MethodNameMinifier(appView, minifyMembers)
- .computeRenaming(interfaces, executorService, timing);
+ .computeRenaming(interfaces, subtypingInfo, executorService, timing);
timing.end();
assert new MinifiedRenaming(appView, classRenaming, methodRenaming, FieldRenaming.empty())
@@ -89,9 +89,16 @@
return lens;
}
- private List<DexClass> computeReachableInterfacesWithDeterministicOrder() {
+ private List<DexClass> computeReachableInterfacesWithDeterministicOrder(
+ SubtypingInfo subtypingInfo) {
List<DexClass> interfaces = new ArrayList<>();
- appView.appInfo().forEachReachableInterface(interfaces::add);
+ subtypingInfo.forAllInterfaceRoots(
+ type -> {
+ DexClass clazz = appView.contextIndependentDefinitionFor(type);
+ if (clazz != null) {
+ interfaces.add(clazz);
+ }
+ });
return classesWithDeterministicOrder(interfaces);
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 7ee0681..43cf17d 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceClassType;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.isCompanionClassType;
-import static com.android.tools.r8.utils.IterableUtils.fromMethod;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -35,6 +34,7 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -93,24 +93,34 @@
}
public NamingLens run(ExecutorService executorService, Timing timing) throws ExecutionException {
- AppInfoWithLiveness appInfo = appView.appInfo();
- SubtypingInfo subtypingInfo = appInfo.computeSubtypingInfo();
-
ArrayDeque<Map<DexReference, MemberNaming>> nonPrivateMembers = new ArrayDeque<>();
Set<DexReference> notMappedReferences = new HashSet<>();
-
+ timing.begin("MappingInterfaces");
+ Set<DexClass> classesToBuildSubtypeInformationFor =
+ SetUtils.newIdentityHashSet(appView.app().classes());
+ appView
+ .appInfo()
+ .getObjectAllocationInfoCollection()
+ .forEachInstantiatedLambdaInterfaces(
+ type -> {
+ DexClass lambdaInterface = appView.contextIndependentDefinitionFor(type);
+ if (lambdaInterface != null) {
+ classesToBuildSubtypeInformationFor.add(lambdaInterface);
+ }
+ });
+ appView.appInfo().forEachReferencedClasspathClass(classesToBuildSubtypeInformationFor::add);
+ SubtypingInfo subtypingInfo =
+ SubtypingInfo.create(classesToBuildSubtypeInformationFor, appView);
timing.begin("MappingInterfaces");
Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(DexClass::getType));
- // For union-find of interface methods we also need to add the library types above live types.
- appInfo.forEachReachableInterface(
+ subtypingInfo.forAllInterfaceRoots(
iFace -> {
- assert iFace.isInterface();
- interfaces.add(iFace);
- if (iFace.interfaces.isEmpty()) {
- computeMapping(iFace.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
+ DexClass iFaceDefinition = appView.definitionFor(iFace);
+ if (iFaceDefinition != null) {
+ interfaces.add(iFaceDefinition);
+ computeMapping(iFace, nonPrivateMembers, notMappedReferences, subtypingInfo);
}
- },
- fromMethod(appInfo::forEachReferencedClasspathClass, DexClass::getType));
+ });
assert nonPrivateMembers.isEmpty();
timing.end();
@@ -148,7 +158,7 @@
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
new MethodNameMinifier(appView, nameStrategy)
- .computeRenaming(interfaces, executorService, timing);
+ .computeRenaming(interfaces, subtypingInfo, executorService, timing);
// Amend the method renamings with the default interface methods.
methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames);
methodRenaming.renaming.putAll(additionalMethodNamings);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index aa91add..63c2dfc 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -64,10 +64,9 @@
private ProguardMapSupplier(ClassNameMapper classNameMapper, InternalOptions options) {
assert classNameMapper != null;
this.classNameMapper = classNameMapper.sorted();
- this.consumer =
- InternalOptions.assertionsEnabled()
- ? new ProguardMapChecker(options.proguardMapConsumer)
- : options.proguardMapConsumer;
+ // TODO(b/217111432): Validate Proguard using ProguardMapChecker without building the entire
+ // Proguard map in memory.
+ this.consumer = options.proguardMapConsumer;
this.options = options;
this.reporter = options.reporter;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 96b68f3..f5b55c6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -225,7 +225,7 @@
timing.end();
timing.begin("Compute unused arguments");
- effectivelyUnusedArgumentsAnalysis.computeEffectivelyUnusedArguments(codeScannerResult);
+ effectivelyUnusedArgumentsAnalysis.computeEffectivelyUnusedArguments();
effectivelyUnusedArgumentsAnalysis = null;
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
index 3417bfa..c428a70 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -130,9 +130,30 @@
.getPosition()
.getOutermostCallerMatchingOrElse(
Position::isRemoveInnerFramesIfThrowingNpe, invoke.getPosition());
+ if (nullCheckPosition.isRemoveInnerFramesIfThrowingNpe()) {
+ // We've found an outermost removeInnerFrames for an invoke with receiver. Assume we
+ // have call chain: inline -> callerInline -> callerCallerInline
+ // where callerInline.isRemoveInnerFramesIfThrowingNpe() == true.
+ // inline must therefore have an immediate use of the receiver which is tracked in
+ // callerInline such that the frame can be removed when retracing.
+ // When synthesizing a nullCheck for the receiver on inline, the check should actually
+ // fail in the callerInline. We therefore use this as the new position.
+ // Since the exception is now moved out to callerInline, we should no longer strip the
+ // topmost frame if we see an NPE in inline, so we update the position on this invoke to
+ // inline -> callerInline' -> callerCallerInline
+ // where callerInline.isRemoveInnerFramesIfThrowingNpe() == false;
+ Position newCallerPositionTail =
+ nullCheckPosition
+ .builderWithCopy()
+ .setRemoveInnerFramesIfThrowingNpe(false)
+ .build();
+ invoke.forceOverwritePosition(
+ invoke.getPosition().replacePosition(nullCheckPosition, newCallerPositionTail));
+ // We can then use pos2 (newCallerPositionTail) as the new null-check position.
+ nullCheckPosition = newCallerPositionTail;
+ }
instructionIterator.insertNullCheckInstruction(
appView, code, blockIterator, receiver, nullCheckPosition);
-
// Reset the block iterator.
if (invoke.getBlock().hasCatchHandlers()) {
BasicBlock splitBlock = invoke.getBlock();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
index c1a322b..b0be28f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
@@ -172,11 +171,11 @@
return effectivelyUnusedConstraints;
}
- public void computeEffectivelyUnusedArguments(MethodStateCollectionByReference methodStates) {
+ public void computeEffectivelyUnusedArguments() {
// Build a graph where nodes are method parameters and there is an edge from method parameter p0
// to method parameter p1 if the removal of p0 depends on the removal of p1.
EffectivelyUnusedArgumentsGraph dependenceGraph =
- EffectivelyUnusedArgumentsGraph.create(appView, constraints, methodStates);
+ EffectivelyUnusedArgumentsGraph.create(appView, constraints);
// Remove all unoptimizable method parameters from the graph, as well as all nodes that depend
// on a node that is unoptimable.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
index fdc01bc..9fbd408 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.dfs.DFSStack;
@@ -38,14 +37,13 @@
public static EffectivelyUnusedArgumentsGraph create(
AppView<AppInfoWithLiveness> appView,
- Map<MethodParameter, Set<MethodParameter>> constraints,
- MethodStateCollectionByReference methodStates) {
+ Map<MethodParameter, Set<MethodParameter>> constraints) {
EffectivelyUnusedArgumentsGraph graph = new EffectivelyUnusedArgumentsGraph(appView);
constraints.forEach(
(methodParameter, constraintsForMethodParameter) -> {
EffectivelyUnusedArgumentsGraphNode node = graph.getOrCreateNode(methodParameter);
for (MethodParameter constraint : constraintsForMethodParameter) {
- graph.addConstraintEdge(node, constraint, constraints, methodStates);
+ graph.addConstraintEdge(node, constraint, constraints);
}
});
return graph;
@@ -54,8 +52,7 @@
void addConstraintEdge(
EffectivelyUnusedArgumentsGraphNode node,
MethodParameter constraint,
- Map<MethodParameter, Set<MethodParameter>> constraints,
- MethodStateCollectionByReference methodStates) {
+ Map<MethodParameter, Set<MethodParameter>> constraints) {
ProgramMethod dependencyMethod =
asProgramMethodOrNull(appView.definitionFor(constraint.getMethod()));
if (dependencyMethod == null) {
@@ -68,7 +65,7 @@
// invoke (or we cannot preserve NPE semantics).
if (dependencyMethod.getDefinition().isInstance()
&& constraint.getIndex() == 0
- && node.isNullable(methodStates)) {
+ && node.isNullable()) {
node.setUnoptimizable();
return;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
index 2b52686..c093924 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
@@ -7,10 +7,7 @@
import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.google.common.collect.Sets;
import java.util.BitSet;
import java.util.Set;
@@ -75,28 +72,13 @@
return successors;
}
- boolean isNullable(MethodStateCollectionByReference methodStates) {
+ boolean isNullable() {
if (method.getDefinition().isInstance() && argumentIndex == 0) {
return false;
}
- MethodState methodState = methodStates.get(method);
- if (methodState.isBottom()) {
- // TODO: this means the method is unreachable? what to do in this case?
- return true;
- }
- assert !methodState.isBottom();
- if (methodState.isUnknown()) {
- return true;
- }
- assert methodState.isMonomorphic();
- ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic();
- ParameterState parameterState = monomorphicMethodState.getParameterState(argumentIndex);
- if (parameterState.isUnknown()) {
- return true;
- }
- assert parameterState.isConcrete();
- assert parameterState.asConcrete().isReferenceParameter();
- return parameterState.asConcrete().asReferenceParameter().getNullability().isMaybeNull();
+ DynamicType dynamicType =
+ method.getOptimizationInfo().getArgumentInfos().getDynamicType(argumentIndex);
+ return dynamicType.getNullability().isNullable();
}
boolean isUnoptimizable() {
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 14b393c..b4467ea 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -602,7 +602,10 @@
RetraceStackTraceElementProxy<T, ST> other,
Function<RetraceStackTraceElementProxy<T, ST>, Boolean> predicate,
Function<RetraceStackTraceElementProxy<T, ST>, V> getter) {
- return Comparator.comparing(predicate).thenComparing(getter).compare(one, other) != 0;
+ return Comparator.comparing(predicate)
+ .thenComparing(getter, Comparator.nullsFirst(V::compareTo))
+ .compare(one, other)
+ != 0;
}
public static <T, ST extends StackTraceElementProxy<T, ST>>
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
index 94e2d31..f29af88 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RewriteAction;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
-import com.android.tools.r8.utils.ListUtils;
import java.util.List;
import java.util.OptionalInt;
@@ -36,12 +35,13 @@
}
RetraceStackTraceCurrentEvaluationInformation.Builder builder =
RetraceStackTraceCurrentEvaluationInformation.builder();
- MappedRange last = ListUtils.last(mappedRanges);
- for (RewriteFrameMappingInformation rewriteInformation :
- last.getRewriteFrameMappingInformation()) {
- if (evaluateConditions(rewriteInformation.getConditions())) {
- for (RewriteAction action : rewriteInformation.getActions()) {
- action.evaluate(builder);
+ for (MappedRange mappedRange : mappedRanges) {
+ for (RewriteFrameMappingInformation rewriteInformation :
+ mappedRange.getRewriteFrameMappingInformation()) {
+ if (evaluateConditions(rewriteInformation.getConditions())) {
+ for (RewriteAction action : rewriteInformation.getActions()) {
+ action.evaluate(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 71d4807..78c8797 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1540,57 +1540,6 @@
app().asDirect().classpathClasses().forEach(fn);
}
- /**
- * Visits all class definitions that are a live program type or a type above it in the hierarchy.
- *
- * <p>Any given definition will be visited at most once. No guarantees are places on the order.
- */
- public void forEachTypeInHierarchyOfLiveProgramClasses(Consumer<DexClass> fn) {
- forEachTypeInHierarchyOfLiveProgramClasses(
- fn,
- ListUtils.map(liveTypes, t -> definitionFor(t).asProgramClass()),
- objectAllocationInfoCollection.getInstantiatedLambdaInterfaces(),
- this);
- }
-
- // Split in a static method so it can be used during construction.
- static void forEachTypeInHierarchyOfLiveProgramClasses(
- Consumer<DexClass> fn,
- Collection<DexProgramClass> liveProgramClasses,
- Set<DexType> lambdaInterfaces,
- AppInfoWithClassHierarchy appInfo) {
- Set<DexType> seen = Sets.newIdentityHashSet();
- liveProgramClasses.forEach(c -> seen.add(c.type));
- Deque<DexType> worklist = new ArrayDeque<>(lambdaInterfaces);
- for (DexProgramClass liveProgramClass : liveProgramClasses) {
- fn.accept(liveProgramClass);
- DexType superType = liveProgramClass.superType;
- if (superType != null && seen.add(superType)) {
- worklist.add(superType);
- }
- for (DexType iface : liveProgramClass.interfaces.values) {
- if (seen.add(iface)) {
- worklist.add(iface);
- }
- }
- }
- while (!worklist.isEmpty()) {
- DexType type = worklist.pop();
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz != null) {
- fn.accept(clazz);
- if (clazz.superType != null && seen.add(clazz.superType)) {
- worklist.add(clazz.superType);
- }
- for (DexType iface : clazz.interfaces.values) {
- if (seen.add(iface)) {
- worklist.add(iface);
- }
- }
- }
- }
- }
-
@Override
public void forEachInstantiatedSubType(
DexType type,
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 33b077d..658cfbf 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -209,10 +209,6 @@
return this == FINAL_TREE_SHAKING;
}
- public boolean isInitialOrFinalTreeShaking() {
- return isInitialTreeShaking() || isFinalTreeShaking();
- }
-
public boolean isInitialMainDexTracing() {
return this == INITIAL_MAIN_DEX_TRACING;
}
@@ -480,7 +476,7 @@
? ProguardCompatibilityActions.builder()
: null;
- if (mode.isInitialOrFinalTreeShaking()) {
+ if (mode.isTreeShaking()) {
GetArrayOfMissingTypeVerifyErrorWorkaround.register(appView, this);
InvokeVirtualToInterfaceVerifyErrorWorkaround.register(appView, this);
if (options.protoShrinking().enableGeneratedMessageLiteShrinking) {
@@ -817,15 +813,9 @@
return;
}
// Add everything if we are not shrinking.
- assert appView.options().getProguardConfiguration().getKeepAllRule() != null;
- ProguardKeepRuleBase keepAllRule =
- appView.options().getProguardConfiguration().getKeepAllRule();
- KeepClassInfo.Joiner keepClassInfo =
- KeepClassInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
- KeepFieldInfo.Joiner keepFieldInfo =
- KeepFieldInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
- KeepMethodInfo.Joiner keepMethodInfo =
- KeepMethodInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
+ KeepClassInfo.Joiner keepClassInfo = KeepClassInfo.newEmptyJoiner().disallowShrinking();
+ KeepFieldInfo.Joiner keepFieldInfo = KeepFieldInfo.newEmptyJoiner().disallowShrinking();
+ KeepMethodInfo.Joiner keepMethodInfo = KeepMethodInfo.newEmptyJoiner().disallowShrinking();
EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (appView.getSyntheticItems().isSyntheticClass(clazz)
@@ -845,8 +835,7 @@
DexProgramClass clazz,
KeepClassInfo.Joiner minimumKeepInfo,
EnqueuerEvent preconditionEvent) {
- assert !minimumKeepInfo.isShrinkingAllowed();
- assert !minimumKeepInfo.getRules().isEmpty();
+ assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
enqueueKeepRuleInstantiatedType(clazz, minimumKeepInfo.getRules(), precondition);
}
@@ -876,8 +865,7 @@
private void enqueueFieldDueToNoShrinkingRule(
ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) {
- assert !minimumKeepInfo.isShrinkingAllowed();
- assert !minimumKeepInfo.getRules().isEmpty();
+ assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
workList.enqueueMarkFieldKeptAction(
field,
@@ -889,8 +877,7 @@
ProgramMethod method,
KeepMethodInfo.Joiner minimumKeepInfo,
EnqueuerEvent preconditionEvent) {
- assert !minimumKeepInfo.isShrinkingAllowed();
- assert !minimumKeepInfo.getRules().isEmpty();
+ assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
workList.enqueueMarkMethodKeptAction(
method,
@@ -2419,7 +2406,8 @@
// because each analysis may depend on seeing all the (clazz, reason) pairs. Thus, not doing so
// could lead to nondeterminism.
analyses.forEach(
- analysis -> analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context));
+ analysis ->
+ analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context, workList));
if (!markInstantiatedClass(clazz, context, instantiationReason, keepReason)) {
return;
@@ -2799,7 +2787,7 @@
applyMinimumKeepInfo(field);
// Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
+ analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context, workList));
}
// Package protected due to entry point from worklist.
@@ -2825,7 +2813,7 @@
traceFieldDefinition(field);
- analyses.forEach(analysis -> analysis.notifyMarkFieldAsReachable(field));
+ analyses.forEach(analysis -> analysis.notifyMarkFieldAsReachable(field, workList));
}
private void traceFieldDefinition(ProgramField field) {
@@ -3076,7 +3064,7 @@
target.accept(
method -> markVirtualDispatchTargetAsLive(method, reason),
lambda -> markVirtualDispatchTargetAsLive(lambda, reason));
- analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target));
+ analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target, workList));
}
private void markVirtualDispatchTargetAsLive(
@@ -3156,7 +3144,9 @@
if (target == null) {
failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
analyses.forEach(
- analyses -> analyses.notifyFailedMethodResolutionTarget(resolution.getResolvedMethod()));
+ analyses ->
+ analyses.notifyFailedMethodResolutionTarget(
+ resolution.getResolvedMethod(), workList));
return;
}
@@ -3305,7 +3295,7 @@
KeepClassInfo.Joiner minimumKeepInfo) {
if ((options.isShrinking() || mode.isMainDexTracing())
&& !minimumKeepInfo.isShrinkingAllowed()) {
- assert !minimumKeepInfo.getRules().isEmpty();
+ assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
enqueueClassDueToNoShrinkingRule(clazz, minimumKeepInfo, preconditionEvent);
}
}
@@ -3352,7 +3342,7 @@
ProgramField field, EnqueuerEvent preconditionEvent, KeepFieldInfo.Joiner minimumKeepInfo) {
if ((options.isShrinking() || mode.isMainDexTracing())
&& !minimumKeepInfo.isShrinkingAllowed()) {
- assert !minimumKeepInfo.getRules().isEmpty();
+ assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
enqueueFieldDueToNoShrinkingRule(field, minimumKeepInfo, preconditionEvent);
}
}
@@ -3401,7 +3391,7 @@
KeepMethodInfo.Joiner minimumKeepInfo) {
if ((options.isShrinking() || mode.isMainDexTracing())
&& !minimumKeepInfo.isShrinkingAllowed()) {
- assert !minimumKeepInfo.getRules().isEmpty();
+ assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
enqueueMethodDueToNoShrinkingRule(method, minimumKeepInfo, preconditionEvent);
if (method.getDefinition().isInstanceInitializer()) {
@@ -4317,7 +4307,7 @@
}
// Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
+ analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context, workList));
}
private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) {
@@ -4337,7 +4327,7 @@
markMethodAsLiveWithCompatRule(method);
}
}
- analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method));
+ analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method, workList));
}
void traceMethodDefinitionExcludingCode(ProgramMethod method) {
@@ -4368,7 +4358,7 @@
useRegistryFactory.create(appView, method, this, appView.apiLevelCompute());
method.registerCodeReferences(registry);
// Notify analyses.
- analyses.forEach(analysis -> analysis.processTracedCode(method, registry));
+ analyses.forEach(analysis -> analysis.processTracedCode(method, registry, workList));
}
private void markReferencedTypesAsLive(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 83055e7..2fe6b81 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.DequeUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Sets;
@@ -45,6 +46,7 @@
public class GraphReporter {
private final AppView<?> appView;
+ private final InternalOptions options;
private final GraphConsumer keptGraphConsumer;
private final CollectingGraphConsumer verificationGraphConsumer;
@@ -58,6 +60,7 @@
GraphReporter(AppView<?> appView, GraphConsumer keptGraphConsumer) {
this.appView = appView;
+ this.options = appView.options();
if (appView.options().testing.verifyKeptGraphInfo) {
this.verificationGraphConsumer = new CollectingGraphConsumer(keptGraphConsumer);
this.keptGraphConsumer = verificationGraphConsumer;
@@ -108,17 +111,17 @@
KeepReasonWitness reportKeepClass(
DexDefinition precondition, ProguardKeepRuleBase rule, DexProgramClass clazz) {
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
+ if (keptGraphConsumer != null) {
+ KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+ EdgeKind edgeKind = reportPrecondition(ruleNode);
+ return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind);
}
- KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
- EdgeKind edgeKind = reportPrecondition(ruleNode);
- return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind);
+ return KeepReasonWitness.INSTANCE;
}
KeepReasonWitness reportKeepClass(
DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexProgramClass clazz) {
- assert !rules.isEmpty();
+ assert !rules.isEmpty() || !options.isShrinking();
if (keptGraphConsumer != null) {
for (ProguardKeepRuleBase rule : rules) {
reportKeepClass(precondition, rule, clazz);
@@ -129,17 +132,17 @@
KeepReasonWitness reportKeepMethod(
DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedMethod method) {
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
+ if (keptGraphConsumer != null) {
+ KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+ EdgeKind edgeKind = reportPrecondition(ruleNode);
+ return reportEdge(ruleNode, getMethodGraphNode(method.getReference()), edgeKind);
}
- KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
- EdgeKind edgeKind = reportPrecondition(ruleNode);
- return reportEdge(ruleNode, getMethodGraphNode(method.getReference()), edgeKind);
+ return KeepReasonWitness.INSTANCE;
}
KeepReasonWitness reportKeepMethod(
DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedMethod method) {
- assert !rules.isEmpty();
+ assert !rules.isEmpty() || !options.isShrinking();
if (keptGraphConsumer != null) {
for (ProguardKeepRuleBase rule : rules) {
reportKeepMethod(precondition, rule, method);
@@ -150,17 +153,17 @@
KeepReasonWitness reportKeepField(
DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedField field) {
- if (keptGraphConsumer == null) {
- return KeepReasonWitness.INSTANCE;
+ if (keptGraphConsumer != null) {
+ KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+ EdgeKind edgeKind = reportPrecondition(ruleNode);
+ return reportEdge(ruleNode, getFieldGraphNode(field.getReference()), edgeKind);
}
- KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
- EdgeKind edgeKind = reportPrecondition(ruleNode);
- return reportEdge(ruleNode, getFieldGraphNode(field.getReference()), edgeKind);
+ return KeepReasonWitness.INSTANCE;
}
KeepReasonWitness reportKeepField(
DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedField field) {
- assert !rules.isEmpty();
+ assert !rules.isEmpty() || !options.isShrinking();
if (keptGraphConsumer != null) {
for (ProguardKeepRuleBase rule : rules) {
reportKeepField(precondition, rule, field);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 598f6a7..9b83643 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.shaking.KeepInfo.Builder;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.Set;
import java.util.function.Consumer;
@@ -560,5 +561,11 @@
assert original.isLessThanOrEquals(joined);
return joined;
}
+
+ public boolean verifyShrinkingDisallowedWithRule(InternalOptions options) {
+ assert !isShrinkingAllowed();
+ assert !getRules().isEmpty() || !options.isShrinking();
+ return true;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 2f6de9b..c1f5a95 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.nio.file.Path;
@@ -68,12 +67,10 @@
ProguardPathFilter.builder().disable();
private boolean forceProguardCompatibility = false;
private boolean overloadAggressively;
- private boolean keepRuleSynthesisForRecompilation = false;
private boolean configurationDebugging = false;
private boolean dontUseMixedCaseClassnames = false;
private boolean protoShrinking = false;
private int maxRemovedAndroidLogLevel = 1;
- private ProguardKeepRule keepAllRule;
private Builder(DexItemFactory dexItemFactory, Reporter reporter) {
this.dexItemFactory = dexItemFactory;
@@ -281,10 +278,6 @@
this.overloadAggressively = overloadAggressively;
}
- public void enableKeepRuleSynthesisForRecompilation() {
- this.keepRuleSynthesisForRecompilation = true;
- }
-
public void setConfigurationDebugging(boolean configurationDebugging) {
this.configurationDebugging = configurationDebugging;
}
@@ -309,24 +302,6 @@
this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
}
- /**
- * This synthesizes a set of keep rules that are necessary in order to be able to successfully
- * recompile the generated dex files with the same keep rules.
- */
- public void synthesizeKeepRulesForRecompilation() {
- List<ProguardConfigurationRule> synthesizedKeepRules = new ArrayList<>();
- for (ProguardConfigurationRule rule : rules) {
- ProguardConfigurationUtils.synthesizeKeepRulesForRecompilation(rule, synthesizedKeepRules);
- }
- if (rules.addAll(synthesizedKeepRules)) {
- parsedConfiguration.add(
- StringUtils.lines(
- synthesizedKeepRules.stream()
- .map(ProguardClassSpecification::toString)
- .toArray(String[]::new)));
- }
- }
-
public ProguardConfiguration buildRaw() {
ProguardConfiguration configuration =
new ProguardConfiguration(
@@ -369,8 +344,7 @@
configurationDebugging,
dontUseMixedCaseClassnames,
protoShrinking,
- maxRemovedAndroidLogLevel,
- keepAllRule);
+ maxRemovedAndroidLogLevel);
reporter.failIfPendingErrors();
@@ -382,31 +356,6 @@
// For Proguard -keepattributes are only applicable when obfuscating.
keepAttributePatterns.addAll(ProguardKeepAttributes.KEEP_ALL);
}
- // If either of the flags -dontshrink or -dontobfuscate, or shrinking or minification is
- // turned off through the API, then add a match all rule which will apply that.
- if (!isObfuscating() || !isOptimizing() || !isShrinking()) {
- ProguardKeepRule rule =
- ProguardKeepRule.defaultKeepAllRule(
- modifiers -> {
- modifiers
- .setAllowsAccessModification(isAccessModificationEnabled())
- .setAllowsObfuscation(isObfuscating())
- .setAllowsOptimization(isOptimizing())
- .setAllowsShrinking(isShrinking());
-
- // In non-compatibility mode, adding -dontoptimize does not cause all annotations
- // to be retained.
- if (!forceProguardCompatibility && isShrinking()) {
- modifiers.setAllowsAnnotationRemoval(true);
- }
- });
- addRule(rule);
- this.keepAllRule = rule;
- }
-
- if (keepRuleSynthesisForRecompilation) {
- synthesizeKeepRulesForRecompilation();
- }
if (packageObfuscationMode == PackageObfuscationMode.NONE && obfuscating) {
packageObfuscationMode = PackageObfuscationMode.MINIFICATION;
@@ -456,7 +405,6 @@
private final boolean dontUseMixedCaseClassnames;
private final boolean protoShrinking;
private final int maxRemovedAndroidLogLevel;
- private final ProguardKeepRule keepAllRule;
private ProguardConfiguration(
String parsedConfiguration,
@@ -498,8 +446,7 @@
boolean configurationDebugging,
boolean dontUseMixedCaseClassnames,
boolean protoShrinking,
- int maxRemovedAndroidLogLevel,
- ProguardKeepRule keepAllRule) {
+ int maxRemovedAndroidLogLevel) {
this.parsedConfiguration = parsedConfiguration;
this.dexItemFactory = factory;
this.injars = ImmutableList.copyOf(injars);
@@ -540,7 +487,6 @@
this.dontUseMixedCaseClassnames = dontUseMixedCaseClassnames;
this.protoShrinking = protoShrinking;
this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
- this.keepAllRule = keepAllRule;
}
/**
@@ -720,10 +666,6 @@
return maxRemovedAndroidLogLevel;
}
- public ProguardKeepRule getKeepAllRule() {
- return keepAllRule;
- }
-
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 8c0d0b4..bf21d1f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -181,21 +181,4 @@
}
return false;
}
-
- public static void synthesizeKeepRulesForRecompilation(
- ProguardConfigurationRule rule, List<ProguardConfigurationRule> synthesizedKeepRules) {
- if (rule.hasInheritanceClassName()) {
- ProguardTypeMatcher inheritanceClassName = rule.getInheritanceClassName();
- synthesizedKeepRules.add(
- ProguardKeepRule.builder()
- .setOrigin(synthesizedRecompilationOrigin)
- .setType(ProguardKeepRuleType.KEEP)
- .setClassType(
- rule.getInheritanceIsExtends()
- ? ProguardClassType.CLASS
- : ProguardClassType.INTERFACE)
- .setClassNames(ProguardClassNameList.singletonList(inheritanceClassName))
- .build());
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 089ccce..8eb6b51 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -11,22 +11,44 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
+import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
public class AndroidApiLevelUtils {
public static boolean isApiSafeForInlining(
ProgramMethod caller, ProgramMethod inlinee, InternalOptions options) {
+ return isApiSafeForInlining(
+ caller, inlinee, options, NopWhyAreYouNotInliningReporter.getInstance());
+ }
+
+ public static boolean isApiSafeForInlining(
+ ProgramMethod caller,
+ ProgramMethod inlinee,
+ InternalOptions options,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
if (!options.apiModelingOptions().enableApiCallerIdentification) {
return true;
}
if (caller.getHolderType() == inlinee.getHolderType()) {
return true;
}
+ ComputedApiLevel callerApiLevelForCode = caller.getDefinition().getApiLevelForCode();
+ if (callerApiLevelForCode.isUnknownApiLevel()) {
+ whyAreYouNotInliningReporter.reportCallerHasUnknownApiLevel();
+ return false;
+ }
// For inlining we only measure if the code has invokes into the library.
- return caller
+ ComputedApiLevel inlineeApiLevelForCode = inlinee.getDefinition().getApiLevelForCode();
+ if (!caller
.getDefinition()
.getApiLevelForCode()
- .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
+ .isGreaterThanOrEqualTo(inlineeApiLevelForCode)) {
+ whyAreYouNotInliningReporter.reportInlineeHigherApiCall(
+ callerApiLevelForCode, inlineeApiLevelForCode);
+ return false;
+ }
+ return true;
}
public static ComputedApiLevel getApiReferenceLevelForMerging(
diff --git a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
index 7dc7b33..df3f944 100644
--- a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
+++ b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
@@ -11,20 +11,20 @@
public class AssertionConfigurationWithDefault {
- public final AssertionTransformation defautlTransformation;
+ public final AssertionsConfiguration defaultConfiguration;
public final List<AssertionsConfiguration> assertionsConfigurations;
public AssertionConfigurationWithDefault(
- AssertionTransformation defautlTransformation,
+ AssertionsConfiguration defautlTransformation,
List<AssertionsConfiguration> assertionsConfigurations) {
- this.defautlTransformation = defautlTransformation;
+ this.defaultConfiguration = defautlTransformation;
assert assertionsConfigurations != null;
this.assertionsConfigurations = assertionsConfigurations;
}
public boolean isPassthroughAll() {
if (assertionsConfigurations.size() == 0) {
- return defautlTransformation == AssertionTransformation.PASSTHROUGH;
+ return defaultConfiguration.getTransformation() == AssertionTransformation.PASSTHROUGH;
}
return assertionsConfigurations.size() == 1
&& assertionsConfigurations.get(0).getScope() == AssertionTransformationScope.ALL
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
index e99ff55..4df05c6 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
@@ -28,4 +28,8 @@
public static Boolean[] falseValues() {
return FALSE_VALUES;
}
+
+ public static boolean xor(boolean x, boolean y) {
+ return (x && !y) || (!x && y);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index 4f652cf..4b20c06 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -6,6 +6,7 @@
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -33,6 +34,11 @@
};
}
+ public static <S, T, R> BiConsumer<S, T> andThen(
+ BiFunction<S, T, R> function, Consumer<R> consumer) {
+ return (s, t) -> consumer.accept(function.apply(s, t));
+ }
+
public static <T> Consumer<T> emptyConsumer() {
return ignore -> {};
}
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 d32fe5c..bc88b16 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.SourceFileProvider;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
-import com.android.tools.r8.androidapi.AndroidApiForHashingClass;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Marker;
@@ -39,12 +38,12 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
@@ -780,10 +779,6 @@
return desugarSpecificOptions;
}
- public static boolean shouldEnableKeepRuleSynthesisForRecompilation() {
- return System.getProperty("com.android.tools.r8.keepRuleSynthesisForRecompilation") != null;
- }
-
private static Set<String> getExtensiveLoggingFilter() {
String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
if (property != null) {
@@ -1489,12 +1484,6 @@
public static class ApiModelTestingOptions {
- // A mapping from references to the api-level introducing them.
- public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
- public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
- public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
- public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
-
public boolean enableApiCallerIdentification =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean checkAllApiReferencesAreSet =
@@ -1504,54 +1493,28 @@
public boolean enableOutliningOfMethods =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+ // A mapping from references to the api-level introducing them.
+ public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
+ public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
+ public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
+ public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
+
public void visitMockedApiLevelsForReferences(
- DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) {
+ DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) {
if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) {
return;
}
- Set<ClassReference> classReferences = new HashSet<>(classApiMapping.keySet());
- methodApiMapping
- .keySet()
- .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
- fieldApiMapping
- .keySet()
- .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
- classReferences.forEach(
- classReference -> {
- consumer.accept(
- new AndroidApiForHashingClass() {
- @Override
- public DexType getType() {
- return factory.createType(classReference.getDescriptor());
- }
-
- @Override
- public AndroidApiLevel getApiLevel() {
- return classApiMapping.getOrDefault(classReference, ANDROID_PLATFORM);
- }
-
- @Override
- public void visitMethodsWithApiLevels(
- BiConsumer<DexMethod, AndroidApiLevel> consumer) {
- methodApiMapping.forEach(
- (methodReference, apiLevel) -> {
- if (methodReference.getHolderClass().equals(classReference)) {
- consumer.accept(factory.createMethod(methodReference), apiLevel);
- }
- });
- }
-
- @Override
- public void visitFieldsWithApiLevels(
- BiConsumer<DexField, AndroidApiLevel> consumer) {
- fieldApiMapping.forEach(
- (fieldReference, apiLevel) -> {
- if (fieldReference.getHolderClass().equals(classReference)) {
- consumer.accept(factory.createField(fieldReference), apiLevel);
- }
- });
- }
- });
+ classApiMapping.forEach(
+ (classReference, apiLevel) -> {
+ apiLevelConsumer.accept(factory.createType(classReference.getDescriptor()), apiLevel);
+ });
+ fieldApiMapping.forEach(
+ (fieldReference, apiLevel) -> {
+ apiLevelConsumer.accept(factory.createField(fieldReference), apiLevel);
+ });
+ methodApiMapping.forEach(
+ (methodReference, apiLevel) -> {
+ apiLevelConsumer.accept(factory.createMethod(methodReference), apiLevel);
});
}
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index 624fb10..6be9cc3 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -60,7 +60,7 @@
return result;
}
- public static <T> Set<T> newIdentityHashSet(Iterable<T> c) {
+ public static <T> Set<T> newIdentityHashSet(Iterable<? extends T> c) {
Set<T> result = Sets.newIdentityHashSet();
c.forEach(result::add);
return result;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 8531ceb..97ee26b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -116,16 +116,16 @@
assertFalse(synthesizedMissingNotReferenced.isPresent());
verifyThat(inspector, parameters, addedOn23).isNotOutlinedFrom(testMethod);
verifyThat(inspector, parameters, addedOn27)
- .isOutlinedFromUntil(testMethod, AndroidApiLevel.O_MR1);
+ .isOutlinedFromUntil(testMethod, finalLibraryMethodLevel);
verifyThat(
inspector,
parameters,
LibraryClass.class.getDeclaredMethod("missingAndReferenced"))
- .isOutlinedFrom(testMethod);
- if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O_MR1)) {
- assertEquals(5, inspector.allClasses().size());
- } else {
+ .isNotOutlinedFrom(testMethod);
+ if (parameters.getApiLevel().isLessThan(finalLibraryMethodLevel)) {
assertEquals(4, inspector.allClasses().size());
+ } else {
+ assertEquals(3, inspector.allClasses().size());
}
});
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
new file mode 100644
index 0000000..f8b3bf7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2022, 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 org.junit.Assert.assertEquals;
+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.FoundClassSubject;
+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 ApiModelOutlineMethodUnknownTest extends TestBase {
+
+ private static final AndroidApiLevel classApiLevel = 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 willInvokeLibraryMethods =
+ parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
+ .compile()
+ .inspect(
+ inspector -> {
+ // Assert that we did not outline any methods.
+ assertEquals(
+ 0,
+ inspector.allClasses().stream()
+ .filter(FoundClassSubject::isCompilerSynthesized)
+ .count());
+ })
+ .applyIf(willInvokeLibraryMethods, b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods, "Not calling API")
+ .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods, "LibraryClass::unknownApiLevel");
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ public static void unknownApiLevel() {
+ System.out.println("LibraryClass::unknownApiLevel");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ LibraryClass.unknownApiLevel();
+ } else {
+ System.out.println("Not calling API");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
new file mode 100644
index 0000000..40b302d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2022, 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.setMockApiLevelForMethod;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
+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.FoundClassSubject;
+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 ApiModelOutlinePackagePrivateTest extends TestBase {
+
+ private static final AndroidApiLevel classApiLevel = AndroidApiLevel.G;
+ private static final AndroidApiLevel methodApiLevel = AndroidApiLevel.G_MR1;
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private boolean willInvokeLibraryMethods() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testD8BootClassPath() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ assumeTrue(parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ compileOnD8()
+ .addBootClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ @Test
+ public void testD8RuntimeClasspath() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ compileOnD8()
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API")
+ .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods(), "LibraryClass::addedOn10");
+ }
+
+ private D8TestCompileResult compileOnD8() throws Exception {
+ return testForD8(parameters.getBackend())
+ .addLibraryClasses(LibraryClass.class)
+ .addProgramClasses(Main.class)
+ .addAndroidBuildVersion()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12.
+ assumeFalse(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(
+ setMockApiLevelForMethod(
+ LibraryClass.class.getDeclaredMethod("addedOn10"), methodApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
+ .compile()
+ .inspect(
+ inspector -> {
+ // Assert that we did not outline any methods.
+ assertEquals(
+ 0,
+ inspector.allClasses().stream()
+ .filter(FoundClassSubject::isCompilerSynthesized)
+ .count());
+ })
+ .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ private void checkResultOnBootClassPath(SingleTestRunResult<?> runResult) {
+ runResult
+ .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API")
+ // We are expecting an IllegalAccessError since LibraryClass is on bootclasspath and
+ // for Main to call the package private method it has to be loaded by the same class loader.
+ .assertFailureWithErrorThatThrowsIf(willInvokeLibraryMethods(), IllegalAccessError.class);
+ }
+
+ // Only present from api level 9.
+ public static class LibraryClass {
+
+ static void addedOn10() {
+ System.out.println("LibraryClass::addedOn10");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 10) {
+ LibraryClass.addedOn10();
+ } else {
+ System.out.println("Not calling API");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index 06bfb5b..e5a169b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.horizontalclassmerging.ClassMerger;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -36,6 +37,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("changed", "0", "42", "foo", "7", "foo", "print a", "print b")
@@ -71,6 +73,7 @@
@NoHorizontalClassMerging
public static class Parent {
@NeverInline
+ @NoMethodStaticizing
public void foo() {
System.out.println("foo");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
index 1f413bc..02e26dc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -27,6 +28,7 @@
.allowStdoutMessages()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertMergedInto(B.class, A.class))
@@ -42,6 +44,7 @@
public interface Interface {
@NeverInline
+ @NoMethodStaticizing
default void print() {
System.out.println("print interface");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java
new file mode 100644
index 0000000..d5bbb8e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2022, 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.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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 NonTrivialClassInitializationAfterMergingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(I.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .applyIf(
+ !parameters.canUseDefaultAndStaticInterfaceMethods(),
+ i -> i.assertIsCompleteMergeGroup(A.class, B.class))
+ .assertNoOtherClassesMerged())
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ runResult -> runResult.assertSuccessWithOutputLines("Hello world!"),
+ runResult -> runResult.assertSuccessWithOutput("Hello"));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A();
+ System.out.print("Hello");
+ new B();
+ }
+ }
+
+ interface I {
+
+ Greeter greeter = new Greeter();
+
+ default void foo() {}
+ }
+
+ static class A {}
+
+ // Prints " world!" when initialized due to implementing I.
+ static class B implements I {}
+
+ @NoHorizontalClassMerging
+ static class Greeter {
+
+ static {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
index 100567d..33aab91 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.compilerapi.mockdata.MockClass;
+import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -70,6 +71,10 @@
return MockClass.class;
}
+ public Class<?> getMockClassWithAssertion() {
+ return MockClassWithAssertion.class;
+ }
+
public Path getJava8RuntimeJar() {
return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar");
}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index c2fab32..24e1de1 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -8,8 +8,10 @@
import static com.android.tools.r8.ToolHelper.isTestingR8Lib;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
import com.android.tools.r8.compilerapi.mockdata.MockClass;
+import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
import com.google.common.collect.ImmutableList;
@@ -26,10 +28,13 @@
Paths.get(ToolHelper.THIRD_PARTY_DIR, "binary_compatibility_tests", DIRNAME, "tests.jar");
private static final List<Class<? extends CompilerApiTest>> CLASSES_FOR_BINARY_COMPATIBILITY =
- ImmutableList.of(ApiTestingSetUpTest.ApiTest.class);
+ ImmutableList.of(
+ ApiTestingSetUpTest.ApiTest.class,
+ CustomMapIdTest.ApiTest.class,
+ CustomSourceFileTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(CustomMapIdTest.ApiTest.class, CustomSourceFileTest.ApiTest.class);
+ ImmutableList.of(AssertionConfigurationTest.ApiTest.class);
private final TemporaryFolder temp;
@@ -54,7 +59,7 @@
@Override
public List<Class<?>> getAdditionalClassesForTests() {
- return ImmutableList.of(CompilerApiTest.class, MockClass.class);
+ return ImmutableList.of(CompilerApiTest.class, MockClass.class, MockClassWithAssertion.class);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
new file mode 100644
index 0000000..aa85988
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2022, 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.compilerapi.assertionconfiguration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.AssertionsConfiguration;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ThrowingBiConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
+import java.nio.file.Path;
+import org.junit.Test;
+
+public class AssertionConfigurationTest extends CompilerApiTestRunner {
+
+ static MethodReference assertionHandler =
+ Reference.methodFromDescriptor(
+ "Lcom/example/SomeClass;", "assertionHandler", "(Ljava/lang/AssertionError;)V");
+
+ public AssertionConfigurationTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ assertEquals(assertionHandler, ApiTest.assertionHandler);
+ runTest(test::runD8);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(test::runR8);
+ }
+
+ private boolean invokesAssertionHandler(InstructionSubject instruction) {
+ return instruction.isInvokeStatic()
+ && ((InvokeInstructionSubject) instruction)
+ .invokedMethod()
+ .asMethodReference()
+ .equals(assertionHandler);
+ }
+
+ private void runTest(ThrowingBiConsumer<ProgramConsumer, MethodReference, Exception> test)
+ throws Exception {
+ Path output = temp.newFolder().toPath().resolve("out.jar");
+ test.accept(new DexIndexedConsumer.ArchiveConsumer(output), assertionHandler);
+
+ // TODO(b/209445989): This should be true when the assertion handler support is implemented.
+ assertFalse(
+ new CodeInspector(output)
+ .clazz(MockClassWithAssertion.class)
+ .uniqueMethodWithName("main")
+ .streamInstructions()
+ .anyMatch(this::invokesAssertionHandler));
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ static MethodReference assertionHandler =
+ Reference.methodFromDescriptor(
+ "Lcom/example/SomeClass;", "assertionHandler", "(Ljava/lang/AssertionError;)V");
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ AssertionsConfiguration buildWithAssertionHandler(AssertionsConfiguration.Builder builder) {
+ AssertionsConfiguration configuration =
+ builder.setAssertionHandler(assertionHandler).setScopeAll().build();
+ assertFalse(configuration.isCompileTimeEnabled());
+ assertFalse(configuration.isCompileTimeDisabled());
+ assertFalse(configuration.isPassthrough());
+ assertTrue(configuration.isAssertionHandler());
+ assertSame(assertionHandler, configuration.getAssertionHandler());
+ return configuration;
+ }
+
+ public void runD8(ProgramConsumer programConsumer, MethodReference assertionHandler)
+ throws Exception {
+ D8.run(
+ D8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .addAssertionsConfiguration(this::buildWithAssertionHandler)
+ .setProgramConsumer(programConsumer)
+ .build());
+ }
+
+ public void runR8(ProgramConsumer programConsumer, MethodReference assertionHandler)
+ throws Exception {
+ R8.run(
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown())
+ .addProguardConfiguration(
+ getKeepMainRules(getMockClassWithAssertion()), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .addAssertionsConfiguration(
+ builder -> builder.setAssertionHandler(assertionHandler).setScopeAll().build())
+ .setProgramConsumer(programConsumer)
+ .build());
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ runD8(DexIndexedConsumer.emptyConsumer(), assertionHandler);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runR8(DexIndexedConsumer.emptyConsumer(), assertionHandler);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java
new file mode 100644
index 0000000..df42fbd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, 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.compilerapi.mockdata;
+
+// Class to use as data for the compilation.
+public class MockClassWithAssertion {
+
+ public static void main(String[] args) {
+ assert false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
new file mode 100644
index 0000000..3f384d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2022, 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEntryBuilder;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress216178582Test extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello world!");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public Regress216178582Test(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path out =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(Regress216178582Test.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .addKeepAttributeLineNumberTable()
+ .compile()
+ .inspect(
+ inspector -> {
+ DexEncodedMethod method =
+ inspector.clazz(TestClass.class).mainMethod().getMethod();
+ DexCode code = method.getCode().asDexCode();
+ if (parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) {
+ assertNull(code.getDebugInfo());
+ return;
+ }
+ assertTrue(code.getDebugInfo().isPcBasedInfo());
+ // Force convert the PC info to events.
+ code.setDebugInfo(DexDebugInfo.convertToEventBased(code, inspector.getFactory()));
+ List<DexDebugEntry> entries =
+ new DexDebugEntryBuilder(method, inspector.getFactory()).build();
+ Iterator<DexDebugEntry> it = entries.iterator();
+ int pc = 0;
+ for (Instruction instruction : code.instructions) {
+ if (instruction.canThrow()) {
+ DexDebugEntry next = it.next();
+ assertEquals(
+ "Invalid entry "
+ + next
+ + " at pc "
+ + StringUtils.hexString(pc, 2)
+ + ", in:\n"
+ + method.codeToString(),
+ pc,
+ next.address);
+ }
+ pc += instruction.getSize();
+ }
+ })
+ .writeToZip();
+
+ testForD8(parameters.getBackend())
+ .addProgramFiles(out)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.testing.forceJumboStringProcessing = true)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ static class TestClass {
+
+ // Use synchronized which will require prolog and epilog of monitor instructions.
+ // These have small sizes and debug event entries which hit a PC between instructions showing
+ // that the event stream is built incorrectly when processing jumbo strings.
+ public static synchronized void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
index d2d8c86..f92b58e 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.desugaring.interfacemethods;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
@@ -36,9 +35,6 @@
.addProgramClasses(Condition.class)
.noTreeShaking()
.compile()
- .inspectProguardConfiguration(p -> {
- assertNotNull(p.getKeepAllRule());
- })
.inspect(i -> assertTrue(i.clazz(Condition.class).isAbstract()));
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
index b0be3f7..2b5ccbd 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -22,13 +22,33 @@
public void testCombineRewritten() {
DexItemFactory factory = new DexItemFactory();
ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
- builder1.addArgumentInfo(1, new RewrittenTypeInfo(factory.intType, factory.longType));
- builder1.addArgumentInfo(3, new RewrittenTypeInfo(factory.intType, factory.longType));
+ builder1.addArgumentInfo(
+ 1,
+ RewrittenTypeInfo.builder()
+ .setOldType(factory.intType)
+ .setNewType(factory.longType)
+ .build());
+ builder1.addArgumentInfo(
+ 3,
+ RewrittenTypeInfo.builder()
+ .setOldType(factory.intType)
+ .setNewType(factory.longType)
+ .build());
ArgumentInfoCollection arguments1 = builder1.build();
ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
- builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
- builder2.addArgumentInfo(4, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+ builder2.addArgumentInfo(
+ 2,
+ RewrittenTypeInfo.builder()
+ .setOldType(factory.floatType)
+ .setNewType(factory.doubleType)
+ .build());
+ builder2.addArgumentInfo(
+ 4,
+ RewrittenTypeInfo.builder()
+ .setOldType(factory.floatType)
+ .setNewType(factory.doubleType)
+ .build());
ArgumentInfoCollection arguments2 = builder2.build();
ArgumentInfoCollection combine = arguments1.combine(arguments2);
@@ -112,8 +132,18 @@
ArgumentInfoCollection arguments1 = builder1.build();
ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
- builder2.addArgumentInfo(1, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
- builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+ builder2.addArgumentInfo(
+ 1,
+ RewrittenTypeInfo.builder()
+ .setOldType(factory.floatType)
+ .setNewType(factory.doubleType)
+ .build());
+ builder2.addArgumentInfo(
+ 2,
+ RewrittenTypeInfo.builder()
+ .setOldType(factory.floatType)
+ .setNewType(factory.doubleType)
+ .build());
ArgumentInfoCollection arguments2 = builder2.build();
ArgumentInfoCollection combine = arguments1.combine(arguments2);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
index 65cc34d..b67037d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
@@ -72,10 +72,12 @@
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject.uniqueMethodWithName("kept"), isPresent());
- // A.notKept() and B.notKept() should not be present, because the only invoke instruction
- // targeting A.notKept() should have been inlined.
+ // A.notKept() should not be present, because the only invoke instruction targeting A.notKept()
+ // should have been inlined.
assertThat(aClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
- assertThat(bClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
+ // B.notKept() is present because the caller has an invoke to an unknown method giving it api
+ // level unknown.
+ assertThat(bClassSubject.uniqueMethodWithName("notKept"), isPresent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
index 89586d8..20adeda 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
@@ -70,10 +70,12 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject.uniqueMethodWithName("kept"), isPresent());
- // I.notKept() and A.notKept() should not be present, because the only invoke instruction
- // targeting I.notKept() should have been inlined.
+ // I.notKept() should not be present, because the only invoke instruction targeting I.notKept()
+ // should have been inlined.
assertThat(iClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
- assertThat(aClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
+ // A.notKept() is present because the caller has an invoke to an unknown method giving it api
+ // level unknown.
+ assertThat(aClassSubject.uniqueMethodWithName("notKept"), isPresent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
index 0c4e336..c0d3be4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -13,7 +12,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ir.optimize.membervaluepropagation.FieldReadForWriteTest.R.anim;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,16 +40,7 @@
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(
- inspector -> {
- ClassSubject animClassSubject = inspector.clazz(anim.class);
- if (parameters.isCfRuntime()) {
- assertThat(animClassSubject, isPresent());
- assertThat(animClassSubject.uniqueFieldWithName("abc_fade_in"), isPresent());
- } else {
- assertThat(animClassSubject, isAbsent());
- }
- });
+ .inspect(inspector -> assertThat(inspector.clazz(anim.class), isAbsent()));
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java
new file mode 100644
index 0000000..b5c1de5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2022, 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.redundantarraygetelimination;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 RedundantArrayGetEliminationTest extends TestBase {
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("0", "0", "0", "42", "84");
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertArrayGetCountEquals(1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGet"));
+ assertArrayGetCountEquals(
+ 1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGetAfterPutToUnrelatedArray"));
+ assertArrayGetCountEquals(
+ 1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGetAfterPutToUnrelatedIndex"));
+ assertArrayGetCountEquals(
+ 2, mainClassSubject.uniqueMethodWithName("testNecessaryArrayGetAfterAliasedArrayPut"));
+ assertArrayGetCountEquals(
+ 2, mainClassSubject.uniqueMethodWithName("testNecessaryArrayGetAfterExternalSideEffect"));
+ }
+
+ static void assertArrayGetCountEquals(int expected, MethodSubject methodSubject) {
+ assertEquals(
+ expected,
+ methodSubject.streamInstructions().filter(InstructionSubject::isArrayGet).count());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ int[] ints = new int[] {0, 1};
+ long[] longs = new long[] {0};
+ testRedundantArrayGet(ints, 0);
+ testRedundantArrayGetAfterPutToUnrelatedArray(ints, longs, 0);
+ testRedundantArrayGetAfterPutToUnrelatedIndex(ints, ints);
+ testNecessaryArrayGetAfterAliasedArrayPut(ints, ints, 0, 0);
+ testNecessaryArrayGetAfterExternalSideEffect(ints, 0, () -> ints[0] = 42);
+ }
+
+ static void testRedundantArrayGet(int[] array, int index) {
+ int first = array[index];
+ int second = array[index]; // Redundant.
+ System.out.println(first + second);
+ }
+
+ static void testRedundantArrayGetAfterPutToUnrelatedArray(
+ int[] array, long[] otherArray, int index) {
+ int first = array[index];
+ otherArray[index] = 42;
+ int second = array[index]; // Redundant.
+ System.out.println(first + second);
+ }
+
+ static void testRedundantArrayGetAfterPutToUnrelatedIndex(int[] array, int[] sameArray) {
+ int first = array[0];
+ sameArray[1] = 42;
+ int second = array[0]; // Redundant.
+ System.out.println(first + second);
+ }
+
+ static void testNecessaryArrayGetAfterAliasedArrayPut(
+ int[] array, int[] sameArray, int index, int sameIndex) {
+ int first = array[index];
+ sameArray[sameIndex] = 42;
+ int second = array[index]; // Not redundant.
+ System.out.println(first + second);
+ }
+
+ static void testNecessaryArrayGetAfterExternalSideEffect(
+ int[] array, int index, Runnable runnable) {
+ int first = array[index];
+ runnable.run();
+ int second = array[index]; // Not redundant.
+ System.out.println(first + second);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
index 35b475b..c12a128 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -68,13 +69,19 @@
"Caught NullPointerException from testRewriteInvokeVirtualToThrowNull"
+ "WithDeadCatchHandler");
- testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expected);
+ }
CodeInspector inspector =
testForR8(parameters.getBackend())
.addInnerClasses(InvokeMethodWithNonNullParamCheckTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.addOptionsModification(
options -> {
// Avoid that the class inliner inlines testRewriteInvokeVirtualToThrowNullWith-
@@ -371,6 +378,7 @@
static class Virtual {
@NeverInline
+ @NoMethodStaticizing
public String throwIfFirstIsNull(Object first) {
if (first == null) {
throw new NullPointerException();
@@ -379,6 +387,7 @@
}
@NeverInline
+ @NoMethodStaticizing
public String throwIfSecondIsNull(Object first, Object second, Object third) {
if (second == null) {
throw new NullPointerException();
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 7e12a9f..ad4f8a2 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
@@ -5,6 +5,7 @@
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -248,11 +249,14 @@
KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
assertThat(familyName, not(isPresent()));
+ FieldSubject ageField = person.uniqueFieldWithName("age");
+ assertThat(ageField, isAbsent());
+
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
assertThat(age, isPresent());
assertThat(age, not(isExtensionProperty()));
- assertEquals(age.fieldSignature().asString(), "a:I");
- assertEquals(age.getterSignature().asString(), "getAge()I");
- assertEquals(age.setterSignature().asString(), "setAge(I)V");
+ assertEquals("age:I", age.fieldSignature().asString());
+ assertEquals("getAge()I", age.getterSignature().asString());
+ assertEquals("setAge(I)V", age.setterSignature().asString());
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
index 99554bf..5b13f6b 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -98,7 +98,7 @@
public void runOnJvm() throws Throwable {
Assume.assumeTrue(parameters.isCfRuntime());
testForJvm()
- .addProgramClasses(CLASSPATH_CLASSES)
+ .addClasspathClasses(CLASSPATH_CLASSES)
.addProgramClasses(PROGRAM_CLASSES)
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java
new file mode 100644
index 0000000..3eb80ae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2022, 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.naming.applymapping;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import 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 ApplyMappingMissingInterfaceTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestCompileResult libraryCompileResult =
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(LibraryI.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(ClassPathI.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRulesWithAllowObfuscation(ClassPathI.class)
+ .compile();
+
+ testForR8(parameters.getBackend())
+ .addClasspathClasses(ClassPathI.class)
+ .addProgramClasses(ProgramInterface.class, Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addApplyMapping(libraryCompileResult.getProguardMap())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .addDontWarn(LibraryI.class)
+ .compile()
+ .addRunClasspathFiles(libraryCompileResult.writeToZip())
+ .addRunClasspathClasses(LibraryI.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Main::foo", "Main::bar");
+ }
+
+ /* Will be missing on compile time */
+ public interface LibraryI {}
+
+ public interface ClassPathI extends LibraryI {
+
+ void foo();
+ }
+
+ public interface ProgramInterface extends ClassPathI {
+
+ void bar();
+ }
+
+ public static class Main implements ProgramInterface {
+
+ public static void main(String[] args) {
+ new Main().foo();
+ new Main().bar();
+ }
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("Main::foo");
+ }
+
+ @Override
+ @NeverInline
+ public void bar() {
+ System.out.println("Main::bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
index 554ea5c..01de767 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
@@ -199,7 +199,8 @@
private R8Command.Builder getCommandForInstrumentation(
Path out, Path flag, Path mainApp, Path instrApp) throws IOException {
return R8Command.builder()
- .addLibraryFiles(runtimeJar(backend), mainApp)
+ .addLibraryFiles(runtimeJar(backend))
+ .addClasspathFiles(mainApp)
.addProgramFiles(instrApp)
.setOutput(out, outputMode(backend))
.addProguardConfigurationFiles(flag);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
index 6b7b2d0..99531f1 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.naming.retrace;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -13,12 +12,10 @@
import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.List;
-import java.util.Objects;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,31 +67,9 @@
(stackTrace, inspector) -> {
ClassSubject callerClass = inspector.clazz(Caller.class);
assertThat(callerClass, isPresent());
- MethodSubject staticized = callerClass.uniqueMethodWithName("outerCaller");
- assertThat(staticized, isPresentAndRenamed());
- // TODO(b/214377135): The stack traces should be the same (when 206427323) is
- // resolved.
- if (throwReceiverNpe && canUseJavaUtilObjectsRequireNonNull(parameters)) {
- StackTrace requireNonNullFrame =
- StackTrace.builder().add(stackTrace.get(0)).build();
- assertThat(
- requireNonNullFrame,
- isSameExceptForLineNumbers(
- StackTrace.builder()
- .add(
- StackTraceLine.builder()
- .setClassName(Objects.class.getTypeName())
- .setMethodName("requireNonNull")
- .setFileName("Objects.java")
- .build())
- .build()));
-
- StackTrace stackTraceWithoutRequireNonNull =
- StackTrace.builder().add(stackTrace).remove(0).build();
- assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
- } else {
- assertThat(stackTrace, isSame(expectedStackTrace));
- }
+ MethodSubject outerCaller = callerClass.uniqueMethodWithName("outerCaller");
+ assertThat(outerCaller, isPresentAndRenamed());
+ assertThat(stackTrace, isSame(expectedStackTrace));
});
}
@@ -119,7 +94,7 @@
static void caller(Foo f) {
Object inlinable = f.inlinable();
- System.out.println(inlinable);
+ System.out.println(inlinable == null ? "null" : "some");
}
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
index bf8856b..be348ec 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
@@ -8,7 +8,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
@@ -37,17 +36,12 @@
@Before
public void setup() throws Exception {
// Get the expected stack trace by running on the JVM.
- SingleTestRunResult<?> runResult =
+ expectedStackTrace =
testForRuntime(parameters)
.addProgramClasses(Caller.class, Foo.class)
.run(parameters.getRuntime(), Caller.class)
- .assertFailureWithErrorThatThrows(NullPointerException.class);
- if (parameters.isCfRuntime()) {
- expectedStackTrace = runResult.map(StackTrace::extractFromJvm);
- } else {
- expectedStackTrace =
- StackTrace.extractFromArt(runResult.getStdErr(), parameters.asDexRuntime().getVm());
- }
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .getStackTrace();
}
@Test
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
index 828a4b5..a728f8c 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.optimize.argumentpropagation;
+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.isStatic;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -16,7 +16,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
@@ -48,19 +47,16 @@
.compile()
.inspect(
inspector -> {
- boolean isCf = parameters.isCfRuntime();
-
ClassSubject hostClassSubject = inspector.clazz(Host.class);
assertThat(hostClassSubject, isPresent());
- assertEquals(1 + BooleanUtils.intValue(isCf), hostClassSubject.allMethods().size());
- assertThat(hostClassSubject.clinit(), onlyIf(isCf, isPresent()));
+ assertEquals(1, hostClassSubject.allMethods().size());
+ assertThat(hostClassSubject.clinit(), isAbsent());
assertThat(hostClassSubject.uniqueMethodWithName("keepHost"), isPresent());
ClassSubject companionClassSubject = inspector.clazz(Host.Companion.class);
assertThat(companionClassSubject, isPresent());
- assertEquals(
- 1 + BooleanUtils.intValue(isCf), companionClassSubject.allMethods().size());
- assertThat(companionClassSubject.init(), onlyIf(isCf, isPresent()));
+ assertEquals(1, companionClassSubject.allMethods().size());
+ assertThat(companionClassSubject.init(), isAbsent());
MethodSubject greetMethodSubject =
companionClassSubject.uniqueMethodWithName("greet");
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
index 3bd714f..ab23d56 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
@@ -30,6 +30,9 @@
public void testRuntime() throws Exception {
// This tests shows that invoke super behaves differently on V5_1_1 and V6_0_1 based on the
// static receiver on invoke-super, skipping the direct resolution target.
+ // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.3 specify that
+ // the static target of an invoke-super should always be an immediate super type and the
+ // generated byte code is therefore invalid according to the spec.
boolean hasIncorrectSuperInvokeSemantics =
parameters.isDexRuntimeVersion(Version.V5_1_1)
|| parameters.isDexRuntimeVersion(Version.V6_0_1);
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
index b769752..7299dfb 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -944,6 +944,108 @@
});
}
+ /* This is a regression test for b/216359244 */
+ @Test
+ public void testMissingSourceFile() {
+ runRetraceTest(
+ ".*FOO\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*",
+ new StackTraceForTest() {
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of(
+ "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] huk.g(1): getDownloads()");
+ }
+
+ @Override
+ public String mapping() {
+ return "foo.Bar -> huk:\n" + " void baz():13:13 -> g\n" + " void qux():12:12 -> g\n";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of(
+ "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.baz(13): getDownloads()",
+ "<OR> 12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.qux(12):"
+ + " getDownloads()");
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return ImmutableList.of(
+ "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.void baz()(13):"
+ + " getDownloads()",
+ "<OR> 12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.void qux()(12):"
+ + " getDownloads()");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+ });
+ }
+
+ @Test
+ public void testBigLine() {
+ runRetraceTest(
+ RetraceOptions.defaultRegularExpression(),
+ new StackTraceForTest() {
+
+ private final String LINE =
+ "12-16 19:29:11.111 media 453 490 D WVCdm : [for_bar.cpp(334):AddKey] key_data ="
+ + " (1401)"
+ + " CAISvgYKmgIKIEIzMkU1QkU3RjlGMThCNzYzQjAwMDAwMDAwMDAwMDAwEuMBeyJ2ZXJzaW9uIjoiM"
+ + "S4wIiwiZXNuIjoiTkZBTkRST0lEMi1QUlYtQk9SRUFMLUdPT0dMQ0hST01FQ0FTVD1IRC0yMTc5My0"
+ + "1Nzk0RDMyRTQxRUI5QTc2QzZFQzIyNTdEQzVGNTQ2MTNDNzdBQTdCNzAwNzcyMkREQjVCRDYzRUFER"
+ + "TZBRUUyIiwic2FsdCI6IjQ5OTI3MDU0ODMyNzI0NjIxMDc0MDM4Nzg3MDkxMjAyNCIsImlzc3VlVGl"
+ + "tZSI6MTYzOTY2MzE1MDAwMCwibW92aWVJZCI6IjgxMTg0NDE3In0gASgAOMDRAkDA0QJIroztjQYSF"
+ + "AgBEAAYACDA0QIowNECWABgAXgBGmYSEBnHXFI_ttc_1lja-bpjqHkaUN_81aKusLLERz_xX0vPIR1"
+ + "I8yu9u9zQndpy1aEFJYtQiB0sed6wAC3c6aeH4oLsGFPiuVgGweP1MGc3yxunqusDpoGe03JGD1VSE"
+ + "9aB0jSKIAEahgEKEAAAAAAGmcHCAAAAAAAAAAASEN4b9_2XH75TnxdBkpJ4YhoaIOdhQxec7DpUs0D"
+ + "ixSQXTlvjbXqX3PIOg9DiQLCa4vbzIAIoBToECAEQKkI0CiDuiCOCIxvTlCqkYTKBWzMOfeh9BpKc5"
+ + "RDdE2omMbOfaRIQRWVqtbR8Qg4JEY0u0CAFRBqGAQoQAAAAAAaZwcMAAAAAAAAAABIQ7iP9449yr2F"
+ + "_97v4QHerRxognKGBtw2cwGpxKltxU7A_1asVTS0aPaw8dr5e2rP2SW4gAigFOgQIARAqQjQKIB-50"
+ + "Ou_RGn8rzRDr76FeWpBLYSt99zHVSZK0xnd_anREhC4eACukLOUwL5TQ8w1oOAVGoYBChAAAAAABpn"
+ + "BwQAAAAAAAAAAEhDsGE2bR6VgRM85PY3YU9qIGiAhD3h7I8Cv5Irr315yecWo0YA1t9_sr0g2z0zsa"
+ + "6Dq0yACKAE6BAgBECpCNAogQoIDcKUGMhCA-HetsARASXxXJUazkIIaSwCKE9asAEwSEMW4TIMZZkc"
+ + "r3LYIITelMlIgroztjQY4ABogzw-SGqQ1AYy8EopI2zSvXX_hkFpOoWxCvFQuQEj5oQwigAIRzNhvB"
+ + "9-YMOy2Muentb574WJKnVqXfEor5mFDIQ3vDELdjbkibaS4THyK2DlUQyp0M3C8n5bQ1JApslsfh5w"
+ + "SEMvA8Y0sXE9H6xnAKnMJTioZJOTElqPrbbRM5APhI3ohL8rp7u8ydBm9aWqkprDngrU3b1KdHd6J9"
+ + "YiJRJ4Y55M4qqCdwbCqr57xYAu_IIH_p8erfnhfNgUE2svtZBiLMP37vOMSQAP08_zkMf87VRHBxuI"
+ + "2rlnGcdgLoJyPgLp2ZBbdLw-b4Nq6uIZQfNABQ1SuM3OZ6uD6eq-NH9FawWvN0EmzWZ88DGiHtgM7h"
+ + "zqvIb8JNmLInt28us-vw6KQOggKBjE2LjUuMEABSoACAAAAAgAAAQAABAAQZrvQTwAAABMAAAE3AAA"
+ + "AEAAAAUkAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAKjAA"
+ + "AAAAAAAqMAAAAAAAAAAAAAAAAMAAAGgAAAAEAAAAbIAAAAQAAABxAAAABAAAAIUAAAAEAAAAfIAAAA"
+ + "gAAACKQAAABAAAAI7AAAAEAAAAk0AAAAQAAACnQAAABAAAAJ7AAAAIAAAArIAAAAQAAACxAAAABAAA"
+ + "ALWAAAAEAAAAyYAAAAQAAADBAAAACC8nuR7W9i8AuG9xF3K__n1kk9F_G1i25QRKR9YXyssdsadWT";
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of(LINE);
+ }
+
+ @Override
+ public String mapping() {
+ return "foo.Bar -> huk:\n" + " void baz():13:13 -> g\n" + " void qux():12:12 -> g\n";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of(LINE);
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return ImmutableList.of(LINE);
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+ });
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(
String regularExpression, StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
index 29960f9..1c7492d 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -7,8 +7,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -59,13 +57,9 @@
assertThat(iClassSubject, isPresent());
assertThat(iClassSubject.clinit(), isPresent());
- // Verify that J is still there.
- assertThat(jClassSubject, isPresent());
-
- // Verify that A still implements J.
- assertThat(aClassSubject, isPresent());
- assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size());
- assertTrue(aClassSubject.isImplementing(jClassSubject));
+ // Verify that J and A are pruned.
+ assertThat(jClassSubject, isAbsent());
+ assertThat(aClassSubject, isAbsent());
} else {
// All interfaces are gone and the default methods companion call is inlined.
assertThat(iClassSubject, isAbsent());
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
index 035ddf7..c238e5d 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -27,6 +28,7 @@
public static class A {
@NeverInline
+ @NoMethodStaticizing
void foo() {
System.out.println("A.foo!");
}
@@ -75,6 +77,7 @@
testForR8(parameters.getBackend())
.enableGraphInspector()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 4465113..40bfdec 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArrayLength;
+import com.android.tools.r8.cf.code.CfArrayLoad;
import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfConstClass;
@@ -368,6 +369,11 @@
}
@Override
+ public boolean isArrayGet() {
+ return instruction instanceof CfArrayLoad;
+ }
+
+ @Override
public boolean isArrayPut() {
return instruction instanceof CfArrayStore;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 91f0ea9..00cfc92 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -4,6 +4,13 @@
package com.android.tools.r8.utils.codeinspector;
+import com.android.tools.r8.code.Aget;
+import com.android.tools.r8.code.AgetBoolean;
+import com.android.tools.r8.code.AgetByte;
+import com.android.tools.r8.code.AgetChar;
+import com.android.tools.r8.code.AgetObject;
+import com.android.tools.r8.code.AgetShort;
+import com.android.tools.r8.code.AgetWide;
import com.android.tools.r8.code.Aput;
import com.android.tools.r8.code.AputBoolean;
import com.android.tools.r8.code.AputByte;
@@ -458,6 +465,17 @@
}
@Override
+ public boolean isArrayGet() {
+ return instruction instanceof Aget
+ || instruction instanceof AgetBoolean
+ || instruction instanceof AgetByte
+ || instruction instanceof AgetChar
+ || instruction instanceof AgetObject
+ || instruction instanceof AgetShort
+ || instruction instanceof AgetWide;
+ }
+
+ @Override
public boolean isArrayPut() {
return instruction instanceof Aput
|| instruction instanceof AputBoolean
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 9f55b56..4488d83 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -144,6 +144,8 @@
boolean isArrayLength();
+ boolean isArrayGet();
+
boolean isArrayPut();
boolean isMonitorEnter();
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index ffee6ee..c55bcfe 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-36741d08e769bd2c5c201698161b3407507c7535
\ No newline at end of file
+bf40dc3b9e57b7759d8da51fb70d9995af1a188d
\ No newline at end of file
diff --git a/tools/retrace.py b/tools/retrace.py
index 14593ab..22753fb 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -61,6 +61,11 @@
default=None,
action='store_true',
help='Enables verbose retracing.')
+ parser.add_argument(
+ '--disable-map-validation',
+ default=None,
+ action='store_true',
+ help='Disable validation of map hash.')
return parser.parse_args()
@@ -108,7 +113,7 @@
print(e)
print('WARNING: Falling back to using local mapping file.')
- if map_path:
+ if map_path and not args.disable_map_validation:
check_maphash(map_path, maphash)
return map_path