Merge "Move dup to block with store in case of linear flow"
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 72e2887..8c16d04 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
@@ -5,7 +5,7 @@
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
-import static com.android.tools.r8.ir.optimize.CodeRewriter.checksNullReceiverBeforeSideEffect;
+import static com.android.tools.r8.ir.optimize.CodeRewriter.checksNullBeforeSideEffect;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -35,7 +35,6 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Value;
@@ -880,10 +879,6 @@
printC1VisualizerHeader(method);
String previous = printMethod(code, "Initial IR (SSA)", null);
- if (method.getCode() != null && method.getCode().isJarCode()) {
- computeKotlinNonNullParamHints(feedback, method, code);
- }
-
if (options.canHaveArtStringNewInitBug()) {
CodeRewriter.ensureDirectStringNewToInit(code);
}
@@ -1110,11 +1105,10 @@
codeRewriter.identifyReturnsArgument(method, code, feedback);
if (options.enableInlining && inliner != null) {
- codeRewriter.identifyInvokeSemanticsForInlining(method, code, feedback);
+ codeRewriter.identifyInvokeSemanticsForInlining(method, code, graphLense(), feedback);
}
- // If hints from Kotlin metadata or use of Kotlin Intrinsics were not available, track usage of
- // parameters and compute their nullability and possibility of NPE.
+ // Track usage of parameters and compute their nullability and possibility of NPE.
if (method.getOptimizationInfo().getNonNullParamOrThrow() == null) {
computeNonNullParamHints(feedback, method, code);
}
@@ -1159,33 +1153,25 @@
finalizeIR(method, code, feedback);
}
- private void computeKotlinNonNullParamHints(
- OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
+ private void computeNonNullParamHints(
+ OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
List<Value> arguments = code.collectArguments(true);
BitSet paramsCheckedForNull = new BitSet();
- DexMethod checkParameterIsNotNull =
- code.options.itemFactory.kotlin.intrinsics.checkParameterIsNotNull;
for (int index = 0; index < arguments.size(); index++) {
Value argument = arguments.get(index);
- for (Instruction user : argument.uniqueUsers()) {
- // To enforce parameter non-null requirement Kotlin uses intrinsic:
- // kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull(param, message)
- //
- // with the following we simply look if the parameter is ever passed
- // to the mentioned intrinsic as the first argument. We do it only for
- // code coming from Java classfile, since after the method is rewritten
- // by R8 this call gets inlined.
- if (!user.isInvokeStatic()) {
- continue;
- }
- InvokeMethod invoke = user.asInvokeMethod();
- DexMethod invokedMethod =
- graphLense().getOriginalMethodSignature(invoke.getInvokedMethod());
- // TODO(b/121377154): Make sure there is no other side-effect before argument's null check.
- // E.g., is this the first method invocation inside the method?
- if (invokedMethod == checkParameterIsNotNull && user.inValues().indexOf(argument) == 0) {
- paramsCheckedForNull.set(index);
- }
+ // This handles cases where the parameter is checked via Kotlin Intrinsics:
+ //
+ // kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull(param, message)
+ //
+ // or its inlined version:
+ //
+ // if (param != null) return;
+ // invoke-static throwParameterIsNullException(msg)
+ //
+ // or some other variants, e.g., throw null or NPE after the direct null check.
+ if (argument.isUsed()
+ && checksNullBeforeSideEffect(code, appInfo, graphLense(), argument)) {
+ paramsCheckedForNull.set(index);
}
}
if (paramsCheckedForNull.length() > 0) {
@@ -1213,28 +1199,6 @@
}
}
- // TODO(b/121377154): Consider merging compute(Kotlin)?NonNullParamHints into one.
- private void computeNonNullParamHints(
- OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
- List<Value> arguments = code.collectArguments(true);
- BitSet paramsCheckedForNull = new BitSet();
- for (int index = 0; index < arguments.size(); index++) {
- Value argument = arguments.get(index);
- if (argument.isUsed()
- && checksNullReceiverBeforeSideEffect(code, appInfo.dexItemFactory, argument)) {
- paramsCheckedForNull.set(index);
- }
- // The above one only catches something like:
- // if (param != null) return;
- // invoke-static throwParameterIsNullException(msg)
- // This is good enough to handle checkParameterIsNotNull(param, msg), which is generated by
- // kotlinc for arguments that are not nullable.
- }
- if (paramsCheckedForNull.length() > 0) {
- feedback.setNonNullParamOrThrow(method, paramsCheckedForNull);
- }
- }
-
private void finalizeIR(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
code.traceBlocks();
if (options.isGeneratingClassFiles()) {
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 2244653..40a7692 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
@@ -38,6 +38,7 @@
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueShort;
import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsageBuilder;
@@ -1003,7 +1004,7 @@
}
public void identifyInvokeSemanticsForInlining(
- DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+ DexEncodedMethod method, IRCode code, GraphLense graphLense, OptimizationFeedback feedback) {
if (method.isStatic()) {
// Identifies if the method preserves class initialization after inlining.
feedback.markTriggerClassInitBeforeAnySideEffect(method,
@@ -1013,7 +1014,7 @@
final Value receiver = code.getThis();
feedback.markCheckNullReceiverBeforeAnySideEffect(method,
receiver.isUsed()
- && checksNullReceiverBeforeSideEffect(code, appInfo.dexItemFactory, receiver));
+ && checksNullBeforeSideEffect(code, appInfo, graphLense, receiver));
}
}
@@ -1342,55 +1343,66 @@
}
/**
- * Returns true if the given code unconditionally throws if receiver is null before any other
- * side effect instruction.
+ * Returns true if the given code unconditionally throws if value is null before any other side
+ * effect instruction.
*
* Note: we do not track phis so we may return false negative. This is a conservative approach.
*/
- public static boolean checksNullReceiverBeforeSideEffect(
- IRCode code, DexItemFactory factory, Value receiver) {
- Wrapper<DexMethod> throwParamIsNullException =
- MethodSignatureEquivalence.get()
- .wrap(factory.kotlin.intrinsics.throwParameterIsNullException);
+ public static boolean checksNullBeforeSideEffect(
+ IRCode code, AppInfo appInfo, GraphLense graphLense, Value value) {
return alwaysTriggerExpectedEffectBeforeAnythingElse(
code,
(instr, it) -> {
BasicBlock currentBlock = instr.getBlock();
- // If the code explicitly checks the nullability of receiver, we should visit the next
- // block that corresponds to the null receiver where NPE semantic could be preserved.
- if (!currentBlock.hasCatchHandlers() && isNullCheck(instr, receiver)) {
+ // If the code explicitly checks the nullability of the value, we should visit the next
+ // block that corresponds to the null value where NPE semantic could be preserved.
+ if (!currentBlock.hasCatchHandlers() && isNullCheck(instr, value)) {
return InstructionEffect.CONDITIONAL_EFFECT;
}
- // Kotlin specific way of throwing NPE: throwParameterIsNullException.
- // Similarly, combined with the above CONDITIONAL_EFFECT, the code checks on NPE on
- // receiver.
- if (instr.isInvokeStatic()) {
- DexMethod method = instr.asInvokeStatic().getInvokedMethod();
- if (MethodSignatureEquivalence.get().wrap(method).equals(throwParamIsNullException)) {
+ if (isKotlinNullCheck(appInfo, graphLense, instr, value)) {
+ DexMethod invokedMethod = instr.asInvokeStatic().getInvokedMethod();
+ // Kotlin specific way of throwing NPE: throwParameterIsNullException.
+ // Similarly, combined with the above CONDITIONAL_EFFECT, the code checks on NPE on
+ // the value.
+ if (invokedMethod.name
+ == appInfo.dexItemFactory.kotlin.intrinsics.throwParameterIsNullException.name) {
// We found a NPE (or similar exception) throwing code.
- // Combined with the above CONDITIONAL_EFFECT, the code checks NPE on receiver.
+ // Combined with the above CONDITIONAL_EFFECT, the code checks NPE on the value.
for (BasicBlock predecessor : currentBlock.getPredecessors()) {
Instruction last =
predecessor.listIterator(predecessor.getInstructions().size()).previous();
- if (isNullCheck(last, receiver)) {
+ if (isNullCheck(last, value)) {
return InstructionEffect.DESIRED_EFFECT;
}
}
+ // Hitting here means that this call might be used for other parameters. If we don't
+ // bail out, it will be regarded as side effects for the current value.
+ return InstructionEffect.NO_EFFECT;
+ } else {
+ // Kotlin specific way of checking parameter nullness: checkParameterIsNotNull.
+ assert invokedMethod.name
+ == appInfo.dexItemFactory.kotlin.intrinsics.checkParameterIsNotNull.name;
+ return InstructionEffect.DESIRED_EFFECT;
}
}
- if (isInstantiationOfNullPointerException(instr, it, factory)) {
+ if (isInstantiationOfNullPointerException(instr, it, appInfo.dexItemFactory)) {
it.next(); // Skip call to NullPointerException.<init>.
return InstructionEffect.NO_EFFECT;
- } else if (instr.throwsNpeIfValueIsNull(receiver, factory)) {
+ } else if (instr.throwsNpeIfValueIsNull(value, appInfo.dexItemFactory)) {
// In order to preserve NPE semantic, the exception must not be caught by any handler.
// Therefore, we must ignore this instruction if it is covered by a catch handler.
// Note: this is a conservative approach where we consider that any catch handler could
// catch the exception, even if it cannot catch a NullPointerException.
if (!currentBlock.hasCatchHandlers()) {
- // We found a NPE check on receiver.
+ // We found a NPE check on the value.
return InstructionEffect.DESIRED_EFFECT;
}
} else if (instructionHasSideEffects(instr)) {
+ // If the current instruction is const-string, this could load the parameter name.
+ // Just make sure it is indeed not throwing.
+ if (instr.isConstString() && !instr.instructionInstanceCanThrow()) {
+ return InstructionEffect.NO_EFFECT;
+ }
// We found a side effect before a NPE check.
return InstructionEffect.OTHER_EFFECT;
}
@@ -1398,9 +1410,36 @@
});
}
- private static boolean isNullCheck(Instruction instr, Value receiver) {
+ // Note that this method may have false positives, since the application could in principle
+ // declare a method called checkParameterIsNotNull(parameter, message) or
+ // throwParameterIsNullException(parameterName) in a package that starts with "kotlin".
+ private static boolean isKotlinNullCheck(
+ AppInfo appInfo, GraphLense graphLense, Instruction instr, Value value) {
+ if (!instr.isInvokeStatic()) {
+ return false;
+ }
+ // We need to strip the holder, since Kotlin adds different versions of null-check machinery,
+ // e.g., kotlin.collections.ArraysKt___ArraysKt... or kotlin.jvm.internal.ArrayIteratorKt...
+ MethodSignatureEquivalence wrapper = MethodSignatureEquivalence.get();
+ Wrapper<DexMethod> checkParameterIsNotNull =
+ wrapper.wrap(appInfo.dexItemFactory.kotlin.intrinsics.checkParameterIsNotNull);
+ Wrapper<DexMethod> throwParamIsNullException =
+ wrapper.wrap(appInfo.dexItemFactory.kotlin.intrinsics.throwParameterIsNullException);
+ DexMethod invokedMethod =
+ graphLense.getOriginalMethodSignature(instr.asInvokeStatic().getInvokedMethod());
+ Wrapper<DexMethod> methodWrap = wrapper.wrap(invokedMethod);
+ if (methodWrap.equals(throwParamIsNullException)
+ || (methodWrap.equals(checkParameterIsNotNull) && instr.inValues().get(0).equals(value))) {
+ if (invokedMethod.getHolder().getPackageDescriptor().startsWith("kotlin")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isNullCheck(Instruction instr, Value value) {
return instr.isIf() && instr.asIf().isZeroTest()
- && instr.inValues().get(0).equals(receiver)
+ && instr.inValues().get(0).equals(value)
&& (instr.asIf().getType() == Type.EQ || instr.asIf().getType() == Type.NE);
}
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 91bb786..ec12f82 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -10,14 +10,12 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugEvent;
-import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
import com.android.tools.r8.graph.DexDebugEvent.SetFile;
-import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.graph.DexDebugEventBuilder;
@@ -401,56 +399,45 @@
new PositionEventEmitter(application.dexItemFactory, method.method, processedEvents);
// Debug event visitor to map line numbers.
- // TODO(117268618): Cleanup the duplicate pc tracking.
DexDebugEventVisitor visitor =
- new DexDebugEventVisitor() {
- DexDebugPositionState state =
- new DexDebugPositionState(debugInfo.startLine, method.method);
- int currentPc = 0;
+ new DexDebugPositionState(debugInfo.startLine, method.method) {
+ // Keep track of what PC has been emitted.
+ private int emittedPc = 0;
+
+ // Force the current PC to emitted.
private void flushPc() {
- if (currentPc != state.getCurrentPc()) {
- positionEventEmitter.emitAdvancePc(state.getCurrentPc());
- currentPc = state.getCurrentPc();
+ if (emittedPc != getCurrentPc()) {
+ positionEventEmitter.emitAdvancePc(getCurrentPc());
+ emittedPc = getCurrentPc();
}
}
- @Override
- public void visit(AdvancePC advancePC) {
- state.visit(advancePC);
- }
-
- @Override
- public void visit(AdvanceLine advanceLine) {
- state.visit(advanceLine);
- }
-
- @Override
- public void visit(SetInlineFrame setInlineFrame) {
- state.visit(setInlineFrame);
- }
-
+ // A default event denotes a line table entry and must always be emitted. Remap its line.
@Override
public void visit(Default defaultEvent) {
- state.visit(defaultEvent);
- int currentLine = state.getCurrentLine();
- assert currentLine >= 0;
+ super.visit(defaultEvent);
+ assert getCurrentLine() >= 0;
Position position =
positionRemapper.createRemappedPosition(
- state.getCurrentLine(),
- state.getCurrentFile(),
- state.getCurrentMethod(),
- state.getCurrentCallerPosition());
+ getCurrentLine(),
+ getCurrentFile(),
+ getCurrentMethod(),
+ getCurrentCallerPosition());
mappedPositions.add(
new MappedPosition(
- state.getCurrentMethod(),
- currentLine,
- state.getCurrentCallerPosition(),
+ getCurrentMethod(),
+ getCurrentLine(),
+ getCurrentCallerPosition(),
position.line));
- positionEventEmitter.emitPositionEvents(state.getCurrentPc(), position);
- currentPc = state.getCurrentPc();
+ positionEventEmitter.emitPositionEvents(getCurrentPc(), position);
+ emittedPc = getCurrentPc();
}
+ // Non-materializing events use super, ie, AdvancePC, AdvanceLine and SetInlineFrame.
+
+ // Materializing events are just amended to the stream.
+
@Override
public void visit(SetFile setFile) {
processedEvents.add(setFile);
@@ -466,6 +453,8 @@
processedEvents.add(setEpilogueBegin);
}
+ // Local changes must force flush the PC ensuing they pertain to the correct point.
+
@Override
public void visit(StartLocal startLocal) {
flushPc();
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index da46d60..3b5ddd8 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -47,6 +47,9 @@
// Proguard configuration file lines.
private final List<String> config = new ArrayList<>();
+ // Additional Proguard configuration files.
+ private List<Path> proguardConfigFiles = new ArrayList<>();
+
private ExternalR8TestBuilder(TestState state, Builder builder, Backend backend) {
super(state, builder, backend);
}
@@ -67,11 +70,8 @@
try {
Path outputFolder = getState().getNewTempFolder();
Path outputJar = outputFolder.resolve("output.jar");
- Path proguardConfigFile = outputFolder.resolve("proguard-config.txt");
Path proguardMapFile = outputFolder.resolve("output.jar.map");
- FileUtils.writeTextFile(proguardConfigFile, config);
-
List<String> command = new ArrayList<>();
Collections.addAll(
command,
@@ -81,12 +81,20 @@
R8.class.getTypeName(),
"--output",
outputJar.toAbsolutePath().toString(),
- "--pg-conf",
- proguardConfigFile.toAbsolutePath().toString(),
"--pg-map-output",
proguardMapFile.toString(),
backend == Backend.CF ? "--classfile" : "--dex",
builder.getMode() == CompilationMode.DEBUG ? "--debug" : "--release");
+ if (!config.isEmpty()) {
+ Path proguardConfigFile = outputFolder.resolve("proguard-config.txt");
+ FileUtils.writeTextFile(proguardConfigFile, config);
+ command.add("--pg-conf");
+ command.add(proguardConfigFile.toAbsolutePath().toString());
+ }
+ for (Path proguardConfigFile : proguardConfigFiles) {
+ command.add("--pg-conf");
+ command.add(proguardConfigFile.toAbsolutePath().toString());
+ }
if (libJars.isEmpty()) {
command.add("--lib");
command.add(TestBase.runtimeJar(backend).toAbsolutePath().toString());
@@ -154,14 +162,8 @@
}
@Override
- public ExternalR8TestBuilder addKeepRuleFiles(List<Path> files) {
- try {
- for (Path file : files) {
- config.addAll(FileUtils.readAllLines(file));
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ public ExternalR8TestBuilder addKeepRuleFiles(List<Path> proguardConfigFiles) {
+ this.proguardConfigFiles.addAll(proguardConfigFiles);
return self();
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 2e4b1bd..ea482fa 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -98,9 +98,13 @@
// Ordered list of injar entries.
private List<Path> injars = new ArrayList<>();
+
// Proguard configuration file lines.
private List<String> config = new ArrayList<>();
+ // Additional Proguard configuration files.
+ private List<Path> proguardConfigFiles = new ArrayList<>();
+
private ProguardTestBuilder(TestState state, Builder builder, Backend backend) {
super(state, builder, backend);
}
@@ -138,6 +142,10 @@
command.add(ToolHelper.getJava8RuntimeJar().toString());
command.add("-include");
command.add(configFile.toString());
+ for (Path proguardConfigFile : proguardConfigFiles) {
+ command.add("-include");
+ command.add(proguardConfigFile.toAbsolutePath().toString());
+ }
command.add("-outjar");
command.add(outJar.toString());
command.add("-printmapping");
@@ -153,11 +161,9 @@
if (result.exitCode != 0) {
throw new CompilationFailedException(result.toString());
}
- AndroidApp.Builder aaabuilder = AndroidApp.builder();
- aaabuilder.addProgramFiles(outJar);
String proguardMap =
Files.exists(mapFile) ? FileUtils.readTextFile(mapFile, Charsets.UTF_8) : "";
- return new ProguardTestCompileResult(getState(), aaabuilder.build(), proguardMap);
+ return new ProguardTestCompileResult(getState(), outJar, proguardMap);
} catch (IOException e) {
throw new CompilationFailedException(e);
}
@@ -203,14 +209,8 @@
}
@Override
- public ProguardTestBuilder addKeepRuleFiles(List<Path> files) {
- try {
- for (Path file : files) {
- config.addAll(FileUtils.readAllLines(file));
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ public ProguardTestBuilder addKeepRuleFiles(List<Path> proguardConfigFiles) {
+ this.proguardConfigFiles.addAll(proguardConfigFiles);
return self();
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index 4cc1def..6f47adb 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -8,18 +8,25 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
public class ProguardTestCompileResult
extends TestCompileResult<ProguardTestCompileResult, ProguardTestRunResult> {
+ private final Path outputJar;
private final String proguardMap;
- ProguardTestCompileResult(TestState state, AndroidApp app, String proguardMap) {
- super(state, app);
+ ProguardTestCompileResult(TestState state, Path outputJar, String proguardMap) {
+ super(state, AndroidApp.builder().addProgramFiles(outputJar).build());
+ this.outputJar = outputJar;
this.proguardMap = proguardMap;
}
+ public Path outputJar() {
+ return outputJar;
+ }
+
@Override
public ProguardTestCompileResult self() {
return this;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 195e01c..7d6af00 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.TestDescriptionWatcher;
import com.google.common.collect.ImmutableList;
@@ -80,8 +81,8 @@
private final CompilationMode mode;
private final String pkg;
private final String mainClass;
- private final Frontend frontend;
- private final Output output;
+ protected final Frontend frontend;
+ protected final Output output;
public R8RunExamplesCommon(
String pkg,
@@ -174,16 +175,7 @@
.setDisableMinification(true)
.addProguardConfiguration(ImmutableList.of("-keepattributes *"), Origin.unknown())
.build();
- ToolHelper.runR8(
- command,
- options -> {
- options.lineNumberOptimization = LineNumberOptimization.OFF;
- options.enableCfFrontend = frontend == Frontend.CF;
- if (output == Output.CF) {
- // Class inliner is not supported with CF backend yet.
- options.enableClassInlining = false;
- }
- });
+ ToolHelper.runR8(command, this::configure);
break;
}
default:
@@ -191,6 +183,15 @@
}
}
+ protected void configure(InternalOptions options) {
+ options.lineNumberOptimization = LineNumberOptimization.OFF;
+ options.enableCfFrontend = frontend == Frontend.CF;
+ if (output == Output.CF) {
+ // Class inliner is not supported with CF backend yet.
+ options.enableClassInlining = false;
+ }
+ }
+
private boolean shouldCompileFail() {
if (output == Output.CF && getFailingCompileCf().contains(mainClass)) {
return true;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
index 4451910..c2c0869 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.utils.InternalOptions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -17,6 +18,15 @@
@RunWith(Parameterized.class)
public class R8RunExamplesKotlinTest extends R8RunExamplesCommon {
+ @Override
+ protected void configure(InternalOptions options) {
+ options.enableCfFrontend = frontend == Frontend.CF;
+ if (output == Output.CF) {
+ // Class inliner is not supported with CF backend yet.
+ options.enableClassInlining = false;
+ }
+ }
+
@Parameters(name = "{0}_{1}_{2}_{3}_{5}_{6}")
public static Collection<String[]> data() {
String[] tests = {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
index 10b9a63..518ea8d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -92,7 +92,7 @@
MethodSubject selfCheck = mainSubject.method("void", "selfCheck", ImmutableList.of());
assertThat(selfCheck, isPresent());
- assertEquals(0, countCallToParamNullCheck(selfCheck));
+ assertEquals(1, countCallToParamNullCheck(selfCheck));
assertEquals(1, countPrintCall(selfCheck));
assertEquals(0, countThrow(selfCheck));
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 1d3f25e..1a690e2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+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;
@@ -212,7 +213,13 @@
protected void runTest(String folder, String mainClass,
boolean enabled, AndroidAppInspector inspector) throws Exception {
runTest(
- folder, mainClass, null,
+ folder, mainClass,
+ // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
+ StringUtils.lines(
+ "-neverinline class * { void test*State*(...); }",
+ "-neverinline class * { void testBigExtraMethod(...); }",
+ "-neverinline class * { void testBigExtraMethodReturningLambda(...); }"
+ ),
options -> {
options.enableInlining = true;
options.enableClassInlining = enabled;
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
index f0031d2..757e06f 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.shaking;
+import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import com.android.tools.r8.ExternalR8TestCompileResult;
import com.android.tools.r8.TestBase;
@@ -38,18 +40,53 @@
@Test
public void testSingleConfigurationWithRelativePath() throws Exception {
+ Path subDirectory = temp.newFolder().toPath();
+ Path proguardConfigFile = subDirectory.resolve("proguard-config.txt");
+ Path proguardConfigOutFile = subDirectory.resolve("proguard-config-out.txt");
+
String proguardConfig =
StringUtils.lines(
keepMainProguardConfiguration(PrintConfigurationTestClass.class),
- "-printconfiguration generated-proguard-config.txt");
+ "-printconfiguration proguard-config-out.txt");
+ FileUtils.writeTextFile(proguardConfigFile, proguardConfig.trim());
+
ExternalR8TestCompileResult result =
testForExternalR8(Backend.DEX)
.addProgramClasses(PrintConfigurationTestClass.class)
- .addKeepRules(proguardConfig.trim())
+ .addKeepRuleFiles(proguardConfigFile)
.compile();
- Path printConfigurationFile =
- result.outputJar().getParent().resolve("generated-proguard-config.txt");
- assertEquals(proguardConfig, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
+
+ assertEquals(proguardConfig, FileUtils.readTextFile(proguardConfigOutFile, Charsets.UTF_8));
+ }
+
+ @Test
+ public void testSingleConfigurationWithRelativePathCompatibility() throws Exception {
+ Path subDirectory = temp.newFolder().toPath();
+ Path proguardConfigFile = subDirectory.resolve("proguard-config.txt");
+ Path proguardConfigOutFile = subDirectory.resolve("proguard-config-out.txt");
+
+ String proguardConfig =
+ StringUtils.lines(
+ keepMainProguardConfiguration(PrintConfigurationTestClass.class),
+ "-printconfiguration proguard-config-out.txt");
+ FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
+
+ testForProguard()
+ .addProgramClasses(PrintConfigurationTestClass.class)
+ .addKeepRuleFiles(proguardConfigFile)
+ .compile();
+
+ String proguardConfigOut = FileUtils.readTextFile(proguardConfigOutFile, Charsets.UTF_8);
+ assertThat(
+ proguardConfigOut,
+ containsString(
+ StringUtils.lines(
+ "-keep class com.android.tools.r8.shaking.PrintConfigurationTestClass {",
+ " public static void main(java.lang.String[]);",
+ "}")));
+ assertThat(
+ proguardConfigOut,
+ containsString("-printconfiguration " + proguardConfigOutFile.toString()));
}
@Test
diff --git a/src/test/kotlinR8TestResources/class_inliner_lambda_j_style/main.kt b/src/test/kotlinR8TestResources/class_inliner_lambda_j_style/main.kt
index ebf26cd..f95125c 100644
--- a/src/test/kotlinR8TestResources/class_inliner_lambda_j_style/main.kt
+++ b/src/test/kotlinR8TestResources/class_inliner_lambda_j_style/main.kt
@@ -16,7 +16,6 @@
testStateful3()
}
-@Synchronized
fun testStateless() {
SamIface.Consumer.consume { "123" }
SamIface.Consumer.consume { "ABC" }
@@ -31,7 +30,6 @@
}
}
-@Synchronized
fun testStateful() {
var someVariable = 0
@@ -62,7 +60,6 @@
}
}
-@Synchronized
fun testStateful2() {
var someVariable = 0
SamIface.Consumer.consumeBig {
@@ -80,7 +77,6 @@
}
}
-@Synchronized
fun testStateful3() {
var someVariable = 0
SamIface.Consumer.consumeBig {
diff --git a/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt b/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
index 06b937e..ea315ba 100644
--- a/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
+++ b/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
@@ -17,14 +17,12 @@
data class Record(val foo: String, val good: Boolean)
-@Synchronized
fun testKotlinSequencesStateless(strings: Sequence<String>) {
useRecord()
// Stateless k-style lambda
strings.map { Record(it, false) }.forEach { println(it) }
}
-@Synchronized
fun testKotlinSequencesStateful(a: Int, b: Int, strings: Sequence<String>) {
useRecord()
// Big stateful k-style lambda
@@ -40,7 +38,6 @@
}
}
-@Synchronized
fun testBigExtraMethod() {
useRecord()
bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
@@ -62,7 +59,6 @@
return "$id: ${lambda()}"
}
-@Synchronized
fun testBigExtraMethodReturningLambda() {
useRecord()
bigUserReturningLambda(next()) { next() } // Not used