Merge "Rename merged exception classes in DeadCodeRemover"
diff --git a/build.gradle b/build.gradle
index 7720cf3..7f41caa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -61,7 +61,10 @@
'-Xep:MissingDefault:ERROR',
'-Xep:MultipleTopLevelClasses:ERROR',
'-Xep:NarrowingCompoundAssignment:ERROR',
- '-Xep:BoxedPrimitiveConstructor:ERROR']
+ '-Xep:BoxedPrimitiveConstructor:ERROR',
+ '-Xep:LogicalAssignment:ERROR',
+ '-Xep:FloatCast:ERROR',
+ '-Xep:ReturnValueIgnored:ERROR']
apply from: 'copyAdditionalJctfCommonFiles.gradle'
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index b3d59fb..af83158 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -17,6 +17,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -60,7 +61,7 @@
assert isArchive(archive);
origin = new PathOrigin(archive);
try {
- zipFile = new ZipFile(archive.toFile());
+ zipFile = new ZipFile(archive.toFile(), StandardCharsets.UTF_8);
} catch (IOException e) {
if (!Files.exists(archive)) {
throw new NoSuchFileException(archive.toString());
diff --git a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
index baac9bc..425b3c3 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
@@ -16,6 +16,7 @@
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -60,7 +61,10 @@
public static ArchiveProgramResourceProvider fromArchive(
Path archive, Predicate<String> include) {
- return fromSupplier(new PathOrigin(archive), () -> new ZipFile(archive.toFile()), include);
+ return fromSupplier(
+ new PathOrigin(archive),
+ () -> new ZipFile(archive.toFile(), StandardCharsets.UTF_8),
+ include);
}
public static ArchiveProgramResourceProvider fromSupplier(
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 9e5d36a..93c0126 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -3,16 +3,79 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.FileUtils.isArchive;
+
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
public class D8CommandParser extends BaseCompilerCommandParser {
+ static class OrderedClassFileResourceProvider implements ClassFileResourceProvider {
+ static class Builder {
+ private final ImmutableList.Builder<ClassFileResourceProvider> builder =
+ ImmutableList.builder();
+ boolean empty = true;
+
+ OrderedClassFileResourceProvider build() {
+ return new OrderedClassFileResourceProvider(builder.build());
+ }
+
+ Builder addClassFileResourceProvider(ClassFileResourceProvider provider) {
+ builder.add(provider);
+ empty = false;
+ return this;
+ }
+
+ boolean isEmpty() {
+ return empty;
+ }
+ }
+
+ final List<ClassFileResourceProvider> providers;
+ final Set<String> descriptors = Sets.newHashSet();
+
+ private OrderedClassFileResourceProvider(ImmutableList<ClassFileResourceProvider> providers) {
+ this.providers = providers;
+ // Collect all descriptors that can be provided.
+ this.providers.forEach(provider -> this.descriptors.addAll(provider.getClassDescriptors()));
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Set<String> getClassDescriptors() {
+ return descriptors;
+ }
+
+ @Override
+ public ProgramResource getProgramResource(String descriptor) {
+ // Search the providers in order. Return the program resource from the first provider that
+ // can provide it.
+ for (ClassFileResourceProvider provider : providers) {
+ if (provider.getClassDescriptors().contains(descriptor)) {
+ return provider.getProgramResource(descriptor);
+ }
+ }
+ return null;
+ }
+ }
+
public static void main(String[] args) throws CompilationFailedException {
D8Command command = parse(args, Origin.root()).build();
if (command.isPrintHelp()) {
@@ -76,6 +139,8 @@
Path outputPath = null;
OutputMode outputMode = null;
boolean hasDefinedApiLevel = false;
+ OrderedClassFileResourceProvider.Builder classpathBuilder =
+ OrderedClassFileResourceProvider.builder();
String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
try {
for (int i = 0; i < expandedArgs.length; i++) {
@@ -115,7 +180,22 @@
} else if (arg.equals("--lib")) {
builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--classpath")) {
- builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
+ Path file = Paths.get(expandedArgs[++i]);
+ try {
+ if (!Files.exists(file)) {
+ throw new NoSuchFileException(file.toString());
+ }
+ if (isArchive(file)) {
+ classpathBuilder.addClassFileResourceProvider(new ArchiveClassFileProvider(file));
+ } else if (Files.isDirectory(file)) {
+ classpathBuilder.addClassFileResourceProvider(
+ DirectoryClassFileProvider.fromDirectory(file));
+ } else {
+ throw new CompilationError("Unsupported classpath file type", new PathOrigin(file));
+ }
+ } catch (IOException e) {
+ builder.error(new ExceptionDiagnostic(e, new PathOrigin(file)));
+ }
} else if (arg.equals("--main-dex-list")) {
builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--optimize-multidex-for-linearalloc")) {
@@ -140,6 +220,9 @@
builder.addProgramFiles(Paths.get(arg));
}
}
+ if (!classpathBuilder.isEmpty()) {
+ builder.addClasspathResourceProvider(classpathBuilder.build());
+ }
if (compilationMode != null) {
builder.setMode(compilationMode);
}
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index 9b1599f..7b17ec8 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -33,6 +32,7 @@
private Path proguardMapFile = null;
private boolean useSmali = false;
private boolean allInfo = false;
+ private boolean useIr;
@Override
Builder self() {
@@ -63,6 +63,11 @@
return this;
}
+ public Builder setUseIr(boolean useIr) {
+ this.useIr = useIr;
+ return this;
+ }
+
@Override
protected DisassembleCommand makeCommand() {
// If printing versions ignore everything else.
@@ -74,24 +79,26 @@
getOutputPath(),
proguardMapFile == null ? null : StringResource.fromFile(proguardMapFile),
allInfo,
- useSmali);
+ useSmali,
+ useIr);
}
}
- static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: disasm [options] <input-files>",
- " where <input-files> are dex files",
- " and options are:",
- " --all # Include all information in disassembly.",
- " --smali # Disassemble using smali syntax.",
- " --pg-map <file> # Proguard map <file> for mapping names.",
- " --output # Specify a file or directory to write to.",
- " --version # Print the version of r8.",
- " --help # Print this message."));
-
+ static final String USAGE_MESSAGE =
+ "Usage: disasm [options] <input-files>\n"
+ + " where <input-files> are dex files\n"
+ + " and options are:\n"
+ + " --all # Include all information in disassembly.\n"
+ + " --smali # Disassemble using smali syntax.\n"
+ + " --ir # Print IR before and after optimization.\n"
+ + " --pg-map <file> # Proguard map <file> for mapping names.\n"
+ + " --output # Specify a file or directory to write to.\n"
+ + " --version # Print the version of r8.\n"
+ + " --help # Print this message.";
private final boolean allInfo;
private final boolean useSmali;
+ private final boolean useIr;
public static Builder builder() {
return new Builder();
@@ -112,10 +119,12 @@
builder.setPrintHelp(true);
} else if (arg.equals("--version")) {
builder.setPrintVersion(true);
- } else if (arg.equals("--all")) {
+ } else if (arg.equals("--all")) {
builder.setAllInfo(true);
} else if (arg.equals("--smali")) {
builder.setUseSmali(true);
+ } else if (arg.equals("--ir")) {
+ builder.setUseIr(true);
} else if (arg.equals("--pg-map")) {
builder.setProguardMapFile(Paths.get(args[++i]));
} else if (arg.equals("--output")) {
@@ -132,13 +141,18 @@
}
private DisassembleCommand(
- AndroidApp inputApp, Path outputPath, StringResource proguardMap,
- boolean allInfo, boolean useSmali) {
+ AndroidApp inputApp,
+ Path outputPath,
+ StringResource proguardMap,
+ boolean allInfo,
+ boolean useSmali,
+ boolean useIr) {
super(inputApp);
this.outputPath = outputPath;
this.proguardMap = proguardMap;
this.allInfo = allInfo;
this.useSmali = useSmali;
+ this.useIr = useIr;
}
private DisassembleCommand(boolean printHelp, boolean printVersion) {
@@ -147,6 +161,7 @@
proguardMap = null;
allInfo = false;
useSmali = false;
+ useIr = false;
}
public Path getOutputPath() {
@@ -157,6 +172,10 @@
return useSmali;
}
+ public boolean useIr() {
+ return useIr;
+ }
+
@Override
InternalOptions getInternalOptions() {
InternalOptions internal = new InternalOptions();
@@ -190,9 +209,10 @@
try {
DexApplication application =
new ApplicationReader(app, options, timing).read(command.proguardMap, executor);
- DexByteCodeWriter writer = command.useSmali()
- ? new SmaliWriter(application, options)
- : new AssemblyWriter(application, options, command.allInfo);
+ DexByteCodeWriter writer =
+ command.useSmali()
+ ? new SmaliWriter(application, options)
+ : new AssemblyWriter(application, options, command.allInfo, command.useIr());
if (command.getOutputPath() != null) {
writer.write(command.getOutputPath());
} else {
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index b1bc7ca..782c91d 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -20,17 +20,32 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+/**
+ * PrintSeeds prints the classes, interfaces, methods and fields selected by a given ProGuard
+ * configuration <pg-conf.txt> when compiling a given program <r8.jar> alongside a given
+ * library <rt.jar>.
+ *
+ * <p>The output format is identical to what is printed when {@code -printseeds} is specified in
+ * <pg-conf.txt>, but running PrintSeeds can be faster than running R8 with {@code
+ * -printseeds}. See also the {@link PrintUses} program in R8.
+ */
public class PrintSeeds {
private static final String USAGE =
"Arguments: <rt.jar> <r8.jar> <pg-conf.txt>\n"
+ "\n"
+ "PrintSeeds prints the classes, interfaces, methods and fields selected by\n"
- + "<pg-conf.txt> when compiling <r8.jar> alongside <rt.jar>.";
+ + "<pg-conf.txt> when compiling <r8.jar> alongside <rt.jar>.\n"
+ + "\n"
+ + "The output format is identical to what is printed when -printseeds is specified in\n"
+ + "<pg-conf.txt>, but running PrintSeeds can be faster than running R8 with \n"
+ + "-printseeds. See also the "
+ + PrintUses.class.getSimpleName()
+ + " program in R8.";
public static void main(String[] args) throws Exception {
if (args.length != 3) {
- System.out.println(USAGE);
+ System.out.println(USAGE.replace("\n", System.lineSeparator()));
System.exit(1);
}
Path rtJar = Paths.get(args[0]);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 0e69de6..c762182 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -31,6 +31,17 @@
import java.util.Map;
import java.util.Set;
+/**
+ * PrintUses prints the classes, interfaces, methods and fields used by a given program
+ * <sample.jar>, restricted to classes and interfaces in a given library <r8.jar> that
+ * are not in <sample.jar>.
+ *
+ * <p>The output is in the same format as what is printed when specifying {@code -printseeds} in a
+ * ProGuard configuration file. See also the {@link PrintSeeds} program in R8.
+ *
+ * <p>Note that this tool is not related to the {@code -printusage} option of ProGuard configuration
+ * files.
+ */
public class PrintUses {
private static final String USAGE =
@@ -38,7 +49,12 @@
+ "\n"
+ "PrintUses prints the classes, interfaces, methods and fields used by <sample.jar>,\n"
+ "restricted to classes and interfaces in <r8.jar> that are not in <sample.jar>.\n"
- + "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.";
+ + "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.\n"
+ + "\n"
+ + "The output is in the same format as what is printed when specifying -printseeds in\n"
+ + "a ProGuard configuration file. See also the "
+ + PrintSeeds.class.getSimpleName()
+ + " program in R8.";
private final Set<String> descriptors;
private final PrintStream out;
@@ -191,7 +207,7 @@
public static void main(String[] args) throws Exception {
if (args.length != 3) {
- System.out.println(USAGE);
+ System.out.println(USAGE.replace("\n", System.lineSeparator()));
return;
}
AndroidApp.Builder builder = AndroidApp.builder();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index e42d8b0..2e0b531 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.ClassAndMemberPublicizer;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -28,6 +27,7 @@
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.naming.SeedMapper;
import com.android.tools.r8.naming.SourceFileRewriter;
+import com.android.tools.r8.optimize.ClassAndMemberPublicizer;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.VisibilityBridgeRemover;
import com.android.tools.r8.origin.CommandLineOrigin;
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index fb3b067..1783f30 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.2.21-dev";
+ public static final String LABEL = "1.2.23-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index 90b863c..2621979 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -17,6 +17,7 @@
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -127,7 +128,7 @@
List<ZipEntry> toDex = new ArrayList<>();
- try (ZipFile zipFile = new ZipFile(input)) {
+ try (ZipFile zipFile = new ZipFile(input, StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index f512a34..871f6d8 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -572,7 +573,7 @@
// For each input archive file, add all class files within.
for (Path input : inputs) {
if (isArchive(input)) {
- try (ZipFile zipFile = new ZipFile(input.toFile())) {
+ try (ZipFile zipFile = new ZipFile(input.toFile(), StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 89abadd..23dbfbf 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -3,9 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.OptimizationFeedback;
+import com.android.tools.r8.ir.conversion.OptimizationFeedbackIgnore;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
import java.io.PrintStream;
public class AssemblyWriter extends DexByteCodeWriter {
@@ -13,12 +19,29 @@
private final boolean writeAllClassInfo;
private final boolean writeFields;
private final boolean writeAnnotations;
+ private final boolean writeIR;
+ private final AppInfoWithSubtyping appInfo;
+ private final Timing timing = new Timing("AssemblyWriter");
+ private final OptimizationFeedback ignoreOptimizationFeedback = new OptimizationFeedbackIgnore();
- public AssemblyWriter(DexApplication application, InternalOptions options, boolean allInfo) {
+ public AssemblyWriter(
+ DexApplication application, InternalOptions options, boolean allInfo, boolean writeIR) {
super(application, options);
this.writeAllClassInfo = allInfo;
this.writeFields = allInfo;
this.writeAnnotations = allInfo;
+ this.writeIR = writeIR;
+ if (writeIR) {
+ this.appInfo = new AppInfoWithSubtyping(application.toDirect());
+ if (options.programConsumer == null) {
+ // Use class-file backend, since the CF frontend for testing does not support desugaring of
+ // synchronized methods for the DEX backend (b/109789541).
+ options.programConsumer = ClassFileConsumer.emptyConsumer();
+ }
+ options.outline.enabled = false;
+ } else {
+ this.appInfo = null;
+ }
}
@Override
@@ -88,10 +111,21 @@
ps.println();
Code code = method.getCode();
if (code != null) {
- ps.println(code.toString(method, naming));
+ if (writeIR) {
+ writeIR(method, ps);
+ } else {
+ ps.println(code.toString(method, naming));
+ }
}
}
+ private void writeIR(DexEncodedMethod method, PrintStream ps) {
+ CfgPrinter printer = new CfgPrinter();
+ new IRConverter(appInfo, options, timing, printer)
+ .processMethod(method, ignoreOptimizationFeedback, null, null, null);
+ ps.println(printer.toString());
+ }
+
private void writeAnnotations(DexAnnotationSet annotations, PrintStream ps) {
if (writeAnnotations) {
if (!annotations.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 831edbe..026c2b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -790,6 +790,7 @@
private Code code;
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
+ private final int classFileVersion;
private Builder(DexEncodedMethod from) {
// Copy all the mutable state of a DexEncodedMethod here.
@@ -800,6 +801,7 @@
code = from.code;
compilationState = from.compilationState;
optimizationInfo = from.optimizationInfo.copy();
+ classFileVersion = from.classFileVersion;
}
public void setMethod(DexMethod method) {
@@ -816,7 +818,8 @@
assert annotations != null;
assert parameterAnnotations != null;
DexEncodedMethod result =
- new DexEncodedMethod(method, accessFlags, annotations, parameterAnnotations, code);
+ new DexEncodedMethod(
+ method, accessFlags, annotations, parameterAnnotations, code, classFileVersion);
result.compilationState = compilationState;
result.optimizationInfo = optimizationInfo;
return result;
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index ed99d25..2310891 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -557,7 +557,7 @@
@Override
public int hashCode() {
- return (int) value * 19;
+ return (int) (value * 19);
}
@Override
@@ -615,7 +615,7 @@
@Override
public int hashCode() {
- return (int) value * 29;
+ return (int) (value * 29);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index ca79b0f..309e1d0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -367,6 +367,13 @@
}
setLocalVariableLists();
readEndingLocals(builder);
+ if (currentBlockInfo != null && instruction.canThrow()) {
+ Snapshot exceptionTransfer =
+ state.getSnapshot().exceptionTransfer(builder.getFactory().throwableType);
+ for (int target : currentBlockInfo.exceptionalSuccessors) {
+ recordStateForTarget(target, exceptionTransfer);
+ }
+ }
if (isControlFlow(instruction)) {
ensureDebugValueLivenessControl(builder);
instruction.buildIR(builder, state, this);
@@ -376,13 +383,6 @@
}
state.clear();
} else {
- if (currentBlockInfo != null && instruction.canThrow()) {
- Snapshot exceptionTransfer =
- state.getSnapshot().exceptionTransfer(builder.getFactory().throwableType);
- for (int target : currentBlockInfo.exceptionalSuccessors) {
- recordStateForTarget(target, exceptionTransfer);
- }
- }
instruction.buildIR(builder, state, this);
ensureDebugValueLiveness(builder);
if (builder.getCFG().containsKey(currentInstructionIndex + 1)) {
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 56e381b..e3ec746 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -8,9 +8,11 @@
import static com.android.tools.r8.utils.DescriptorUtils.getPackageBinaryNameFromJavaType;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
@@ -18,16 +20,20 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.signature.GenericSignatureAction;
import com.android.tools.r8.naming.signature.GenericSignatureParser;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import java.lang.reflect.GenericSignatureFormatError;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -40,6 +46,7 @@
private final AppInfoWithLiveness appInfo;
private final RootSet rootSet;
+ private final Reporter reporter;
private final PackageObfuscationMode packageObfuscationMode;
private final boolean isAccessModificationAllowed;
private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
@@ -66,6 +73,7 @@
InternalOptions options) {
this.appInfo = appInfo;
this.rootSet = rootSet;
+ this.reporter = options.reporter;
this.packageObfuscationMode = options.proguardConfiguration.getPackageObfuscationMode();
this.isAccessModificationAllowed = options.proguardConfiguration.isAccessModificationAllowed();
this.packageDictionary = options.proguardConfiguration.getPackageObfuscationDictionary();
@@ -152,27 +160,75 @@
}
}
+ private void parseError(DexItem item, Origin origin, GenericSignatureFormatError e) {
+ StringBuilder message = new StringBuilder("Invalid class signature for ");
+ if (item instanceof DexClass) {
+ message.append("class ");
+ message.append(((DexClass) item).getType().toSourceString());
+ } else if (item instanceof DexEncodedField) {
+ message.append("field ");
+ message.append(item.toSourceString());
+ } else {
+ assert item instanceof DexEncodedMethod;
+ message.append("method ");
+ message.append(item.toSourceString());
+ }
+ message.append(".\n");
+ message.append(e.getMessage());
+ reporter.warning(new StringDiagnostic(message.toString(), origin));
+ }
+
private void renameTypesInGenericSignatures() {
for (DexClass clazz : appInfo.classes()) {
- rewriteGenericSignatures(clazz.annotations.annotations,
- genericSignatureParser::parseClassSignature);
+ clazz.annotations = rewriteGenericSignatures(clazz.annotations,
+ genericSignatureParser::parseClassSignature,
+ e -> parseError(clazz, clazz.getOrigin(), e));
clazz.forEachField(field -> rewriteGenericSignatures(
- field.annotations.annotations, genericSignatureParser::parseFieldSignature));
+ field.annotations, genericSignatureParser::parseFieldSignature,
+ e -> parseError(field, clazz.getOrigin(), e)));
clazz.forEachMethod(method -> rewriteGenericSignatures(
- method.annotations.annotations, genericSignatureParser::parseMethodSignature));
+ method.annotations, genericSignatureParser::parseMethodSignature,
+ e -> parseError(method, clazz.getOrigin(), e)));
}
}
- private void rewriteGenericSignatures(DexAnnotation[] annotations, Consumer<String> parser) {
- for (int i = 0; i < annotations.length; i++) {
- DexAnnotation annotation = annotations[i];
+ private DexAnnotationSet rewriteGenericSignatures(
+ DexAnnotationSet annotations,
+ Consumer<String> parser,
+ Consumer<GenericSignatureFormatError> parseError) {
+ // There can be no more than one signature annotation in an annotation set.
+ final int VALID = -1;
+ int invalid = VALID;
+ for (int i = 0; i < annotations.annotations.length && invalid == VALID; i++) {
+ DexAnnotation annotation = annotations.annotations[i];
if (DexAnnotation.isSignatureAnnotation(annotation, appInfo.dexItemFactory)) {
- parser.accept(DexAnnotation.getSignature(annotation));
- annotations[i] = DexAnnotation.createSignatureAnnotation(
- genericSignatureRewriter.getRenamedSignature(),
- appInfo.dexItemFactory);
+ try {
+ parser.accept(DexAnnotation.getSignature(annotation));
+ annotations.annotations[i] = DexAnnotation.createSignatureAnnotation(
+ genericSignatureRewriter.getRenamedSignature(),
+ appInfo.dexItemFactory);
+ } catch (GenericSignatureFormatError e) {
+ parseError.accept(e);
+ invalid = i;
+ }
}
}
+
+ // Return the rewritten signatures if it was valid and could be rewritten.
+ if (invalid == VALID) {
+ return annotations;
+ }
+ // Remove invalid signature if found.
+ DexAnnotation[] prunedAnnotations =
+ new DexAnnotation[annotations.annotations.length - 1];
+ int dest = 0;
+ for (int i = 0; i < annotations.annotations.length; i++) {
+ if (i != invalid) {
+ prunedAnnotations[dest++] = annotations.annotations[i];
+ }
+ }
+ assert dest == prunedAnnotations.length;
+ return new DexAnnotationSet(prunedAnnotations);
}
/**
@@ -453,7 +509,6 @@
String renamed =
getClassBinaryNameFromDescriptor(
renaming.getOrDefault(type, type.descriptor).toString());
- assert renamed.startsWith(enclosingRenamedBinaryName + Minifier.INNER_CLASS_SEPARATOR);
String outName = renamed.substring(enclosingRenamedBinaryName.length() + 1);
renamedSignature.append(outName);
return type;
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
rename to src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 7a13348..297dd57 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -1,7 +1,13 @@
// Copyright (c) 2016, 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.graph;
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.MethodAccessFlags;
public final class ClassAndMemberPublicizer {
private final DexItemFactory factory;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index ed9cbdb..c4276dc 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -812,45 +812,45 @@
skipWhitespace();
switch (peekChar()) {
case 'a':
- if (found = acceptString("abstract")) {
+ if ((found = acceptString("abstract"))) {
flags.setAbstract();
}
break;
case 'f':
- if (found = acceptString("final")) {
+ if ((found = acceptString("final"))) {
flags.setFinal();
}
break;
case 'n':
- if (found = acceptString("native")) {
+ if ((found = acceptString("native"))) {
flags.setNative();
}
break;
case 'p':
- if (found = acceptString("public")) {
+ if ((found = acceptString("public"))) {
flags.setPublic();
- } else if (found = acceptString("private")) {
+ } else if ((found = acceptString("private"))) {
flags.setPrivate();
- } else if (found = acceptString("protected")) {
+ } else if ((found = acceptString("protected"))) {
flags.setProtected();
}
break;
case 's':
- if (found = acceptString("synchronized")) {
+ if ((found = acceptString("synchronized"))) {
flags.setSynchronized();
- } else if (found = acceptString("static")) {
+ } else if ((found = acceptString("static"))) {
flags.setStatic();
- } else if (found = acceptString("strictfp")) {
+ } else if ((found = acceptString("strictfp"))) {
flags.setStrict();
}
break;
case 't':
- if (found = acceptString("transient")) {
+ if ((found = acceptString("transient"))) {
flags.setTransient();
}
break;
case 'v':
- if (found = acceptString("volatile")) {
+ if ((found = acceptString("volatile"))) {
flags.setVolatile();
}
break;
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
index 499ba47..0f06ca8 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
@@ -22,6 +22,7 @@
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -49,7 +50,7 @@
private List<ProgramResource> readArchive() throws IOException {
List<ProgramResource> dexResources = new ArrayList<>();
List<ProgramResource> classResources = new ArrayList<>();
- try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) {
+ try (ZipFile zipFile = new ZipFile(archive.getPath().toFile(), StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
@@ -106,7 +107,7 @@
@Override
public void accept(Visitor resourceBrowser) throws ResourceException {
- try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) {
+ try (ZipFile zipFile = new ZipFile(archive.getPath().toFile(), StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
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 b938596..f10ff69 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.ClassNaming.Builder;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -187,6 +188,22 @@
}
}
+ // We will be remapping positional debug events and collect them as MappedPositions.
+ private static class MappedPosition {
+ private final DexMethod method;
+ private final int originalLine;
+ private final Position caller;
+ private final int obfuscatedLine;
+
+ private MappedPosition(
+ DexMethod method, int originalLine, Position caller, int obfuscatedLine) {
+ this.method = method;
+ this.originalLine = originalLine;
+ this.caller = caller;
+ this.obfuscatedLine = obfuscatedLine;
+ }
+ }
+
public static ClassNameMapper run(
DexApplication application, NamingLens namingLens, boolean identityMapping) {
IdentityHashMap<DexString, List<DexProgramClass>> classesOfFiles = new IdentityHashMap<>();
@@ -199,25 +216,8 @@
continue;
}
- // Group methods by name
IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByName =
- new IdentityHashMap<>(clazz.directMethods().length + clazz.virtualMethods().length);
- clazz.forEachMethod(
- method -> {
- // Add method only if renamed or contains positions.
- if (namingLens.lookupName(method.method) != method.method.name
- || doesContainPositions(method)) {
- methodsByName.compute(
- method.method.name,
- (name, methods) -> {
- if (methods == null) {
- methods = new ArrayList<>();
- }
- methods.add(method);
- return methods;
- });
- }
- });
+ groupMethodsByName(namingLens, clazz);
// At this point we don't know if we really need to add this class to the builder.
// It depends on whether any methods/fields are renamed or some methods contain positions.
@@ -230,24 +230,11 @@
DescriptorUtils.descriptorToJavaType(renamedClassName.toString()),
clazz.toString()));
- // We do know we need to create a ClassNaming.Builder if the class itself had been renamed.
- if (!clazz.toString().equals(renamedClassName.toString())) {
- // Not using return value, it's registered in classNameMapperBuilder
- onDemandClassNamingBuilder.get();
- }
+ // If the class is renamed add it to the classNamingBuilder.
+ addClassToClassNaming(clazz, renamedClassName, onDemandClassNamingBuilder);
// First transfer renamed fields to classNamingBuilder.
- clazz.forEachField(
- dexEncodedField -> {
- DexField dexField = dexEncodedField.field;
- DexString renamedName = namingLens.lookupName(dexField);
- if (renamedName != dexField.name) {
- FieldSignature signature =
- new FieldSignature(dexField.name.toString(), dexField.type.toString());
- MemberNaming memberNaming = new MemberNaming(signature, renamedName.toString());
- onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
- }
- });
+ addFieldsToClassNaming(namingLens, clazz, onDemandClassNamingBuilder);
// Then process the methods.
for (List<DexEncodedMethod> methods : methodsByName.values()) {
@@ -256,47 +243,13 @@
// deterministic behaviour: the algorithm will assign new line numbers in this order.
// Methods with different names can share the same line numbers, that's why they don't
// need to be sorted.
- methods.sort(
- (lhs, rhs) -> {
- // Sort by startline, then DexEncodedMethod.slowCompare.
- // Use startLine = 0 if no debuginfo.
- Code lhsCode = lhs.getCode();
- Code rhsCode = rhs.getCode();
- DexCode lhsDexCode =
- lhsCode == null || !lhsCode.isDexCode() ? null : lhsCode.asDexCode();
- DexCode rhsDexCode =
- rhsCode == null || !rhsCode.isDexCode() ? null : rhsCode.asDexCode();
- DexDebugInfo lhsDebugInfo = lhsDexCode == null ? null : lhsDexCode.getDebugInfo();
- DexDebugInfo rhsDebugInfo = rhsDexCode == null ? null : rhsDexCode.getDebugInfo();
- int lhsStartLine = lhsDebugInfo == null ? 0 : lhsDebugInfo.startLine;
- int rhsStartLine = rhsDebugInfo == null ? 0 : rhsDebugInfo.startLine;
- int startLineDiff = lhsStartLine - rhsStartLine;
- if (startLineDiff != 0) return startLineDiff;
- return DexEncodedMethod.slowCompare(lhs, rhs);
- });
+ sortMethods(methods);
}
PositionRemapper positionRemapper =
identityMapping ? new IdentityPositionRemapper() : new OptimizingPositionRemapper();
for (DexEncodedMethod method : methods) {
-
- // We will be remapping positional debug events and collect them as MappedPositions.
- class MappedPosition {
- private final DexMethod method;
- private final int originalLine;
- private final Position caller;
- private final int obfuscatedLine;
-
- private MappedPosition(
- DexMethod method, int originalLine, Position caller, int obfuscatedLine) {
- this.method = method;
- this.originalLine = originalLine;
- this.caller = caller;
- this.obfuscatedLine = obfuscatedLine;
- }
- }
-
List<MappedPosition> mappedPositions = new ArrayList<>();
if (doesContainPositions(method)) {
@@ -429,6 +382,75 @@
return classNameMapperBuilder.build();
}
+ // Sort by startline, then DexEncodedMethod.slowCompare.
+ // Use startLine = 0 if no debuginfo.
+ private static void sortMethods(List<DexEncodedMethod> methods) {
+ methods.sort(
+ (lhs, rhs) -> {
+ Code lhsCode = lhs.getCode();
+ Code rhsCode = rhs.getCode();
+ DexCode lhsDexCode =
+ lhsCode == null || !lhsCode.isDexCode() ? null : lhsCode.asDexCode();
+ DexCode rhsDexCode =
+ rhsCode == null || !rhsCode.isDexCode() ? null : rhsCode.asDexCode();
+ DexDebugInfo lhsDebugInfo = lhsDexCode == null ? null : lhsDexCode.getDebugInfo();
+ DexDebugInfo rhsDebugInfo = rhsDexCode == null ? null : rhsDexCode.getDebugInfo();
+ int lhsStartLine = lhsDebugInfo == null ? 0 : lhsDebugInfo.startLine;
+ int rhsStartLine = rhsDebugInfo == null ? 0 : rhsDebugInfo.startLine;
+ int startLineDiff = lhsStartLine - rhsStartLine;
+ if (startLineDiff != 0) return startLineDiff;
+ return DexEncodedMethod.slowCompare(lhs, rhs);
+ });
+ }
+
+ @SuppressWarnings("ReturnValueIgnored")
+ private static void addClassToClassNaming(DexProgramClass clazz, DexString renamedClassName,
+ Supplier<Builder> onDemandClassNamingBuilder) {
+ // We do know we need to create a ClassNaming.Builder if the class itself had been renamed.
+ if (!clazz.toString().equals(renamedClassName.toString())) {
+ // Not using return value, it's registered in classNameMapperBuilder
+ onDemandClassNamingBuilder.get();
+ }
+ }
+
+ private static void addFieldsToClassNaming(NamingLens namingLens, DexProgramClass clazz,
+ Supplier<Builder> onDemandClassNamingBuilder) {
+ clazz.forEachField(
+ dexEncodedField -> {
+ DexField dexField = dexEncodedField.field;
+ DexString renamedName = namingLens.lookupName(dexField);
+ if (renamedName != dexField.name) {
+ FieldSignature signature =
+ new FieldSignature(dexField.name.toString(), dexField.type.toString());
+ MemberNaming memberNaming = new MemberNaming(signature, renamedName.toString());
+ onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
+ }
+ });
+ }
+
+ private static IdentityHashMap<DexString, List<DexEncodedMethod>> groupMethodsByName(
+ NamingLens namingLens, DexProgramClass clazz) {
+ IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByName =
+ new IdentityHashMap<>(clazz.directMethods().length + clazz.virtualMethods().length);
+ clazz.forEachMethod(
+ method -> {
+ // Add method only if renamed or contains positions.
+ if (namingLens.lookupName(method.method) != method.method.name
+ || doesContainPositions(method)) {
+ methodsByName.compute(
+ method.method.name,
+ (name, methods) -> {
+ if (methods == null) {
+ methods = new ArrayList<>();
+ }
+ methods.add(method);
+ return methods;
+ });
+ }
+ });
+ return methodsByName;
+ }
+
private static boolean doesContainPositions(DexEncodedMethod method) {
Code code = method.getCode();
if (code == null || !code.isDexCode()) {
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index 1b032e5..849377b 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -10,6 +10,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
@@ -27,7 +28,7 @@
}
public static void iter(String zipFileStr, OnEntryHandler handler) throws IOException {
- try (ZipFile zipFile = new ZipFile(zipFileStr)) {
+ try (ZipFile zipFile = new ZipFile(zipFileStr, StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
index ad31005..2ef0123 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
@@ -22,6 +22,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -387,7 +388,7 @@
for (Path file : files) {
if (isArchive(file)) {
Origin zipOrigin = new PathOrigin(file);
- ZipInputStream zip = new ZipInputStream(Files.newInputStream(file));
+ ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8);
ZipEntry entry;
while (null != (entry = zip.getNextEntry())) {
if (isClassFile(Paths.get(entry.getName()))) {
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
index 7230f55..2c2e8d7 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
@@ -22,6 +22,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -378,7 +379,7 @@
for (Path file : files) {
if (isArchive(file)) {
Origin zipOrigin = new PathOrigin(file);
- ZipInputStream zip = new ZipInputStream(Files.newInputStream(file));
+ ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8);
ZipEntry entry;
while (null != (entry = zip.getNextEntry())) {
if (isClassFile(Paths.get(entry.getName()))) {
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 4e07849..4f749b5 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -85,6 +85,11 @@
}
@Test
+ public void testInlining() throws Exception {
+ runTest("inlining.Inlining");
+ }
+
+ @Test
public void testInstanceVariable() throws Exception {
runTest("instancevariable.InstanceVariable");
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 1b846a5..87453d0 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -6,13 +6,13 @@
import static com.android.tools.r8.R8CommandTest.getOutputPath;
import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.sdklib.AndroidVersion;
+import com.android.tools.r8.D8CommandParser.OrderedClassFileResourceProvider;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
@@ -23,8 +23,8 @@
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@@ -263,13 +263,37 @@
tmpClassesDir.toString());
AndroidApp inputApp = ToolHelper.getApp(command);
assertEquals(1, inputApp.getClasspathResourceProviders().size());
+ OrderedClassFileResourceProvider classpathProvider =
+ (OrderedClassFileResourceProvider) inputApp.getClasspathResourceProviders().get(0);
+ assertEquals(1, classpathProvider.providers.size());
assertTrue(Files.isSameFile(tmpClassesDir,
- ((DirectoryClassFileProvider) inputApp.getClasspathResourceProviders().get(0)).getRoot()));
+ ((DirectoryClassFileProvider) classpathProvider.providers.get(0)).getRoot()));
assertEquals(1, inputApp.getLibraryResourceProviders().size());
assertTrue(Files.isSameFile(tmpClassesDir,
((DirectoryClassFileProvider) inputApp.getLibraryResourceProviders().get(0)).getRoot()));
}
+ @Test
+ public void folderClasspathMultiple() throws Throwable {
+ Path inputFile =
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION);
+ Path tmpClassesDir1 = temp.newFolder().toPath();
+ Path tmpClassesDir2 = temp.newFolder().toPath();
+ ZipUtils.unzip(inputFile.toString(), tmpClassesDir1.toFile());
+ ZipUtils.unzip(inputFile.toString(), tmpClassesDir2.toFile());
+ D8Command command = parse("--classpath", tmpClassesDir1.toString(), "--classpath",
+ tmpClassesDir2.toString());
+ AndroidApp inputApp = ToolHelper.getApp(command);
+ assertEquals(1, inputApp.getClasspathResourceProviders().size());
+ OrderedClassFileResourceProvider classpathProvider =
+ (OrderedClassFileResourceProvider) inputApp.getClasspathResourceProviders().get(0);
+ assertEquals(2, classpathProvider.providers.size());
+ assertTrue(Files.isSameFile(tmpClassesDir1,
+ ((DirectoryClassFileProvider) classpathProvider.providers.get(0)).getRoot()));
+ assertTrue(Files.isSameFile(tmpClassesDir2,
+ ((DirectoryClassFileProvider) classpathProvider.providers.get(1)).getRoot()));
+ }
+
@Test(expected = CompilationFailedException.class)
public void classFolderProgram() throws Throwable {
Path inputFile =
@@ -319,7 +343,7 @@
Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar");
ProgramResourceProvider myProvider =
ArchiveProgramResourceProvider.fromSupplier(
- new MyOrigin(), () -> new ZipFile(input.toFile()));
+ new MyOrigin(), () -> new ZipFile(input.toFile(), StandardCharsets.UTF_8));
D8Command command =
D8Command.builder()
.setProgramConsumer(DexIndexedConsumer.emptyConsumer())
@@ -464,7 +488,7 @@
.setOutput(emptyZip, OutputMode.DexIndexed)
.build());
assertTrue(Files.exists(emptyZip));
- assertEquals(0, new ZipFile(emptyZip.toFile()).size());
+ assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
}
private D8Command parse(String... args) throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/OrderedClassFileResourceProviderTest.java b/src/test/java/com/android/tools/r8/OrderedClassFileResourceProviderTest.java
new file mode 100644
index 0000000..dc497c4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/OrderedClassFileResourceProviderTest.java
@@ -0,0 +1,113 @@
+// 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;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.D8CommandParser.OrderedClassFileResourceProvider;
+import com.android.tools.r8.origin.Origin;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+
+public class OrderedClassFileResourceProviderTest extends TestBase {
+ class SimpleClassFileResourceProvider implements ClassFileResourceProvider {
+
+ private final Set<String> descriptors;
+ private final ProgramResource fixedProgramResource;
+
+ SimpleClassFileResourceProvider(int id, Set<String> descriptors) {
+ this.descriptors = descriptors;
+ this.fixedProgramResource = new SimpleProgramResource(id);
+ }
+
+ @Override
+ public Set<String> getClassDescriptors() {
+ return descriptors;
+ }
+
+ @Override
+ public ProgramResource getProgramResource(String descriptor) {
+ return fixedProgramResource;
+ }
+ }
+
+ class SimpleProgramResource implements ProgramResource {
+
+ private final Origin origin;
+
+ SimpleProgramResource(int id) {
+ origin = new SimpleOrigin(id);
+ }
+
+ @Override
+ public Kind getKind() {
+ return null;
+ }
+
+ @Override
+ public InputStream getByteStream() throws ResourceException {
+ return null;
+ }
+
+ @Override
+ public Set<String> getClassDescriptors() {
+ return null;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+ }
+
+ public class SimpleOrigin extends Origin {
+
+ private final int id;
+
+ private SimpleOrigin(int index) {
+ super(root());
+ this.id = index;
+ }
+
+ int getId() {
+ return id;
+ }
+
+ @Override
+ public String part() {
+ return "Test";
+ }
+ }
+
+ @Test
+ public void test() {
+ OrderedClassFileResourceProvider.Builder builder = OrderedClassFileResourceProvider.builder();
+ builder.addClassFileResourceProvider(new SimpleClassFileResourceProvider(1, ImmutableSet.of(
+ "L/a/a/a", "L/a/a/b", "L/a/a/c"
+ )));
+ builder.addClassFileResourceProvider(new SimpleClassFileResourceProvider(2, ImmutableSet.of(
+ "L/a/a/b", "L/a/a/c", "L/a/a/d"
+ )));
+ ClassFileResourceProvider provider = builder.build();
+ assertEquals(
+ ImmutableSet.of("L/a/a/a", "L/a/a/b", "L/a/a/c", "L/a/a/d"),
+ provider.getClassDescriptors());
+
+ Map<String, Integer> expectations = ImmutableMap.of(
+ "L/a/a/a", 1,
+ "L/a/a/b", 1,
+ "L/a/a/c", 1,
+ "L/a/a/d", 2
+ );
+ expectations.forEach((descriptor, id) ->
+ assertEquals(
+ (int) id,
+ ((SimpleOrigin) provider.getProgramResource(descriptor).getOrigin()).getId()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index fbd88be..16bbae9 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -459,7 +460,7 @@
.setOutput(emptyZip, OutputMode.DexIndexed)
.build());
assertTrue(Files.exists(emptyZip));
- assertEquals(0, new ZipFile(emptyZip.toFile()).size());
+ assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
}
private R8Command parse(String... args) throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 348dbf1..087012c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -41,6 +41,7 @@
"filledarray.FilledArray",
"hello.Hello",
"ifstatements.IfStatements",
+ "inlining.Inlining",
"instancevariable.InstanceVariable",
"instanceofstring.InstanceofString",
"invoke.Invoke",
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 2c6a72a..b0876c9 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -27,6 +27,7 @@
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -580,7 +581,7 @@
protected DexInspector getMainDexInspector(Path zip)
throws ZipException, IOException, ExecutionException {
- try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+ try (ZipFile zipFile = new ZipFile(zip.toFile(), StandardCharsets.UTF_8)) {
try (InputStream in =
zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
return new DexInspector(
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index 182fe80..4701ac3 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -25,6 +25,7 @@
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -256,7 +257,7 @@
protected DexInspector getMainDexInspector(Path zip)
throws ZipException, IOException, ExecutionException {
- try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+ try (ZipFile zipFile = new ZipFile(zip.toFile(), StandardCharsets.UTF_8)) {
try (InputStream in =
zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
return new DexInspector(
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 284ed68..9560c1f 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -25,6 +25,7 @@
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -309,7 +310,7 @@
protected DexInspector getMainDexInspector(Path zip)
throws ZipException, IOException, ExecutionException {
- try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+ try (ZipFile zipFile = new ZipFile(zip.toFile(), StandardCharsets.UTF_8)) {
try (InputStream in =
zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
return new DexInspector(
diff --git a/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java b/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java
index e49c740..341fb67 100644
--- a/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java
+++ b/src/test/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderTests.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.HashSet;
@@ -65,7 +66,7 @@
for (String className : CLASS_NAMES) {
expectedNames.add(SUBDIR + "/" + className + ".class.dex");
}
- try (ZipFile zipFile = new ZipFile(outputZip.toFile())) {
+ try (ZipFile zipFile = new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8)) {
for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry ze = e.nextElement();
expectedNames.remove(ze.getName());
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
index c55428f..7d181bc 100644
--- a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
+++ b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
@@ -17,6 +17,7 @@
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
@@ -197,8 +198,8 @@
}
private void compareArchiveFiles(Path d8File, Path dxFile) throws IOException {
- ZipFile d8Zip = new ZipFile(d8File.toFile());
- ZipFile dxZip = new ZipFile(dxFile.toFile());
+ ZipFile d8Zip = new ZipFile(d8File.toFile(), StandardCharsets.UTF_8);
+ ZipFile dxZip = new ZipFile(dxFile.toFile(), StandardCharsets.UTF_8);
// TODO(zerny): This should test resource containment too once supported.
Set<String> d8Content = d8Zip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
Set<String> dxContent = dxZip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
diff --git a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
index 751ac83..1d1604b 100644
--- a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
+++ b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
@@ -82,7 +82,7 @@
parseSimpleError(
GenericSignatureParser::parseClassSignature,
e -> assertTrue(e.getMessage().startsWith("Expected L at position 1")));
- // TODO(sgjesse): The position 2 reported here is onr off.
+ // TODO(sgjesse): The position 2 reported here is one off.
parseSimpleError(
GenericSignatureParser::parseFieldSignature,
e -> assertTrue(e.getMessage().startsWith("Expected L, [ or T at position 2")));
@@ -104,7 +104,7 @@
fail("Succesfully parsed " + signature.substring(0, i) + " (position " + i +")");
}
} catch (GenericSignatureFormatError e) {
- assertTrue("" + i + " Was: " + e.getMessage(), e.getMessage().contains("at position " + (i + 1)));
+ assertTrue(e.getMessage().contains("at position " + (i + 1)));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
new file mode 100644
index 0000000..68cd36f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
@@ -0,0 +1,496 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.PUTFIELD;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.V1_8;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.invokesuper.Consumer;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+public class MinifierClassSignatureTest extends TestBase {
+ /*
+
+ class Simple {
+ }
+ class Base<T> {
+ }
+ class Outer<T> {
+ class Inner {
+ class InnerInner {
+ }
+ class ExtendsInnerInner extends InnerInner {
+ }
+ }
+ class ExtendsInner extends Inner {
+ }
+ }
+
+ */
+
+ String baseSignature = "<T:Ljava/lang/Object;>Ljava/lang/Object;";
+ String outerSignature = "<T:Ljava/lang/Object;>Ljava/lang/Object;";
+ String extendsInnerSignature = "LOuter<TT;>.Inner;";
+ String extendsInnerInnerSignature = "LOuter<TT;>.Inner.InnerInner;";
+
+ private byte[] dumpSimple(String classSignature) throws Exception {
+
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ String signature = classSignature;
+ cw.visit(V1_8, ACC_SUPER, "Simple", signature, "java/lang/Object", null);
+
+ {
+ mv = cw.visitMethod(0, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private byte[] dumpBase(String classSignature) throws Exception {
+
+ final String javacClassSignature = baseSignature;
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ String signature = classSignature != null ? classSignature : javacClassSignature;
+ cw.visit(V1_8, ACC_SUPER, "Base", signature, "java/lang/Object", null);
+
+ {
+ mv = cw.visitMethod(0, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+
+ private byte[] dumpOuter(String classSignature) {
+
+ final String javacClassSignature = outerSignature;
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ String signature = classSignature != null ? classSignature : javacClassSignature;
+ cw.visit(V1_8, ACC_SUPER, "Outer", signature, "java/lang/Object", null);
+
+ cw.visitInnerClass("Outer$ExtendsInner", "Outer", "ExtendsInner", 0);
+
+ cw.visitInnerClass("Outer$Inner", "Outer", "Inner", 0);
+
+ {
+ mv = cw.visitMethod(0, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private byte[] dumpInner(String classSignature) {
+
+ final String javacClassSignature = null;
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+
+ String signature = classSignature != null ? classSignature : javacClassSignature;
+ cw.visit(V1_8, ACC_SUPER, "Outer$Inner", signature, "java/lang/Object", null);
+
+ cw.visitInnerClass("Outer$Inner", "Outer", "Inner", 0);
+
+ cw.visitInnerClass("Outer$Inner$ExtendsInnerInner", "Outer$Inner", "ExtendsInnerInner", 0);
+
+ cw.visitInnerClass("Outer$Inner$InnerInner", "Outer$Inner", "InnerInner", 0);
+
+ {
+ fv = cw.visitField(ACC_FINAL + ACC_SYNTHETIC, "this$0", "LOuter;", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(0, "<init>", "(LOuter;)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, "Outer$Inner", "this$0", "LOuter;");
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private byte[] dumpExtendsInner(String classSignature) throws Exception {
+
+ final String javacClassSignature = extendsInnerSignature;
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+
+ String signature = classSignature != null ? classSignature : javacClassSignature;
+ cw.visit(V1_8, ACC_SUPER, "Outer$ExtendsInner", signature, "Outer$Inner", null);
+
+ cw.visitInnerClass("Outer$Inner", "Outer", "Inner", 0);
+
+ cw.visitInnerClass("Outer$ExtendsInner", "Outer", "ExtendsInner", 0);
+
+ {
+ fv = cw.visitField(ACC_FINAL + ACC_SYNTHETIC, "this$0", "LOuter;", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(0, "<init>", "(LOuter;)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, "Outer$ExtendsInner", "this$0", "LOuter;");
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, "Outer$Inner", "<init>", "(LOuter;)V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private byte[] dumpInnerInner(String classSignature) throws Exception {
+
+ final String javacClassSignature = null;
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+
+ String signature = classSignature != null ? classSignature : javacClassSignature;
+ cw.visit(V1_8, ACC_SUPER, "Outer$Inner$InnerInner", signature, "java/lang/Object", null);
+
+ cw.visitInnerClass("Outer$Inner", "Outer", "Inner", 0);
+
+ cw.visitInnerClass("Outer$Inner$InnerInner", "Outer$Inner", "InnerInner", 0);
+
+ {
+ fv = cw.visitField(ACC_FINAL + ACC_SYNTHETIC, "this$1", "LOuter$Inner;", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(0, "<init>", "(LOuter$Inner;)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, "Outer$Inner$InnerInner", "this$1", "LOuter$Inner;");
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private byte[] dumpExtendsInnerInner(String classSignature) throws Exception {
+
+ final String javacClassSignature = extendsInnerInnerSignature;
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+
+ String signature = classSignature != null ? classSignature : javacClassSignature;
+ cw.visit(V1_8, ACC_SUPER, "Outer$Inner$ExtendsInnerInner", signature, "Outer$Inner$InnerInner",
+ null);
+
+ cw.visitInnerClass("Outer$Inner", "Outer", "Inner", 0);
+
+ cw.visitInnerClass("Outer$Inner$InnerInner", "Outer$Inner", "InnerInner", 0);
+
+ cw.visitInnerClass("Outer$Inner$ExtendsInnerInner", "Outer$Inner", "ExtendsInnerInner", 0);
+
+ {
+ fv = cw.visitField(ACC_FINAL + ACC_SYNTHETIC, "this$1", "LOuter$Inner;", null, null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(0, "<init>", "(LOuter$Inner;)V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, "Outer$Inner$ExtendsInnerInner", "this$1", "LOuter$Inner;");
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, "Outer$Inner$InnerInner", "<init>", "(LOuter$Inner;)V",
+ false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ public void runTest(
+ ImmutableMap<String, String> signatures,
+ Consumer<DiagnosticsChecker> diagnostics,
+ Consumer<DexInspector> inspect)
+ throws Exception {
+ DiagnosticsChecker checker = new DiagnosticsChecker();
+ DexInspector inspector = new DexInspector(
+ ToolHelper.runR8(R8Command.builder(checker)
+ .addClassProgramData(dumpSimple(signatures.get("Simple")), Origin.unknown())
+ .addClassProgramData(dumpBase(signatures.get("Base")), Origin.unknown())
+ .addClassProgramData(dumpOuter(signatures.get("Outer")), Origin.unknown())
+ .addClassProgramData(dumpInner(signatures.get("Outer$Inner")), Origin.unknown())
+ .addClassProgramData(
+ dumpExtendsInner(signatures.get("Outer$ExtendsInner")), Origin.unknown())
+ .addClassProgramData(
+ dumpInnerInner(signatures.get("Outer$Inner$InnerInner")), Origin.unknown())
+ .addClassProgramData(
+ dumpExtendsInnerInner(
+ signatures.get("Outer$Inner$ExtendsInnerInner")), Origin.unknown())
+ .addProguardConfiguration(ImmutableList.of(
+ "-keepattributes InnerClasses,EnclosingMethod,Signature",
+ "-keep,allowobfuscation class **"
+ ), Origin.unknown())
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setProguardMapConsumer(StringConsumer.emptyConsumer())
+ .build()));
+ // All classes are kept, and renamed.
+ assertThat(inspector.clazz("Simple"), isRenamed());
+ assertThat(inspector.clazz("Base"), isRenamed());
+ assertThat(inspector.clazz("Outer"), isRenamed());
+ assertThat(inspector.clazz("Outer$Inner"), isRenamed());
+ assertThat(inspector.clazz("Outer$ExtendsInner"), isRenamed());
+ assertThat(inspector.clazz("Outer$Inner$InnerInner"), isRenamed());
+ assertThat(inspector.clazz("Outer$Inner$ExtendsInnerInner"), isRenamed());
+
+ // Test that classes with have their original signature if the default was provided.
+ if (!signatures.containsKey("Simple")) {
+ assertNull(inspector.clazz("Simple").getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("Base")) {
+ assertEquals(baseSignature, inspector.clazz("Base").getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("Outer")) {
+ assertEquals(outerSignature, inspector.clazz("Outer").getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("Outer$Inner")) {
+ assertNull(inspector.clazz("Outer$Inner").getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("Outer$ExtendsInner")) {
+ assertEquals(extendsInnerSignature,
+ inspector.clazz("Outer$ExtendsInner").getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("Outer$Inner$InnerInner")) {
+ assertNull(inspector.clazz("Outer$Inner$InnerInner").getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("Outer$Inner$ExtendsInnerInner")) {
+ assertEquals(extendsInnerInnerSignature,
+ inspector.clazz("Outer$Inner$ExtendsInnerInner").getOriginalSignatureAttribute());
+ }
+
+ diagnostics.accept(checker);
+ inspect.accept(inspector);
+ }
+
+ private void testSingleClass(String name, String signature,
+ Consumer<DiagnosticsChecker> diagnostics,
+ Consumer<DexInspector> inspector)
+ throws Exception {
+ ImmutableMap<String, String> signatures = ImmutableMap.of(name, signature);
+ runTest(signatures, diagnostics, inspector);
+ }
+
+ private void isOriginUnknown(Origin origin) {
+ assertSame(Origin.unknown(), origin);
+ }
+
+ private void noWarnings(DiagnosticsChecker checker) {
+ assertEquals(0, checker.warnings.size());
+ }
+
+ private void noInspection(DexInspector inspector) {
+ }
+
+ private void noSignatureAttribute(ClassSubject clazz) {
+ assertNull(clazz.getFinalSignatureAttribute());
+ assertNull(clazz.getOriginalSignatureAttribute());
+ }
+
+ @Test
+ public void originalJavacSignatures() throws Exception {
+ // Test using the signatures generated by javac.
+ runTest(ImmutableMap.of(), this::noWarnings, this::noInspection);
+ }
+
+ @Test
+ public void classSignature_empty() throws Exception {
+ testSingleClass("Outer", "", this::noWarnings, inspector -> {
+ ClassSubject outer = inspector.clazz("Outer");
+ assertNull(outer.getFinalSignatureAttribute());
+ assertNull(outer.getOriginalSignatureAttribute());
+ });
+ }
+
+ @Test
+ public void classSignatureOuter_valid() throws Exception {
+ // class Outer<T extends Simple> extends Base<T>
+ String signature = "<T:LSimple;>LBase<TT;>;";
+ testSingleClass("Outer", signature, this::noWarnings, inspector -> {
+ ClassSubject outer = inspector.clazz("Outer");
+ ClassSubject simple = inspector.clazz("Simple");
+ ClassSubject base = inspector.clazz("Base");
+ String baseDescriptorWithoutSemicolon =
+ base.getFinalDescriptor().substring(0, base.getFinalDescriptor().length() - 1);
+ String minifiedSignature =
+ "<T:" + simple.getFinalDescriptor() + ">" + baseDescriptorWithoutSemicolon + "<TT;>;";
+ assertEquals(minifiedSignature, outer.getFinalSignatureAttribute());
+ assertEquals(signature, outer.getOriginalSignatureAttribute());
+ });
+ }
+
+ @Test
+ public void classSignatureExtendsInner_valid() throws Exception {
+ String signature = "LOuter<TT;>.Inner;";
+ testSingleClass("Outer$ExtendsInner", signature, this::noWarnings, inspector -> {
+ ClassSubject extendsInner = inspector.clazz("Outer$ExtendsInner");
+ ClassSubject outer = inspector.clazz("Outer");
+ ClassSubject inner = inspector.clazz("Outer$Inner");
+ String outerDescriptorWithoutSemicolon =
+ outer.getFinalDescriptor().substring(0, outer.getFinalDescriptor().length() - 1);
+ String innerFinalDescriptor = inner.getFinalDescriptor();
+ String innerLastPart =
+ innerFinalDescriptor.substring(innerFinalDescriptor.indexOf("$") + 1);
+ String minifiedSignature = outerDescriptorWithoutSemicolon + "<TT;>." + innerLastPart;
+ assertEquals(minifiedSignature, extendsInner.getFinalSignatureAttribute());
+ assertEquals(signature, extendsInner.getOriginalSignatureAttribute());
+ });
+ }
+
+ @Test
+ public void classSignatureOuter_classNotFound() throws Exception {
+ String signature = "<T:LNotFound;>LNotFound;";
+ testSingleClass("Outer", signature, this::noWarnings, inspector -> {
+ assertThat(inspector.clazz("NotFound"), not(isPresent()));
+ ClassSubject outer = inspector.clazz("Outer");
+ assertEquals(signature, outer.getOriginalSignatureAttribute());
+ });
+ }
+
+ @Test
+ public void classSignatureOuter_invalid() throws Exception {
+ testSingleClass("Outer", "X", diagnostics -> {
+ assertEquals(1, diagnostics.warnings.size());
+ DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
+ "Invalid class signature for class Outer", "Expected L at position 1");
+ }, inspector -> noSignatureAttribute(inspector.clazz("Outer")));
+ }
+
+ @Test
+ public void classSignatureOuter_invalidEnd() throws Exception {
+ testSingleClass("Outer", "<L", diagnostics -> {
+ assertEquals(1, diagnostics.warnings.size());
+ DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
+ "Invalid class signature for class Outer", "Unexpected end of signature at position 3");
+ }, inspector -> noSignatureAttribute(inspector.clazz("Outer")));
+ }
+
+ @Test
+ public void classSignatureExtendsInner_invalid() throws Exception {
+ testSingleClass("Outer$ExtendsInner", "X", diagnostics -> {
+ assertEquals(1, diagnostics.warnings.size());
+ DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
+ "Invalid class signature for class Outer$ExtendsInner", "Expected L at position 1");
+ }, inspector -> noSignatureAttribute(inspector.clazz("Outer$ExtendsInner")));
+ }
+
+ @Test
+ public void classSignatureExtendsInnerInner_invalid() throws Exception {
+ testSingleClass("Outer$Inner$ExtendsInnerInner", "X", diagnostics -> {
+ assertEquals(1, diagnostics.warnings.size());
+ DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
+ "Invalid class signature for class Outer$Inner$ExtendsInnerInner",
+ "Expected L at position 1");
+ }, inspector -> noSignatureAttribute(inspector.clazz("Outer$Inner$ExtendsInnerInner")));
+ }
+
+ @Test
+ public void multipleWarnings() throws Exception {
+ runTest(ImmutableMap.of(
+ "Outer", "X",
+ "Outer$ExtendsInner", "X",
+ "Outer$Inner$ExtendsInnerInner", "X"), diagnostics -> {
+ assertEquals(3, diagnostics.warnings.size());
+ }, inspector -> {
+ noSignatureAttribute(inspector.clazz("Outer"));
+ noSignatureAttribute(inspector.clazz("Outer$ExtendsInner"));
+ noSignatureAttribute(inspector.clazz("Outer$Inner$ExtendsInnerInner"));
+ });
+ }
+ @Test
+ public void regress80029761() throws Exception {
+ String signature = "LOuter<TT;>.com/example/Inner;";
+ testSingleClass("Outer$ExtendsInner", signature, diagnostics -> {
+ assertEquals(1, diagnostics.warnings.size());
+ DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
+ "Invalid class signature for class Outer$ExtendsInner", "Expected ; at position 16");
+ }, inspector -> {
+ noSignatureAttribute(inspector.clazz("Outer$ExtendsInner"));
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index d9808c0..f311c15 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -5,9 +5,9 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.ClassAndMemberPublicizer;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.optimize.ClassAndMemberPublicizer;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardRuleParserException;
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index ace5c9b..38de25e 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.StringResource;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstString;
@@ -53,6 +55,7 @@
import com.android.tools.r8.code.Throw;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexCode;
@@ -65,6 +68,8 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
@@ -72,6 +77,8 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.signature.GenericSignatureAction;
+import com.android.tools.r8.naming.signature.GenericSignatureParser;
import com.android.tools.r8.smali.SmaliBuilder;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableList;
@@ -367,6 +374,10 @@
public abstract boolean isLocalClass();
public abstract boolean isAnonymousClass();
+
+ public abstract String getOriginalSignatureAttribute();
+
+ public abstract String getFinalSignatureAttribute();
}
private class AbsentClassSubject extends ClassSubject {
@@ -448,6 +459,16 @@
public boolean isAnonymousClass() {
return false;
}
+
+ @Override
+ public String getOriginalSignatureAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ return null;
+ }
}
public class FoundClassSubject extends ClassSubject {
@@ -641,6 +662,98 @@
}
@Override
+ public String getOriginalSignatureAttribute() {
+ // Build the generic signature using the current mapping if any.
+ class GenericSignatureGenerater implements GenericSignatureAction<String> {
+
+ private StringBuilder signature;
+
+ public String getSignature() {
+ return signature.toString();
+ }
+
+ @Override
+ public void parsedSymbol(char symbol) {
+ signature.append(symbol);
+ }
+
+ @Override
+ public void parsedIdentifier(String identifier) {
+ signature.append(identifier);
+ }
+
+ @Override
+ public String parsedTypeName(String name) {
+ String type = name;
+ if (originalToObfuscatedMapping != null) {
+ String original = originalToObfuscatedMapping.inverse().get(name);
+ type = original != null ? original : name;
+ }
+ signature.append(type);
+ return type;
+ }
+
+ @Override
+ public String parsedInnerTypeName(String enclosingType, String name) {
+ if (originalToObfuscatedMapping != null) {
+ // The enclosingType has already been mapped if a mapping is present.
+ String minifiedEnclosing = originalToObfuscatedMapping.get(enclosingType);
+ String type = originalToObfuscatedMapping.inverse().get(minifiedEnclosing + "$" + name);
+ if (type != null) {
+ assert type.startsWith(enclosingType + "$");
+ name = type.substring(enclosingType.length() + 1);
+ }
+ signature.append(name);
+ return type;
+ } else {
+ String type = enclosingType + "$" + name;
+ signature.append(name);
+ return type;
+ }
+ }
+
+ @Override
+ public void start() {
+ signature = new StringBuilder();
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+ }
+
+ String finalSignature = getFinalSignatureAttribute();
+ if (finalSignature == null || mapping == null) {
+ return finalSignature;
+ }
+
+ GenericSignatureGenerater rewriter = new GenericSignatureGenerater();
+ GenericSignatureParser<String> parser = new GenericSignatureParser<>(rewriter);
+ parser.parseClassSignature(finalSignature);
+ return rewriter.getSignature();
+ }
+
+ @Override
+ public String getFinalSignatureAttribute() {
+ DexAnnotation annotation = findAnnotation("dalvik.annotation.Signature");
+ if (annotation == null) {
+ return null;
+ }
+ assert annotation.annotation.elements.length == 1;
+ DexAnnotationElement element = annotation.annotation.elements[0];
+ assert element.value instanceof DexValueArray;
+ StringBuilder builder = new StringBuilder();
+ DexValueArray valueArray = (DexValueArray) element.value;
+ for (DexValue value : valueArray.getValues()) {
+ assertTrue(value instanceof DexValueString);
+ DexValueString s = (DexValueString) value;
+ builder.append(s.getValue());
+ }
+ return builder.toString();
+ }
+
+ @Override
public String toString() {
return dexClass.toSourceString();
}