Merge "Add precise source information to Proguard rule parser"
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 283716b..7ea7379 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -97,7 +97,7 @@
break;
}
// TODO(b/111080693): we may need to collect all meaningful constraints.
- result = ConstraintWithTarget.min(result, state, appInfo);
+ result = ConstraintWithTarget.meet(result, state, appInfo);
}
return result;
}
@@ -179,11 +179,17 @@
*/
public enum Constraint {
// The ordinal values are important so please do not reorder.
- NEVER, // Never inline this.
- SAMECLASS, // Only inline this into methods with same holder.
- PACKAGE, // Only inline this into methods with holders from the same package.
- SUBCLASS, // Only inline this into methods with holders from a subclass in a different package.
- ALWAYS; // No restrictions for inlining this.
+ NEVER(1), // Never inline this.
+ SAMECLASS(2), // Inlineable into methods with same holder.
+ PACKAGE(4), // Inlineable into methods with holders from the same package.
+ SUBCLASS(8), // Inlineable into methods with holders from a subclass in a different package.
+ ALWAYS(16); // No restrictions for inlining this.
+
+ int value;
+
+ Constraint(int value) {
+ this.value = value;
+ }
static {
assert NEVER.ordinal() < SAMECLASS.ordinal();
@@ -192,8 +198,8 @@
assert SUBCLASS.ordinal() < ALWAYS.ordinal();
}
- static Constraint min(Constraint one, Constraint other) {
- return one.ordinal() < other.ordinal() ? one : other;
+ boolean isSet(int value) {
+ return (this.value & value) != 0;
}
}
@@ -292,26 +298,26 @@
: deriveConstraint(context, clazz, definition.accessFlags, appInfo);
}
- public static ConstraintWithTarget min(
+ public static ConstraintWithTarget meet(
ConstraintWithTarget one, ConstraintWithTarget other, AppInfoWithSubtyping appInfo) {
if (one.equals(other)) {
return one;
}
- if (one == NEVER || other == NEVER) {
- return NEVER;
- }
if (other.constraint.ordinal() < one.constraint.ordinal()) {
- return min(other, one, appInfo);
+ return meet(other, one, appInfo);
}
// From now on, one.constraint.ordinal() <= other.constraint.ordinal()
+ if (one == NEVER) {
+ return NEVER;
+ }
if (other == ALWAYS) {
return one;
}
- Constraint minConstraint = Constraint.min(one.constraint, other.constraint);
- assert minConstraint != Constraint.NEVER;
- assert minConstraint != Constraint.ALWAYS;
+ int constraint = one.constraint.value | other.constraint.value;
+ assert !Constraint.NEVER.isSet(constraint);
+ assert !Constraint.ALWAYS.isSet(constraint);
// SAMECLASS <= SAMECLASS, PACKAGE, SUBCLASS
- if (minConstraint == Constraint.SAMECLASS) {
+ if (Constraint.SAMECLASS.isSet(constraint)) {
assert one.constraint == Constraint.SAMECLASS;
if (other.constraint == Constraint.SAMECLASS) {
assert one.targetHolder != other.targetHolder;
@@ -330,14 +336,14 @@
return NEVER;
}
// PACKAGE <= PACKAGE, SUBCLASS
- if (minConstraint == Constraint.PACKAGE) {
+ if (Constraint.PACKAGE.isSet(constraint)) {
assert one.constraint == Constraint.PACKAGE;
if (other.constraint == Constraint.PACKAGE) {
assert one.targetHolder != other.targetHolder;
if (one.targetHolder.isSamePackage(other.targetHolder)) {
return one;
}
- // PACKAGE of x and PACKAGE of y can be satisfied together.
+ // PACKAGE of x and PACKAGE of y cannot be satisfied together.
return NEVER;
}
assert other.constraint == Constraint.SUBCLASS;
@@ -346,12 +352,12 @@
return one;
}
// TODO(b/111080693): towards finer-grained constraints, we need both.
- // Even though they're in different package, it is still inlinable to a class that is
- // in the same package of one's context and a sub type of other's context.
+ // The target method is still inlineable to methods with a holder from the same package of
+ // one's holder and a subtype of other's holder.
return NEVER;
}
// SUBCLASS <= SUBCLASS
- assert minConstraint == Constraint.SUBCLASS;
+ assert Constraint.SUBCLASS.isSet(constraint);
assert one.constraint == other.constraint;
assert one.targetHolder != other.targetHolder;
if (one.targetHolder.isSubtypeOf(other.targetHolder, appInfo)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index fe10637..129e9cb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -254,7 +254,7 @@
ConstraintWithTarget classConstraintWithTarget =
ConstraintWithTarget.deriveConstraint(
invocationContext, fieldHolder, fieldClass.accessFlags, appInfo);
- return ConstraintWithTarget.min(
+ return ConstraintWithTarget.meet(
fieldConstraintWithTarget, classConstraintWithTarget, appInfo);
}
return ConstraintWithTarget.NEVER;
@@ -276,7 +276,7 @@
ConstraintWithTarget classConstraintWithTarget =
ConstraintWithTarget.deriveConstraint(
invocationContext, methodHolder, methodClass.accessFlags, appInfo);
- return ConstraintWithTarget.min(
+ return ConstraintWithTarget.meet(
methodConstraintWithTarget, classConstraintWithTarget, appInfo);
}
}
@@ -316,7 +316,7 @@
ConstraintWithTarget.deriveConstraint(
invocationContext, methodHolder, methodClass.accessFlags, appInfo);
ConstraintWithTarget result =
- ConstraintWithTarget.min(methodConstraintWithTarget, classConstraintWithTarget, appInfo);
+ ConstraintWithTarget.meet(methodConstraintWithTarget, classConstraintWithTarget, appInfo);
if (result == ConstraintWithTarget.NEVER) {
return result;
}
@@ -329,7 +329,7 @@
methodConstraintWithTarget =
ConstraintWithTarget.deriveConstraint(
invocationContext, methodHolder, target.accessFlags, appInfo);
- result = ConstraintWithTarget.min(result, methodConstraintWithTarget, appInfo);
+ result = ConstraintWithTarget.meet(result, methodConstraintWithTarget, appInfo);
if (result == ConstraintWithTarget.NEVER) {
return result;
}
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index dbc48a3..c707968 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -67,7 +67,7 @@
}
private void updateConstraint(ConstraintWithTarget other) {
- constraint = ConstraintWithTarget.min(constraint, other, appInfo);
+ constraint = ConstraintWithTarget.meet(constraint, other, appInfo);
}
// Used to signal that the result is ready, such that we do not need to visit all instructions of
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index b4e6ae2..d177b36 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -153,14 +153,9 @@
addRule(clazz, feature, 0);
}
- public void addNonClassMapping(String name, String feature) throws FeatureMappingException {
- if (parseNonClassRules.containsKey(name)) {
- throw new FeatureMappingException(
- "Non-code files with the same name present in multiple feature splits. " +
- "File '" + name + "' present in both '" + feature + "' and '" +
- parseNonClassRules.get(name) + "'.");
- }
- parseNonClassRules.put(name, feature);
+ public void addNonClassMapping(String name, String feature) {
+ // If a non-class file is present in multiple features put the resource in the base.
+ parseNonClassRules.put(name, parseNonClassRules.containsKey(name) ? baseName : feature);
}
FeatureClassMapping(List<String> lines) throws FeatureMappingException {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index bc8bd8c..5848396 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -39,8 +39,6 @@
import java.util.zip.ZipOutputStream;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.internal.runners.statements.ExpectException;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
public class DexSplitterTests {
@@ -206,19 +204,6 @@
}
}
- private void validateNonClassOutput(Path base, Path feature) throws IOException {
- byte[] contents = Files.readAllBytes(Paths.get(TEXT_FILE));
- byte[] contents2 = new byte[contents.length * 2];
- System.arraycopy(contents, 0, contents2, 0, contents.length);
- System.arraycopy(contents, 0, contents2, contents.length, contents.length);
- Path baseTextFile = base.resolve("dexsplitsample/TextFile.txt");
- Path featureTextFile = feature.resolve("dexsplitsample/TextFile2.txt");
- assert Files.exists(baseTextFile);
- assert Files.exists(featureTextFile);
- assert Arrays.equals(Files.readAllBytes(baseTextFile), contents);
- assert Arrays.equals(Files.readAllBytes(featureTextFile), contents2);
- }
-
private Path createSplitSpec() throws FileNotFoundException, UnsupportedEncodingException {
Path splitSpec = temp.getRoot().toPath().resolve("split_spec");
try (PrintWriter out = new PrintWriter(splitSpec.toFile(), "UTF-8")) {
@@ -392,12 +377,13 @@
ZipOutputStream inputZipStream = new ZipOutputStream(Files.newOutputStream(inputZip));
String name = "dexsplitsample/TextFile.txt";
inputZipStream.putNextEntry(new ZipEntry(name));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ byte[] fileBytes = Files.readAllBytes(Paths.get(TEXT_FILE));
+ inputZipStream.write(fileBytes);
inputZipStream.closeEntry();
name = "dexsplitsample/TextFile2.txt";
inputZipStream.putNextEntry(new ZipEntry(name));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ inputZipStream.write(fileBytes);
+ inputZipStream.write(fileBytes);
inputZipStream.closeEntry();
inputZipStream.close();
Path output = temp.newFolder().toPath().resolve("output");
@@ -407,14 +393,14 @@
ZipOutputStream baseStream = new ZipOutputStream(Files.newOutputStream(baseJar));
name = "dexsplitsample/TextFile.txt";
baseStream.putNextEntry(new ZipEntry(name));
- baseStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ baseStream.write(fileBytes);
baseStream.closeEntry();
baseStream.close();
ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar));
name = "dexsplitsample/TextFile2.txt";
featureStream.putNextEntry(new ZipEntry(name));
- featureStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
- featureStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ featureStream.write(fileBytes);
+ featureStream.write(fileBytes);
featureStream.closeEntry();
featureStream.close();
Options options = new Options();
@@ -426,33 +412,43 @@
DexSplitter.run(options);
Path baseDir = output.resolve("base");
Path featureDir = output.resolve("feature1");
- validateNonClassOutput(baseDir, featureDir);
+ byte[] contents = fileBytes;
+ byte[] contents2 = new byte[contents.length * 2];
+ System.arraycopy(contents, 0, contents2, 0, contents.length);
+ System.arraycopy(contents, 0, contents2, contents.length, contents.length);
+ Path baseTextFile = baseDir.resolve("dexsplitsample/TextFile.txt");
+ Path featureTextFile = featureDir.resolve("dexsplitsample/TextFile2.txt");
+ assert Files.exists(baseTextFile);
+ assert Files.exists(featureTextFile);
+ assert Arrays.equals(Files.readAllBytes(baseTextFile), contents);
+ assert Arrays.equals(Files.readAllBytes(featureTextFile), contents2);
}
- @Test(expected = FeatureMappingException.class)
+ @Test
public void splitDuplicateNonClassFiles()
throws IOException, CompilationFailedException, FeatureMappingException {
Path inputZip = temp.getRoot().toPath().resolve("input-zip-with-non-class-files.jar");
ZipOutputStream inputZipStream = new ZipOutputStream(Files.newOutputStream(inputZip));
String name = "dexsplitsample/TextFile.txt";
inputZipStream.putNextEntry(new ZipEntry(name));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ byte[] fileBytes = Files.readAllBytes(Paths.get(TEXT_FILE));
+ inputZipStream.write(fileBytes);
inputZipStream.closeEntry();
inputZipStream.close();
Path output = temp.newFolder().toPath().resolve("output");
Files.createDirectory(output);
Path featureJar = temp.getRoot().toPath().resolve("feature1.jar");
Path feature2Jar = temp.getRoot().toPath().resolve("feature2.jar");
- ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(feature2Jar));
+ ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar));
name = "dexsplitsample/TextFile.txt";
featureStream.putNextEntry(new ZipEntry(name));
- featureStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ featureStream.write(fileBytes);
featureStream.closeEntry();
featureStream.close();
- ZipOutputStream feature2Stream = new ZipOutputStream(Files.newOutputStream(featureJar));
+ ZipOutputStream feature2Stream = new ZipOutputStream(Files.newOutputStream(feature2Jar));
name = "dexsplitsample/TextFile.txt";
feature2Stream.putNextEntry(new ZipEntry(name));
- feature2Stream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ feature2Stream.write(fileBytes);
feature2Stream.closeEntry();
feature2Stream.close();
Options options = new Options();
@@ -462,5 +458,15 @@
options.addFeatureJar(featureJar.toString());
options.setSplitNonClassResources(true);
DexSplitter.run(options);
+ Path baseDir = output.resolve("base");
+ Path feature1Dir = output.resolve("feature1");
+ Path feature2Dir = output.resolve("feature2");
+ Path baseTextFile = baseDir.resolve("dexsplitsample/TextFile.txt");
+ Path feature1TextFile = feature1Dir.resolve("dexsplitsample/TextFile2.txt");
+ Path feature2TextFile = feature2Dir.resolve("dexsplitsample/TextFile2.txt");
+ assert !Files.exists(feature1TextFile);
+ assert !Files.exists(feature2TextFile);
+ assert Files.exists(baseTextFile);
+ assert Arrays.equals(Files.readAllBytes(baseTextFile), fileBytes);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
index f3dbe46..33994b1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
@@ -52,7 +52,7 @@
}
private ConstraintWithTarget meet(ConstraintWithTarget e1, ConstraintWithTarget e2) {
- return ConstraintWithTarget.min(e1, e2, appInfo);
+ return ConstraintWithTarget.meet(e1, e2, appInfo);
}
@Test