Merge "Upgrade kotlinx metadata to 0.0.3"
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 409c437..b7fc6b3 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.3.6-dev";
+ public static final String LABEL = "1.3.7-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 5bed061..8ab2c1a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
import com.android.tools.r8.graph.DexItemFactory;
@@ -467,7 +468,7 @@
return instruction;
}
}
- return null;
+ throw new Unreachable();
}
public void clearUserInfo() {
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 fffbc44..c6ba97d 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
@@ -46,7 +46,6 @@
import com.android.tools.r8.ir.optimize.NonNullTracker;
import com.android.tools.r8.ir.optimize.Outliner;
import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
-import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
@@ -696,7 +695,6 @@
codeRewriter.rewriteSwitch(code);
codeRewriter.processMethodsNeverReturningNormally(code);
codeRewriter.simplifyIf(code, typeEnvironment);
- new RedundantFieldLoadElimination(code).run();
if (options.testing.invertConditionals) {
invertConditionalsForTesting(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
deleted file mode 100644
index 24012b1..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2018, 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;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.DominatorTree;
-import com.android.tools.r8.ir.code.FieldInstruction;
-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.Phi;
-import com.android.tools.r8.ir.code.Value;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Eliminate redundant field loads.
- *
- * <p>Simple algorithm that goes through all blocks in one pass in dominator order and propagates
- * active field sets across control-flow edges where the target has only one predecessor.
- */
-// TODO(ager): Evaluate speed/size for computing active field sets in a fixed-point computation.
-public class RedundantFieldLoadElimination {
-
- private final IRCode code;
- private final DominatorTree dominatorTree;
-
- // Maps keeping track of fields that have an already loaded value at basic block entry.
- private final HashMap<BasicBlock, HashMap<FieldAndObject, Instruction>>
- activeInstanceFieldsAtEntry = new HashMap<>();
- private final HashMap<BasicBlock, HashMap<DexField, Instruction>> activeStaticFieldsAtEntry =
- new HashMap<>();
-
- // Maps keeping track of fields with already loaded values for the current block during
- // elimination.
- private HashMap<FieldAndObject, Instruction> activeInstanceFields;
- private HashMap<DexField, Instruction> activeStaticFields;
-
- public RedundantFieldLoadElimination(IRCode code) {
- this.code = code;
- dominatorTree = new DominatorTree(code);
- }
-
- private static class FieldAndObject {
- private final DexField field;
- private final Value object;
-
- private FieldAndObject(DexField field, Value receiver) {
- this.field = field;
- this.object = receiver;
- }
-
- @Override
- public int hashCode() {
- return field.hashCode() * 7 + object.hashCode();
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof FieldAndObject)) {
- return false;
- }
- FieldAndObject o = (FieldAndObject) other;
- return o.object == object && o.field == field;
- }
- }
-
- public void run() {
- for (BasicBlock block : dominatorTree.getSortedBlocks()) {
- activeInstanceFields =
- activeInstanceFieldsAtEntry.containsKey(block)
- ? activeInstanceFieldsAtEntry.get(block)
- : new HashMap<>();
- activeStaticFields =
- activeStaticFieldsAtEntry.containsKey(block)
- ? activeStaticFieldsAtEntry.get(block)
- : new HashMap<>();
- InstructionListIterator it = block.listIterator();
- while (it.hasNext()) {
- Instruction instruction = it.next();
- if (instruction.isFieldInstruction()) {
- DexField field = instruction.asFieldInstruction().getField();
- if (instruction.isInstancePut() || instruction.isStaticPut()) {
- killActiveFields(instruction.asFieldInstruction());
- } else if (instruction.isInstanceGet() && !instruction.outValue().hasLocalInfo()) {
- Value object = instruction.asInstanceGet().object();
- FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- if (activeInstanceFields.containsKey(fieldAndObject)) {
- Instruction active = activeInstanceFields.get(fieldAndObject);
- eliminateRedundantRead(it, instruction, active);
- } else {
- activeInstanceFields.put(fieldAndObject, instruction);
- }
- } else if (instruction.isStaticGet() && !instruction.outValue().hasLocalInfo()) {
- if (activeStaticFields.containsKey(field)) {
- Instruction active = activeStaticFields.get(field);
- eliminateRedundantRead(it, instruction, active);
- } else {
- // A field get on a different class can cause <clinit> to run and change static
- // field values.
- killActiveFields(instruction.asFieldInstruction());
- activeStaticFields.put(field, instruction);
- }
- }
- }
- if (instruction.isMonitor() || instruction.isInvokeMethod()) {
- activeInstanceFields.clear();
- activeStaticFields.clear();
- }
- }
- propagateActiveFieldsFrom(block);
- }
- assert code.isConsistentSSA();
- }
-
- private void propagateActiveFieldsFrom(BasicBlock block) {
- for (BasicBlock successor : block.getSuccessors()) {
- // Allow propagation across exceptional edges, just be careful not to propagate if the
- // throwing instruction is a field instruction.
- if (successor.getPredecessors().size() == 1) {
- if (block.hasCatchSuccessor(successor)) {
- Instruction exceptionalExit = block.exceptionalExit();
- if (exceptionalExit != null && exceptionalExit.isFieldInstruction()) {
- killActiveFieldsForExceptionalExit(exceptionalExit.asFieldInstruction());
- }
- }
- assert !activeInstanceFieldsAtEntry.containsKey(successor);
- activeInstanceFieldsAtEntry.put(successor, new HashMap<>(activeInstanceFields));
- assert !activeStaticFieldsAtEntry.containsKey(successor);
- activeStaticFieldsAtEntry.put(successor, new HashMap<>(activeStaticFields));
- }
- }
- }
-
- private void killActiveFields(FieldInstruction instruction) {
- DexField field = instruction.getField();
- if (instruction.isInstancePut()) {
- // Remove all the field/object pairs that refer to this field to make sure
- // that we are conservative.
- List<FieldAndObject> keysToRemove = new ArrayList<>();
- for (FieldAndObject key : activeInstanceFields.keySet()) {
- if (key.field == field) {
- keysToRemove.add(key);
- }
- }
- keysToRemove.forEach((k) -> activeInstanceFields.remove(k));
- } else if (instruction.isInstanceGet()) {
- Value object = instruction.asInstanceGet().object();
- FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- activeInstanceFields.remove(fieldAndObject);
- } else if (instruction.isStaticPut()) {
- if (field.clazz != code.method.method.holder) {
- // Accessing a static field on a different object could cause <clinit> to run which
- // could modify any static field on any other object.
- activeStaticFields.clear();
- } else {
- activeStaticFields.remove(field);
- }
- } else if (instruction.isStaticGet()) {
- if (field.clazz != code.method.method.holder) {
- // Accessing a static field on a different object could cause <clinit> to run which
- // could modify any static field on any other object.
- activeStaticFields.clear();
- }
- }
- }
-
- // If a field get instruction throws an exception it did not have an effect on the
- // value of the field. Therefore, when propagating across exceptional edges for a
- // field get instruction we have to exclude that field from the set of known
- // field values.
- private void killActiveFieldsForExceptionalExit(FieldInstruction instruction) {
- DexField field = instruction.getField();
- if (instruction.isInstanceGet()) {
- Value object = instruction.asInstanceGet().object();
- FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- activeInstanceFields.remove(fieldAndObject);
- } else if (instruction.isStaticGet()) {
- activeStaticFields.remove(field);
- }
- }
-
- private void eliminateRedundantRead(
- InstructionListIterator it, Instruction redundant, Instruction active) {
- redundant.outValue().replaceUsers(active.outValue());
- it.removeOrReplaceByDebugLocalRead();
- active.outValue().uniquePhiUsers().forEach(Phi::removeTrivialPhi);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 581ac8a..b0f4dc0 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.inferMemberOrTypeFromNameString;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifier;
+import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexEncodedField;
@@ -32,7 +32,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Streams;
@@ -82,7 +81,7 @@
}
public void decoupleIdentifierNameStringsInMethod(DexEncodedMethod encodedMethod, IRCode code) {
- Origin origin = appInfo.originFor(code.method.method.getHolder());
+ DexType originHolder = code.method.method.getHolder();
ListIterator<BasicBlock> blocks = code.listIterator();
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
@@ -110,17 +109,15 @@
? instruction.asStaticPut().inValue()
: instruction.asInstancePut().value();
if (!in.isConstString()) {
- if (options.proguardConfiguration.isObfuscating()) {
- warnUndeterminedIdentifier(options.reporter, field, origin, instruction, null);
- }
+ warnUndeterminedIdentifierIfNecessary(
+ appInfo, options, field, originHolder, instruction, null);
continue;
}
DexString original = in.getConstInstruction().asConstString().getValue();
DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original);
if (itemBasedString == null) {
- if (options.proguardConfiguration.isObfuscating()) {
- warnUndeterminedIdentifier(options.reporter, field, origin, instruction, original);
- }
+ warnUndeterminedIdentifierIfNecessary(
+ appInfo, options, field, originHolder, instruction, original);
continue;
}
// Move the cursor back to $fieldPut
@@ -174,10 +171,8 @@
if (isReflectionMethod(dexItemFactory, invokedMethod)) {
DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
if (itemBasedString == null) {
- if (options.proguardConfiguration.isObfuscating()) {
- warnUndeterminedIdentifier(
- options.reporter, invokedMethod, origin, instruction, null);
- }
+ warnUndeterminedIdentifierIfNecessary(
+ appInfo, options, invokedMethod, originHolder, instruction, null);
continue;
}
DexType returnType = invoke.getReturnType();
@@ -222,20 +217,16 @@
for (int i = 0; i < ins.size(); i++) {
Value in = ins.get(i);
if (!in.isConstString()) {
- if (options.proguardConfiguration.isObfuscating()) {
- warnUndeterminedIdentifier(
- options.reporter, invokedMethod, origin, instruction, null);
- }
+ warnUndeterminedIdentifierIfNecessary(
+ appInfo, options, invokedMethod, originHolder, instruction, null);
continue;
}
DexString original = in.getConstInstruction().asConstString().getValue();
DexItemBasedString itemBasedString =
inferMemberOrTypeFromNameString(appInfo, original);
if (itemBasedString == null) {
- if (options.proguardConfiguration.isObfuscating()) {
- warnUndeterminedIdentifier(
- options.reporter, invokedMethod, origin, instruction, original);
- }
+ warnUndeterminedIdentifierIfNecessary(
+ appInfo, options, invokedMethod, originHolder, instruction, original);
continue;
}
// Move the cursor back to $invoke
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index 471b3f8..bf23f59 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -26,7 +26,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.TextPosition;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import java.util.List;
@@ -372,13 +372,24 @@
return new DexTypeList(types);
}
- public static void warnUndeterminedIdentifier(
- Reporter reporter,
+ public static void warnUndeterminedIdentifierIfNecessary(
+ AppInfo appInfo,
+ InternalOptions options,
DexItem member,
- Origin origin,
+ DexType originHolder,
Instruction instruction,
DexString original) {
assert member instanceof DexField || member instanceof DexMethod;
+ DexClass originClass = appInfo.definitionFor(originHolder);
+ // If the origin is a library class, it is out of developers' control.
+ if (originClass != null && originClass.isLibraryClass()) {
+ return;
+ }
+ // Undetermined identifiers matter only if minification is enabled.
+ if (!options.proguardConfiguration.isObfuscating()) {
+ return;
+ }
+ Origin origin = appInfo.originFor(originHolder);
String kind = member instanceof DexField ? "field" : "method";
String originalMessage = original == null ? "what identifier string flows to "
: "what '" + original.toString() + "' refers to, which flows to ";
@@ -392,6 +403,6 @@
? new StringDiagnostic(message, origin,
new TextPosition(0L, instruction.getPosition().line, 1))
: new StringDiagnostic(message, origin);
- reporter.warning(diagnostic);
+ options.reporter.warning(diagnostic);
}
}
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 061b9bf..8335cba 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,7 +5,7 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifier;
+import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
import com.android.tools.r8.Diagnostic;
@@ -1359,13 +1359,14 @@
}
private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
- Origin origin = appInfo.originFor(method.method.holder);
+ DexType originHolder = method.method.holder;
+ Origin origin = appInfo.originFor(originHolder);
IRCode code = method.buildIR(appInfo, options, origin);
code.instructionIterator().forEachRemaining(instr ->
- handleProguardReflectiveBehavior(instr, origin));
+ handleProguardReflectiveBehavior(instr, originHolder));
}
- private void handleProguardReflectiveBehavior(Instruction instruction, Origin origin) {
+ private void handleProguardReflectiveBehavior(Instruction instruction, DexType originHolder) {
if (!instruction.isInvokeMethod()) {
return;
}
@@ -1376,9 +1377,8 @@
}
DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
if (itemBasedString == null) {
- if (options.proguardConfiguration.isObfuscating()) {
- warnUndeterminedIdentifier(options.reporter, invokedMethod, origin, instruction, null);
- }
+ warnUndeterminedIdentifierIfNecessary(
+ appInfo, options, invokedMethod, originHolder, instruction, null);
return;
}
if (itemBasedString.basedOn instanceof DexType) {
diff --git a/src/test/examples/uninitializedfinal/UninitializedFinalFieldLeak.java b/src/test/examples/uninitializedfinal/UninitializedFinalFieldLeak.java
deleted file mode 100644
index 781d2b4..0000000
--- a/src/test/examples/uninitializedfinal/UninitializedFinalFieldLeak.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2018 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 uninitializedfinal;
-
-// Test that leaks an instance before its final field has been initialized to a thread that
-// reads that field. This tests that redundant field load elimination does not eliminate
-// field reads (even of final fields) that cross a monitor operation.
-public class UninitializedFinalFieldLeak {
-
- public static class PollingThread extends Thread {
- public int result = 0;
- UninitializedFinalFieldLeak f;
-
- PollingThread(UninitializedFinalFieldLeak f) {
- this.f = f;
- }
-
- // Read the field a number of times. Then lock on the object to await field initialization.
- public void run() {
- result += f.i;
- result += f.i;
- result += f.i;
- f.threadReadsDone = true;
- synchronized (f) {
- result += f.i;
- }
- // The right result is 42. Reading the uninitialized 0 three times and then
- // reading the initialized value. It is safe to remove the two redundant loads
- // before the monitor operation.
- System.out.println(result);
- }
- }
-
- public final int i;
- public volatile boolean threadReadsDone = false;
-
- public UninitializedFinalFieldLeak() throws InterruptedException {
- // Leak the object to a thread and start the thread with the lock on the object taken.
- // Then allow the other thread to run and read the uninitialized field.
- // Finally, initialize the field and release the lock.
- PollingThread t = new PollingThread(this);
- synchronized (this) {
- t.start();
- while (!threadReadsDone) {
- Thread.yield();
- }
- i = 42;
- }
- t.join();
- }
-
- public static void main(String[] args) throws InterruptedException {
- new UninitializedFinalFieldLeak();
- }
-}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 221d427..36f7594 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -45,11 +45,9 @@
"instancevariable.InstanceVariable",
"instanceofstring.InstanceofString",
"invoke.Invoke",
- "invokeempty.InvokeEmpty",
"jumbostring.JumboString",
"loadconst.LoadConst",
"loop.UdpServer",
- "nestedtrycatches.NestedTryCatches",
"newarray.NewArray",
"regalloc.RegAlloc",
"returns.Returns",
@@ -60,7 +58,9 @@
"throwing.Throwing",
"trivial.Trivial",
"trycatch.TryCatch",
+ "nestedtrycatches.NestedTryCatches",
"trycatchmany.TryCatchMany",
+ "invokeempty.InvokeEmpty",
"regress.Regress",
"regress2.Regress2",
"regress_37726195.Regress",
@@ -82,7 +82,6 @@
"enclosingmethod_proguarded.Main",
"interfaceinlining.Main",
"switchmaps.Switches",
- "uninitializedfinal.UninitializedFinalFieldLeak",
};
List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 5200a09..7b4f199 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -56,6 +56,8 @@
// Actually running Proguard should only be during development.
private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
+ // Actually running r8.jar in a forked process.
+ private static final boolean RUN_R8_JAR = System.getProperty("run_r8_jar") != null;
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -71,6 +73,13 @@
}
/**
+ * Check if tests should run R8 in a forked process when applicable.
+ */
+ protected boolean isRunR8Jar() {
+ return RUN_R8_JAR;
+ }
+
+ /**
* Write lines of text to a temporary file.
*/
protected Path writeTextToTempFile(String... lines) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 10f62a1..200a1e7 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -70,6 +70,7 @@
public class ToolHelper {
public static final String BUILD_DIR = "build/";
+ public static final String LIBS_DIR = BUILD_DIR + "libs/";
public static final String TESTS_DIR = "src/test/";
public static final String EXAMPLES_DIR = TESTS_DIR + "examples/";
public static final String EXAMPLES_ANDROID_O_DIR = TESTS_DIR + "examplesAndroidO/";
@@ -78,6 +79,8 @@
public static final String TESTS_BUILD_DIR = BUILD_DIR + "test/";
public static final String EXAMPLES_BUILD_DIR = TESTS_BUILD_DIR + "examples/";
public static final String EXAMPLES_KOTLIN_BUILD_DIR = TESTS_BUILD_DIR + "examplesKotlin/";
+ public static final String EXAMPLES_KOTLIN_RESOURCE_DIR =
+ TESTS_BUILD_DIR + "kotlinR8TestResources/";
public static final String EXAMPLES_ANDROID_N_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidN/";
public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
@@ -979,6 +982,12 @@
return forkJava(dir, R8.class, args);
}
+ public static ProcessResult forkR8Jar(Path dir, String... args)
+ throws IOException, InterruptedException {
+ String r8Jar = Paths.get(LIBS_DIR, "r8.jar").toAbsolutePath().toString();
+ return forkJavaWithJar(dir, r8Jar, Arrays.asList(args));
+ }
+
public static ProcessResult forkGenerateMainDexList(Path dir, List<String> args1, String... args2)
throws IOException, InterruptedException {
List<String> args = new ArrayList<>();
@@ -997,8 +1006,18 @@
return forkJava(dir, clazz, Arrays.asList(args));
}
+ private static ProcessResult forkJavaWithJar(Path dir, String jarPath, List<String> args)
+ throws IOException {
+ List<String> command = new ImmutableList.Builder<String>()
+ .add(getJavaExecutable())
+ .add("-jar").add(jarPath)
+ .addAll(args)
+ .build();
+ return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
+ }
+
private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
- throws IOException, InterruptedException {
+ throws IOException {
List<String> command = new ImmutableList.Builder<String>()
.add(getJavaExecutable())
.add("-cp").add(System.getProperty("java.class.path"))
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index e1431a6..062095c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -43,7 +43,7 @@
abstract boolean match(DexClass clazz);
}
- private static class Group extends LambdaOrGroup {
+ static class Group extends LambdaOrGroup {
final String pkg;
final String capture;
final int arity;
@@ -83,31 +83,32 @@
}
}
- private Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
+ private static Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
assertEquals(capture.isEmpty(), singletons != 0);
return new Group(pkg, capture, arity, KOTLIN_FUNCTION_IFACE_STR + arity, singletons);
}
- private Group kstyle(String pkg, int arity, int singletons) {
+ static Group kstyle(String pkg, int arity, int singletons) {
assertTrue(singletons != 0);
return kstyleImpl(pkg, "", arity, singletons);
}
- private Group kstyle(String pkg, String capture, int arity) {
+ private static Group kstyle(String pkg, String capture, int arity) {
assertFalse(capture.isEmpty());
return kstyleImpl(pkg, capture, arity, 0);
}
- private Group jstyleImpl(String pkg, String capture, int arity, String sam, int singletons) {
+ private static Group jstyleImpl(
+ String pkg, String capture, int arity, String sam, int singletons) {
assertTrue(capture.isEmpty() || singletons == 0);
return new Group(pkg, capture, arity, sam, singletons);
}
- private Group jstyle(String pkg, String capture, int arity, String sam) {
+ private static Group jstyle(String pkg, String capture, int arity, String sam) {
return jstyleImpl(pkg, capture, arity, sam, 0);
}
- private Group jstyle(String pkg, int arity, String sam, int singletons) {
+ private static Group jstyle(String pkg, int arity, String sam, int singletons) {
return jstyleImpl(pkg, "", arity, sam, singletons);
}
@@ -116,7 +117,7 @@
final String name;
final int arity;
- private Lambda(String pkg, String name, int arity) {
+ Lambda(String pkg, String name, int arity) {
this.pkg = pkg;
this.name = name;
this.arity = arity;
@@ -143,7 +144,15 @@
final List<DexClass> groups = new ArrayList<>();
Verifier(AndroidApp app) throws IOException, ExecutionException {
- this.dexInspector = new DexInspector(app);
+ this(new DexInspector(app));
+ }
+
+ Verifier(DexInspector dexInspector) {
+ this.dexInspector = dexInspector;
+ initGroupsAndLambdas();
+ }
+
+ private void initGroupsAndLambdas() {
dexInspector.forAllClasses(clazz -> {
DexClass dexClass = clazz.getDexClass();
if (isLambdaOrGroup(dexClass)) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
new file mode 100644
index 0000000..2beb777
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_KOTLIN_RESOURCE_DIR;
+import static com.android.tools.r8.kotlin.KotlinLambdaMergingTest.kstyle;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Group;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Lambda;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Verifier;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Test;
+
+public class KotlinxMetadataExtensionsServiceTest extends TestBase {
+
+ private void forkR8_kstyle_trivial(boolean allowAccessModification) throws Exception {
+ if (!isRunR8Jar()) {
+ return;
+ }
+ Path working = temp.getRoot().toPath();
+ Path kotlinJar =
+ Paths.get(EXAMPLES_KOTLIN_RESOURCE_DIR, "JAVA_8", "lambdas_kstyle_trivial.jar")
+ .toAbsolutePath();
+ Path output = working.resolve("classes.dex");
+ assertFalse(Files.exists(output));
+ Path proguardConfiguration = temp.newFile("test.conf").toPath();
+ List<String> lines = ImmutableList.of(
+ "-keepattributes Signature,InnerClasses,EnclosingMethod",
+ "-keep class **MainKt {",
+ " public static void main(...);",
+ "}",
+ "-printmapping",
+ "-dontobfuscate",
+ allowAccessModification ? "-allowaccessmodification" : ""
+ );
+ FileUtils.writeTextFile(proguardConfiguration, lines);
+ ProcessResult result = ToolHelper.forkR8Jar(working,
+ "--pg-conf", proguardConfiguration.toString(),
+ "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.O).toAbsolutePath().toString(),
+ kotlinJar.toString());
+ assertEquals(0, result.exitCode);
+ assertThat(result.stderr, not(containsString(
+ "No MetadataExtensions instances found in the classpath")));
+ assertTrue(Files.exists(output));
+
+ DexInspector inspector = new DexInspector(output);
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_kstyle_trivial";
+ verifier.assertLambdaGroups(
+ allowAccessModification ?
+ new Group[]{
+ kstyle("", 0, 4),
+ kstyle("", 1, 8),
+ kstyle("", 2, 2), // -\
+ kstyle("", 2, 5), // - 3 groups different by main method
+ kstyle("", 2, 4), // -/
+ kstyle("", 3, 2),
+ kstyle("", 22, 2)} :
+ new Group[]{
+ kstyle(pkg, 0, 2),
+ kstyle(pkg, 1, 4),
+ kstyle(pkg, 2, 5), // - 2 groups different by main method
+ kstyle(pkg, 2, 4), // -/
+ kstyle(pkg, 3, 2),
+ kstyle(pkg, 22, 2),
+ kstyle(pkg + "/inner", 0, 2),
+ kstyle(pkg + "/inner", 1, 4)}
+ );
+
+ verifier.assertLambdas(
+ allowAccessModification ?
+ new Lambda[]{
+ new Lambda(pkg, "MainKt$testStateless$6", 1) /* Banned for limited inlining */} :
+ new Lambda[]{
+ new Lambda(pkg, "MainKt$testStateless$6", 1), /* Banned for limited inlining */
+ new Lambda(pkg, "MainKt$testStateless$8", 2),
+ new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)}
+ );
+ }
+
+ @Test
+ public void testTrivialKs_allowAccessModification() throws Exception {
+ forkR8_kstyle_trivial(true);
+ }
+
+ @Test
+ public void testTrivialKs_notAllowAccessModification() throws Exception {
+ forkR8_kstyle_trivial(false);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index 9a9ca34..ec27895 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -21,8 +21,8 @@
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.List;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,7 +46,7 @@
allowAccessModification ? "-allowaccessmodification" : "",
!minify ? "-dontobfuscate" : "",
"-keep class " + mainClass.getCanonicalName() + " {",
- " public void main(java.lang.String[]);",
+ " public static void main(java.lang.String[]);",
"}",
keep + " class " + TestClass.class.getCanonicalName() + " {",
" *;",
@@ -97,7 +97,6 @@
doTest_keepAll(Shrinker.R8, null, false, false);
}
- @Ignore("b/92236970")
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void test_keepAll_R8Compat() throws Exception {
@@ -137,7 +136,7 @@
allowAccessModification ? "-allowaccessmodification" : "",
!minify ? "-dontobfuscate" : "",
"-keep class " + mainClass.getCanonicalName() + " {",
- " public void main(java.lang.String[]);",
+ " public static void main(java.lang.String[]);",
"}",
keep + " class " + TestClass.class.getCanonicalName() + " {",
" !public <methods>;",
@@ -188,7 +187,6 @@
doTest_keepNonPublic(Shrinker.R8, null, false, false);
}
- @Ignore("b/92236970")
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void test_keepNonPublic_R8Compat() throws Exception {
@@ -222,13 +220,13 @@
boolean minify) throws Exception {
Class mainClass = TestMain.class;
String keep = !minify ? "-keep" : "-keep,allowobfuscation";
- List<String> config = ImmutableList.of(
+ Iterable<String> config = ImmutableList.of(
"-printmapping",
repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
allowAccessModification ? "-allowaccessmodification" : "",
!minify ? "-dontobfuscate" : "",
"-keep class " + mainClass.getCanonicalName() + " {",
- " public void main(java.lang.String[]);",
+ " public static void main(java.lang.String[]);",
"}",
keep + " class " + TestClass.class.getCanonicalName() + " {",
" public <methods>;",
@@ -238,6 +236,13 @@
"}",
"-dontwarn java.lang.invoke.*"
);
+ if (isR8(shrinker)) {
+ config = Iterables.concat(config, ImmutableList.of(
+ "-neverinline class " + TestClass.class.getCanonicalName() + " {",
+ " * staticMethod();",
+ "}"
+ ));
+ }
AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
@@ -253,15 +258,11 @@
// Test an indirectly referred method.
staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
- if (isR8(shrinker)) {
- // Inlined.
- assertThat(staticMethod, not(isPresent()));
- } else {
- assertThat(staticMethod, isPresent());
- assertEquals(minify, staticMethod.isRenamed());
- boolean publicizeCondition = minify && repackagePrefix != null && allowAccessModification;
- assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
- }
+ assertThat(staticMethod, isPresent());
+ assertEquals(minify, staticMethod.isRenamed());
+ boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+ : minify && repackagePrefix != null && allowAccessModification;
+ assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
}
@Test
@@ -277,7 +278,6 @@
doTest_keepPublic(Shrinker.R8, null, false, false);
}
- @Ignore("b/92236970")
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void test_keepPublic_R8Compat() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index 693f02d..e3bffc6 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -44,7 +44,7 @@
}
protected AndroidApp runShrinkerRaw(
- Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
+ Shrinker mode, List<Class> programClasses, Iterable<String> proguadConfigs) throws Exception {
return runShrinkerRaw(
mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
}
@@ -111,10 +111,11 @@
protected AndroidApp runR8CompatRaw(
List<Class> programClasses, String proguardConfig) throws Exception {
CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder(true);
+ ToolHelper.allowTestProguardOptions(builder);
builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
programClasses.forEach(
clazz -> builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)));
- builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
+ builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
return ToolHelper.runR8(builder.build());
}