Add D8 and R8 API to control assertion code at compile time
First step:
* Full API added
* Only support setting ENABLE, DISABLE or FULL for all classes
Bug: 139898386
Change-Id: Ieee4aa72b1bcac25225236bbe3c9bec5e84e52fe
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
new file mode 100644
index 0000000..f77bbce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2019, 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;
+
+import com.android.tools.r8.utils.StringDiagnostic;
+
+@Keep
+public class AssertionsConfiguration {
+
+ /** The possible transformations of the javac generated assertion code during compilation. */
+ public enum AssertionTransformation {
+ /** Unconditionally enable the javac generated assertion code. */
+ ENABLE,
+ /**
+ * Unconditionally disable the javac generated assertion code. This will most likely remove the
+ * javac generated assertion code completely.
+ */
+ DISABLE,
+ /** Passthrough of the javac generated assertion code. */
+ PASSTHROUGH
+ }
+
+ private AssertionTransformation transformation;
+
+ private AssertionsConfiguration(AssertionTransformation transformation) {
+ this.transformation = transformation;
+ }
+
+ static Builder builder(DiagnosticsHandler handler) {
+ return new Builder(handler);
+ }
+
+ /**
+ * Builder for constructing a <code>{@link AssertionsConfiguration}</code>.
+ *
+ * <p>A builder is obtained by calling {@link
+ * BaseCompilerCommand.Builder#addAssertionsConfiguration}.
+ */
+ public AssertionTransformation getTransformation() {
+ return transformation;
+ }
+
+ @Keep
+ public static class Builder {
+ private AssertionTransformation transformation = null;
+
+ private final DiagnosticsHandler handler;
+
+ private Builder(DiagnosticsHandler handler) {
+ this.handler = handler;
+ }
+
+ /** Set how to handle javac generated assertion code. */
+ public AssertionsConfiguration.Builder setTransformation(
+ AssertionTransformation transformation) {
+ this.transformation = transformation;
+ return this;
+ }
+
+ /**
+ * 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 enable() {
+ setTransformation(AssertionTransformation.ENABLE);
+ return this;
+ }
+
+ /**
+ * 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 disable() {
+ setTransformation(AssertionTransformation.DISABLE);
+ return this;
+ }
+
+ /** Passthrough of the javac generated assertion code in all packages and classes. */
+ public AssertionsConfiguration.Builder passthrough() {
+ setTransformation(AssertionTransformation.PASSTHROUGH);
+ return this;
+ }
+
+ /** Set how to handle javac generated assertion code in package and all subpackages. */
+ public AssertionsConfiguration.Builder setTransformationForPackage(
+ String packageName, AssertionTransformation transformation) {
+ handler.error(new StringDiagnostic("Unsupported"));
+ return this;
+ }
+
+ /**
+ * Unconditionally enable javac generated assertion code in package <code>packageName</code> and
+ * all subpackages. This corresponds to passing <code>-enableassertions:packageName...</code> or
+ * <code>-ea:packageName...</code> to the java CLI.
+ *
+ * <p>If <code>packageName</code> is the empty string, assertions are enabled in the unnamed
+ * package, which corresponds to passing <code>-enableassertions:...</code> or <code>-ea:...
+ * </code> to the java CLI.
+ */
+ public AssertionsConfiguration.Builder enableForPackage(String packageName) {
+ return setTransformationForPackage(packageName, AssertionTransformation.ENABLE);
+ }
+
+ /**
+ * Disable the javac generated assertion code in package <code>packageName</code> and all
+ * subpackages. This corresponds to passing <code>-disableassertions:packageName...</code> or
+ * <code>-da:packageName...</code> to the java CLI.
+ *
+ * <p>If <code>packageName</code> is the empty string assertions are disabled in the unnamed
+ * package, which corresponds to passing <code>-disableassertions:...</code> or <code>-da:...
+ * </code> to the java CLI.
+ */
+ public AssertionsConfiguration.Builder disableForPackage(String packageName) {
+ return setTransformationForPackage(packageName, AssertionTransformation.DISABLE);
+ }
+
+ public AssertionsConfiguration.Builder passthroughForPackage(String packageName) {
+ return setTransformationForPackage(packageName, AssertionTransformation.PASSTHROUGH);
+ }
+
+ /** Set how to handle javac generated assertion code in class. */
+ public AssertionsConfiguration.Builder setTransformationForClass(
+ String className, AssertionTransformation transformation) {
+ handler.error(new StringDiagnostic("Unsupported"));
+ return this;
+ }
+
+ /**
+ * Unconditionally enable javac generated assertion in class <code>className</code>. This
+ * corresponds to passing <code> -enableassertions:className</code> or <code>-ea:className
+ * </code> to the java CLI.
+ */
+ public AssertionsConfiguration.Builder enableForClass(String className) {
+ return setTransformationForClass(className, AssertionTransformation.ENABLE);
+ }
+
+ /**
+ * Disable the javac generated assertion code in class <code>className</code>. This corresponds
+ * to passing <code> -disableassertions:className</code> or <code>-da:className</code> to the
+ * java CLI.
+ */
+ public AssertionsConfiguration.Builder disableForClass(String className) {
+ return setTransformationForClass(className, AssertionTransformation.DISABLE);
+ }
+
+ public AssertionsConfiguration.Builder passthroughForClass(String className) {
+ return setTransformationForClass(className, AssertionTransformation.PASSTHROUGH);
+ }
+
+ /** Build and return the {@link AssertionsConfiguration}. */
+ public AssertionsConfiguration build() {
+ return new AssertionsConfiguration(transformation);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index b0d5055..ce60661 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -17,6 +17,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiPredicate;
+import java.util.function.Function;
/**
* Base class for commands and command builders for compiler applications/tools which besides an
@@ -38,6 +39,7 @@
private final boolean includeClassesChecksum;
private final boolean optimizeMultidexForLinearAlloc;
private final BiPredicate<String, Long> dexClassChecksumFilter;
+ private final AssertionsConfiguration assertionsConfiguration;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -50,6 +52,7 @@
includeClassesChecksum = false;
optimizeMultidexForLinearAlloc = false;
dexClassChecksumFilter = (name, checksum) -> true;
+ assertionsConfiguration = null;
}
BaseCompilerCommand(
@@ -62,7 +65,8 @@
boolean enableDesugaring,
boolean optimizeMultidexForLinearAlloc,
boolean includeClassesChecksum,
- BiPredicate<String, Long> dexClassChecksumFilter) {
+ BiPredicate<String, Long> dexClassChecksumFilter,
+ AssertionsConfiguration assertionsConfiguration) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -75,6 +79,7 @@
this.optimizeMultidexForLinearAlloc = optimizeMultidexForLinearAlloc;
this.includeClassesChecksum = includeClassesChecksum;
this.dexClassChecksumFilter = dexClassChecksumFilter;
+ this.assertionsConfiguration = assertionsConfiguration;
}
/**
@@ -129,6 +134,10 @@
return optimizeMultidexForLinearAlloc;
}
+ public AssertionsConfiguration getAssertionsConfiguration() {
+ return assertionsConfiguration;
+ }
+
Reporter getReporter() {
return reporter;
}
@@ -158,6 +167,7 @@
private boolean lookupLibraryBeforeProgram = true;
private boolean optimizeMultidexForLinearAlloc = false;
private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
+ private AssertionsConfiguration assertionsConfiguration;
abstract CompilationMode defaultCompilationMode();
@@ -476,6 +486,19 @@
return includeClassesChecksum;
}
+ /** Configure compile time assertion enabling through a {@link AssertionsConfiguration}. */
+ public B addAssertionsConfiguration(
+ Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+ assertionsConfigurationGenerator) {
+ assertionsConfiguration =
+ assertionsConfigurationGenerator.apply(AssertionsConfiguration.builder(getReporter()));
+ return self();
+ }
+
+ public AssertionsConfiguration getAssertionsConfiguration() {
+ return assertionsConfiguration;
+ }
+
@Override
void validate() {
Reporter reporter = getReporter();
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index ebebbf1..2214e2d 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.D8Command.USAGE_MESSAGE;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
@@ -25,7 +26,6 @@
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -159,7 +159,7 @@
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
- if (options.assertionProcessing != AssertionProcessing.LEAVE) {
+ if (options.assertionTransformation != AssertionTransformation.PASSTHROUGH) {
// Run analysis to mark all <clinit> methods having the javac generated assertion
// enabling code.
ClassInitializerAssertionEnablingAnalysis analysis =
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index b72548b..6bdc35c 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
@@ -218,6 +219,7 @@
getDesugarGraphConsumer(),
desugaredLibraryKeepRuleConsumer,
libraryConfiguration,
+ getAssertionsConfiguration(),
factory);
}
}
@@ -285,6 +287,7 @@
DesugarGraphConsumer desugarGraphConsumer,
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
+ AssertionsConfiguration assertionsConfiguration,
DexItemFactory factory) {
super(
inputApp,
@@ -296,7 +299,8 @@
enableDesugaring,
optimizeMultidexForLinearAlloc,
encodeChecksum,
- dexClassChecksumFilter);
+ dexClassChecksumFilter,
+ assertionsConfiguration);
this.intermediate = intermediate;
this.desugarGraphConsumer = desugarGraphConsumer;
this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -357,6 +361,16 @@
internal.desugaredLibraryConfiguration = libraryConfiguration;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+ assert internal.assertionTransformation == null;
+ if (getAssertionsConfiguration() != null) {
+ internal.assertionTransformation = getAssertionsConfiguration().getTransformation();
+ }
+ if (internal.assertionTransformation == null) {
+ // Default, when no configuration is provided, is to disable all javac generated assertion
+ // code when generating dex.
+ internal.assertionTransformation = AssertionTransformation.DISABLE;
+ }
+
return internal;
}
}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index d96a5a1..ff64d62 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -61,7 +61,8 @@
true,
false,
false,
- (name, checksum) -> true);
+ (name, checksum) -> true,
+ AssertionsConfiguration.builder(null).build());
this.d8Command = d8Command;
this.r8Command = r8Command;
this.libraryConfiguration = libraryConfiguration;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b4ae1c8..03235a4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.R8Command.USAGE_MESSAGE;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
@@ -81,7 +82,6 @@
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
import com.android.tools.r8.utils.LineNumberOptimizer;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SelfRetraceTest;
@@ -813,7 +813,7 @@
if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
}
- if (appView.options().assertionProcessing != AssertionProcessing.LEAVE) {
+ if (appView.options().assertionTransformation != AssertionTransformation.PASSTHROUGH) {
enqueuer.registerAnalysis(
new ClassInitializerAssertionEnablingAnalysis(
appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance()));
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 8c63d06..45f9e84 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
@@ -23,7 +24,6 @@
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -562,7 +562,8 @@
getDexClassChecksumFilter(),
desugaredLibraryKeepRuleConsumer,
libraryConfiguration,
- featureSplitConfiguration);
+ featureSplitConfiguration,
+ getAssertionsConfiguration());
return command;
}
@@ -718,7 +719,8 @@
BiPredicate<String, Long> dexClassChecksumFilter,
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
- FeatureSplitConfiguration featureSplitConfiguration) {
+ FeatureSplitConfiguration featureSplitConfiguration,
+ AssertionsConfiguration assertionsConfiguration) {
super(
inputApp,
mode,
@@ -729,7 +731,8 @@
enableDesugaring,
optimizeMultidexForLinearAlloc,
encodeChecksum,
- dexClassChecksumFilter);
+ dexClassChecksumFilter,
+ assertionsConfiguration);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
@@ -869,9 +872,16 @@
// Default is to remove Java assertion code as Dalvik and Art does not reliable support
// Java assertions. When generation class file output always keep the Java assertions code.
- assert internal.assertionProcessing == AssertionProcessing.REMOVE;
- if (internal.isGeneratingClassFiles()) {
- internal.assertionProcessing = AssertionProcessing.LEAVE;
+ assert internal.assertionTransformation == null;
+ if (getAssertionsConfiguration() == null) {
+ // Default, when no configuration is provided, is to disable all javac generated assertion
+ // code when generating dex and leave it when generating class files.
+ internal.assertionTransformation =
+ internal.isGeneratingClassFiles()
+ ? AssertionTransformation.PASSTHROUGH
+ : AssertionTransformation.DISABLE;
+ } else {
+ internal.assertionTransformation = getAssertionsConfiguration().getTransformation();
}
// When generating class files the build is "intermediate" and we cannot pollute the namespace
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 5041e1d..32b7cbe 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
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -86,11 +87,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -171,7 +170,7 @@
OptimizationFeedbackSimple.getInstance();
private DexString highestSortingString;
- private List<Action> onWaveDoneActions = null;
+ private List<com.android.tools.r8.utils.Action> onWaveDoneActions = null;
private final List<DexString> neverMergePrefixes;
boolean seenNotNeverMergePrefix = false;
@@ -819,11 +818,11 @@
private void waveDone() {
delayedOptimizationFeedback.updateVisibleOptimizationInfo();
- onWaveDoneActions.forEach(Action::execute);
+ onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
onWaveDoneActions = null;
}
- public void addWaveDoneAction(Action action) {
+ public void addWaveDoneAction(com.android.tools.r8.utils.Action action) {
if (!appView.enableWholeProgramOptimizations()) {
throw new Unreachable("addWaveDoneAction() should never be used in D8.");
}
@@ -1177,7 +1176,7 @@
assert appView.enableWholeProgramOptimizations();
codeRewriter.removeSwitchMaps(code);
}
- if (options.assertionProcessing != AssertionProcessing.LEAVE) {
+ if (options.assertionTransformation != AssertionTransformation.PASSTHROUGH) {
codeRewriter.processAssertions(appView, method, code, feedback);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 04dd590..527874a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isTypeVisibleFromContext;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
@@ -83,7 +84,6 @@
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.shaking.AppInfoWithLiveness.EnumValueInfo;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.SetUtils;
@@ -1320,7 +1320,7 @@
*/
public void processAssertions(
AppView<?> appView, DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
- assert appView.options().assertionProcessing != AssertionProcessing.LEAVE;
+ assert appView.options().assertionTransformation != AssertionTransformation.PASSTHROUGH;
DexEncodedMethod clinit;
// If the <clinit> of this class did not have have code to turn on assertions don't try to
// remove assertion code from the method (including <clinit> itself.
@@ -1356,7 +1356,9 @@
if (staticGet.getField().name == dexItemFactory.assertionsDisabled) {
iterator.replaceCurrentInstruction(
code.createIntConstant(
- appView.options().assertionProcessing == AssertionProcessing.REMOVE ? 1 : 0));
+ appView.options().assertionTransformation == AssertionTransformation.DISABLE
+ ? 1
+ : 0));
}
}
}
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 a5f672e..98ac732 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -5,6 +5,7 @@
import static com.google.common.base.Predicates.not;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DataResourceConsumer;
@@ -74,15 +75,6 @@
ON
}
- public enum AssertionProcessing {
- /** Leave the conditional javac generated assertion code untouched. */
- LEAVE,
- /** Remove the javac generated assertion code completely. */
- REMOVE,
- /** Enable the javac generated assertion code unconditionally at compile time. */
- ENABLE
- }
-
public static final int SUPPORTED_CF_MAJOR_VERSION = Opcodes.V11;
public static final int SUPPORTED_DEX_VERSION =
AndroidApiLevel.LATEST.getDexVersion().getIntValue();
@@ -442,7 +434,7 @@
public boolean ignoreMissingClasses = false;
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
- public AssertionProcessing assertionProcessing = AssertionProcessing.REMOVE;
+ public AssertionTransformation assertionTransformation = null;
public boolean configurationDebugging = false;
// Don't convert Code objects to IRCode.
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index e3f52a1..e04a2c1 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -204,6 +204,24 @@
return self();
}
+ public CR enableRuntimeAssertions(boolean enable) {
+ if (getBackend() == Backend.CF) {
+ if (enable) {
+ enableRuntimeAssertions();
+ }
+ } else {
+ // Assertions cannot be enabled on dex VMs.
+ assert !enable;
+ }
+
+ if (enable) {
+ if (!this.vmArguments.contains("-ea")) {
+ this.vmArguments.add("-ea");
+ }
+ }
+ return self();
+ }
+
public Path writeToZip() throws IOException {
Path file = state.getNewTempFolder().resolve("out.zip");
writeToZip(file);
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 3b368b6..2f4664e 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -21,6 +21,7 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
public abstract class TestCompilerBuilder<
@@ -299,4 +300,11 @@
additionalRunClassPath.addAll(files);
return self();
}
+
+ public T addAssertionsConfiguration(
+ Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+ assertionsConfigurationGenerator) {
+ builder.addAssertionsConfiguration(assertionsConfigurationGenerator);
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
new file mode 100644
index 0000000..af84f69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
@@ -0,0 +1,287 @@
+// Copyright (c) 2019, 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.rewrite.assertions;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.AssertionsConfiguration;
+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.ThrowingConsumer;
+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.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Function;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionsConfigurationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public AssertionsConfigurationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void runD8Test(
+ Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+ assertionsConfigurationBuilder,
+ ThrowingConsumer<CodeInspector, RuntimeException> inspector,
+ List<String> outputLines)
+ throws Exception {
+ testForD8()
+ .addProgramClasses(
+ TestClass.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.Class1.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.Class2.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAssertionsConfiguration(assertionsConfigurationBuilder)
+ .compile()
+ .inspect(inspector)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(StringUtils.lines(outputLines));
+ }
+
+ public void runR8Test(
+ Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+ assertionsConfigurationBuilder,
+ ThrowingConsumer<CodeInspector, RuntimeException> inspector,
+ List<String> outputLines)
+ throws Exception {
+ runR8Test(assertionsConfigurationBuilder, inspector, outputLines, false);
+ }
+
+ public void runR8Test(
+ Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
+ assertionsConfigurationBuilder,
+ ThrowingConsumer<CodeInspector, RuntimeException> inspector,
+ List<String> outputLines,
+ boolean enableJvmAssertions)
+ throws Exception {
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ TestClass.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.Class1.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.Class2.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(
+ com.android.tools.r8.rewrite.assertions.testclasses.Class1.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.Class2.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class,
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAssertionsConfiguration(assertionsConfigurationBuilder)
+ .compile()
+ .inspect(inspector)
+ .enableRuntimeAssertions(enableJvmAssertions)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(StringUtils.lines(outputLines));
+ }
+
+ private AssertionsConfiguration enableAllAssertions(AssertionsConfiguration.Builder builder) {
+ return builder.enable().build();
+ }
+
+ private AssertionsConfiguration disableAllAssertions(AssertionsConfiguration.Builder builder) {
+ return builder.disable().build();
+ }
+
+ private AssertionsConfiguration leaveAllAssertions(AssertionsConfiguration.Builder builder) {
+ return builder.passthrough().build();
+ }
+
+ private List<String> allAssertionsExpectedLines() {
+ return ImmutableList.of(
+ "AssertionError in testclasses.Class1",
+ "AssertionError in testclasses.Class2",
+ "AssertionError in testclasses.subpackage.Class1",
+ "AssertionError in testclasses.subpackage.Class2",
+ "DONE");
+ }
+
+ private List<String> noAllAssertionsExpectedLines() {
+ return ImmutableList.of("DONE");
+ }
+
+ private void checkAssertionCodeRemoved(CodeInspector inspector, Class<?> clazz) {
+ ClassSubject subject = inspector.clazz(clazz);
+ assertThat(subject, isPresent());
+ // <clinit> is removed by R8 as it becomes empty.
+ if (subject.uniqueMethodWithName("<clinit>").isPresent()) {
+ assertFalse(
+ subject
+ .uniqueMethodWithName("<clinit>")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isStaticPut));
+ }
+ assertFalse(
+ subject
+ .uniqueMethodWithName("m")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isThrow));
+ }
+
+ private void checkAssertionCodeEnabled(CodeInspector inspector, Class<?> clazz) {
+ ClassSubject subject = inspector.clazz(clazz);
+ assertThat(subject, isPresent());
+ // <clinit> is removed by R8.
+ if (subject.uniqueMethodWithName("<clinit>").isPresent()) {
+ assertFalse(
+ subject
+ .uniqueMethodWithName("<clinit>")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isStaticPut));
+ }
+ assertTrue(
+ subject
+ .uniqueMethodWithName("m")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isThrow));
+ }
+
+ private void checkAssertionCodeLeft(CodeInspector inspector, Class<?> clazz) {
+ ClassSubject subject = inspector.clazz(clazz);
+ assertThat(subject, isPresent());
+ assertTrue(
+ subject
+ .uniqueMethodWithName("<clinit>")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isStaticPut));
+ assertTrue(
+ subject
+ .uniqueMethodWithName("m")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isThrow));
+ }
+
+ private void checkAssertionCodeRemoved(CodeInspector inspector) {
+ checkAssertionCodeRemoved(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class1.class);
+ checkAssertionCodeRemoved(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class2.class);
+ checkAssertionCodeRemoved(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class);
+ checkAssertionCodeRemoved(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class);
+ }
+
+ private void checkAssertionCodeEnabled(CodeInspector inspector) {
+ checkAssertionCodeEnabled(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class1.class);
+ checkAssertionCodeEnabled(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class2.class);
+ checkAssertionCodeEnabled(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class);
+ checkAssertionCodeEnabled(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class);
+ }
+
+ private void checkAssertionCodeLeft(CodeInspector inspector) {
+ checkAssertionCodeLeft(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class1.class);
+ checkAssertionCodeLeft(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.Class2.class);
+ checkAssertionCodeLeft(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.class);
+ checkAssertionCodeLeft(
+ inspector, com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.class);
+ }
+
+ @Test
+ public void testEnableAllAssertionsForDex() throws Exception {
+ Assume.assumeTrue(parameters.isDexRuntime());
+ // Leaving assertions in or disabling them on Dalvik/Art means no assertions.
+ runD8Test(
+ this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+ runR8Test(
+ this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+ runD8Test(
+ this::disableAllAssertions,
+ this::checkAssertionCodeRemoved,
+ noAllAssertionsExpectedLines());
+ runR8Test(
+ this::disableAllAssertions,
+ this::checkAssertionCodeRemoved,
+ noAllAssertionsExpectedLines());
+ // Compile time enabling assertions gives assertions on Dalvik/Art.
+ runD8Test(
+ this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+ runR8Test(
+ this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+ }
+
+ @Test
+ public void testAssertionsForCf() throws Exception {
+ Assume.assumeTrue(parameters.isCfRuntime());
+ // Leaving assertion code means assertions are controlled by the -ea flag.
+ runR8Test(
+ this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+ runR8Test(
+ this::leaveAllAssertions, this::checkAssertionCodeLeft, allAssertionsExpectedLines(), true);
+ // Compile time enabling or disabling assertions means the -ea flag has no effect.
+ runR8Test(
+ this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+ runR8Test(
+ this::enableAllAssertions,
+ this::checkAssertionCodeEnabled,
+ allAssertionsExpectedLines(),
+ true);
+ runR8Test(
+ this::disableAllAssertions,
+ this::checkAssertionCodeRemoved,
+ noAllAssertionsExpectedLines());
+ runR8Test(
+ this::disableAllAssertions,
+ this::checkAssertionCodeRemoved,
+ noAllAssertionsExpectedLines(),
+ true);
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ try {
+ com.android.tools.r8.rewrite.assertions.testclasses.Class1.m();
+ } catch (AssertionError e) {
+ System.out.println("AssertionError in testclasses.Class1");
+ }
+ try {
+ com.android.tools.r8.rewrite.assertions.testclasses.Class2.m();
+ } catch (AssertionError e) {
+ System.out.println("AssertionError in testclasses.Class2");
+ }
+ try {
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class1.m();
+ } catch (AssertionError e) {
+ System.out.println("AssertionError in testclasses.subpackage.Class1");
+ }
+ try {
+ com.android.tools.r8.rewrite.assertions.testclasses.subpackage.Class2.m();
+ } catch (AssertionError e) {
+ System.out.println("AssertionError in testclasses.subpackage.Class2");
+ }
+ System.out.println("DONE");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index 298134a..446821a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.R8TestCompileResult;
@@ -18,7 +19,6 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AssertionProcessing;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -192,14 +192,14 @@
.compile();
}
- private static R8TestCompileResult compileCf(InternalOptions.AssertionProcessing assertionsState)
+ private static R8TestCompileResult compileCf(AssertionTransformation transformation)
throws CompilationFailedException {
return testForR8(staticTemp, Backend.CF)
.addProgramClasses(ClassWithAssertions.class)
.debug()
.noTreeShaking()
.noMinification()
- .addOptionsModification(o -> o.assertionProcessing = assertionsState)
+ .addOptionsModification(o -> o.assertionTransformation = transformation)
.compile();
}
@@ -232,9 +232,9 @@
if (backend == Backend.CF) {
return new CompilationResults(
withAccess,
- compileCf(AssertionProcessing.LEAVE),
- compileCf(AssertionProcessing.REMOVE),
- compileCf(AssertionProcessing.ENABLE));
+ compileCf(AssertionTransformation.PASSTHROUGH),
+ compileCf(AssertionTransformation.DISABLE),
+ compileCf(AssertionTransformation.ENABLE));
}
return new CompilationResults(
withAccess,
@@ -331,18 +331,18 @@
checkResultWithChromiumAssertions(results.withAssertions);
}
- private D8TestCompileResult compileD8(InternalOptions.AssertionProcessing assertionsState)
+ private D8TestCompileResult compileD8(AssertionTransformation transformation)
throws CompilationFailedException {
return testForD8()
.addProgramClasses(ClassWithAssertions.class)
.debug()
.setMinApi(AndroidApiLevel.B)
- .addOptionsModification(o -> o.assertionProcessing = assertionsState)
+ .addOptionsModification(o -> o.assertionTransformation = transformation)
.compile();
}
- private D8TestCompileResult compileR8FollowedByD8(
- InternalOptions.AssertionProcessing assertionsState) throws Exception {
+ private D8TestCompileResult compileR8FollowedByD8(AssertionTransformation transformation)
+ throws Exception {
Path program =
testForR8(Backend.CF)
.addProgramClasses(ClassWithAssertions.class)
@@ -357,16 +357,16 @@
.addProgramFiles(program)
.debug()
.setMinApi(AndroidApiLevel.B)
- .addOptionsModification(o -> o.assertionProcessing = assertionsState)
+ .addOptionsModification(o -> o.assertionTransformation = transformation)
.compile();
}
@Test
public void testD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
- checkResultWithAssertionsInactive(compileD8(AssertionProcessing.REMOVE));
- checkResultWithAssertionsInactive(compileD8(AssertionProcessing.LEAVE));
- checkResultWithAssertionsEnabledAtCompileTime(compileD8(AssertionProcessing.ENABLE));
+ checkResultWithAssertionsInactive(compileD8(AssertionTransformation.DISABLE));
+ checkResultWithAssertionsInactive(compileD8(AssertionTransformation.PASSTHROUGH));
+ checkResultWithAssertionsEnabledAtCompileTime(compileD8(AssertionTransformation.ENABLE));
}
private D8TestCompileResult compileD8Regress110887293(Function<byte[], byte[]> rewriter)
@@ -390,9 +390,9 @@
@Test
public void testR8FollowedByD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
- checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionProcessing.REMOVE));
- checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionProcessing.LEAVE));
+ checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionTransformation.DISABLE));
+ checkResultWithAssertionsInactive(compileR8FollowedByD8(AssertionTransformation.PASSTHROUGH));
checkResultWithAssertionsEnabledAtCompileTime(
- compileR8FollowedByD8(AssertionProcessing.ENABLE));
+ compileR8FollowedByD8(AssertionTransformation.ENABLE));
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class1.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class1.java
new file mode 100644
index 0000000..9de87c9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class1.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, 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.rewrite.assertions.testclasses;
+
+public class Class1 {
+ public static void m() {
+ assert false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class2.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class2.java
new file mode 100644
index 0000000..93b24e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/Class2.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, 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.rewrite.assertions.testclasses;
+
+public class Class2 {
+ public static void m() {
+ assert false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class1.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class1.java
new file mode 100644
index 0000000..79d0949
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class1.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, 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.rewrite.assertions.testclasses.subpackage;
+
+public class Class1 {
+ public static void m() {
+ assert false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class2.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class2.java
new file mode 100644
index 0000000..e001cf0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/subpackage/Class2.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, 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.rewrite.assertions.testclasses.subpackage;
+
+public class Class2 {
+ public static void m() {
+ assert false;
+ }
+}