Add Proguard-map ID to marker.
Bug: 70040153
Change-Id: I35c50c76d0761c5785f61a6a044ce991b378beda
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 186caee..64bdee0 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -169,10 +169,13 @@
if (options.hasMarker()) {
return options.getMarker();
}
- Marker marker = new Marker(Tool.R8)
- .setVersion(Version.LABEL)
- .setCompilationMode(options.debug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
- .setMinApi(options.minApiLevel);
+ Marker marker =
+ new Marker(Tool.R8)
+ .setVersion(Version.LABEL)
+ .setCompilationMode(options.debug ? CompilationMode.DEBUG : CompilationMode.RELEASE);
+ if (!options.isGeneratingClassFiles()) {
+ marker.setMinApi(options.minApiLevel);
+ }
if (Version.isDev()) {
marker.setSha1(VersionProperties.INSTANCE.getSha());
}
@@ -191,6 +194,7 @@
throws ExecutionException {
try {
Marker marker = getMarker(options);
+ assert marker != null;
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(
application,
@@ -206,7 +210,7 @@
new ApplicationWriter(
application,
options,
- marker == null ? null : Collections.singletonList(marker),
+ Collections.singletonList(marker),
deadCode,
graphLense,
namingLens,
@@ -632,8 +636,7 @@
namingLens,
options.lineNumberOptimization == LineNumberOptimization.OFF);
timing.end();
- proguardMapSupplier =
- ProguardMapSupplier.fromClassNameMapper(classNameMapper, options.minApiLevel);
+ proguardMapSupplier = ProguardMapSupplier.fromClassNameMapper(classNameMapper, options);
// If a method filter is present don't produce output since the application is likely partial.
if (options.hasMethodsFilter()) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 24e82a7..6ba7802 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -64,6 +64,7 @@
public final NamingLens namingLens;
public final String proguardSeedsData;
public final InternalOptions options;
+ public List<Marker> markers;
public List<DexString> markerStrings;
public DexIndexedConsumer programConsumer;
public final ProguardMapSupplier proguardMapSupplier;
@@ -160,12 +161,7 @@
this.application = application;
assert options != null;
this.options = options;
- if (markers != null && !markers.isEmpty()) {
- this.markerStrings = new ArrayList<>();
- for (Marker marker : markers) {
- this.markerStrings.add(application.dexItemFactory.createString(marker.toString()));
- }
- }
+ this.markers = markers;
this.deadCode = deadCode;
this.graphLense = graphLense;
this.namingLens = namingLens;
@@ -195,12 +191,29 @@
public void write(ExecutorService executorService) throws IOException, ExecutionException {
application.timing.begin("DexApplication.write");
+ ProguardMapSupplier.ProguardMapAndId proguardMapAndId = null;
+ if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
+ proguardMapAndId = proguardMapSupplier.getProguardMapAndId();
+ }
+
+ // If we do have a map then we're called from R8. In that case we have exactly one marker.
+ assert proguardMapAndId == null || (markers != null && markers.size() == 1);
+
+ if (markers != null && !markers.isEmpty()) {
+ if (proguardMapAndId != null) {
+ markers.get(0).setPgMapId(proguardMapAndId.id);
+ }
+ markerStrings = new ArrayList<>(markers.size());
+ for (Marker marker : markers) {
+ markerStrings.add(application.dexItemFactory.createString(marker.toString()));
+ }
+ }
try {
insertAttributeAnnotations();
application.dexItemFactory.sort(namingLens);
- assert this.markerStrings == null
- || this.markerStrings.isEmpty()
+ assert markers == null
+ || markers.isEmpty()
|| application.dexItemFactory.extractMarker() != null;
SortAnnotations sortAnnotations = new SortAnnotations();
@@ -288,7 +301,7 @@
namingLens,
options,
deadCode,
- proguardMapSupplier,
+ proguardMapAndId == null ? null : proguardMapAndId.map,
proguardSeedsData);
} finally {
application.timing.end();
@@ -301,7 +314,7 @@
NamingLens namingLens,
InternalOptions options,
String deadCode,
- ProguardMapSupplier proguardMapSupplier,
+ String proguardMapContent,
String proguardSeedsData) {
if (options.configurationConsumer != null) {
ExceptionUtils.withConsumeResourceHandler(
@@ -312,15 +325,11 @@
ExceptionUtils.withConsumeResourceHandler(
options.reporter, options.usageInformationConsumer, deadCode);
}
- // Write the proguard map file after writing the dex files, as the map writer traverses
- // the DexProgramClass structures, which are destructively updated during dex file writing.
- if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
- String content = proguardMapSupplier.get();
- if (content != null) {
- ExceptionUtils.withConsumeResourceHandler(
- options.reporter, options.proguardMapConsumer, content);
+ if (proguardMapContent != null) {
+ ExceptionUtils.withConsumeResourceHandler(
+ options.reporter, options.proguardMapConsumer, proguardMapContent);
}
- }
+
if (options.proguardSeedsConsumer != null && proguardSeedsData != null) {
ExceptionUtils.withConsumeResourceHandler(
options.reporter, options.proguardSeedsConsumer, proguardSeedsData);
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 3d977f6..66a2d463 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -21,6 +21,7 @@
public static final String MIN_API = "min-api";
public static final String SHA1 = "sha-1";
public static final String COMPILATION_MODE = "compilation-mode";
+ public static final String PG_MAP_ID = "pg-map-id";
public enum Tool {D8, R8}
@@ -93,6 +94,16 @@
return this;
}
+ public String getPgMapId() {
+ return jsonObject.get(PG_MAP_ID).getAsString();
+ }
+
+ public Marker setPgMapId(String pgMapId) {
+ assert !jsonObject.has(PG_MAP_ID);
+ jsonObject.addProperty(PG_MAP_ID, pgMapId);
+ return this;
+ }
+
@Override
public String toString() {
// In order to make printing of markers deterministic we sort the entries by key.
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 05120db..8d55a59 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -70,7 +70,7 @@
private final GraphLense graphLense;
private final NamingLens namingLens;
private final InternalOptions options;
- private final String markerString;
+ private final Marker marker;
public final ProguardMapSupplier proguardMapSupplier;
public final String deadCode;
@@ -89,7 +89,8 @@
this.graphLense = graphLense;
this.namingLens = namingLens;
this.options = options;
- this.markerString = marker == null ? null : marker.toString();
+ assert marker != null;
+ this.marker = marker;
this.proguardMapSupplier = proguardMapSupplier;
this.deadCode = deadCode;
this.proguardSeedsData = proguardSeedsData;
@@ -106,9 +107,17 @@
private void writeApplication(ClassFileConsumer consumer, ExecutorService executor)
throws IOException {
+ ProguardMapSupplier.ProguardMapAndId proguardMapAndId = null;
+ if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
+ proguardMapAndId = proguardMapSupplier.getProguardMapAndId();
+ if (proguardMapAndId != null) {
+ marker.setPgMapId(proguardMapAndId.id);
+ }
+ }
+ String markerString = marker.toString();
for (DexProgramClass clazz : application.classes()) {
if (clazz.getSynthesizedFrom().isEmpty()) {
- writeClass(clazz, consumer);
+ writeClass(clazz, consumer, markerString);
} else {
throw new Unimplemented("No support for synthetics in the Java bytecode backend.");
}
@@ -119,16 +128,14 @@
namingLens,
options,
deadCode,
- proguardMapSupplier,
+ proguardMapAndId == null ? null : proguardMapAndId.map,
proguardSeedsData);
}
- private void writeClass(DexProgramClass clazz, ClassFileConsumer consumer) throws IOException {
+ private void writeClass(DexProgramClass clazz, ClassFileConsumer consumer, String markerString) {
ClassWriter writer = new ClassWriter(0);
- if (markerString != null) {
- int index = writer.newConst(markerString);
- assert index == MARKER_STRING_CONSTANT_POOL_INDEX;
- }
+ int markerStringPoolIndex = writer.newConst(markerString);
+ assert markerStringPoolIndex == MARKER_STRING_CONSTANT_POOL_INDEX;
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null);
int version = getClassFileVersion(clazz);
int access = clazz.accessFlags.getAsCfAccessFlags();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 5ee5891..3b18383 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -5,7 +5,10 @@
import com.android.tools.r8.Version;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.VersionProperties;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -18,66 +21,84 @@
public static final String MARKER_KEY_COMPILER_VERSION = "compiler_version";
public static final String MARKER_KEY_COMPILER_HASH = "compiler_hash";
public static final String MARKER_KEY_MIN_API = "min_api";
+ public static final String MARKER_KEY_PG_MAP_ID = "pg_map_id";
+
+ public static int PG_MAP_ID_LENGTH = 7;
public static ProguardMapSupplier fromClassNameMapper(
- ClassNameMapper classNameMapper, int minApiLevel) {
- return new ProguardMapSupplier(classNameMapper, minApiLevel);
+ ClassNameMapper classNameMapper, InternalOptions options) {
+ return new ProguardMapSupplier(true, classNameMapper, null, null, options);
}
public static ProguardMapSupplier fromNamingLens(
- NamingLens namingLens, DexApplication dexApplication, int minApiLevel) {
- return new ProguardMapSupplier(namingLens, dexApplication, minApiLevel);
+ NamingLens namingLens, DexApplication dexApplication, InternalOptions options) {
+ return new ProguardMapSupplier(false, null, namingLens, dexApplication, options);
}
- private ProguardMapSupplier(ClassNameMapper classNameMapper, int minApiLevel) {
- this.useClassNameMapper = true;
+ public static class ProguardMapAndId {
+ public final String map;
+ public final String id;
+
+ ProguardMapAndId(String map, String id) {
+ assert map != null && id != null;
+ this.map = map;
+ this.id = id;
+ }
+ }
+
+ public ProguardMapSupplier(
+ boolean useClassNameMapper,
+ ClassNameMapper classNameMapper,
+ NamingLens namingLens,
+ DexApplication application,
+ InternalOptions options) {
+ this.useClassNameMapper = useClassNameMapper;
this.classNameMapper = classNameMapper;
- this.namingLens = null;
- this.application = null;
- this.minApiLevel = minApiLevel;
- }
-
- private ProguardMapSupplier(
- NamingLens namingLens, DexApplication dexApplication, int minApiLevel) {
- this.useClassNameMapper = false;
- this.classNameMapper = null;
this.namingLens = namingLens;
- this.application = dexApplication;
- this.minApiLevel = minApiLevel;
+ this.application = application;
+ this.minApiLevel = options.isGeneratingClassFiles() ? null : options.minApiLevel;
}
private final boolean useClassNameMapper;
private final ClassNameMapper classNameMapper;
private final NamingLens namingLens;
private final DexApplication application;
- private final int minApiLevel;
+ private final Integer minApiLevel;
- public String get() {
+ public ProguardMapAndId getProguardMapAndId() {
String body = getBody();
if (body == null || body.trim().length() == 0) {
return null;
}
- String shaLine = "";
- if (Version.isDev()) {
- shaLine = "# " + MARKER_KEY_COMPILER_HASH + ": " + VersionProperties.INSTANCE.getSha() + "\n";
+ // Algorithm:
+ // Hash of the non-whitespace codepoints of the input string.
+ Hasher hasher = Hashing.murmur3_32().newHasher();
+ body.codePoints().filter(c -> !Character.isWhitespace(c)).forEach(hasher::putInt);
+ String proguardMapId = hasher.hash().toString().substring(0, PG_MAP_ID_LENGTH);
+
+ StringBuilder builder = new StringBuilder();
+ builder.append(
+ "# "
+ + MARKER_KEY_COMPILER
+ + ": "
+ + MARKER_VALUE_COMPILER
+ + "\n"
+ + "# "
+ + MARKER_KEY_COMPILER_VERSION
+ + ": "
+ + Version.LABEL
+ + "\n");
+ if (minApiLevel != null) {
+ builder.append("# " + MARKER_KEY_MIN_API + ": " + minApiLevel + "\n");
}
- return "# "
- + MARKER_KEY_COMPILER
- + ": "
- + MARKER_VALUE_COMPILER
- + "\n"
- + "# "
- + MARKER_KEY_COMPILER_VERSION
- + ": "
- + Version.LABEL
- + "\n"
- + "# "
- + MARKER_KEY_MIN_API
- + ": "
- + minApiLevel
- + "\n"
- + shaLine
- + body;
+ if (Version.isDev()) {
+ builder.append(
+ "# " + MARKER_KEY_COMPILER_HASH + ": " + VersionProperties.INSTANCE.getSha() + "\n");
+ }
+ builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + proguardMapId + "\n");
+ builder.append(body);
+
+ return new ProguardMapAndId(builder.toString(), proguardMapId);
}
private String getBody() {
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
index 6a47607..948ab96 100644
--- a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -14,14 +14,22 @@
import org.junit.Test;
public class ExtractMarkerTest {
+ private static final String CLASS_FILE =
+ ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
+
+ private static void verifyMarker(Marker marker, Tool tool) {
+ assertEquals(tool, marker.getTool());
+ assertEquals(Version.LABEL, marker.getVersion());
+ assertEquals(CompilationMode.DEBUG.toString().toLowerCase(), marker.getCompilationMode());
+ }
@Test
public void extractMarkerTestDex() throws CompilationFailedException {
- String classFile = ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
boolean[] testExecuted = {false};
+
D8.run(
D8Command.builder()
- .addProgramFiles(Paths.get(classFile))
+ .addProgramFiles(Paths.get(CLASS_FILE))
.setProgramConsumer(
new DexIndexedConsumer.ForwardingConsumer(null) {
@Override
@@ -39,11 +47,7 @@
} catch (Exception e) {
throw new RuntimeException(e);
}
- assertEquals(Tool.D8, marker.getTool());
- assertEquals(Version.LABEL, marker.getVersion());
- assertEquals(
- CompilationMode.DEBUG.toString().toLowerCase(),
- marker.getCompilationMode());
+ verifyMarker(marker, Tool.D8);
testExecuted[0] = true;
}
})
@@ -53,11 +57,10 @@
@Test
public void extractMarkerTestCf() throws CompilationFailedException {
- String classFile = ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
boolean[] testExecuted = {false};
R8.run(
R8Command.builder()
- .addProgramFiles(Paths.get(classFile))
+ .addProgramFiles(Paths.get(CLASS_FILE))
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.setMode(CompilationMode.DEBUG)
.setDisableTreeShaking(true)
@@ -75,11 +78,7 @@
} catch (Exception e) {
throw new RuntimeException(e);
}
- assertEquals(Tool.R8, marker.getTool());
- assertEquals(Version.LABEL, marker.getVersion());
- assertEquals(
- CompilationMode.DEBUG.toString().toLowerCase(),
- marker.getCompilationMode());
+ verifyMarker(marker, Tool.R8);
testExecuted[0] = true;
}
})
diff --git a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
index 6310054..c5961e8 100644
--- a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
@@ -6,55 +6,126 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.VersionProperties;
import java.nio.file.Paths;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
public class ProguardMapMarkerTest {
+ private static final int EXPECTED_NUMBER_OF_KEYS_DEX = 5;
+ private static final int EXPECTED_NUMBER_OF_KEYS_CF = 4;
+ private static final String CLASS_FILE =
+ ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
+
@Test
public void proguardMapMarkerTest24() throws CompilationFailedException {
- proguardMapMarkerTest(AndroidApiLevel.N);
+ proguardMapMarkerTestDex(AndroidApiLevel.N);
}
@Test
public void proguardMapMarkerTest26() throws CompilationFailedException {
- proguardMapMarkerTest(AndroidApiLevel.O);
+ proguardMapMarkerTestDex(AndroidApiLevel.O);
}
- private void proguardMapMarkerTest(AndroidApiLevel minApiLevel) throws CompilationFailedException {
- String classFile = ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
+ private static class ProguardMapIds {
+ String fromProgram = null;
+ String fromMap = null;
+ }
+
+ private void proguardMapMarkerTestDex(AndroidApiLevel minApiLevel)
+ throws CompilationFailedException {
+ ProguardMapIds proguardMapIds = new ProguardMapIds();
R8.run(
R8Command.builder()
- .addProgramFiles(Paths.get(classFile))
+ .addProgramFiles(Paths.get(CLASS_FILE))
+ .setDisableTreeShaking(true)
.setProgramConsumer(
- new DexIndexedConsumer() {
+ new DexIndexedConsumer.ForwardingConsumer(null) {
@Override
public void accept(
int fileIndex,
ByteDataView data,
Set<String> descriptors,
- DiagnosticsHandler handler) {}
-
- @Override
- public void finished(DiagnosticsHandler handler) {}
+ DiagnosticsHandler handler) {
+ Marker marker;
+ try {
+ Collection<Marker> markers =
+ ExtractMarker.extractMarkerFromDexProgramData(data.copyByteData());
+ assertEquals(1, markers.size());
+ marker = markers.iterator().next();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ proguardMapIds.fromProgram = marker.getPgMapId();
+ }
})
.addLibraryFiles(ToolHelper.getAndroidJar(minApiLevel))
.setMinApiLevel(minApiLevel.getLevel())
.setProguardMapConsumer(
(proguardMap, handler) -> {
- verifyMarkers(proguardMap, minApiLevel.getLevel());
+ proguardMapIds.fromMap =
+ verifyMarkersGetPgMapId(
+ proguardMap, minApiLevel.getLevel(), EXPECTED_NUMBER_OF_KEYS_DEX);
})
.build());
+ verifyProguardMapIds(proguardMapIds);
}
- private static void verifyMarkers(String proguardMap, int minApiLevel) {
+ private void verifyProguardMapIds(ProguardMapIds proguardMapIds) {
+ assertTrue(
+ proguardMapIds.fromProgram != null
+ && proguardMapIds.fromProgram.length() == ProguardMapSupplier.PG_MAP_ID_LENGTH);
+ assertTrue(proguardMapIds.fromMap != null);
+ assertEquals(proguardMapIds.fromMap, proguardMapIds.fromProgram);
+ }
+
+ @Test
+ public void proguardMapMarkerTestCf() throws CompilationFailedException {
+ ProguardMapIds buildIds = new ProguardMapIds();
+ R8.run(
+ R8Command.builder()
+ .addProgramFiles(Paths.get(CLASS_FILE))
+ .setDisableTreeShaking(true)
+ .setProgramConsumer(
+ new ClassFileConsumer.ForwardingConsumer(null) {
+ @Override
+ public void accept(
+ ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+ Marker marker;
+ try {
+ Collection<Marker> markers =
+ ExtractMarker.extractMarkerFromClassProgramData(data.copyByteData());
+ assertEquals(1, markers.size());
+ marker = markers.iterator().next();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ buildIds.fromProgram = marker.getPgMapId();
+ }
+ })
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+ .setProguardMapConsumer(
+ (proguardMap, handler) -> {
+ buildIds.fromMap =
+ verifyMarkersGetPgMapId(proguardMap, null, EXPECTED_NUMBER_OF_KEYS_CF);
+ })
+ .build());
+ verifyProguardMapIds(buildIds);
+ }
+
+ private static String verifyMarkersGetPgMapId(
+ String proguardMap, Integer minApiLevel, int expectedNumberOfKeys) {
String[] lines = proguardMap.split("\n");
Set<String> keysFound = new HashSet<>();
+ String proguardMapId = null;
for (String line : lines) {
if (!line.startsWith("#")) {
continue;
@@ -71,14 +142,19 @@
} else if (key.equals(ProguardMapSupplier.MARKER_KEY_COMPILER_VERSION)) {
assertEquals(Version.LABEL, value);
} else if (key.equals(ProguardMapSupplier.MARKER_KEY_MIN_API)) {
- assertEquals(minApiLevel, Integer.parseInt(value));
+ assertNotNull(minApiLevel);
+ assertEquals(minApiLevel.intValue(), Integer.parseInt(value));
} else if (key.equals(ProguardMapSupplier.MARKER_KEY_COMPILER_HASH)) {
assertEquals(VersionProperties.INSTANCE.getSha(), value);
+ } else if (key.equals(ProguardMapSupplier.MARKER_KEY_PG_MAP_ID)) {
+ proguardMapId = value;
} else {
continue;
}
assertFalse(keysFound.contains(key));
keysFound.add(key);
}
+ assertEquals(expectedNumberOfKeys, keysFound.size());
+ return proguardMapId;
}
}