aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.gradle1
-rw-r--r--src/main/java/com/android/tools/r8/BaseCompilerCommand.java10
-rw-r--r--src/main/java/com/android/tools/r8/D8.java2
-rw-r--r--src/main/java/com/android/tools/r8/D8Command.java2
-rw-r--r--src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java2
-rw-r--r--src/main/java/com/android/tools/r8/R8.java12
-rw-r--r--src/main/java/com/android/tools/r8/R8Command.java22
-rw-r--r--src/main/java/com/android/tools/r8/ReadMainDexList.java3
-rw-r--r--src/main/java/com/android/tools/r8/ReadProguardMap.java4
-rw-r--r--src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java5
-rw-r--r--src/main/java/com/android/tools/r8/code/Format45cc.java6
-rw-r--r--src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java12
-rw-r--r--src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java5
-rw-r--r--src/main/java/com/android/tools/r8/dex/ApplicationReader.java4
-rw-r--r--src/main/java/com/android/tools/r8/dex/BaseFile.java4
-rw-r--r--src/main/java/com/android/tools/r8/dex/DexFileReader.java2
-rw-r--r--src/main/java/com/android/tools/r8/errors/InternalCompilerError.java4
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexClass.java2
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexCode.java5
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java4
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexItemFactory.java28
-rw-r--r--src/main/java/com/android/tools/r8/graph/GraphLense.java43
-rw-r--r--src/main/java/com/android/tools/r8/graph/JarApplicationReader.java3
-rw-r--r--src/main/java/com/android/tools/r8/graph/JarCode.java20
-rw-r--r--src/main/java/com/android/tools/r8/ir/code/BasicBlock.java4
-rw-r--r--src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java4
-rw-r--r--src/main/java/com/android/tools/r8/ir/code/IRCode.java4
-rw-r--r--src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java10
-rw-r--r--src/main/java/com/android/tools/r8/ir/code/Phi.java2
-rw-r--r--src/main/java/com/android/tools/r8/ir/code/Value.java8
-rw-r--r--src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java8
-rw-r--r--src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java17
-rw-r--r--src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java21
-rw-r--r--src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java9
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java2
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java20
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java2
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java4
-rw-r--r--src/main/java/com/android/tools/r8/ir/optimize/Inliner.java6
-rw-r--r--src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java2
-rw-r--r--src/main/java/com/android/tools/r8/naming/ClassNameMapper.java94
-rw-r--r--src/main/java/com/android/tools/r8/naming/ClassNaming.java112
-rw-r--r--src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java193
-rw-r--r--src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java194
-rw-r--r--src/main/java/com/android/tools/r8/naming/MemberNaming.java30
-rw-r--r--src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java20
-rw-r--r--src/main/java/com/android/tools/r8/naming/ProguardMap.java17
-rw-r--r--src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java475
-rw-r--r--src/main/java/com/android/tools/r8/naming/ProguardMapError.java38
-rw-r--r--src/main/java/com/android/tools/r8/naming/ProguardMapReader.java46
-rw-r--r--src/main/java/com/android/tools/r8/naming/SeedMapper.java90
-rw-r--r--src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java13
-rw-r--r--src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java2
-rw-r--r--src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java2
-rw-r--r--src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java7
-rw-r--r--src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java4
-rw-r--r--src/main/java/com/android/tools/r8/utils/AndroidApp.java13
-rw-r--r--src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java2
-rw-r--r--src/main/java/com/android/tools/r8/utils/ArrayUtils.java36
-rw-r--r--src/main/java/com/android/tools/r8/utils/InternalOptions.java5
-rw-r--r--src/main/java/com/android/tools/r8/utils/StringDiagnostic.java5
-rw-r--r--src/main/java/com/android/tools/r8/utils/ZipUtils.java6
-rw-r--r--src/test/examples/applymapping044/AsubB.java13
-rw-r--r--src/test/examples/applymapping044/Main.java20
-rw-r--r--src/test/examples/applymapping044/keep-rules-apply-mapping.txt13
-rw-r--r--src/test/examples/applymapping044/keep-rules.txt11
-rw-r--r--src/test/examples/applymapping044/test-mapping.txt9
-rw-r--r--src/test/examples/minification/conflict-mapping.txt4
-rw-r--r--src/test/examples/minification/keep-rules-apply-conflict-mapping.txt14
-rw-r--r--src/test/examples/naming001/keep-rules-105.txt11
-rw-r--r--src/test/examples/naming001/mapping-105.txt2
-rw-r--r--src/test/examples/naming044/B.java3
-rw-r--r--src/test/examples/naming044/sub/SubB.java4
-rw-r--r--src/test/examplesAndroidO/invokecustom/TestGenerator.java57
-rw-r--r--src/test/examplesAndroidO/invokecustom2/TestGenerator.java49
-rw-r--r--src/test/examplesAndroidO/stringconcat/TestGenerator.java335
-rw-r--r--src/test/examplesAndroidP/invokecustom/TestGenerator.java37
-rw-r--r--src/test/java/com/android/tools/r8/debug/DebugTestBase.java9
-rw-r--r--src/test/java/com/android/tools/r8/debug/JasminDebugTest.java6
-rw-r--r--src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java5
-rw-r--r--src/test/java/com/android/tools/r8/ir/InlineTest.java52
-rw-r--r--src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java4
-rw-r--r--src/test/java/com/android/tools/r8/ir/SplitBlockTest.java12
-rw-r--r--src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java3
-rw-r--r--src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java2
-rw-r--r--src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java5
-rw-r--r--src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java238
-rw-r--r--src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java8
-rw-r--r--src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java10
-rw-r--r--src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java2
-rw-r--r--src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java102
-rw-r--r--src/test/java/com/android/tools/r8/utils/DexInspector.java13
92 files changed, 2164 insertions, 621 deletions
diff --git a/build.gradle b/build.gradle
index 9299592c7..c97aa9dfb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,6 +23,7 @@ def errorProneConfiguration = [
'-Xep:OvershadowingSubclassFields:WARN',
'-Xep:IntLongMath:WARN',
'-Xep:EqualsHashCode:WARN',
+ '-Xep:InconsistentOverloads:WARN',
'-Xep:ArrayHashCode:WARN',
'-Xep:EqualsIncompatibleType:WARN',
'-Xep:NonOverridingEquals:WARN',
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 6c4b9bcd0..6961024fd 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -109,19 +109,19 @@ abstract class BaseCompilerCommand extends BaseCommand {
private boolean enableDesugaring = true;
protected Builder(CompilationMode mode) {
- this(AndroidApp.builder(), mode, false);
+ this(mode, false, AndroidApp.builder());
}
protected Builder(CompilationMode mode, boolean ignoreDexInArchive) {
- this(AndroidApp.builder(), mode, ignoreDexInArchive);
+ this(mode, ignoreDexInArchive, AndroidApp.builder());
}
// Internal constructor for testing.
- Builder(AndroidApp app, CompilationMode mode) {
- this(AndroidApp.builder(app), mode, false);
+ Builder(CompilationMode mode, AndroidApp app) {
+ this(mode, false, AndroidApp.builder(app));
}
- private Builder(AndroidApp.Builder builder, CompilationMode mode, boolean ignoreDexInArchive) {
+ private Builder(CompilationMode mode, boolean ignoreDexInArchive, AndroidApp.Builder builder) {
super(builder, ignoreDexInArchive);
assert mode != null;
this.mode = mode;
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index a5213c5b7..ae370ae38 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -199,7 +199,7 @@ public final class D8 {
throws IOException, ExecutionException, ApiLevelException {
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
- IRConverter converter = new IRConverter(timing, appInfo, options, printer);
+ IRConverter converter = new IRConverter(appInfo, options, timing, printer);
application = converter.convertToDex(application, executor);
if (options.printCfg) {
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index b96dfb73d..d0c99d503 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -44,7 +44,7 @@ public class D8Command extends BaseCompilerCommand {
}
private Builder(AndroidApp app) {
- super(app, CompilationMode.DEBUG);
+ super(CompilationMode.DEBUG, app);
}
/** Add classpath file resources. */
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index 9d7478d59..d29d9ae19 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -68,7 +68,7 @@ public class GenerateMainDexListCommand extends BaseCommand {
* Add proguard configuration for automatic main dex list calculation.
*/
public GenerateMainDexListCommand.Builder addMainDexRules(List<String> lines) {
- mainDexRules.add(new ProguardConfigurationSourceStrings(Paths.get("."), lines));
+ mainDexRules.add(new ProguardConfigurationSourceStrings(lines, Paths.get(".")));
return self();
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d9066bdf1..a9f3329f9 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -22,6 +22,8 @@ import com.android.tools.r8.ir.optimize.SwitchMapCollector;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.naming.Minifier;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.naming.ProguardMapApplier;
+import com.android.tools.r8.naming.SeedMapper;
import com.android.tools.r8.naming.SourceFileRewriter;
import com.android.tools.r8.optimize.BridgeMethodAnalysis;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
@@ -129,7 +131,7 @@ public class R8 {
timing.begin("Create IR");
try {
IRConverter converter = new IRConverter(
- timing, appInfo, options, printer, graphLense);
+ appInfo, options, timing, printer, graphLense);
application = converter.optimize(application, executorService);
} finally {
timing.end();
@@ -259,6 +261,14 @@ public class R8 {
appInfo = appInfo.withLiveness()
.prunedCopyFrom(application, classMerger.getRemovedClasses());
}
+ if (options.proguardConfiguration.hasApplyMappingFile()) {
+ SeedMapper seedMapper = SeedMapper.seedMapperFromFile(
+ options.proguardConfiguration.getApplyMappingFile());
+ timing.begin("apply-mapping");
+ graphLense = new ProguardMapApplier(appInfo.withLiveness(), graphLense, seedMapper)
+ .run(timing);
+ timing.end();
+ }
application = application.asDirect().rewrittenWithLense(graphLense);
appInfo = appInfo.withLiveness().rewrittenWithLense(application.asDirect(), graphLense);
// Collect switch maps and ordinals maps.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 1df56dbe7..ad23c038b 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -35,6 +35,7 @@ public class R8Command extends BaseCompilerCommand {
private Optional<Boolean> treeShaking = Optional.empty();
private Optional<Boolean> discardedChecker = Optional.empty();
private Optional<Boolean> minification = Optional.empty();
+ private boolean ignoreMissingClassesWhenNotShrinking = false;
private boolean ignoreMissingClasses = false;
private boolean forceProguardCompatibility = false;
private Path proguardMapOutput = null;
@@ -43,13 +44,16 @@ public class R8Command extends BaseCompilerCommand {
super(CompilationMode.RELEASE);
}
- protected Builder(boolean ignoreDexInArchive, boolean forceProguardCompatibility) {
+ protected Builder(boolean ignoreDexInArchive, boolean forceProguardCompatibility,
+ boolean ignoreMissingClassesWhenNotShrinking, boolean ignoreMissingClasses) {
super(CompilationMode.RELEASE, ignoreDexInArchive);
this.forceProguardCompatibility = forceProguardCompatibility;
+ this.ignoreMissingClassesWhenNotShrinking = ignoreMissingClassesWhenNotShrinking;
+ this.ignoreMissingClasses = ignoreMissingClasses;
}
private Builder(AndroidApp app) {
- super(app, CompilationMode.RELEASE);
+ super(CompilationMode.RELEASE, app);
}
@Override
@@ -105,7 +109,7 @@ public class R8Command extends BaseCompilerCommand {
* Add proguard configuration for automatic main dex list calculation.
*/
public Builder addMainDexRules(List<String> lines) {
- mainDexRules.add(new ProguardConfigurationSourceStrings(Paths.get("."), lines));
+ mainDexRules.add(new ProguardConfigurationSourceStrings(lines, Paths.get(".")));
return self();
}
@@ -138,7 +142,7 @@ public class R8Command extends BaseCompilerCommand {
* Add proguard configuration.
*/
public Builder addProguardConfiguration(List<String> lines) {
- proguardConfigs.add(new ProguardConfigurationSourceStrings(Paths.get("."), lines));
+ proguardConfigs.add(new ProguardConfigurationSourceStrings(lines, Paths.get(".")));
return self();
}
@@ -234,8 +238,6 @@ public class R8Command extends BaseCompilerCommand {
getAppBuilder().addProgramFiles(configuration.getInjars());
getAppBuilder().addLibraryFiles(configuration.getLibraryjars());
- // TODO(b/64802420): setProguardMapFile if configuration.hasApplyMappingFile
-
boolean useTreeShaking = treeShaking.orElse(configuration.isShrinking());
boolean useDiscardedChecker = discardedChecker.orElse(true);
boolean useMinification = minification.orElse(configuration.isObfuscating());
@@ -256,6 +258,7 @@ public class R8Command extends BaseCompilerCommand {
useMinification,
ignoreMissingClasses,
forceProguardCompatibility,
+ ignoreMissingClassesWhenNotShrinking,
proguardMapOutput);
}
}
@@ -297,6 +300,7 @@ public class R8Command extends BaseCompilerCommand {
private final boolean useMinification;
private final boolean ignoreMissingClasses;
private final boolean forceProguardCompatibility;
+ private final boolean ignoreMissingClassesWhenNotShrinking;
private final Path proguardMapOutput;
public static Builder builder() {
@@ -415,6 +419,7 @@ public class R8Command extends BaseCompilerCommand {
boolean useMinification,
boolean ignoreMissingClasses,
boolean forceProguardCompatibility,
+ boolean ignoreMissingClassesWhenNotShrinking,
Path proguardMapOutput) {
super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler,
enableDesugaring);
@@ -429,6 +434,7 @@ public class R8Command extends BaseCompilerCommand {
this.useMinification = useMinification;
this.ignoreMissingClasses = ignoreMissingClasses;
this.forceProguardCompatibility = forceProguardCompatibility;
+ this.ignoreMissingClassesWhenNotShrinking = ignoreMissingClassesWhenNotShrinking;
this.proguardMapOutput = proguardMapOutput;
}
@@ -442,6 +448,7 @@ public class R8Command extends BaseCompilerCommand {
useMinification = false;
ignoreMissingClasses = false;
forceProguardCompatibility = false;
+ ignoreMissingClassesWhenNotShrinking = false;
proguardMapOutput = null;
}
public boolean useTreeShaking() {
@@ -479,6 +486,9 @@ public class R8Command extends BaseCompilerCommand {
assert !internal.ignoreMissingClasses;
internal.ignoreMissingClasses = ignoreMissingClasses;
internal.ignoreMissingClasses |= proguardConfiguration.isIgnoreWarnings();
+ internal.ignoreMissingClasses |=
+ ignoreMissingClassesWhenNotShrinking && !proguardConfiguration.isShrinking();
+
assert !internal.verbose;
internal.mainDexKeepRules = mainDexKeepRules;
internal.minimalMainDex = internal.debug;
diff --git a/src/main/java/com/android/tools/r8/ReadMainDexList.java b/src/main/java/com/android/tools/r8/ReadMainDexList.java
index a0c47895e..661e324af 100644
--- a/src/main/java/com/android/tools/r8/ReadMainDexList.java
+++ b/src/main/java/com/android/tools/r8/ReadMainDexList.java
@@ -4,7 +4,6 @@
package com.android.tools.r8;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ProguardMapReader;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.Iterators;
import java.nio.file.Path;
@@ -57,7 +56,7 @@ public class ReadMainDexList {
Path mainDexList = Paths.get(arg);
final ClassNameMapper mapper =
- arguments.hasNext() ? ProguardMapReader.mapperFromFile(Paths.get(arguments.next())) : null;
+ arguments.hasNext() ? ClassNameMapper.mapperFromFile(Paths.get(arguments.next())) : null;
FileUtils.readTextFile(mainDexList)
.stream()
diff --git a/src/main/java/com/android/tools/r8/ReadProguardMap.java b/src/main/java/com/android/tools/r8/ReadProguardMap.java
index 2ea2ca78b..a05537c26 100644
--- a/src/main/java/com/android/tools/r8/ReadProguardMap.java
+++ b/src/main/java/com/android/tools/r8/ReadProguardMap.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.nio.file.Paths;
@@ -22,7 +22,7 @@ public class ReadProguardMap {
try {
System.out.println(" - reading " + fileName);
timing.begin("Reading " + fileName);
- ProguardMapReader.mapperFromFile(Paths.get(fileName));
+ ClassNameMapper.mapperFromFile(Paths.get(fileName));
timing.end();
} catch (IOException e) {
System.err.print("Failed to parse Proguard mapping file: " + e.getMessage());
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index 2120714ba..e904e53e9 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -22,6 +22,7 @@ import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -174,7 +175,9 @@ public class FrameworkIncrementalDexingBenchmark {
throws IOException, CompilationException {
List<byte[]> bytes = new ArrayList<>(outputs.size());
for (Resource input : outputs.values()) {
- bytes.add(ByteStreams.toByteArray(input.getStream()));
+ try (InputStream inputStream = input.getStream()) {
+ bytes.add(ByteStreams.toByteArray(inputStream));
+ }
}
long start = System.nanoTime();
D8Output out =
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java
index c269fad73..f5123cf70 100644
--- a/src/main/java/com/android/tools/r8/code/Format45cc.java
+++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -117,13 +117,13 @@ public abstract class Format45cc extends Base4Format {
StringBuilder builder = new StringBuilder();
appendRegisterArguments(builder, " ");
builder.append(" ");
- builder.append(toString(BBBB, naming));
+ builder.append(itemToString(BBBB, naming));
builder.append(", ");
- builder.append(toString(HHHH, naming));
+ builder.append(itemToString(HHHH, naming));
return formatString(builder.toString());
}
- private String toString(IndexedDexItem indexedDexItem, ClassNameMapper naming) {
+ private String itemToString(IndexedDexItem indexedDexItem, ClassNameMapper naming) {
String str;
if (naming == null) {
str = indexedDexItem.toSmaliString();
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 4c49a6a1b..a6b45ae17 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -33,14 +33,16 @@ public class CompatProguard {
public final String output;
public final int minApi;
public final boolean forceProguardCompatibility;
+ public final boolean ignoreMissingClasses;
public final boolean multiDex;
public final List<String> proguardConfig;
CompatProguardOptions(List<String> proguardConfig, String output, int minApi,
- boolean multiDex, boolean forceProguardCompatibility) {
+ boolean multiDex, boolean forceProguardCompatibility, boolean ignoreMissingClasses) {
this.output = output;
this.minApi = minApi;
this.forceProguardCompatibility = forceProguardCompatibility;
+ this.ignoreMissingClasses = ignoreMissingClasses;
this.multiDex = multiDex;
this.proguardConfig = proguardConfig;
}
@@ -49,6 +51,7 @@ public class CompatProguard {
String output = null;
int minApi = 1;
boolean forceProguardCompatibility = false;
+ boolean ignoreMissingClasses = false;
boolean multiDex = false;
boolean coreLibrary = false;
@@ -62,6 +65,8 @@ public class CompatProguard {
minApi = Integer.valueOf(args[++i]);
} else if (arg.equals("--force-proguard-compatibility")) {
forceProguardCompatibility = true;
+ } else if (arg.equals("--ignore-missing-classes")) {
+ ignoreMissingClasses = true;
} else if (arg.equals("--output")) {
output = args[++i];
} else if (arg.equals("--multi-dex")) {
@@ -82,7 +87,7 @@ public class CompatProguard {
builder.add(currentLine.toString());
}
return new CompatProguardOptions(builder.build(), output, minApi, multiDex,
- forceProguardCompatibility);
+ forceProguardCompatibility, ignoreMissingClasses);
}
}
@@ -95,7 +100,8 @@ public class CompatProguard {
// Run R8 passing all the options from the command line as a Proguard configuration.
CompatProguardOptions options = CompatProguardOptions.parse(args);
R8Command.Builder builder =
- new CompatProguardCommandBuilder(options.forceProguardCompatibility);
+ new CompatProguardCommandBuilder(
+ options.forceProguardCompatibility, options.ignoreMissingClasses);
builder.setOutputPath(Paths.get(options.output))
.addProguardConfiguration(options.proguardConfig)
.setMinApiLevel(options.minApi);
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
index 71bc6eb0d..18ec474ac 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
@@ -7,8 +7,9 @@ package com.android.tools.r8.compatproguard;
import com.android.tools.r8.R8Command;
public class CompatProguardCommandBuilder extends R8Command.Builder {
- public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
- super(true, forceProguardCompatibility);
+ public CompatProguardCommandBuilder(boolean forceProguardCompatibility,
+ boolean ignoreMissingClasses) {
+ super(true, forceProguardCompatibility, true, ignoreMissingClasses);
setEnableDesugaring(false);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 3e4d28db2..db5ebf20b 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -20,7 +20,7 @@ import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.JarClassFileReader;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
-import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ClassProvider;
@@ -111,7 +111,7 @@ public class ApplicationReader {
if (inputApp.hasProguardMap()) {
futures.add(executorService.submit(() -> {
try (InputStream map = inputApp.getProguardMap()) {
- builder.setProguardMap(ProguardMapReader.mapperFromInputStream(map));
+ builder.setProguardMap(ClassNameMapper.mapperFromInputStream(map));
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/src/main/java/com/android/tools/r8/dex/BaseFile.java b/src/main/java/com/android/tools/r8/dex/BaseFile.java
index dd9b325c9..86143c84a 100644
--- a/src/main/java/com/android/tools/r8/dex/BaseFile.java
+++ b/src/main/java/com/android/tools/r8/dex/BaseFile.java
@@ -18,7 +18,9 @@ public abstract class BaseFile {
protected final ByteBuffer buffer;
protected BaseFile(Resource resource) throws IOException {
- buffer = ByteBuffer.wrap(ByteStreams.toByteArray(resource.getStream()));
+ try (InputStream input = resource.getStream()) {
+ buffer = ByteBuffer.wrap(ByteStreams.toByteArray(input));
+ }
}
protected BaseFile(String name) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 770afdf06..64909cc3a 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -983,7 +983,7 @@ public class DexFileReader {
DexString shorty = indexedItems.getString(shortyIndex);
DexType returnType = indexedItems.getType(returnTypeIndex);
DexTypeList parameters = typeListAt(parametersOffsetIndex);
- return dexItemFactory.createProto(shorty, returnType, parameters);
+ return dexItemFactory.createProto(returnType, shorty, parameters);
}
private DexMethod methodAt(int index) {
diff --git a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
index b51c7ecae..a5080d39a 100644
--- a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
+++ b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
@@ -14,8 +14,8 @@ public class InternalCompilerError extends IllegalStateException {
public InternalCompilerError() {
}
- public InternalCompilerError(String s) {
- super(s);
+ public InternalCompilerError(String message) {
+ super(message);
}
public InternalCompilerError(String message, Throwable cause) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 505071166..dda559cd5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -18,7 +18,7 @@ public abstract class DexClass extends DexItem {
private static final DexEncodedField[] NO_FIELDS = {};
public final Origin origin;
- public final DexType type;
+ public DexType type;
public final DexAccessFlags accessFlags;
public DexType superType;
public DexTypeList interfaces;
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 6a2fcbf51..a05299c77 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -168,11 +168,10 @@ public class DexCode extends Code {
public IRCode buildIR(
DexEncodedMethod encodedMethod,
- ValueNumberGenerator valueNumberGenerator,
- InternalOptions options)
+ InternalOptions options, ValueNumberGenerator valueNumberGenerator)
throws ApiLevelException {
DexSourceCode source = new DexSourceCode(this, encodedMethod);
- IRBuilder builder = new IRBuilder(encodedMethod, source, valueNumberGenerator, options);
+ IRBuilder builder = new IRBuilder(encodedMethod, source, options, valueNumberGenerator);
return builder.build();
}
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 99325f908..19743978f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -265,11 +265,11 @@ public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
return code == null ? null : code.buildIR(this, options);
}
- public IRCode buildIR(ValueNumberGenerator valueNumberGenerator, InternalOptions options)
+ public IRCode buildIR(InternalOptions options, ValueNumberGenerator valueNumberGenerator)
throws ApiLevelException {
return code == null
? null
- : code.asDexCode().buildIR(this, valueNumberGenerator, options);
+ : code.asDexCode().buildIR(this, options, valueNumberGenerator);
}
public void setCode(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 153c2e651..a7d44ba31 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -346,23 +346,19 @@ public class DexItemFactory {
return createField(clazz, type, createString(name));
}
- public DexProto createProto(DexString shorty, DexType returnType, DexTypeList parameters) {
+ public DexProto createProto(DexType returnType, DexString shorty, DexTypeList parameters) {
assert !sorted;
DexProto proto = new DexProto(shorty, returnType, parameters);
return canonicalize(protos, proto);
}
- public DexProto createProto(DexString shorty, DexType returnType, DexType[] parameters) {
+ public DexProto createProto(DexType returnType, DexType... parameters) {
assert !sorted;
- return createProto(shorty, returnType,
+ return createProto(returnType, createShorty(returnType, parameters),
parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
}
- public DexProto createProto(DexType returnType, DexType... parameters) {
- return createProto(createShorty(returnType, parameters), returnType, parameters);
- }
-
- public DexString createShorty(DexType returnType, DexType[] argumentTypes) {
+ private DexString createShorty(DexType returnType, DexType[] argumentTypes) {
StringBuilder shortyBuilder = new StringBuilder();
shortyBuilder.append(returnType.toShorty());
for (DexType argumentType : argumentTypes) {
@@ -407,7 +403,7 @@ public class DexItemFactory {
for (int i = 0; i < parameterDescriptors.length; i++) {
parameterTypes[i] = createType(parameterDescriptors[i]);
}
- DexProto proto = createProto(shorty(returnType, parameterTypes), returnType, parameterTypes);
+ DexProto proto = createProto(returnType, parameterTypes);
return createMethod(clazz, proto, name);
}
@@ -464,20 +460,6 @@ public class DexItemFactory {
return method.name == classConstructorMethodName;
}
- private DexString shorty(DexType returnType, DexType[] parameters) {
- StringBuilder builder = new StringBuilder();
- addToShorty(builder, returnType);
- for (DexType parameter : parameters) {
- addToShorty(builder, parameter);
- }
- return createString(builder.toString());
- }
-
- private void addToShorty(StringBuilder builder, DexType type) {
- char first = type.toDescriptorString().charAt(0);
- builder.append(first == '[' ? 'L' : first);
- }
-
private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
NamingLens namingLens) {
List<S> sorted = new ArrayList<>(items);
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 3abe7c5bb..ed4106e0c 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -23,13 +23,12 @@ public abstract class GraphLense {
public static class Builder {
- private Builder() {
-
+ protected Builder() {
}
- private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
- private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
+ protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+ protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
+ protected final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
public void map(DexType from, DexType to) {
typeMap.put(from, to);
@@ -44,10 +43,13 @@ public abstract class GraphLense {
}
public GraphLense build(DexItemFactory dexItemFactory) {
- return build(new IdentityGraphLense(), dexItemFactory);
+ return build(dexItemFactory, new IdentityGraphLense());
}
- public GraphLense build(GraphLense previousLense, DexItemFactory dexItemFactory) {
+ public GraphLense build(DexItemFactory dexItemFactory, GraphLense previousLense) {
+ if (typeMap.isEmpty() && methodMap.isEmpty() && fieldMap.isEmpty()) {
+ return previousLense;
+ }
return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense, dexItemFactory);
}
@@ -96,17 +98,17 @@ public abstract class GraphLense {
}
}
- private static class NestedGraphLense extends GraphLense {
+ public static class NestedGraphLense extends GraphLense {
private final GraphLense previousLense;
- private final DexItemFactory dexItemFactory;
+ protected final DexItemFactory dexItemFactory;
private final Map<DexType, DexType> typeMap;
private final Map<DexType, DexType> arrayTypeCache = new IdentityHashMap<>();
private final Map<DexMethod, DexMethod> methodMap;
private final Map<DexField, DexField> fieldMap;
- private NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
+ public NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap, GraphLense previousLense, DexItemFactory dexItemFactory) {
this.typeMap = typeMap;
this.methodMap = methodMap;
@@ -118,7 +120,7 @@ public abstract class GraphLense {
@Override
public DexType lookupType(DexType type, DexEncodedMethod context) {
if (type.isArrayType()) {
- synchronized(this) {
+ synchronized (this) {
// This block need to be synchronized due to arrayTypeCache.
DexType result = arrayTypeCache.get(type);
if (result == null) {
@@ -154,5 +156,24 @@ public abstract class GraphLense {
public boolean isContextFree() {
return previousLense.isContextFree();
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (Map.Entry<DexType, DexType> entry : typeMap.entrySet()) {
+ builder.append(entry.getKey().toSourceString()).append(" -> ");
+ builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
+ }
+ for (Map.Entry<DexMethod, DexMethod> entry : methodMap.entrySet()) {
+ builder.append(entry.getKey().toSourceString()).append(" -> ");
+ builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
+ }
+ for (Map.Entry<DexField, DexField> entry : fieldMap.entrySet()) {
+ builder.append(entry.getKey().toSourceString()).append(" -> ");
+ builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
+ }
+ builder.append(previousLense.toString());
+ return builder.toString();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
index 757ae71fb..6bf1a1772 100644
--- a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
@@ -18,6 +18,7 @@ import org.objectweb.asm.Type;
* It does not currently support multithreaded reading.
*/
public class JarApplicationReader {
+
public final InternalOptions options;
ConcurrentHashMap<String, DexString> stringCache = new ConcurrentHashMap<>();
@@ -111,8 +112,8 @@ public class JarApplicationReader {
argumentDescriptors[i] = arguments[i].getDescriptor();
}
DexProto proto = options.itemFactory.createProto(
- getString(shortyDescriptor.toString()),
getTypeFromDescriptor(returnType.getDescriptor()),
+ getString(shortyDescriptor.toString()),
getTypeListFromDescriptors(argumentDescriptors));
return proto;
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index e8420284f..44bc52926 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -94,34 +94,34 @@ public class JarCode extends Code {
throws ApiLevelException {
triggerDelayedParsingIfNeccessary();
return options.debug
- ? internalBuildWithLocals(encodedMethod, null, options)
- : internalBuild(encodedMethod, null, options);
+ ? internalBuildWithLocals(encodedMethod, options, null)
+ : internalBuild(encodedMethod, options, null);
}
public IRCode buildIR(
- DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)
+ DexEncodedMethod encodedMethod, InternalOptions options, ValueNumberGenerator generator)
throws ApiLevelException {
assert generator != null;
triggerDelayedParsingIfNeccessary();
return options.debug
- ? internalBuildWithLocals(encodedMethod, generator, options)
- : internalBuild(encodedMethod, generator, options);
+ ? internalBuildWithLocals(encodedMethod, options, generator)
+ : internalBuild(encodedMethod, options, generator);
}
private IRCode internalBuildWithLocals(
- DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)
+ DexEncodedMethod encodedMethod, InternalOptions options, ValueNumberGenerator generator)
throws ApiLevelException {
try {
- return internalBuild(encodedMethod, generator, options);
+ return internalBuild(encodedMethod, options, generator);
} catch (InvalidDebugInfoException e) {
options.warningInvalidDebugInfo(encodedMethod, origin, e);
node.localVariables.clear();
- return internalBuild(encodedMethod, generator, options);
+ return internalBuild(encodedMethod, options, generator);
}
}
private IRCode internalBuild(
- DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)
+ DexEncodedMethod encodedMethod, InternalOptions options, ValueNumberGenerator generator)
throws ApiLevelException {
if (!options.debug) {
node.localVariables.clear();
@@ -130,7 +130,7 @@ public class JarCode extends Code {
IRBuilder builder =
(generator == null)
? new IRBuilder(encodedMethod, source, options)
- : new IRBuilder(encodedMethod, source, generator, options);
+ : new IRBuilder(encodedMethod, source, options, generator);
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index bfbfb02c6..37b311962 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -959,10 +959,10 @@ public class BasicBlock {
* <p>The constructed basic block has no predecessors and has one
* successors which is the target block.
*
- * @param target the target of the goto block
* @param blockNumber the block number of the goto block
+ * @param target the target of the goto block
*/
- public static BasicBlock createGotoBlock(BasicBlock target, int blockNumber) {
+ public static BasicBlock createGotoBlock(int blockNumber, BasicBlock target) {
BasicBlock block = createGotoBlock(blockNumber);
block.getSuccessors().add(target);
return block;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index ff9e25732..7bf8ec102 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -219,7 +219,7 @@ public class BasicBlockInstructionIterator implements InstructionIterator, Instr
}
@Override
- public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blocksIterator) {
+ public BasicBlock split(IRCode code, int instructions, ListIterator<BasicBlock> blocksIterator) {
// Split at the current cursor position.
BasicBlock newBlock = split(code, blocksIterator);
assert blocksIterator == null || IteratorUtils.peekPrevious(blocksIterator) == newBlock;
@@ -341,7 +341,7 @@ public class BasicBlockInstructionIterator implements InstructionIterator, Instr
List<BasicBlock> blocksToRemove, DexType downcast) {
assert blocksToRemove != null;
boolean inlineeCanThrow = canThrow(inlinee);
- BasicBlock invokeBlock = split(1, code, blocksIterator);
+ BasicBlock invokeBlock = split(code, 1, blocksIterator);
assert invokeBlock.getInstructions().size() == 2;
assert invokeBlock.getInstructions().getFirst().isInvoke();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 5e0fe8ebf..e5fb6edfb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -74,7 +74,7 @@ public class IRCode {
// correct predecessor and successor structure. It is inserted
// at the end of the list of blocks disregarding branching
// structure.
- BasicBlock newBlock = BasicBlock.createGotoBlock(block, nextBlockNumber++);
+ BasicBlock newBlock = BasicBlock.createGotoBlock(nextBlockNumber++, block);
newBlocks.add(newBlock);
pred.replaceSuccessor(block, newBlock);
newBlock.getPredecessors().add(pred);
@@ -108,7 +108,7 @@ public class IRCode {
fallthrough = fallthrough.exit().fallthroughBlock();
}
if (fallthrough != null) {
- BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++);
+ BasicBlock newFallthrough = BasicBlock.createGotoBlock(nextBlockNumber++, fallthrough);
current.exit().setFallthroughBlock(newFallthrough);
newFallthrough.getPredecessors().add(current);
fallthrough.replacePredecessor(current, newFallthrough);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index fc7817b57..603ce6ecb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -126,8 +126,8 @@ public interface InstructionListIterator extends ListIterator<Instruction>,
* If the current block have catch handlers these catch handlers will be attached to the block
* containing the throwing instruction after the split.
*
- * @param instructions the number of instructions to include in the second block.
* @param code the IR code for the block this iterator originates from.
+ * @param instructions the number of instructions to include in the second block.
* @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
* just after the block for this is the instruction iterator. After this method returns it will be
* positioned just after the second block inserted. Calling {@link #remove} without further
@@ -135,13 +135,13 @@ public interface InstructionListIterator extends ListIterator<Instruction>,
* @return Returns the new block with the instructions after the cursor.
*/
// TODO(sgjesse): Refactor to avoid the need for passing code and blockIterator.
- BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator);
+ BasicBlock split(IRCode code, int instructions, ListIterator<BasicBlock> blockIterator);
/**
- * See {@link #split(int, IRCode, ListIterator)}.
+ * See {@link #split(IRCode, int, ListIterator)}.
*/
- default BasicBlock split(int instructions, IRCode code) {
- return split(instructions, code, null);
+ default BasicBlock split(IRCode code, int instructions) {
+ return split(code, instructions, null);
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 78c5c4e45..dc6d50067 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -70,7 +70,7 @@ public class Phi extends Value {
for (BasicBlock pred : block.getPredecessors()) {
EdgeType edgeType = pred.getEdgeType(block);
// Since this read has been delayed we must provide the local info for the value.
- Value operand = builder.readRegister(register, pred, edgeType, type, getLocalInfo());
+ Value operand = builder.readRegister(register, type, pred, edgeType, getLocalInfo());
canBeNull |= operand.canBeNull();
appendOperand(operand);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 1915155fb..ea43cf88c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -593,10 +593,10 @@ public class Value {
public boolean isDead(InternalOptions options) {
// Totally unused values are trivially dead.
- return !isUsed() || isDead(new HashSet<>(), options);
+ return !isUsed() || isDead(options, new HashSet<>());
}
- protected boolean isDead(Set<Value> active, InternalOptions options) {
+ protected boolean isDead(InternalOptions options, Set<Value> active) {
// If the value has debug users we cannot eliminate it since it represents a value in a local
// variable that should be visible in the debugger.
if (numberOfDebugUsers() != 0) {
@@ -613,12 +613,12 @@ public class Value {
// Instructions with no out value cannot be dead code by the current definition
// (unused out value). They typically side-effect input values or deals with control-flow.
assert outValue != null;
- if (!active.contains(outValue) && !outValue.isDead(active, options)) {
+ if (!active.contains(outValue) && !outValue.isDead(options, active)) {
return false;
}
}
for (Phi phi : uniquePhiUsers()) {
- if (!active.contains(phi) && !phi.isDead(active, options)) {
+ if (!active.contains(phi) && !phi.isDead(options, active)) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 042232b82..2335f2b46 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -386,7 +386,7 @@ public class DexBuilder {
if (ifsNeedingRewrite.contains(block)) {
If theIf = block.exit().asIf();
BasicBlock trueTarget = theIf.getTrueTarget();
- BasicBlock newBlock = BasicBlock.createGotoBlock(trueTarget, ir.blocks.size());
+ BasicBlock newBlock = BasicBlock.createGotoBlock(ir.blocks.size(), trueTarget);
theIf.setTrueTarget(newBlock);
theIf.invert();
it.add(newBlock);
@@ -641,7 +641,8 @@ public class DexBuilder {
item = tryItems.get(i);
coalescedTryItems.add(item);
// Trim the range start for non-throwing instructions when starting a new range.
- List<com.android.tools.r8.ir.code.Instruction> instructions = blocksWithHandlers.get(i).getInstructions();
+ List<com.android.tools.r8.ir.code.Instruction> instructions = blocksWithHandlers.get(i)
+ .getInstructions();
for (com.android.tools.r8.ir.code.Instruction insn : instructions) {
if (insn.instructionTypeCanThrow()) {
item.start = getInfo(insn).getOffset();
@@ -819,7 +820,8 @@ public class DexBuilder {
private Instruction[] instructions;
private final int size;
- public MultiFixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, Instruction[] instructions) {
+ public MultiFixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir,
+ Instruction[] instructions) {
super(ir);
this.instructions = instructions;
int size = 0;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 2dceeab8f..4a96ae664 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -291,14 +291,13 @@ public class IRBuilder {
private int nextBlockNumber = 0;
public IRBuilder(DexEncodedMethod method, SourceCode source, InternalOptions options) {
- this(method, source, new ValueNumberGenerator(), options);
+ this(method, source, options, new ValueNumberGenerator());
}
public IRBuilder(
DexEncodedMethod method,
SourceCode source,
- ValueNumberGenerator valueNumberGenerator,
- InternalOptions options) {
+ InternalOptions options, ValueNumberGenerator valueNumberGenerator) {
assert source != null;
this.method = method;
this.source = source;
@@ -1624,7 +1623,7 @@ public class IRBuilder {
public Value readRegister(int register, MoveType type) {
DebugLocalInfo local = getCurrentLocal(register);
- Value value = readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
+ Value value = readRegister(register, type, currentBlock, EdgeType.NON_EDGE, local);
// Check that any information about a current-local is consistent with the read.
if (local != null && value.getLocalInfo() != local && !value.isUninitializedLocal()) {
throw new InvalidDebugInfoException(
@@ -1643,10 +1642,10 @@ public class IRBuilder {
public Value readRegisterIgnoreLocal(int register, MoveType type) {
DebugLocalInfo local = getCurrentLocal(register);
- return readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
+ return readRegister(register, type, currentBlock, EdgeType.NON_EDGE, local);
}
- public Value readRegister(int register, BasicBlock block, EdgeType readingEdge, MoveType type,
+ public Value readRegister(int register, MoveType type, BasicBlock block, EdgeType readingEdge,
DebugLocalInfo local) {
checkRegister(register);
Value value = block.readCurrentDefinition(register, readingEdge);
@@ -1665,7 +1664,7 @@ public class IRBuilder {
assert block.verifyFilledPredecessors();
BasicBlock pred = block.getPredecessors().get(0);
EdgeType edgeType = pred.getEdgeType(block);
- value = readRegister(register, pred, edgeType, type, local);
+ value = readRegister(register, type, pred, edgeType, local);
} else {
Phi phi = new Phi(valueNumberGenerator.next(), block, type, local);
// We need to write the phi before adding operands to break cycles. If the phi is trivial
@@ -1939,7 +1938,7 @@ public class IRBuilder {
BasicBlock target = pair.second;
// New block with one unfilled predecessor.
- BasicBlock newBlock = BasicBlock.createGotoBlock(target, nextBlockNumber++);
+ BasicBlock newBlock = BasicBlock.createGotoBlock(nextBlockNumber++, target);
blocks.add(newBlock);
newBlock.incrementUnfilledPredecessorCount();
@@ -2018,7 +2017,7 @@ public class IRBuilder {
int otherPredecessorIndex = values.get(v);
BasicBlock joinBlock = joinBlocks.get(otherPredecessorIndex);
if (joinBlock == null) {
- joinBlock = BasicBlock.createGotoBlock(block, blocks.size() + blocksToAdd.size());
+ joinBlock = BasicBlock.createGotoBlock(blocks.size() + blocksToAdd.size(), block);
joinBlocks.put(otherPredecessorIndex, joinBlock);
blocksToAdd.add(joinBlock);
BasicBlock otherPredecessor = block.getPredecessors().get(otherPredecessorIndex);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index dda8031a6..da022fe86 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -82,11 +82,8 @@ public class IRConverter {
private DexString highestSortingString;
private IRConverter(
- Timing timing,
- AppInfo appInfo,
- GraphLense graphLense,
- InternalOptions options,
- CfgPrinter printer,
+ AppInfo appInfo, InternalOptions options, Timing timing,
+ CfgPrinter printer, GraphLense graphLense,
boolean enableWholeProgramOptimizations) {
assert appInfo != null;
assert options != null;
@@ -127,30 +124,26 @@ public class IRConverter {
public IRConverter(
AppInfo appInfo,
InternalOptions options) {
- this(null, appInfo, null, options, null, false);
+ this(appInfo, options, null, null, null, false);
}
/**
* Create an IR converter for processing methods with full program optimization disabled.
*/
public IRConverter(
- Timing timing,
- AppInfo appInfo,
- InternalOptions options,
+ AppInfo appInfo, InternalOptions options, Timing timing,
CfgPrinter printer) {
- this(timing, appInfo, null, options, printer, false);
+ this(appInfo, options, timing, printer, null, false);
}
/**
* Create an IR converter for processing methods with full program optimization enabled.
*/
public IRConverter(
- Timing timing,
- AppInfoWithSubtyping appInfo,
- InternalOptions options,
+ AppInfoWithSubtyping appInfo, InternalOptions options, Timing timing,
CfgPrinter printer,
GraphLense graphLense) {
- this(timing, appInfo, graphLense, options, printer, true);
+ this(appInfo, options, timing, printer, graphLense, true);
}
private boolean enableInterfaceMethodDesugaring() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index ab3efdbd4..8a309e270 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -116,7 +116,7 @@ public class LensCodeRewriter {
// If the current block has catch handlers split the check cast into its own block.
if (newInvoke.getBlock().hasCatchHandlers()) {
iterator.previous();
- iterator.split(1, code, blocks);
+ iterator.split(code, 1, blocks);
}
}
}
@@ -228,7 +228,9 @@ public class LensCodeRewriter {
return methodHandle;
}
- private Type getInvokeType(InvokeMethod invoke, DexMethod actualTarget,
+ private Type getInvokeType(
+ InvokeMethod invoke,
+ DexMethod actualTarget,
DexMethod originalTarget) {
if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
// Get the invoke type of the actual definition.
@@ -237,7 +239,8 @@ public class LensCodeRewriter {
return invoke.getType();
} else {
DexClass originalTargetClass = appInfo.definitionFor(originalTarget.holder);
- if (originalTargetClass.isInterface() ^ (invoke.getType() == Type.INTERFACE)) {
+ if ((originalTargetClass != null && originalTargetClass.isInterface())
+ ^ (invoke.getType() == Type.INTERFACE)) {
// The invoke was wrong to start with, so we keep it wrong. This is to ensure we get
// the IncompatibleClassChangeError the original invoke would have triggered.
return newTargetClass.accessFlags.isInterface()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index 160da3c16..1cf64fd2b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -20,7 +20,7 @@ import java.util.List;
final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {
AccessorMethodSourceCode(LambdaClass lambda) {
- super(/* no receiver for static method */ null, lambda, lambda.target.callTarget);
+ super(lambda, lambda.target.callTarget, null /* no receiver for static method */);
// We should never need an accessor for interface methods since
// they are supposed to be public.
assert !descriptor().implHandle.type.isInvokeInterface();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 31c6de175..408f53b60 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -58,6 +58,7 @@ import java.util.Set;
// forward the call to an appropriate method in interface companion class.
//
public final class InterfaceMethodRewriter {
+
// Public for testing.
public static final String COMPANION_CLASS_NAME_SUFFIX = "-CC";
private static final String DEFAULT_METHOD_PREFIX = "$default$";
@@ -75,11 +76,17 @@ public final class InterfaceMethodRewriter {
*/
private Set<DexItem> reportedMissing = Sets.newIdentityHashSet();
- /** Defines a minor variation in desugaring. */
+ /**
+ * Defines a minor variation in desugaring.
+ */
public enum Flavor {
- /** Process all application resources. */
+ /**
+ * Process all application resources.
+ */
IncludeAllResources,
- /** Process all but DEX application resources. */
+ /**
+ * Process all but DEX application resources.
+ */
ExcludeDexResources
}
@@ -174,13 +181,14 @@ public final class InterfaceMethodRewriter {
} else if (holderClass.isInterface()) {
throw new Unimplemented(
"Desugaring of static interface method handle as in `"
- + referencedFrom.toSourceString() + "` in is not yet supported.");
+ + referencedFrom.toSourceString() + "` in is not yet supported.");
}
}
}
/**
* Returns the class definition for the specified type.
+ *
* @return may return null if no definition for the given type is available.
*/
final DexClass findDefinitionFor(DexType type) {
@@ -313,7 +321,7 @@ public final class InterfaceMethodRewriter {
.append("`");
}
options.diagnosticsHandler.warning(
- new StringDiagnostic(classToDesugar.getOrigin(), builder.toString()));
+ new StringDiagnostic(builder.toString(), classToDesugar.getOrigin()));
}
private void warnMissingType(DexMethod referencedFrom, DexType missing) {
@@ -331,6 +339,6 @@ public final class InterfaceMethodRewriter {
.append("`");
DexClass referencedFromClass = converter.appInfo.definitionFor(referencedFrom.getHolder());
options.diagnosticsHandler.warning(
- new StringDiagnostic(referencedFromClass.getOrigin(), builder.toString()));
+ new StringDiagnostic(builder.toString(), referencedFromClass.getOrigin()));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
index 539fcf07d..411ab4ced 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -15,7 +15,7 @@ import java.util.Collections;
final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode {
LambdaClassConstructorSourceCode(LambdaClass lambda) {
- super(null /* Class initializer is static */, lambda, lambda.classConstructor);
+ super(lambda, lambda.classConstructor, null /* Class initializer is static */);
assert lambda.instanceField != null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
index 9a3531f14..bd38e1acb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -16,14 +16,14 @@ abstract class SynthesizedLambdaSourceCode extends SingleBlockSourceCode {
final DexMethod currentMethod;
final LambdaClass lambda;
- SynthesizedLambdaSourceCode(DexType receiver, LambdaClass lambda, DexMethod currentMethod) {
+ SynthesizedLambdaSourceCode(LambdaClass lambda, DexMethod currentMethod, DexType receiver) {
super(receiver, currentMethod.proto);
this.lambda = lambda;
this.currentMethod = currentMethod;
}
SynthesizedLambdaSourceCode(LambdaClass lambda, DexMethod currentMethod) {
- this(lambda.type, lambda, currentMethod);
+ this(lambda, currentMethod, lambda.type);
}
final LambdaDescriptor descriptor() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 9699ec16b..ad4639e7f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -232,14 +232,14 @@ public class Inliner {
GraphLense graphLense, InternalOptions options) throws ApiLevelException {
if (target.isProcessed()) {
assert target.getCode().isDexCode();
- return target.buildIR(generator, options);
+ return target.buildIR(options, generator);
} else {
// Build the IR for a yet not processed method, and perform minimal IR processing.
IRCode code;
if (target.getCode().isJarCode()) {
- code = target.getCode().asJarCode().buildIR(target, generator, options);
+ code = target.getCode().asJarCode().buildIR(target, options, generator);
} else {
- code = target.getCode().asDexCode().buildIR(target, generator, options);
+ code = target.getCode().asDexCode().buildIR(target, options, generator);
}
new LensCodeRewriter(graphLense, appInfo).rewrite(code, target);
return code;
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 4662cd0a8..83788c7c2 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -16,9 +16,9 @@ import com.android.tools.r8.utils.InternalOptions;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
-import jdk.internal.org.objectweb.asm.Type;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
public class CfApplicationWriter {
private final DexApplication application;
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 1360964ad..2e654a869 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -10,26 +12,79 @@ import com.android.tools.r8.graph.IndexedDexItem;
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.utils.DescriptorUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
-public class ClassNameMapper {
+public class ClassNameMapper implements ProguardMap {
+
+ static class Builder extends ProguardMap.Builder {
+ final ImmutableMap.Builder<String, ClassNamingForNameMapper.Builder> mapBuilder;
+
+ private Builder() {
+ mapBuilder = ImmutableMap.builder();
+ }
+
+ @Override
+ ClassNamingForNameMapper.Builder classNamingBuilder(String renamedName, String originalName) {
+ ClassNamingForNameMapper.Builder classNamingBuilder =
+ ClassNamingForNameMapper.builder(renamedName, originalName);
+ mapBuilder.put(renamedName, classNamingBuilder);
+ return classNamingBuilder;
+ }
+
+ @Override
+ ClassNameMapper build(){
+ return new ClassNameMapper(mapBuilder.build());
+ }
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ public static ClassNameMapper mapperFromInputStream(InputStream in) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+ try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ ClassNameMapper.Builder builder = ClassNameMapper.builder();
+ proguardReader.parse(builder);
+ return builder.build();
+ }
+ }
+
+ public static ClassNameMapper mapperFromFile(Path path) throws IOException {
+ return mapperFromInputStream(Files.newInputStream(path));
+ }
- private final ImmutableMap<String, ClassNaming> classNameMappings;
+ static ClassNameMapper mapperFromString(String contents) throws IOException {
+ return mapperFromInputStream(
+ new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ private final ImmutableMap<String, ClassNamingForNameMapper> classNameMappings;
private ImmutableBiMap<String, String> nameMapping;
private Map<Signature, Signature> signatureMap = new HashMap<>();
- ClassNameMapper(Map<String, ClassNaming> classNameMappings) {
- this.classNameMappings = ImmutableMap.copyOf(classNameMappings);
+ private ClassNameMapper(Map<String, ClassNamingForNameMapper.Builder> classNameMappings) {
+ ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
+ for(Map.Entry<String, ClassNamingForNameMapper.Builder> entry : classNameMappings.entrySet()) {
+ builder.put(entry.getKey(), entry.getValue().build());
+ }
+ this.classNameMappings = builder.build();
}
private Signature canonicalizeSignature(Signature signature) {
@@ -65,7 +120,7 @@ public class ClassNameMapper {
* Returns the deobfuscated name if a mapping was found. Otherwise it returns the passed in name.
*/
public String deobfuscateClassName(String name) {
- ClassNaming classNaming = classNameMappings.get(name);
+ ClassNamingForNameMapper classNaming = classNameMappings.get(name);
if (classNaming == null) {
return name;
}
@@ -73,15 +128,27 @@ public class ClassNameMapper {
}
private String deobfuscateType(String asString) {
- return DescriptorUtils.descriptorToJavaType(asString, this);
+ return descriptorToJavaType(asString, this);
}
- public ClassNaming getClassNaming(String name) {
+ @Override
+ public boolean hasMapping(DexType type) {
+ String decoded = descriptorToJavaType(type.descriptor.toString());
+ return classNameMappings.containsKey(decoded);
+ }
+
+ @Override
+ public ClassNamingForNameMapper getClassNaming(DexType type) {
+ String decoded = descriptorToJavaType(type.descriptor.toString());
+ return classNameMappings.get(decoded);
+ }
+
+ public ClassNamingForNameMapper getClassNaming(String name) {
return classNameMappings.get(name);
}
public void write(Writer writer, boolean collapseRanges) throws IOException {
- for (ClassNaming naming : classNameMappings.values()) {
+ for (ClassNamingForNameMapper naming : classNameMappings.values()) {
naming.write(writer, collapseRanges);
}
}
@@ -129,15 +196,15 @@ public class ClassNameMapper {
} else if (item instanceof DexMethod) {
return lookupName(getRenamedMethodSignature((DexMethod) item), ((DexMethod) item).holder);
} else if (item instanceof DexType) {
- return DescriptorUtils.descriptorToJavaType(((DexType) item).toDescriptorString(), this);
+ return descriptorToJavaType(((DexType) item).toDescriptorString(), this);
} else {
return item.toString();
}
}
private String lookupName(Signature signature, DexType clazz) {
- String decoded = DescriptorUtils.descriptorToJavaType(clazz.descriptor.toString());
- ClassNaming classNaming = getClassNaming(decoded);
+ String decoded = descriptorToJavaType(clazz.descriptor.toString());
+ ClassNamingForNameMapper classNaming = getClassNaming(decoded);
if (classNaming == null) {
return decoded + " " + signature.toString();
}
@@ -149,8 +216,7 @@ public class ClassNameMapper {
}
public Signature originalSignatureOf(DexMethod method) {
- String decoded = DescriptorUtils
- .descriptorToJavaType(method.holder.descriptor.toString());
+ String decoded = descriptorToJavaType(method.holder.descriptor.toString());
MethodSignature memberSignature = getRenamedMethodSignature(method);
ClassNaming classNaming = getClassNaming(decoded);
if (classNaming == null) {
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
index 74c50068c..d211e7b67 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -4,114 +4,30 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.naming.MemberNaming.Signature;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
+import com.android.tools.r8.utils.ThrowingConsumer;
/**
* Stores name information for a class.
* <p>
- * This includes how the class was renamed and information on the classes members.
+ * Implementers will include how the class was renamed and information on the class's members.
*/
-public class ClassNaming {
+public interface ClassNaming {
- public final String originalName;
- public final String renamedName;
-
- /**
- * Mapping from the renamed signature to the naming information for a member.
- * <p>
- * A renamed signature is a signature where the member's name has been obfuscated but not the type
- * information.
- **/
- final Map<Signature, MemberNaming> members = new LinkedHashMap<>();
-
- ClassNaming(String renamedName, String originalName) {
- this.renamedName = renamedName;
- this.originalName = originalName;
- }
-
- void addMemberEntry(MemberNaming entry) {
- Signature renamedSignature = entry.renamedSignature;
- members.put(renamedSignature, entry);
- }
-
- public MemberNaming lookup(Signature renamedSignature) {
- return members.get(renamedSignature);
+ abstract class Builder {
+ abstract Builder addMemberEntry(MemberNaming entry);
+ abstract ClassNaming build();
}
- public MemberNaming lookupByOriginalSignature(Signature original) {
- for (MemberNaming naming : members.values()) {
- if (naming.signature.equals(original)) {
- return naming;
- }
- }
- return null;
- }
+ MemberNaming lookup(Signature renamedSignature);
- public List<MemberNaming> lookupByOriginalName(String originalName) {
- List<MemberNaming> result = new ArrayList<>();
- for (MemberNaming naming : members.values()) {
- if (naming.signature.name.equals(originalName)) {
- result.add(naming);
- }
- }
- return result;
- }
+ MemberNaming lookupByOriginalSignature(Signature original);
- public void forAllMemberNaming(Consumer<MemberNaming> consumer) {
- members.values().forEach(consumer);
- }
+ <T extends Throwable> void forAllMemberNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T;
- void write(Writer writer, boolean collapseRanges) throws IOException {
- writer.append(originalName);
- writer.append(" -> ");
- writer.append(renamedName);
- writer.append(":\n");
- for (MemberNaming member : members.values()) {
- member.write(writer, collapseRanges, true);
- }
- }
+ <T extends Throwable> void forAllFieldNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T;
- @Override
- public String toString() {
- try {
- StringWriter writer = new StringWriter();
- write(writer, false);
- return writer.toString();
- } catch (IOException e) {
- return e.toString();
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof ClassNaming)) {
- return false;
- }
-
- ClassNaming that = (ClassNaming) o;
-
- return originalName.equals(that.originalName)
- && renamedName.equals(that.renamedName)
- && members.equals(that.members);
-
- }
-
- @Override
- public int hashCode() {
- int result = originalName.hashCode();
- result = 31 * result + renamedName.hashCode();
- result = 31 * result + members.hashCode();
- return result;
- }
+ <T extends Throwable> void forAllMethodNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T;
}
-
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
new file mode 100644
index 000000000..d94fe57c0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -0,0 +1,193 @@
+// Copyright (c) 2017, 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 com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+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.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Stores name information for a class.
+ * <p>
+ * The main differences of this against {@link ClassNamingForNameMapper} are:
+ * 1) field and method mappings are maintained and searched separately for faster lookup;
+ * 2) similar to the relation between {@link ClassNameMapper} and {@link SeedMapper}, this one
+ * uses original {@link Signature} as a key to look up {@link MemberNaming},
+ * whereas {@link ClassNamingForNameMapper} uses renamed {@link Signature} as a key; and thus
+ * 3) logic of {@link #lookup} and {@link #lookupByOriginalSignature} are inverted; and
+ * 4) {@link #lookupByOriginalItem}'s are introduced for lightweight lookup.
+ */
+public class ClassNamingForMapApplier implements ClassNaming {
+
+ public static class Builder extends ClassNaming.Builder {
+ private final String originalName;
+ private final String renamedName;
+ private final Map<MethodSignature, MemberNaming> methodMembers = new HashMap<>();
+ private final Map<FieldSignature, MemberNaming> fieldMembers = new HashMap<>();
+
+ private Builder(String renamedName, String originalName) {
+ this.originalName = originalName;
+ this.renamedName = renamedName;
+ }
+
+ @Override
+ ClassNaming.Builder addMemberEntry(MemberNaming entry) {
+ // Unlike {@link ClassNamingForNameMapper.Builder#addMemberEntry},
+ // the key is original signature.
+ if (entry.isMethodNaming()) {
+ methodMembers.put((MethodSignature) entry.getOriginalSignature(), entry);
+ } else {
+ fieldMembers.put((FieldSignature) entry.getOriginalSignature(), entry);
+ }
+ return this;
+ }
+
+ @Override
+ ClassNamingForMapApplier build() {
+ return new ClassNamingForMapApplier(renamedName, originalName, methodMembers, fieldMembers);
+ }
+ }
+
+ static Builder builder(String renamedName, String originalName) {
+ return new Builder(renamedName, originalName);
+ }
+
+ private final String originalName;
+ final String renamedName;
+
+ private final ImmutableMap<MethodSignature, MemberNaming> methodMembers;
+ private final ImmutableMap<FieldSignature, MemberNaming> fieldMembers;
+
+ // Constructor to help chaining {@link ClassNamingForMapApplier} according to class hierarchy.
+ ClassNamingForMapApplier(ClassNamingForMapApplier proxy) {
+ this(proxy.renamedName, proxy.originalName, proxy.methodMembers, proxy.fieldMembers);
+ }
+
+ private ClassNamingForMapApplier(
+ String renamedName,
+ String originalName,
+ Map<MethodSignature, MemberNaming> methodMembers,
+ Map<FieldSignature, MemberNaming> fieldMembers) {
+ this.renamedName = renamedName;
+ this.originalName = originalName;
+ this.methodMembers = ImmutableMap.copyOf(methodMembers);
+ this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
+ }
+
+ @Override
+ public <T extends Throwable> void forAllMemberNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ forAllFieldNaming(consumer);
+ forAllMethodNaming(consumer);
+ }
+
+ @Override
+ public <T extends Throwable> void forAllFieldNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ for (MemberNaming naming : fieldMembers.values()) {
+ consumer.accept(naming);
+ }
+ }
+
+ @Override
+ public <T extends Throwable> void forAllMethodNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ for (MemberNaming naming : methodMembers.values()) {
+ consumer.accept(naming);
+ }
+ }
+
+ @Override
+ public MemberNaming lookup(Signature renamedSignature) {
+ // As the key is inverted, this looks a lot like
+ // {@link ClassNamingForNameMapper#lookupByOriginalSignature}.
+ if (renamedSignature.kind() == SignatureKind.METHOD) {
+ for (MemberNaming memberNaming : methodMembers.values()) {
+ if (memberNaming.getRenamedSignature().equals(renamedSignature)) {
+ return memberNaming;
+ }
+ }
+ return null;
+ } else {
+ assert renamedSignature.kind() == SignatureKind.FIELD;
+ for (MemberNaming memberNaming : fieldMembers.values()) {
+ if (memberNaming.getRenamedSignature().equals(renamedSignature)) {
+ return memberNaming;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public MemberNaming lookupByOriginalSignature(Signature original) {
+ // As the key is inverted, this looks a lot like {@link ClassNamingForNameMapper#lookup}.
+ if (original.kind() == SignatureKind.METHOD) {
+ return methodMembers.get(original);
+ } else {
+ assert original.kind() == SignatureKind.FIELD;
+ return fieldMembers.get(original);
+ }
+ }
+
+ MemberNaming lookupByOriginalItem(DexField field) {
+ for (Map.Entry<FieldSignature, MemberNaming> entry : fieldMembers.entrySet()) {
+ FieldSignature signature = entry.getKey();
+ if (signature.name.equals(field.name.toString())
+ && signature.type.equals(field.type.getName())) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ protected MemberNaming lookupByOriginalItem(DexMethod method) {
+ for (Map.Entry<MethodSignature, MemberNaming> entry : methodMembers.entrySet()) {
+ MethodSignature signature = entry.getKey();
+ if (signature.name.equals(method.name.toString())
+ && signature.type.equals(method.proto.returnType.toString())
+ && Arrays.equals(signature.parameters,
+ Arrays.stream(method.proto.parameters.values)
+ .map(DexType::toString).toArray(String[]::new))) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ClassNamingForMapApplier)) {
+ return false;
+ }
+
+ ClassNamingForMapApplier that = (ClassNamingForMapApplier) o;
+
+ return originalName.equals(that.originalName)
+ && renamedName.equals(that.renamedName)
+ && methodMembers.equals(that.methodMembers)
+ && fieldMembers.equals(that.fieldMembers);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = originalName.hashCode();
+ result = 31 * result + renamedName.hashCode();
+ result = 31 * result + methodMembers.hashCode();
+ result = 31 * result + fieldMembers.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
new file mode 100644
index 000000000..158549293
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -0,0 +1,194 @@
+// Copyright (c) 2017, 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 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.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stores name information for a class.
+ * <p>
+ * This includes how the class was renamed and information on the classes members.
+ */
+public class ClassNamingForNameMapper implements ClassNaming {
+
+ public static class Builder extends ClassNaming.Builder {
+ private final String originalName;
+ private final String renamedName;
+ private final Map<MethodSignature, MemberNaming> methodMembers = new HashMap<>();
+ private final Map<FieldSignature, MemberNaming> fieldMembers = new HashMap<>();
+
+ private Builder(String renamedName, String originalName) {
+ this.originalName = originalName;
+ this.renamedName = renamedName;
+ }
+
+ @Override
+ ClassNaming.Builder addMemberEntry(MemberNaming entry) {
+ if (entry.isMethodNaming()) {
+ methodMembers.put((MethodSignature) entry.getRenamedSignature(), entry);
+ } else {
+ fieldMembers.put((FieldSignature) entry.getRenamedSignature(), entry);
+ }
+ return this;
+ }
+
+ @Override
+ ClassNamingForNameMapper build() {
+ return new ClassNamingForNameMapper(renamedName, originalName, methodMembers, fieldMembers);
+ }
+ }
+
+ static Builder builder(String renamedName, String originalName) {
+ return new Builder(renamedName, originalName);
+ }
+
+ public final String originalName;
+ private final String renamedName;
+
+ /**
+ * Mapping from the renamed signature to the naming information for a member.
+ * <p>
+ * A renamed signature is a signature where the member's name has been obfuscated but not the type
+ * information.
+ **/
+ private final ImmutableMap<MethodSignature, MemberNaming> methodMembers;
+ private final ImmutableMap<FieldSignature, MemberNaming> fieldMembers;
+
+ private ClassNamingForNameMapper(
+ String renamedName,
+ String originalName,
+ Map<MethodSignature, MemberNaming> methodMembers,
+ Map<FieldSignature, MemberNaming> fieldMembers) {
+ this.renamedName = renamedName;
+ this.originalName = originalName;
+ this.methodMembers = ImmutableMap.copyOf(methodMembers);
+ this.fieldMembers = ImmutableMap.copyOf(fieldMembers);
+ }
+
+ @Override
+ public MemberNaming lookup(Signature renamedSignature) {
+ if (renamedSignature.kind() == SignatureKind.METHOD) {
+ return methodMembers.get(renamedSignature);
+ } else {
+ assert renamedSignature.kind() == SignatureKind.FIELD;
+ return fieldMembers.get(renamedSignature);
+ }
+ }
+
+ @Override
+ public MemberNaming lookupByOriginalSignature(Signature original) {
+ if (original.kind() == SignatureKind.METHOD) {
+ for (MemberNaming memberNaming: methodMembers.values()) {
+ if (memberNaming.signature.equals(original)) {
+ return memberNaming;
+ }
+ }
+ return null;
+ } else {
+ assert original.kind() == SignatureKind.FIELD;
+ for (MemberNaming memberNaming : fieldMembers.values()) {
+ if (memberNaming.signature.equals(original)) {
+ return memberNaming;
+ }
+ }
+ return null;
+ }
+ }
+
+ public List<MemberNaming> lookupByOriginalName(String originalName) {
+ List<MemberNaming> result = new ArrayList<>();
+ for (MemberNaming naming : methodMembers.values()) {
+ if (naming.signature.name.equals(originalName)) {
+ result.add(naming);
+ }
+ }
+ for (MemberNaming naming : fieldMembers.values()) {
+ if (naming.signature.name.equals(originalName)) {
+ result.add(naming);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public <T extends Throwable> void forAllMemberNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ forAllFieldNaming(consumer);
+ forAllMethodNaming(consumer);
+ }
+
+ @Override
+ public <T extends Throwable> void forAllFieldNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ for (MemberNaming naming : fieldMembers.values()) {
+ consumer.accept(naming);
+ }
+ }
+
+ @Override
+ public <T extends Throwable> void forAllMethodNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ for (MemberNaming naming : methodMembers.values()) {
+ consumer.accept(naming);
+ }
+ }
+
+ void write(Writer writer, boolean collapseRanges) throws IOException {
+ writer.append(originalName);
+ writer.append(" -> ");
+ writer.append(renamedName);
+ writer.append(":\n");
+ forAllMemberNaming(memberNaming -> memberNaming.write(writer, collapseRanges, true));
+ }
+
+ @Override
+ public String toString() {
+ try {
+ StringWriter writer = new StringWriter();
+ write(writer, false);
+ return writer.toString();
+ } catch (IOException e) {
+ return e.toString();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ClassNamingForNameMapper)) {
+ return false;
+ }
+
+ ClassNamingForNameMapper that = (ClassNamingForNameMapper) o;
+
+ return originalName.equals(that.originalName)
+ && renamedName.equals(that.renamedName)
+ && methodMembers.equals(that.methodMembers)
+ && fieldMembers.equals(that.fieldMembers);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = originalName.hashCode();
+ result = 31 * result + renamedName.hashCode();
+ result = 31 * result + methodMembers.hashCode();
+ result = 31 * result + fieldMembers.hashCode();
+ return result;
+ }
+}
+
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 5f2ea9391..c494c0881 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -3,8 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
@@ -90,6 +93,14 @@ public class MemberNaming {
return signature;
}
+ public String getOriginalName() {
+ return signature.name;
+ }
+
+ public Signature getRenamedSignature() {
+ return renamedSignature;
+ }
+
public String getRenamedName() {
return renamedSignature.name;
}
@@ -222,6 +233,13 @@ public class MemberNaming {
field.type.toSourceString());
}
+ DexField toDexField(DexItemFactory factory, DexType clazz) {
+ return factory.createField(
+ clazz,
+ factory.createType(javaTypeToDescriptor(type)),
+ factory.createString(name));
+ }
+
@Override
Signature asRenamed(String renamedName) {
return new FieldSignature(renamedName, type);
@@ -297,6 +315,18 @@ public class MemberNaming {
parameterTypes);
}
+ DexMethod toDexMethod(DexItemFactory factory, DexType clazz) {
+ DexType[] paramTypes = new DexType[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ paramTypes[i] = factory.createType(javaTypeToDescriptor(parameters[i]));
+ }
+ DexType returnType = factory.createType(javaTypeToDescriptor(type));
+ return factory.createMethod(
+ clazz,
+ factory.createProto(returnType, paramTypes),
+ factory.createString(name));
+ }
+
public static MethodSignature initializer(String[] parameters) {
return new MethodSignature(Constants.INSTANCE_INITIALIZER_NAME, "void", parameters);
}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
index e9ec08c3c..0e971c2e5 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
@@ -43,24 +43,24 @@ public class MinifiedNameMapPrinter {
return copy;
}
- private void write(DexProgramClass clazz, PrintStream out) {
+ private void writeClass(DexProgramClass clazz, PrintStream out) {
seenTypes.add(clazz.type);
DexString descriptor = namingLens.lookupDescriptor(clazz.type);
out.print(DescriptorUtils.descriptorToJavaType(clazz.type.descriptor.toSourceString()));
out.print(" -> ");
out.print(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
out.println(":");
- write(sortedCopy(
+ writeFields(sortedCopy(
clazz.instanceFields(), Comparator.comparing(DexEncodedField::toSourceString)), out);
- write(sortedCopy(
+ writeFields(sortedCopy(
clazz.staticFields(), Comparator.comparing(DexEncodedField::toSourceString)), out);
- write(sortedCopy(
+ writeMethods(sortedCopy(
clazz.directMethods(), Comparator.comparing(DexEncodedMethod::toSourceString)), out);
- write(sortedCopy(
+ writeMethods(sortedCopy(
clazz.virtualMethods(), Comparator.comparing(DexEncodedMethod::toSourceString)), out);
}
- private void write(DexType type, PrintStream out) {
+ private void writeType(DexType type, PrintStream out) {
if (type.isClassType() && seenTypes.add(type)) {
DexString descriptor = namingLens.lookupDescriptor(type);
out.print(DescriptorUtils.descriptorToJavaType(type.descriptor.toSourceString()));
@@ -70,7 +70,7 @@ public class MinifiedNameMapPrinter {
}
}
- private void write(DexEncodedField[] fields, PrintStream out) {
+ private void writeFields(DexEncodedField[] fields, PrintStream out) {
for (DexEncodedField encodedField : fields) {
DexField field = encodedField.field;
DexString renamed = namingLens.lookupName(field);
@@ -102,7 +102,7 @@ public class MinifiedNameMapPrinter {
out.println(renamed);
}
- private void write(DexEncodedMethod[] methods, PrintStream out) {
+ private void writeMethods(DexEncodedMethod[] methods, PrintStream out) {
for (DexEncodedMethod encodedMethod : methods) {
DexMethod method = encodedMethod.method;
DexString renamed = namingLens.lookupName(method);
@@ -125,9 +125,9 @@ public class MinifiedNameMapPrinter {
// First write out all classes that have been renamed.
List<DexProgramClass> classes = new ArrayList<>(application.classes());
classes.sort(Comparator.comparing(DexProgramClass::toSourceString));
- classes.forEach(clazz -> write(clazz, out));
+ classes.forEach(clazz -> writeClass(clazz, out));
// Now write out all types only mentioned in descriptors that have been renamed.
- namingLens.forAllRenamedTypes(type -> write(type, out));
+ namingLens.forAllRenamedTypes(type -> writeType(type, out));
}
public void write(Path destination) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMap.java b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
new file mode 100644
index 000000000..5b5c76e05
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, 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 com.android.tools.r8.graph.DexType;
+
+public interface ProguardMap {
+
+ abstract class Builder {
+ abstract ClassNaming.Builder classNamingBuilder(String renamedName, String originalName);
+ abstract ProguardMap build();
+ }
+
+ boolean hasMapping(DexType type);
+ ClassNaming getClassNaming(DexType type);
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
new file mode 100644
index 000000000..1a2551ed0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
@@ -0,0 +1,475 @@
+// Copyright (c) 2017, 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 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.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.Timing;
+import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class ProguardMapApplier {
+
+ private final AppInfoWithLiveness appInfo;
+ private final GraphLense previousLense;
+ private final SeedMapper seedMapper;
+
+ public ProguardMapApplier(
+ AppInfoWithLiveness appInfo,
+ GraphLense previousLense,
+ SeedMapper seedMapper) {
+ this.appInfo = appInfo;
+ this.previousLense = previousLense;
+ this.seedMapper = seedMapper;
+ }
+
+ public GraphLense run(Timing timing) {
+ timing.begin("from-pg-map-to-lense");
+ GraphLense lenseFromMap = new MapToLenseConverter().run(previousLense);
+ timing.end();
+ timing.begin("fix-types-in-programs");
+ GraphLense typeFixedLense = new TreeFixer(lenseFromMap).run();
+ timing.end();
+ return typeFixedLense;
+ }
+
+ class MapToLenseConverter {
+
+ private final ConflictFreeBuilder lenseBuilder;
+ private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
+
+ MapToLenseConverter() {
+ lenseBuilder = new ConflictFreeBuilder();
+ }
+
+ private GraphLense run(GraphLense previousLense) {
+ // To handle inherited yet undefined methods in library classes, we are traversing types in
+ // a subtyping order. That also helps us detect conflicted mappings in a diamond case:
+ // LibItfA#foo -> a, LibItfB#foo -> b, while PrgA implements LibItfA and LibItfB.
+ // For all type appearances in members, we apply class mappings on-the-fly, e.g.,
+ // LibA -> a:
+ // ...foo(LibB) -> bar
+ // LibB -> b:
+ // Suppose PrgA extends LibA, and type map for LibB to b is not applied when visiting PrgA.
+ // Then, method map would look like: PrgA#foo(LibB) -> PrgA#bar(LibB),
+ // which should be: PrgA#foo(LibB) -> PrgA#bar(b).
+ // In this case, we should check class naming for LibB in the given pg-map, and if exist,
+ // should apply that naming at the time when making a mapping for PrgA#foo.
+ applyClassMappingForClasses(appInfo.dexItemFactory.objectType, null);
+ // TODO(b/64802420): maybe worklist-based visiting?
+ // Suppose PrgA implements LibItfA, LibItfB, and LibItfC; and PrgB extends PrgA.
+ // With interface hierarchy-based visiting, both program classes are visited three times.
+ DexType.forAllInterfaces(
+ appInfo.dexItemFactory,
+ itf -> applyClassMappingForInterfaces(itf, null));
+ return lenseBuilder.build(appInfo.dexItemFactory, previousLense);
+ }
+
+ private void applyClassMappingForClasses(
+ DexType type, ChainedClassNaming classNamingFromSuperType) {
+ ChainedClassNaming classNaming = chainClassNaming(type, classNamingFromSuperType);
+ applyClassMapping(type, classNaming);
+ type.forAllExtendsSubtypes(subtype -> {
+ applyClassMappingForClasses(subtype, classNaming);
+ });
+ }
+
+ private void applyClassMappingForInterfaces(
+ DexType type, ChainedClassNaming classNamingFromSuperType) {
+ ChainedClassNaming classNaming = chainClassNaming(type, classNamingFromSuperType);
+ DexClass clazz = appInfo.definitionFor(type);
+ // We account program classes that implement library interfaces (to be obfuscated).
+ if (clazz != null && clazz.isProgramClass()) {
+ applyClassMapping(type, classNaming);
+ }
+ type.forAllExtendsSubtypes(subtype -> {
+ applyClassMappingForInterfaces(subtype, classNaming);
+ });
+ type.forAllImplementsSubtypes(subtype -> {
+ applyClassMappingForInterfaces(subtype, classNaming);
+ });
+ }
+
+ private ChainedClassNaming chainClassNaming(
+ DexType type, ChainedClassNaming classNamingFromSuperType) {
+ // Use super-type's mapping or update the mapping if the current type has its own mapping.
+ return seedMapper.hasMapping(type)
+ ? new ChainedClassNaming(classNamingFromSuperType, seedMapper.getClassNaming(type))
+ : classNamingFromSuperType;
+ }
+
+ private void applyClassMapping(DexType type, ChainedClassNaming classNaming) {
+ if (classNaming != null) {
+ if (seedMapper.hasMapping(type) && lenseBuilder.lookup(type) == type) {
+ DexType appliedType = appInfo.dexItemFactory.createType(classNaming.renamedName);
+ lenseBuilder.map(type, appliedType);
+ }
+ applyMemberMapping(type, classNaming);
+ }
+ }
+
+ private void applyMemberMapping(DexType from, ChainedClassNaming classNaming) {
+ DexClass clazz = appInfo.definitionFor(from);
+ if (clazz == null) return;
+
+ Set<MemberNaming> appliedMemberNaming = new HashSet<>();
+ clazz.forEachField(encodedField -> {
+ MemberNaming memberNaming = classNaming.lookupByOriginalItem(encodedField.field);
+ if (memberNaming != null) {
+ appliedMemberNaming.add(memberNaming);
+ applyFieldMapping(encodedField.field, memberNaming);
+ }
+ });
+
+ clazz.forEachMethod(encodedMethod -> {
+ MemberNaming memberNaming = classNaming.lookupByOriginalItem(encodedMethod.method);
+ if (memberNaming != null) {
+ appliedMemberNaming.add(memberNaming);
+ applyMethodMapping(encodedMethod.method, memberNaming);
+ }
+ });
+
+ // We need to handle a lib class that extends another lib class where some members are not
+ // overridden, resulting in absence of definitions. References to those members need to be
+ // redirected via lense as well.
+ if (clazz.isLibraryClass()) {
+ classNaming.forAllFieldNaming(memberNaming -> {
+ if (!appliedMemberNaming.contains(memberNaming)) {
+ DexField pretendedOriginalField =
+ ((FieldSignature) memberNaming.getOriginalSignature())
+ .toDexField(appInfo.dexItemFactory, from);
+ applyFieldMapping(pretendedOriginalField, memberNaming);
+ }
+ });
+ classNaming.forAllMethodNaming(memberNaming -> {
+ if (!appliedMemberNaming.contains(memberNaming)) {
+ DexMethod pretendedOriginalMethod =
+ ((MethodSignature) memberNaming.getOriginalSignature())
+ .toDexMethod(appInfo.dexItemFactory, from);
+ applyMethodMapping(pretendedOriginalMethod, memberNaming);
+ }
+ });
+ }
+ }
+
+ private void applyFieldMapping(DexField originalField, MemberNaming memberNaming) {
+ FieldSignature appliedSignature = (FieldSignature) memberNaming.getRenamedSignature();
+ DexField appliedField =
+ appInfo.dexItemFactory.createField(
+ applyClassMappingOnTheFly(originalField.clazz),
+ applyClassMappingOnTheFly(originalField.type),
+ appInfo.dexItemFactory.createString(appliedSignature.name));
+ lenseBuilder.map(originalField, appliedField);
+ }
+
+ private void applyMethodMapping(DexMethod originalMethod, MemberNaming memberNaming) {
+ MethodSignature appliedSignature = (MethodSignature) memberNaming.getRenamedSignature();
+ DexMethod appliedMethod =
+ appInfo.dexItemFactory.createMethod(
+ applyClassMappingOnTheFly(originalMethod.holder),
+ applyClassMappingOnTheFly(originalMethod.proto),
+ appInfo.dexItemFactory.createString(appliedSignature.name));
+ lenseBuilder.map(originalMethod, appliedMethod);
+ }
+
+ private DexType applyClassMappingOnTheFly(DexType from) {
+ if (seedMapper.hasMapping(from)) {
+ DexType appliedType = lenseBuilder.lookup(from);
+ if (appliedType != from) {
+ return appliedType;
+ }
+ // If not applied yet, build the type mapping here.
+ // Note that, unlike {@link #applyClassMapping}, we don't apply member mappings.
+ ClassNamingForMapApplier classNaming = seedMapper.getClassNaming(from);
+ appliedType = appInfo.dexItemFactory.createType(classNaming.renamedName);
+ lenseBuilder.map(from, appliedType);
+ return appliedType;
+ }
+ return from;
+ }
+
+ private DexProto applyClassMappingOnTheFly(DexProto proto) {
+ DexProto result = protoFixupCache.get(proto);
+ if (result == null) {
+ DexType returnType = applyClassMappingOnTheFly(proto.returnType);
+ DexType[] arguments = applyClassMappingOnTheFly(proto.parameters.values);
+ if (arguments != null || returnType != proto.returnType) {
+ arguments = arguments == null ? proto.parameters.values : arguments;
+ result = appInfo.dexItemFactory.createProto(returnType, arguments);
+ } else {
+ result = proto;
+ }
+ protoFixupCache.put(proto, result);
+ }
+ return result;
+ }
+
+ private DexType[] applyClassMappingOnTheFly(DexType[] types) {
+ Map<Integer, DexType> changed = new Int2ObjectArrayMap<>();
+ for (int i = 0; i < types.length; i++) {
+ DexType applied = applyClassMappingOnTheFly(types[i]);
+ if (applied != types[i]) {
+ changed.put(i, applied);
+ }
+ }
+ return changed.isEmpty()
+ ? null
+ : ArrayUtils.copyWithSparseChanges(DexType[].class, types, changed);
+ }
+ }
+
+ static class ChainedClassNaming extends ClassNamingForMapApplier {
+ final ChainedClassNaming superClassNaming;
+
+ ChainedClassNaming(
+ ChainedClassNaming superClassNaming,
+ ClassNamingForMapApplier thisClassNaming) {
+ super(thisClassNaming);
+ this.superClassNaming = superClassNaming;
+ }
+
+ @Override
+ public <T extends Throwable> void forAllMethodNaming(
+ ThrowingConsumer<MemberNaming, T> consumer) throws T {
+ super.forAllMethodNaming(consumer);
+ if (superClassNaming != null) {
+ superClassNaming.forAllMethodNaming(consumer);
+ }
+ }
+
+ @Override
+ protected MemberNaming lookupByOriginalItem(DexMethod method) {
+ MemberNaming memberNaming = super.lookupByOriginalItem(method);
+ if (memberNaming != null) {
+ return memberNaming;
+ }
+ // Moving up if chained.
+ if (superClassNaming != null) {
+ return superClassNaming.lookupByOriginalItem(method);
+ }
+ return null;
+ }
+ }
+
+ class TreeFixer {
+ private final ConflictFreeBuilder lenseBuilder;
+ private final GraphLense appliedLense;
+ private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
+
+ TreeFixer(GraphLense appliedLense) {
+ this.lenseBuilder = new ConflictFreeBuilder();
+ this.appliedLense = appliedLense;
+ }
+
+ private GraphLense run() {
+ // Suppose PrgA extends LibA, and adds its own method, say foo that inputs LibA instance.
+ // If that library class and members are renamed somehow, all the inherited members in PrgA
+ // will be also renamed when applying those mappings. However, that newly added method, foo,
+ // with renamed LibA as an argument, won't be updated. Here at TreeFixer, we want to change
+ // PrgA#foo signature: from LibA to a renamed name.
+ appInfo.classes().forEach(this::fixClass);
+ appInfo.libraryClasses().forEach(this::fixClass);
+ return lenseBuilder.build(appInfo.dexItemFactory, appliedLense);
+ }
+
+ private void fixClass(DexClass clazz) {
+ clazz.type = substituteType(clazz.type, null);
+ clazz.superType = substituteType(clazz.superType, null);
+ clazz.interfaces = substituteTypesIn(clazz.interfaces);
+ clazz.annotations = substituteTypesIn(clazz.annotations);
+ clazz.setDirectMethods(substituteTypesIn(clazz.directMethods()));
+ clazz.setVirtualMethods(substituteTypesIn(clazz.virtualMethods()));
+ clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
+ clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
+ }
+
+ private DexEncodedMethod[] substituteTypesIn(DexEncodedMethod[] methods) {
+ if (methods == null) {
+ return null;
+ }
+ for (int i = 0; i < methods.length; i++) {
+ DexEncodedMethod encodedMethod = methods[i];
+ DexMethod appliedMethod = appliedLense.lookupMethod(encodedMethod.method, encodedMethod);
+ DexProto newProto = substituteTypesIn(appliedMethod.proto, encodedMethod);
+ DexMethod newMethod;
+ if (newProto != appliedMethod.proto) {
+ newMethod = appInfo.dexItemFactory.createMethod(
+ substituteType(appliedMethod.holder, encodedMethod), newProto, appliedMethod.name);
+ lenseBuilder.map(encodedMethod.method, newMethod);
+ } else {
+ newMethod = appliedMethod;
+ }
+ // Explicitly fix members.
+ methods[i] = encodedMethod.toTypeSubstitutedMethod(newMethod);
+ }
+ return methods;
+ }
+
+ private DexEncodedField[] substituteTypesIn(DexEncodedField[] fields) {
+ if (fields == null) {
+ return null;
+ }
+ for (int i = 0; i < fields.length; i++) {
+ DexEncodedField encodedField = fields[i];
+ DexField appliedField = appliedLense.lookupField(encodedField.field, null);
+ DexType newType = substituteType(appliedField.type, null);
+ DexField newField;
+ if (newType != appliedField.type) {
+ newField = appInfo.dexItemFactory.createField(
+ substituteType(appliedField.clazz, null), newType, appliedField.name);
+ lenseBuilder.map(encodedField.field, newField);
+ } else {
+ newField = appliedField;
+ }
+ // Explicitly fix members.
+ fields[i] = encodedField.toTypeSubstitutedField(newField);
+ }
+ return fields;
+ }
+
+ private DexProto substituteTypesIn(DexProto proto, DexEncodedMethod context) {
+ DexProto result = protoFixupCache.get(proto);
+ if (result == null) {
+ DexType returnType = substituteType(proto.returnType, context);
+ DexType[] arguments = substituteTypesIn(proto.parameters.values, context);
+ if (arguments != null || returnType != proto.returnType) {
+ arguments = arguments == null ? proto.parameters.values : arguments;
+ result = appInfo.dexItemFactory.createProto(returnType, arguments);
+ } else {
+ result = proto;
+ }
+ protoFixupCache.put(proto, result);
+ }
+ return result;
+ }
+
+ private DexAnnotationSet substituteTypesIn(DexAnnotationSet annotations) {
+ if (annotations.isEmpty()) {
+ return annotations;
+ }
+ DexAnnotation[] result = substituteTypesIn(annotations.annotations);
+ return result == null ? annotations : new DexAnnotationSet(result);
+ }
+
+ private DexAnnotation[] substituteTypesIn(DexAnnotation[] annotations) {
+ Map<Integer, DexAnnotation> changed = new Int2ObjectArrayMap<>();
+ for (int i = 0; i < annotations.length; i++) {
+ DexAnnotation applied = substituteTypesIn(annotations[i]);
+ if (applied != annotations[i]) {
+ changed.put(i, applied);
+ }
+ }
+ return changed.isEmpty()
+ ? null
+ : ArrayUtils.copyWithSparseChanges(DexAnnotation[].class, annotations, changed);
+ }
+
+ private DexAnnotation substituteTypesIn(DexAnnotation annotation) {
+ return new DexAnnotation(annotation.visibility, substituteTypesIn(annotation.annotation));
+ }
+
+ private DexEncodedAnnotation substituteTypesIn(DexEncodedAnnotation annotation) {
+ return new DexEncodedAnnotation(substituteType(annotation.type, null), annotation.elements);
+ }
+
+ private DexTypeList substituteTypesIn(DexTypeList types) {
+ if (types.isEmpty()) {
+ return types;
+ }
+ DexType[] result = substituteTypesIn(types.values, null);
+ return result == null ? types : new DexTypeList(result);
+ }
+
+ private DexType[] substituteTypesIn(DexType[] types, DexEncodedMethod context) {
+ Map<Integer, DexType> changed = new Int2ObjectArrayMap<>();
+ for (int i = 0; i < types.length; i++) {
+ DexType applied = substituteType(types[i], context);
+ if (applied != types[i]) {
+ changed.put(i, applied);
+ }
+ }
+ return changed.isEmpty()
+ ? null
+ : ArrayUtils.copyWithSparseChanges(DexType[].class, types, changed);
+ }
+
+ private DexType substituteType(DexType type, DexEncodedMethod context) {
+ if (type == null) {
+ return null;
+ }
+ if (type.isArrayType()) {
+ DexType base = type.toBaseType(appInfo.dexItemFactory);
+ DexType fixed = substituteType(base, context);
+ if (base == fixed) {
+ return type;
+ } else {
+ return type.replaceBaseType(fixed, appInfo.dexItemFactory);
+ }
+ }
+ return appliedLense.lookupType(type, context);
+ }
+ }
+
+ private static class ConflictFreeBuilder extends GraphLense.Builder {
+ ConflictFreeBuilder() {
+ super();
+ }
+
+ @Override
+ public void map(DexType from, DexType to) {
+ if (typeMap.containsKey(from)) {
+ String keptName = typeMap.get(from).getName();
+ if (!keptName.equals(to.getName())) {
+ throw ProguardMapError.keptTypeWasRenamed(from, keptName, to.getName());
+ }
+ }
+ super.map(from, to);
+ }
+
+ @Override
+ public void map(DexMethod from, DexMethod to) {
+ if (methodMap.containsKey(from)) {
+ String keptName = methodMap.get(from).name.toString();
+ if (!keptName.equals(to.name.toString())) {
+ throw ProguardMapError.keptMethodWasRenamed(from, keptName, to.name.toString());
+ }
+ }
+ super.map(from, to);
+ }
+
+ @Override
+ public void map(DexField from, DexField to) {
+ if (fieldMap.containsKey(from)) {
+ String keptName = fieldMap.get(from).name.toString();
+ if (!keptName.equals(to.name.toString())) {
+ throw ProguardMapError.keptFieldWasRenamed(from, keptName, to.name.toString());
+ }
+ }
+ super.map(from, to);
+ }
+
+ // Helper to determine whether to apply the class mapping on-the-fly or applied already.
+ DexType lookup(DexType from) {
+ return typeMap.getOrDefault(from, from);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapError.java b/src/main/java/com/android/tools/r8/naming/ProguardMapError.java
new file mode 100644
index 000000000..ce659e1c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapError.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2017, 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 com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+
+public class ProguardMapError extends CompilationError {
+ private ProguardMapError(String message) {
+ super(message);
+ }
+
+ private ProguardMapError(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ static ProguardMapError keptTypeWasRenamed(DexType type, String keptName, String rename) {
+ return new ProguardMapError(
+ "Warning: " + type + createMessageForConflict(keptName, rename));
+ }
+
+ static ProguardMapError keptMethodWasRenamed(DexMethod method, String keptName, String rename) {
+ return new ProguardMapError(
+ "Warning: " + method.toSourceString() + createMessageForConflict(keptName, rename));
+ }
+
+ static ProguardMapError keptFieldWasRenamed(DexField field, String keptName, String rename) {
+ return new ProguardMapError(
+ "Warning: " + field.toSourceString() + createMessageForConflict(keptName, rename));
+ }
+
+ private static String createMessageForConflict(String keptName, String rename) {
+ return " is not being kept as '" + keptName + "', but remapped to '" + rename + "'";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 415b8a719..5f1e150f3 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -9,20 +9,12 @@ import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Range;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.SingleLineRange;
-import com.google.common.collect.ImmutableMap;
import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.function.Consumer;
/**
@@ -71,26 +63,10 @@ public class ProguardMapReader implements AutoCloseable {
}
}
- private ProguardMapReader(BufferedReader reader) {
+ ProguardMapReader(BufferedReader reader) {
this.reader = reader;
}
- public static ClassNameMapper mapperFromInputStream(InputStream in) throws IOException {
- BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF8"));
- try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
- return proguardReader.parse();
- }
- }
-
- public static ClassNameMapper mapperFromFile(Path path) throws IOException {
- return mapperFromInputStream(Files.newInputStream(path));
- }
-
- public static ClassNameMapper mapperFromString(String contents) throws IOException {
- return mapperFromInputStream(
- new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)));
- }
-
// Internal parser state
private int lineNo = 0;
private int lineOffset = 0;
@@ -146,17 +122,15 @@ public class ProguardMapReader implements AutoCloseable {
return c;
}
- public ClassNameMapper parse() throws IOException {
+ void parse(ProguardMap.Builder mapBuilder) throws IOException {
// Read the first line.
line = reader.readLine();
- Map<String, ClassNaming> classNames = parseClassMappings();
- return new ClassNameMapper(classNames);
+ parseClassMappings(mapBuilder);
}
// Parsing of entries
- private Map<String, ClassNaming> parseClassMappings() throws IOException {
- ImmutableMap.Builder<String, ClassNaming> builder = ImmutableMap.builder();
+ private void parseClassMappings(ProguardMap.Builder mapBuilder) throws IOException {
while (hasLine()) {
String before = parseType(false);
skipWhitespace();
@@ -172,16 +146,14 @@ public class ProguardMapReader implements AutoCloseable {
skipWhitespace();
String after = parseType(false);
expect(':');
- ClassNaming currentClass = new ClassNaming(after, before);
- builder.put(after, currentClass);
+ ClassNaming.Builder currentClassBuilder = mapBuilder.classNamingBuilder(after, before);
if (nextLine()) {
- parseMemberMappings(currentClass);
+ parseMemberMappings(currentClassBuilder);
}
}
- return builder.build();
}
- private void parseMemberMappings(ClassNaming currentClass) throws IOException {
+ private void parseMemberMappings(ClassNaming.Builder classNamingBuilder) throws IOException {
MemberNaming current = null;
Range previousInlineRange = null;
Signature previousSignature = null;
@@ -226,7 +198,7 @@ public class ProguardMapReader implements AutoCloseable {
if (current == null || !previousSignature.equals(current.signature)) {
if (collectedInfos.size() == 1) {
current = new MemberNaming(previousSignature, previousRenamedName, previousInlineRange);
- currentClass.addMemberEntry(current);
+ classNamingBuilder.addMemberEntry(current);
} else {
if (Log.ENABLED && !collectedInfos.isEmpty()) {
Log.warn(getClass(),
@@ -254,7 +226,7 @@ public class ProguardMapReader implements AutoCloseable {
if (current == null || !previousSignature.equals(current.signature)) {
if (collectedInfos.size() == 1) {
current = new MemberNaming(previousSignature, previousRenamedName, previousInlineRange);
- currentClass.addMemberEntry(current);
+ classNamingBuilder.addMemberEntry(current);
}
} else {
MemberNaming finalCurrent = current;
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
new file mode 100644
index 000000000..c4931f8d3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2017, 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.DescriptorUtils.javaTypeToDescriptor;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.google.common.collect.ImmutableMap;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+
+/**
+ * Mappings read from the given ProGuard map.
+ * <p>
+ * The main differences of this against {@link ClassNameMapper} and
+ * {@link ClassNameMapper#getObfuscatedToOriginalMapping()} are:
+ * 1) the key is the original descriptor, not the obfuscated java name. Thus, it is much easier
+ * to look up what mapping to apply while traversing {@link DexType}s; and
+ * 2) the value is {@link ClassNamingForMapApplier}, another variant of {@link ClassNaming},
+ * which also uses original {@link Signature} as a key, instead of renamed {@link Signature}.
+ */
+public class SeedMapper implements ProguardMap {
+
+ static class Builder extends ProguardMap.Builder {
+ final ImmutableMap.Builder<String, ClassNamingForMapApplier.Builder> mapBuilder;
+
+ private Builder() {
+ mapBuilder = ImmutableMap.builder();
+ }
+
+ @Override
+ ClassNamingForMapApplier.Builder classNamingBuilder(String renamedName, String originalName) {
+ String originalDescriptor = javaTypeToDescriptor(originalName);
+ ClassNamingForMapApplier.Builder classNamingBuilder =
+ ClassNamingForMapApplier.builder(javaTypeToDescriptor(renamedName), originalDescriptor);
+ mapBuilder.put(originalDescriptor, classNamingBuilder);
+ return classNamingBuilder;
+ }
+
+ @Override
+ SeedMapper build() {
+ return new SeedMapper(mapBuilder.build());
+ }
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ private static SeedMapper seedMapperFromInputStream(InputStream in) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+ try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+ SeedMapper.Builder builder = SeedMapper.builder();
+ proguardReader.parse(builder);
+ return builder.build();
+ }
+ }
+
+ public static SeedMapper seedMapperFromFile(Path path) throws IOException {
+ return seedMapperFromInputStream(Files.newInputStream(path));
+ }
+
+ private final ImmutableMap<String, ClassNamingForMapApplier> mappings;
+
+ private SeedMapper(Map<String, ClassNamingForMapApplier.Builder> mappings) {
+ ImmutableMap.Builder<String, ClassNamingForMapApplier> builder = ImmutableMap.builder();
+ for(Map.Entry<String, ClassNamingForMapApplier.Builder> entry : mappings.entrySet()) {
+ builder.put(entry.getKey(), entry.getValue().build());
+ }
+ this.mappings = builder.build();
+ }
+
+ @Override
+ public boolean hasMapping(DexType type) {
+ return mappings.containsKey(type.descriptor.toString());
+ }
+
+ @Override
+ public ClassNamingForMapApplier getClassNaming(DexType type) {
+ return mappings.get(type.descriptor.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
index 72ecedfbd..3fabf4705 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -112,5 +112,18 @@ public class BridgeMethodAnalysis {
public boolean isContextFree() {
return false;
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("------ BridgeMap ------").append(System.lineSeparator());
+ for (Map.Entry<DexMethod, DexMethod> entry : bridgeTargetToBridgeMap.entrySet()) {
+ builder.append(entry.getKey().toSourceString()).append(" -> ");
+ builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
+ }
+ builder.append("-----------------------").append(System.lineSeparator());
+ builder.append(previousLense.toString());
+ return builder.toString();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 17da59b6c..71d6e468d 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -219,6 +219,6 @@ public class MemberRebindingAnalysis {
appInfo::lookupStaticTarget, DexClass::findStaticTarget);
computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
appInfo::lookupInstanceTarget, DexClass::findInstanceTarget);
- return builder.build(lense, appInfo.dexItemFactory);
+ return builder.build(appInfo.dexItemFactory, lense);
}
}
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 a24bdb9b7..3f64afdaf 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -256,8 +256,6 @@ public class ProguardConfigurationParser {
}
} else if (acceptString("applymapping")) {
configurationBuilder.setApplyMappingFile(parseFileName());
- // TODO(b/64802420): warn until it is fully implemented.
- warnIgnoringOptions("applymapping");
} else if (acceptString("assumenosideeffects")) {
ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule();
configurationBuilder.addRule(rule);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java
index 44b3393f0..cb69320e8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationSourceStrings.java
@@ -12,6 +12,7 @@ import java.util.List;
import joptsimple.internal.Strings;
public class ProguardConfigurationSourceStrings implements ProguardConfigurationSource {
+
private final Path basePath;
private final List<String> config;
@@ -20,13 +21,13 @@ public class ProguardConfigurationSourceStrings implements ProguardConfiguration
* {@param basePath}, which allows all other options that use a relative path to reach out
* to desired paths appropriately.
*/
- public ProguardConfigurationSourceStrings(Path basePath, List<String> config) {
+ public ProguardConfigurationSourceStrings(List<String> config, Path basePath) {
this.basePath = basePath;
this.config = config;
}
private ProguardConfigurationSourceStrings(List<String> config) {
- this(Paths.get("."), config);
+ this(config, Paths.get("."));
}
@VisibleForTesting
@@ -36,7 +37,7 @@ public class ProguardConfigurationSourceStrings implements ProguardConfiguration
}
@Override
- public String get() throws IOException{
+ public String get() throws IOException {
return Strings.join(config, System.lineSeparator());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
index 9affabf24..b50d78ea4 100644
--- a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
@@ -181,7 +181,7 @@ public class SimpleClassMerger {
if (Log.ENABLED) {
Log.debug(getClass(), "Merged %d classes.", numberOfMerges);
}
- return renamedMembersLense.build(graphLense, application.dexItemFactory);
+ return renamedMembersLense.build(application.dexItemFactory, graphLense);
}
private class ClassMerger {
@@ -438,7 +438,7 @@ public class SimpleClassMerger {
DexType fixed = fixupType(type);
lense.map(type, fixed);
}
- return lense.build(graphLense, application.dexItemFactory);
+ return lense.build(application.dexItemFactory, graphLense);
}
private DexEncodedMethod[] removeDupes(DexEncodedMethod[] methods) {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 24f5c08b2..67aa28822 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -596,11 +596,11 @@ public class AndroidApp {
/**
* Add Java-bytecode program data.
*/
- public Builder addClassProgramData(Origin origin, byte[] data) {
+ public Builder addClassProgramData(byte[] data, Origin origin) {
return addProgramResources(Kind.CLASS, Resource.fromBytes(origin, data));
}
- public Builder addClassProgramData(Origin origin, byte[] data, Set<String> classDescriptors) {
+ public Builder addClassProgramData(byte[] data, Origin origin, Set<String> classDescriptors) {
return addProgramResources(Kind.CLASS, Resource.fromBytes(origin, data, classDescriptors));
}
@@ -613,15 +613,6 @@ public class AndroidApp {
}
/**
- * Inform whether ProGuard map has already been set or not.
- *
- * ProGuard option -applymapping will override R8/Dissemble option -pg-map.
- */
- public boolean hasProguardMap() {
- return proguardMap != null;
- }
-
- /**
* Set proguard-map file.
*/
public Builder setProguardMapFile(Path file) {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
index a4f223663..0f85cacec 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
@@ -92,7 +92,7 @@ public class AndroidAppOutputSink extends ForwardingOutputSink {
} else if (!classFiles.isEmpty()) {
assert dexFilesWithPrimary.isEmpty() && dexFilesWithId.isEmpty();
classFiles.forEach(
- d -> builder.addClassProgramData(Origin.unknown(), d.contents, d.descriptors));
+ d -> builder.addClassProgramData(d.contents, Origin.unknown(), d.descriptors));
}
closed = true;
super.close();
diff --git a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
new file mode 100644
index 000000000..976768bd8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, 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.utils;
+
+import java.lang.reflect.Array;
+import java.util.Map;
+
+public class ArrayUtils {
+
+ /**
+ * Copies the input array and then applies specified sparse changes.
+ *
+ * @param clazz target type's Class to cast
+ * @param original an array of original elements
+ * @param changedElements sparse changes to apply
+ * @param <T> target type
+ * @return a copy of original arrays while sparse changes are applied
+ */
+ public static <T> T[] copyWithSparseChanges(
+ Class<T[]> clazz, T[] original, Map<Integer, T> changedElements) {
+ T[] results = clazz.cast(Array.newInstance(clazz.getComponentType(), original.length));
+ int pos = 0;
+ for (Map.Entry<Integer, T> entry : changedElements.entrySet()) {
+ int i = entry.getKey();
+ System.arraycopy(original, pos, results, pos, i - pos);
+ results[i] = entry.getValue();
+ pos = i + 1;
+ }
+ if (pos < original.length) {
+ System.arraycopy(original, pos, results, pos, original.length - pos);
+ }
+ return results;
+ }
+
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index a04e28fe5..1c0cc61bb 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -125,6 +125,7 @@ public class InternalOptions {
public boolean minimalMainDex;
public static class InvalidParameterAnnotationInfo {
+
final DexMethod method;
final int expectedParameterCount;
final int actualParameterCount;
@@ -190,7 +191,7 @@ public class InternalOptions {
.append(" actual count: ")
.append(info.actualParameterCount);
}
- diagnosticsHandler.info(new StringDiagnostic(origin, builder.toString()));
+ diagnosticsHandler.info(new StringDiagnostic(builder.toString(), origin));
}
printed = true;
}
@@ -209,7 +210,7 @@ public class InternalOptions {
for (DexEncodedMethod method : warningInvalidDebugInfo.get(origin)) {
builder.append("\n ").append(method.toSourceString());
}
- diagnosticsHandler.info(new StringDiagnostic(origin, builder.toString()));
+ diagnosticsHandler.info(new StringDiagnostic(builder.toString(), origin));
}
printed = true;
printOutdatedToolchain = true;
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index b2add4aaa..d2c6560a5 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -7,14 +7,15 @@ import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.Resource.Origin;
public class StringDiagnostic implements Diagnostic {
+
private final Origin origin;
private final String message;
public StringDiagnostic(String message) {
- this(Origin.unknown(), message);
+ this(message, Origin.unknown());
}
- public StringDiagnostic(Origin origin, String message) {
+ public StringDiagnostic(String message, Origin origin) {
this.origin = origin;
this.message = message;
}
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 ca9a40433..76d84c2fa 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -9,6 +9,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
@@ -53,8 +54,9 @@ public class ZipUtils {
Path outPath = outDirectoryPath.resolve(name);
File outFile = outPath.toFile();
outFile.getParentFile().mkdirs();
- FileOutputStream output = new FileOutputStream(outFile);
- ByteStreams.copy(input, output);
+ try (OutputStream output = new FileOutputStream(outFile)) {
+ ByteStreams.copy(input, output);
+ }
outFiles.add(outFile);
}
});
diff --git a/src/test/examples/applymapping044/AsubB.java b/src/test/examples/applymapping044/AsubB.java
new file mode 100644
index 000000000..2c5fd0cd2
--- /dev/null
+++ b/src/test/examples/applymapping044/AsubB.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 applymapping044;
+
+import naming044.A;
+import naming044.sub.SubB;
+
+public class AsubB extends SubB {
+ public int boo(A a) {
+ return f(a) * 3;
+ }
+}
diff --git a/src/test/examples/applymapping044/Main.java b/src/test/examples/applymapping044/Main.java
new file mode 100644
index 000000000..9d5f57752
--- /dev/null
+++ b/src/test/examples/applymapping044/Main.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, 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 applymapping044;
+
+import naming044.A;
+import naming044.B;
+import naming044.sub.SubB;
+
+public class Main {
+ public static void main(String[] args) {
+ B.m();
+ SubB.n();
+ A a = new A();
+ B b = new B();
+ b.f(a);
+ AsubB subB = new AsubB();
+ subB.f(a);
+ }
+}
diff --git a/src/test/examples/applymapping044/keep-rules-apply-mapping.txt b/src/test/examples/applymapping044/keep-rules-apply-mapping.txt
new file mode 100644
index 000000000..56f43b2c8
--- /dev/null
+++ b/src/test/examples/applymapping044/keep-rules-apply-mapping.txt
@@ -0,0 +1,13 @@
+# Copyright (c) 2017, 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.
+
+-keep public class applymapping044.Main {
+ public static void main(...);
+}
+
+-keep,allowobfuscation class * {
+ *;
+}
+
+-applymapping test-mapping.txt
diff --git a/src/test/examples/applymapping044/keep-rules.txt b/src/test/examples/applymapping044/keep-rules.txt
new file mode 100644
index 000000000..efed0ec62
--- /dev/null
+++ b/src/test/examples/applymapping044/keep-rules.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, 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.
+
+-keep public class applymapping044.Main {
+ public static void main(...);
+}
+
+-keep,allowobfuscation class * {
+ *;
+}
diff --git a/src/test/examples/applymapping044/test-mapping.txt b/src/test/examples/applymapping044/test-mapping.txt
new file mode 100644
index 000000000..41c2c5c2a
--- /dev/null
+++ b/src/test/examples/applymapping044/test-mapping.txt
@@ -0,0 +1,9 @@
+naming044.A -> naming044.x:
+ int f -> o
+naming044.B -> naming044.y:
+ int m() -> n
+ int f(naming044.A) -> p
+naming044.sub.SubA -> naming044.z.x:
+ int f -> q
+naming044.sub.SubB -> naming044.z.y:
+ int n() -> m
diff --git a/src/test/examples/minification/conflict-mapping.txt b/src/test/examples/minification/conflict-mapping.txt
new file mode 100644
index 000000000..a4453da05
--- /dev/null
+++ b/src/test/examples/minification/conflict-mapping.txt
@@ -0,0 +1,4 @@
+minification.InterfaceA -> ItfA:
+ int functionFromIntToInt(int) -> foo
+minification.InterfaceB -> ItfB:
+ int functionFromIntToInt(int) -> bar
diff --git a/src/test/examples/minification/keep-rules-apply-conflict-mapping.txt b/src/test/examples/minification/keep-rules-apply-conflict-mapping.txt
new file mode 100644
index 000000000..41f2261af
--- /dev/null
+++ b/src/test/examples/minification/keep-rules-apply-conflict-mapping.txt
@@ -0,0 +1,14 @@
+# Copyright (c) 2017, 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.
+
+-applymapping conflict-mapping.txt
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class minification.Minification {
+ public static void main(...);
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
diff --git a/src/test/examples/naming001/keep-rules-105.txt b/src/test/examples/naming001/keep-rules-105.txt
new file mode 100644
index 000000000..f3bf7f6f9
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-105.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, 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.
+
+-allowaccessmodification
+
+-keep class naming001.D {
+ public static void main(...);
+}
+
+-applymapping mapping-105.txt
diff --git a/src/test/examples/naming001/mapping-105.txt b/src/test/examples/naming001/mapping-105.txt
new file mode 100644
index 000000000..3437a9331
--- /dev/null
+++ b/src/test/examples/naming001/mapping-105.txt
@@ -0,0 +1,2 @@
+naming001.D -> naming001.D:
+ void keep() -> peek
diff --git a/src/test/examples/naming044/B.java b/src/test/examples/naming044/B.java
index 758094582..f723423d9 100644
--- a/src/test/examples/naming044/B.java
+++ b/src/test/examples/naming044/B.java
@@ -7,4 +7,7 @@ public class B {
public static int m() {
return A.f;
}
+ public int f(A a) {
+ return a.f;
+ }
}
diff --git a/src/test/examples/naming044/sub/SubB.java b/src/test/examples/naming044/sub/SubB.java
index badc5f2e6..823de8c2b 100644
--- a/src/test/examples/naming044/sub/SubB.java
+++ b/src/test/examples/naming044/sub/SubB.java
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package naming044.sub;
-public class SubB {
+import naming044.B;
+
+public class SubB extends B {
public static int n() {
return SubA.f;
}
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
index 574fe1431..e42b9c9c9 100644
--- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -3,13 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package invokecustom;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.objectweb.asm.ClassReader;
@@ -36,30 +37,34 @@ public class TestGenerator {
}
private void generateTests() throws IOException {
- ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
- ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
- cr.accept(
- new ClassVisitor(Opcodes.ASM6, cw) {
- @Override
- public void visitEnd() {
- generateMethodTest1(cw);
- generateMethodTest2(cw);
- generateMethodTest3(cw);
- generateMethodTest4(cw);
- generateMethodTest5(cw);
- generateMethodTest6(cw);
- generateMethodTest7(cw);
- generateMethodTest8(cw);
- generateMethodTest9(cw);
- generateMethodTest10(cw);
- generateMethodTest11(cw);
- generateMethodTest12(cw);
- generateMethodTest13(cw);
- generateMethodMain(cw);
- super.visitEnd();
- }
- }, 0);
- new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+ try (InputStream input = Files.newInputStream(classNamePath)) {
+ ClassReader cr = new ClassReader(input);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public void visitEnd() {
+ generateMethodTest1(cw);
+ generateMethodTest2(cw);
+ generateMethodTest3(cw);
+ generateMethodTest4(cw);
+ generateMethodTest5(cw);
+ generateMethodTest6(cw);
+ generateMethodTest7(cw);
+ generateMethodTest8(cw);
+ generateMethodTest9(cw);
+ generateMethodTest10(cw);
+ generateMethodTest11(cw);
+ generateMethodTest12(cw);
+ generateMethodTest13(cw);
+ generateMethodMain(cw);
+ super.visitEnd();
+ }
+ }, 0);
+ try (OutputStream output = Files.newOutputStream(classNamePath)) {
+ output.write(cw.toByteArray());
+ }
+ }
}
/* generate main method that only call all test methods. */
diff --git a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
index ab77d32a1..9bbc03a59 100644
--- a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
@@ -4,13 +4,14 @@
package invokecustom2;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.objectweb.asm.ClassReader;
@@ -37,26 +38,30 @@ public class TestGenerator {
}
private void generateTests() throws IOException {
- ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
- ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
- cr.accept(
- new ClassVisitor(Opcodes.ASM6, cw) {
- @Override
- public void visitEnd() {
- generateMethodTest1(cw);
- generateMethodTest2(cw);
- generateMethodTest3(cw);
- generateMethodTest4(cw);
- generateMethodTest5(cw);
- generateMethodTest6(cw);
- generateMethodTest7(cw);
- generateMethodTest8(cw);
- generateMethodTest9(cw);
- generateMethodMain(cw);
- super.visitEnd();
- }
- }, 0);
- new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+ try (InputStream input = Files.newInputStream(classNamePath)) {
+ ClassReader cr = new ClassReader(input);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public void visitEnd() {
+ generateMethodTest1(cw);
+ generateMethodTest2(cw);
+ generateMethodTest3(cw);
+ generateMethodTest4(cw);
+ generateMethodTest5(cw);
+ generateMethodTest6(cw);
+ generateMethodTest7(cw);
+ generateMethodTest8(cw);
+ generateMethodTest9(cw);
+ generateMethodMain(cw);
+ super.visitEnd();
+ }
+ }, 0);
+ try (OutputStream output = Files.newOutputStream(classNamePath)) {
+ output.write(cw.toByteArray());
+ }
+ }
}
/* generate main method that only call all test methods. */
diff --git a/src/test/examplesAndroidO/stringconcat/TestGenerator.java b/src/test/examplesAndroidO/stringconcat/TestGenerator.java
index 6a837a4f4..e72f43813 100644
--- a/src/test/examplesAndroidO/stringconcat/TestGenerator.java
+++ b/src/test/examplesAndroidO/stringconcat/TestGenerator.java
@@ -3,12 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package stringconcat;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -46,177 +47,181 @@ public class TestGenerator {
}
private static void generateTests(Path classNamePath) throws IOException {
- ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
- ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
- cr.accept(
- new ClassVisitor(Opcodes.ASM6, cw) {
- @Override
- public MethodVisitor visitMethod(int access,
- final String methodName, String desc, String signature, String[] exceptions) {
- MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions);
- return new MethodVisitor(Opcodes.ASM6, mv) {
- private List<Object> recentConstants = new ArrayList<>();
-
- @Override
- public void visitLdcInsn(Object cst) {
- if (!recentConstants.isEmpty() ||
- (cst instanceof String && ((String) cst).startsWith(RECIPE_PREFIX))) {
- // Add the constant, don't push anything on stack.
- recentConstants.add(cst);
- return;
+ try (InputStream input = Files.newInputStream(classNamePath)) {
+ ClassReader cr = new ClassReader(input);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public MethodVisitor visitMethod(int access,
+ final String methodName, String desc, String signature, String[] exceptions) {
+ MethodVisitor mv = super.visitMethod(access, methodName, desc, signature, exceptions);
+ return new MethodVisitor(Opcodes.ASM6, mv) {
+ private List<Object> recentConstants = new ArrayList<>();
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (!recentConstants.isEmpty() ||
+ (cst instanceof String && ((String) cst).startsWith(RECIPE_PREFIX))) {
+ // Add the constant, don't push anything on stack.
+ recentConstants.add(cst);
+ return;
+ }
+ super.visitLdcInsn(cst);
}
- super.visitLdcInsn(cst);
- }
-
- @Override
- public void visitMethodInsn(
- int opcode, String owner, String name, String desc, boolean itf) {
- // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`.
- if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcat")) {
- mv.visitInvokeDynamicInsn(MAKE_CONCAT.getName(), desc, MAKE_CONCAT);
- recentConstants.clear();
- return;
+
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String desc, boolean itf) {
+ // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`.
+ if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcat")) {
+ mv.visitInvokeDynamicInsn(MAKE_CONCAT.getName(), desc, MAKE_CONCAT);
+ recentConstants.clear();
+ return;
+ }
+
+ // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`.
+ if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcatWithConstants")) {
+ if (recentConstants.isEmpty()) {
+ throw new AssertionError("No constants detected in `" +
+ methodName + "`: call to " + name + desc);
+ }
+ recentConstants.set(0,
+ ((String) recentConstants.get(0)).substring(RECIPE_PREFIX.length()));
+
+ mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS.getName(),
+ removeLastParams(desc, recentConstants.size()), MAKE_CONCAT_WITH_CONSTANTS,
+ recentConstants.toArray(new Object[recentConstants.size()]));
+ recentConstants.clear();
+ return;
+ }
+
+ // Otherwise fall back to default implementation.
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
}
- // Replace calls to 'makeConcat(...)' with appropriate `invokedynamic`.
- if (opcode == Opcodes.INVOKESTATIC && name.equals("makeConcatWithConstants")) {
- if (recentConstants.isEmpty()) {
- throw new AssertionError("No constants detected in `" +
- methodName + "`: call to " + name + desc);
+ private String removeLastParams(String descr, int paramsToRemove) {
+ MethodType methodType =
+ MethodType.fromMethodDescriptorString(
+ descr, this.getClass().getClassLoader());
+ return methodType
+ .dropParameterTypes(
+ methodType.parameterCount() - paramsToRemove,
+ methodType.parameterCount())
+ .toMethodDescriptorString();
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ switch (opcode) {
+ case Opcodes.ICONST_0:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(0);
+ return;
+ }
+ break;
+ case Opcodes.ICONST_1:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(1);
+ return;
+ }
+ break;
+ case Opcodes.ICONST_2:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(2);
+ return;
+ }
+ break;
+ case Opcodes.ICONST_3:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(3);
+ return;
+ }
+ break;
+ case Opcodes.ICONST_4:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(4);
+ return;
+ }
+ break;
+ case Opcodes.ICONST_5:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(5);
+ return;
+ }
+ break;
+ case Opcodes.ICONST_M1:
+ if (!recentConstants.isEmpty()) {
+ recentConstants.add(-1);
+ return;
+ }
+ break;
+ default:
+ recentConstants.clear();
+ break;
}
- recentConstants.set(0,
- ((String) recentConstants.get(0)).substring(RECIPE_PREFIX.length()));
+ super.visitInsn(opcode);
+ }
- mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS.getName(),
- removeLastParams(desc, recentConstants.size()), MAKE_CONCAT_WITH_CONSTANTS,
- recentConstants.toArray(new Object[recentConstants.size()]));
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
recentConstants.clear();
- return;
+ super.visitIntInsn(opcode, operand);
}
- // Otherwise fall back to default implementation.
- super.visitMethodInsn(opcode, owner, name, desc, itf);
- }
-
- private String removeLastParams(String descr, int paramsToRemove) {
- MethodType methodType =
- MethodType.fromMethodDescriptorString(
- descr, this.getClass().getClassLoader());
- return methodType
- .dropParameterTypes(
- methodType.parameterCount() - paramsToRemove,
- methodType.parameterCount())
- .toMethodDescriptorString();
- }
-
- @Override
- public void visitInsn(int opcode) {
- switch (opcode) {
- case Opcodes.ICONST_0:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(0);
- return;
- }
- break;
- case Opcodes.ICONST_1:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(1);
- return;
- }
- break;
- case Opcodes.ICONST_2:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(2);
- return;
- }
- break;
- case Opcodes.ICONST_3:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(3);
- return;
- }
- break;
- case Opcodes.ICONST_4:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(4);
- return;
- }
- break;
- case Opcodes.ICONST_5:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(5);
- return;
- }
- break;
- case Opcodes.ICONST_M1:
- if (!recentConstants.isEmpty()) {
- recentConstants.add(-1);
- return;
- }
- break;
- default:
- recentConstants.clear();
- break;
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ recentConstants.clear();
+ super.visitVarInsn(opcode, var);
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ recentConstants.clear();
+ super.visitTypeInsn(opcode, type);
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ recentConstants.clear();
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ recentConstants.clear();
+ super.visitJumpInsn(opcode, label);
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment) {
+ recentConstants.clear();
+ super.visitIincInsn(var, increment);
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
+ recentConstants.clear();
+ super.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ recentConstants.clear();
+ super.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ recentConstants.clear();
+ super.visitMultiANewArrayInsn(desc, dims);
}
- super.visitInsn(opcode);
- }
-
- @Override
- public void visitIntInsn(int opcode, int operand) {
- recentConstants.clear();
- super.visitIntInsn(opcode, operand);
- }
-
- @Override
- public void visitVarInsn(int opcode, int var) {
- recentConstants.clear();
- super.visitVarInsn(opcode, var);
- }
-
- @Override
- public void visitTypeInsn(int opcode, String type) {
- recentConstants.clear();
- super.visitTypeInsn(opcode, type);
- }
-
- @Override
- public void visitFieldInsn(int opcode, String owner, String name, String desc) {
- recentConstants.clear();
- super.visitFieldInsn(opcode, owner, name, desc);
- }
-
- @Override
- public void visitJumpInsn(int opcode, Label label) {
- recentConstants.clear();
- super.visitJumpInsn(opcode, label);
- }
-
- @Override
- public void visitIincInsn(int var, int increment) {
- recentConstants.clear();
- super.visitIincInsn(var, increment);
- }
-
- @Override
- public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
- recentConstants.clear();
- super.visitTableSwitchInsn(min, max, dflt, labels);
- }
-
- @Override
- public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
- recentConstants.clear();
- super.visitLookupSwitchInsn(dflt, keys, labels);
- }
-
- @Override
- public void visitMultiANewArrayInsn(String desc, int dims) {
- recentConstants.clear();
- super.visitMultiANewArrayInsn(desc, dims);
- }
- };
- }
- }, 0);
- new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+ };
+ }
+ }, 0);
+ try (OutputStream output = Files.newOutputStream(classNamePath)) {
+ output.write(cw.toByteArray());
+ }
+ }
}
}
diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
index 375dc3413..7ee4bdc0b 100644
--- a/src/test/examplesAndroidP/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
@@ -3,13 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package invokecustom;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
+
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.invoke.CallSite;
-import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.objectweb.asm.ClassReader;
@@ -36,19 +37,23 @@ public class TestGenerator {
}
private void generateTests() throws IOException {
- ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
- ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
- cr.accept(
- new ClassVisitor(Opcodes.ASM6, cw) {
- @Override
- public void visitEnd() {
- generateMethodTest1(cw);
- generateMethodTest2(cw);
- generateMethodMain(cw);
- super.visitEnd();
- }
- }, 0);
- new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+ try (InputStream inputStream = Files.newInputStream(classNamePath)) {
+ ClassReader cr = new ClassReader(inputStream);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public void visitEnd() {
+ generateMethodTest1(cw);
+ generateMethodTest2(cw);
+ generateMethodMain(cw);
+ super.visitEnd();
+ }
+ }, 0);
+ try (OutputStream output = Files.newOutputStream(classNamePath)) {
+ output.write(cw.toByteArray());
+ }
+ }
}
/* Generate main method that only call all test methods. */
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index de8c673d2..f3fd9d21a 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -14,11 +14,10 @@ import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.naming.ProguardMapReader;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -353,7 +352,7 @@ public abstract class DebugTestBase {
paths[indexPath++] = languageFeatures.getDexPath().toString();
Path proguardMapPath = Paths.get(paths[indexPath - 1]).resolveSibling(PROGUARD_MAP_FILENAME);
if (Files.exists(proguardMapPath)) {
- classNameMapper = ProguardMapReader.mapperFromFile(proguardMapPath);
+ classNameMapper = ClassNameMapper.mapperFromFile(proguardMapPath);
}
}
for (Path extraPath : extraPaths) {
@@ -670,7 +669,7 @@ public abstract class DebugTestBase {
@Override
public String getObfuscatedMethodName(
String originalClassName, String originalMethodName, String methodSignatureOrNull) {
- ClassNaming naming;
+ ClassNamingForNameMapper naming;
String obfuscatedClassName =
classNameMapper.getObfuscatedToOriginalMapping().inverse().get(originalClassName);
if (obfuscatedClassName != null) {
@@ -705,7 +704,7 @@ public abstract class DebugTestBase {
/** Assumes classNameMapper is valid. Return null if no member naming found. */
private MemberNaming getMemberNaming(
String obfuscatedClassName, String obfuscatedMethodName, String genericMethodSignature) {
- ClassNaming classNaming = classNameMapper.getClassNaming(obfuscatedClassName);
+ ClassNamingForNameMapper classNaming = classNameMapper.getClassNaming(obfuscatedClassName);
if (classNaming == null) {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
index 2b0084d55..ddcbf8cf5 100644
--- a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
@@ -9,7 +9,7 @@ import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.google.common.collect.ImmutableList;
import jasmin.ClassFile;
import java.io.File;
-import java.io.FileOutputStream;
+import java.io.OutputStream;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -88,7 +88,9 @@ public class JasminDebugTest extends DebugTestBase {
file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
Path path = out.toPath().resolve(clazz.name + ".class");
Files.createDirectories(path.getParent());
- file.write(new FileOutputStream(path.toFile()));
+ try (OutputStream outputStream = Files.newOutputStream(path)) {
+ file.write(outputStream);
+ }
if (isRunningJava()) {
extraPaths.add(path);
} else {
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
index cf191de53..ad9a26ae5 100644
--- a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir;
-import com.android.tools.r8.CompilationException;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -52,12 +51,12 @@ public class BasicBlockIteratorTest extends SmaliTestBase {
// Build the code, and split the code into three blocks.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
ListIterator<BasicBlock> blocks = code.listIterator();
InstructionListIterator iter = blocks.next().listIterator();
iter.nextUntil(i -> !i.isArgument());
iter.previous();
- iter.split(1, code, blocks);
+ iter.split(code, 1, blocks);
return code;
}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 825832416..27a3ce979 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -76,13 +76,13 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
return new SmaliTestBase.TestApplication(application, method, code,
ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
@@ -159,10 +159,10 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code,
ImmutableList.of(codeA), valueNumberGenerator, options);
@@ -239,19 +239,19 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
// Build three copies of a and b for inlining three times.
List<IRCode> additionalCode = new ArrayList<>();
for (int i = 0; i < 3; i++) {
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
additionalCode.add(codeA);
}
for (int i = 0; i < 3; i++) {
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
additionalCode.add(codeB);
}
@@ -373,13 +373,13 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code,
ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
@@ -487,13 +487,13 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code,
ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
@@ -600,13 +600,13 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code,
ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
@@ -714,19 +714,19 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
// Build three copies of a and b for inlining three times.
List<IRCode> additionalCode = new ArrayList<>();
for (int i = 0; i < 3; i++) {
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
additionalCode.add(codeA);
}
for (int i = 0; i < 3; i++) {
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
additionalCode.add(codeB);
}
@@ -871,19 +871,19 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
// Build three copies of a and b for inlining three times.
List<IRCode> additionalCode = new ArrayList<>();
for (int i = 0; i < 3; i++) {
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
additionalCode.add(codeA);
}
for (int i = 0; i < 3; i++) {
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
additionalCode.add(codeB);
}
@@ -1118,13 +1118,13 @@ public class InlineTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodA = getMethod(application, signatureA);
- IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeA = methodA.buildIR(new InternalOptions(), valueNumberGenerator);
DexEncodedMethod methodB = getMethod(application, signatureB);
- IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code,
ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
diff --git a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
index b9eef9cf3..a688ffe21 100644
--- a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
@@ -52,12 +52,12 @@ public class InstructionIteratorTest extends SmaliTestBase {
// Build the code, and split the code into three blocks.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
ListIterator<BasicBlock> blocks = code.listIterator();
InstructionListIterator iter = blocks.next().listIterator();
iter.nextUntil(i -> !i.isArgument());
iter.previous();
- iter.split(1, code, blocks);
+ iter.split(code, 1, blocks);
return code;
}
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 735120621..9192b2fad 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -64,7 +64,7 @@ public class SplitBlockTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code, valueNumberGenerator, options);
}
@@ -123,7 +123,7 @@ public class SplitBlockTest extends SmaliTestBase {
assertTrue(!block.getInstructions().get(i).isArgument());
InstructionListIterator iterator = test.listIteratorAt(block, i);
- BasicBlock newBlock = iterator.split(1, code);
+ BasicBlock newBlock = iterator.split(code, 1);
assertTrue(code.isConsistentSSA());
assertEquals(initialBlockCount + 2, code.blocks.size());
@@ -182,7 +182,7 @@ public class SplitBlockTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code, valueNumberGenerator, options);
}
@@ -247,7 +247,7 @@ public class SplitBlockTest extends SmaliTestBase {
assertEquals(secondBlockInstructions, instructionCount);
InstructionListIterator iterator = test.listIteratorAt(block, i);
- BasicBlock newBlock = iterator.split(1, code);
+ BasicBlock newBlock = iterator.split(code, 1);
assertTrue(code.isConsistentSSA());
assertEquals(initialBlockCount + 2, code.blocks.size());
@@ -307,7 +307,7 @@ public class SplitBlockTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code, valueNumberGenerator, options);
}
@@ -431,7 +431,7 @@ public class SplitBlockTest extends SmaliTestBase {
// Return the processed method for inspection.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
- IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+ IRCode code = method.buildIR(new InternalOptions(), valueNumberGenerator);
return new TestApplication(application, method, code, valueNumberGenerator, options);
}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index c8ea3bbfc..a1046dbe5 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -99,7 +99,8 @@ public class RegisterMoveSchedulerTest {
}
@Override
- public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator) {
+ public BasicBlock split(IRCode code, int instructions,
+ ListIterator<BasicBlock> blockIterator) {
throw new Unimplemented();
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 9baed4897..caada82c7 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -191,7 +191,7 @@ public class JasminBuilder {
}
};
builder.addClassProgramData(
- origin, compile(clazz), Collections.singleton(clazz.getDescriptor()));
+ compile(clazz), origin, Collections.singleton(clazz.getDescriptor()));
}
return builder.build();
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 67decf2a7..15cf848b2 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -132,7 +132,10 @@ public class JasminTestBase {
for (ClassBuilder clazz : builder.getClasses()) {
ClassFile file = new ClassFile();
file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
- file.write(new FileOutputStream(classes.toPath().resolve(clazz.name + ".class").toFile()));
+ try (OutputStream outputStream =
+ Files.newOutputStream(classes.toPath().resolve(clazz.name + ".class"))) {
+ file.write(outputStream);
+ }
}
List<String> args = new ArrayList<>();
args.add("--output=" + dex.toString());
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
new file mode 100644
index 000000000..dac440a4d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -0,0 +1,238 @@
+// Copyright (c) 2017, 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Iterator;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class ApplyMappingTest {
+
+ private static final String MAPPING = "test-mapping.txt";
+
+ private static final Path MINIFICATION_JAR =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "minification" + FileUtils.JAR_EXTENSION);
+
+ private static final Path NAMING001_JAR =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "naming001" + FileUtils.JAR_EXTENSION);
+
+ private static final Path NAMING044_JAR =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "naming044" + FileUtils.JAR_EXTENSION);
+
+ private static final Path APPLYMAPPING044_JAR =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "applymapping044" + FileUtils.JAR_EXTENSION);
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ private Path out;
+
+ @Before
+ public void setup() throws IOException {
+ out = temp.newFolder("outdex").toPath();
+ }
+
+ @Test
+ public void test044_obfuscate_and_apply()
+ throws IOException, CompilationException, ProguardRuleParserException, ExecutionException {
+ // keep rules that allow obfuscations while keeping everything.
+ Path flagForObfuscation =
+ Paths.get(ToolHelper.EXAMPLES_DIR, "naming044", "keep-rules-005.txt");
+ Path proguardMap = out.resolve(MAPPING);
+ AndroidApp obfuscatedApp = runR8(
+ getCommandForApps(out, flagForObfuscation, NAMING044_JAR)
+ .addProguardConfigurationConsumer(c -> {
+ c.setPrintMapping(true);
+ c.setPrintMappingFile(proguardMap);
+ }).build());
+
+ // Obviously, dumped map and resource inside the app should be *identical*.
+ ClassNameMapper mapperFromFile = ClassNameMapper.mapperFromFile(proguardMap);
+ ClassNameMapper mapperFromApp =
+ ClassNameMapper.mapperFromInputStream(obfuscatedApp.getProguardMap());
+ assertEquals(mapperFromFile, mapperFromApp);
+
+ Path instrOut = temp.newFolder("instr").toPath();
+ Path flag = Paths.get(ToolHelper.EXAMPLES_DIR, "applymapping044", "keep-rules.txt");
+ AndroidApp instrApp = runR8(
+ getCommandForInstrumentation(instrOut, flag, NAMING044_JAR, APPLYMAPPING044_JAR)
+ .addProguardConfigurationConsumer(c -> {
+ c.setApplyMappingFile(proguardMap);
+ })
+ .setMinification(false)
+ .build());
+
+ DexInspector inspector = new DexInspector(instrApp);
+ MethodSubject main = inspector.clazz("applymapping044.Main").method(DexInspector.MAIN);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ // B#m()
+ String b = iterator.next().holder().toString();
+ assertEquals("naming044.B", mapperFromApp.deobfuscateClassName(b));
+ // sub.SubB#n()
+ String subB = iterator.next().holder().toString();
+ assertEquals("naming044.sub.SubB", mapperFromApp.deobfuscateClassName(subB));
+ // Skip A#<init>
+ iterator.next();
+ // Skip B#<init>
+ iterator.next();
+ // B#f(A)
+ InvokeInstructionSubject f = iterator.next();
+ DexType a1 = f.invokedMethod().proto.parameters.values[0];
+ assertNotEquals("naming044.A", a1.toString());
+ assertEquals("naming044.A", mapperFromApp.deobfuscateClassName(a1.toString()));
+ assertNotEquals("f", f.invokedMethod().name.toSourceString());
+ // Skip AsubB#<init>
+ iterator.next();
+ // AsubB#f(A)
+ InvokeInstructionSubject overloaded_f = iterator.next();
+ DexMethod aSubB_f = overloaded_f.invokedMethod();
+ DexType a2 = aSubB_f.proto.parameters.values[0];
+ assertNotEquals("naming044.A", a2.toString());
+ assertEquals("naming044.A", mapperFromApp.deobfuscateClassName(a2.toString()));
+ assertNotEquals("f", overloaded_f.invokedMethod().name.toSourceString());
+ // B#f == AsubB#f
+ assertEquals(f.invokedMethod().name.toString(), aSubB_f.name.toString());
+ }
+
+ @Test
+ public void test044_apply()
+ throws IOException, CompilationException, ProguardRuleParserException, ExecutionException {
+ Path flag =
+ Paths.get(ToolHelper.EXAMPLES_DIR, "applymapping044", "keep-rules-apply-mapping.txt");
+ AndroidApp outputApp = runR8(
+ getCommandForInstrumentation(out, flag, NAMING044_JAR, APPLYMAPPING044_JAR)
+ .setMinification(false)
+ .build());
+
+ // Make sure the given proguard map is indeed applied.
+ DexInspector inspector = new DexInspector(outputApp);
+ MethodSubject main = inspector.clazz("applymapping044.Main").method(DexInspector.MAIN);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ // B#m() -> y#n()
+ InvokeInstructionSubject m = iterator.next();
+ assertEquals("naming044.y", m.holder().toString());
+ assertEquals("n", m.invokedMethod().name.toSourceString());
+ // sub.SubB#n() -> z.y#m()
+ InvokeInstructionSubject n = iterator.next();
+ assertEquals("naming044.z.y", n.holder().toString());
+ assertEquals("m", n.invokedMethod().name.toSourceString());
+ // Skip A#<init>
+ iterator.next();
+ // Skip B#<init>
+ iterator.next();
+ // B#f(A) -> y#p(x)
+ InvokeInstructionSubject f = iterator.next();
+ DexType a1 = f.invokedMethod().proto.parameters.values[0];
+ assertEquals("naming044.x", a1.toString());
+ assertEquals("p", f.invokedMethod().name.toSourceString());
+ // Skip AsubB#<init>
+ iterator.next();
+ // AsubB#f(A) -> AsubB#p(x)
+ InvokeInstructionSubject overloaded_f = iterator.next();
+ DexMethod aSubB_f = overloaded_f.invokedMethod();
+ DexType a2 = aSubB_f.proto.parameters.values[0];
+ assertEquals("naming044.x", a2.toString());
+ assertEquals("p", aSubB_f.name.toSourceString());
+ // B#f == AsubB#f
+ assertEquals(f.invokedMethod().name.toString(), aSubB_f.name.toString());
+ }
+
+ @Test
+ public void test_naming001_rule105()
+ throws IOException, CompilationException, ProguardRuleParserException, ExecutionException {
+ // keep rules to reserve D and E, along with a proguard map.
+ Path flag = Paths.get(ToolHelper.EXAMPLES_DIR, "naming001", "keep-rules-105.txt");
+ Path proguardMap = out.resolve(MAPPING);
+ AndroidApp outputApp = runR8(
+ getCommandForApps(out, flag, NAMING001_JAR)
+ .addProguardConfigurationConsumer(c -> {
+ c.setPrintMapping(true);
+ c.setPrintMappingFile(proguardMap);
+ })
+ .setMinification(false)
+ .build());
+
+ // Make sure the given proguard map is indeed applied.
+ DexInspector inspector = new DexInspector(outputApp);
+ MethodSubject main = inspector.clazz("naming001.D").method(DexInspector.MAIN);
+ Iterator<InvokeInstructionSubject> iterator =
+ main.iterateInstructions(InstructionSubject::isInvoke);
+ // mapping-105 simply includes: naming001.D#keep -> peek
+ // naming001.E extends D, hence its keep() should be renamed to peek as well.
+ // Skip E#<init>
+ iterator.next();
+ // E#keep() should be replaced with peek by applying the map.
+ InvokeInstructionSubject m = iterator.next();
+ assertEquals("peek", m.invokedMethod().name.toSourceString());
+ // E could be renamed randomly, though.
+ assertNotEquals("naming001.E", m.holder().toString());
+ }
+
+ @Test
+ public void test_minification_conflict_mapping()
+ throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ Path flag = Paths.get(
+ ToolHelper.EXAMPLES_DIR, "minification", "keep-rules-apply-conflict-mapping.txt");
+ try {
+ runR8(getCommandForApps(out, flag, MINIFICATION_JAR).build());
+ fail("Expect to detect renaming conflict");
+ } catch (ProguardMapError e) {
+ assertTrue(e.getMessage().contains("functionFromIntToInt"));
+ }
+ }
+
+ private R8Command.Builder getCommandForInstrumentation(
+ Path out, Path flag, Path mainApp, Path instrApp)
+ throws CompilationException, IOException {
+ return R8Command.builder()
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()), mainApp)
+ .addProgramFiles(instrApp)
+ .setOutputPath(out)
+ .addProguardConfigurationFiles(flag);
+ }
+
+ private R8Command.Builder getCommandForApps(
+ Path out, Path flag, Path... jars)
+ throws CompilationException, IOException {
+ return R8Command.builder()
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .addProgramFiles(jars)
+ .setOutputPath(out)
+ .addProguardConfigurationFiles(flag);
+ }
+
+ private static AndroidApp runR8(R8Command command)
+ throws ProguardRuleParserException, ExecutionException, CompilationException, IOException {
+ return ToolHelper.runR8(command, options -> {
+ // Disable inlining to make this test not depend on inlining decisions.
+ options.inlineAccessors = false;
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
index dccfd8a38..a818a58e6 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -19,19 +19,19 @@ public class ProguardMapReaderTest {
@Test
public void parseThrowingMap() throws IOException {
- ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
}
@Test
public void roundTripTest() throws IOException {
- ClassNameMapper firstMapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
- ClassNameMapper secondMapper = ProguardMapReader.mapperFromString(firstMapper.toString());
+ ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper secondMapper = ClassNameMapper.mapperFromString(firstMapper.toString());
Assert.assertEquals(firstMapper, secondMapper);
}
@Test
public void parseMapWithPackageInfo() throws IOException {
- ClassNameMapper mapper = ProguardMapReader.mapperFromString(EXAMPLE_MAP_WITH_PACKAGE_INFO);
+ ClassNameMapper mapper = ClassNameMapper.mapperFromString(EXAMPLE_MAP_WITH_PACKAGE_INFO);
Assert.assertTrue(mapper.getObfuscatedToOriginalMapping().isEmpty());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index b285ac060..c45ae376e 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -61,8 +61,11 @@ public class TreeShakingTest {
private static final List<Path> JAR_LIBRARIES = ListUtils.map(ImmutableList
.of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"), Paths::get);
private static final String EMPTY_FLAGS = "src/test/proguard/valid/empty.flags";
-
- private static Set<String> IGNORED = ImmutableSet.of(
+ private static final Set<String> IGNORED_FLAGS = ImmutableSet.of(
+ "minification:conflict-mapping.txt",
+ "minification:keep-rules-apply-conflict-mapping.txt"
+ );
+ private static final Set<String> IGNORED = ImmutableSet.of(
// there's no point in running those without obfuscation
"shaking1:keep-rules-repackaging.txt:DEX:false",
"shaking1:keep-rules-repackaging.txt:JAR:false",
@@ -844,7 +847,8 @@ public class TreeShakingTest {
String mainClass, String keepName, List<String> keepList, boolean minify,
Consumer<DexInspector> inspection, BiConsumer<String, String> outputComparator,
BiConsumer<DexInspector, DexInspector> dexComparator) {
- if (!IGNORED.contains(test + ":" + keepName + ":" + kind + ":" + minify)) {
+ if (!IGNORED_FLAGS.contains(test + ":" + keepName)
+ && !IGNORED.contains(test + ":" + keepName + ":" + kind + ":" + minify)) {
testCases.add(new Object[]{
test, kind, mainClass, keepList, minify, inspection, outputComparator, dexComparator});
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 1519f6c8a..298876ff3 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -55,7 +55,7 @@ public class ForceProguardCompatibilityTest extends TestBase {
private void runAnnotationsTest(boolean forceProguardCompatibility, boolean keepAnnotations) throws Exception {
R8Command.Builder builder =
- new CompatProguardCommandBuilder(forceProguardCompatibility);
+ new CompatProguardCommandBuilder(forceProguardCompatibility, false);
// Add application classes including the annotation class.
Class mainClass = TestMain.class;
Class mentionedClassWithAnnotations = TestMain.MentionedClassWithAnnotation.class;
diff --git a/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
new file mode 100644
index 000000000..c44d1cf9e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2017, 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.utils;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
+import java.util.Map;
+import org.junit.Test;
+
+public class ArrayUtilsTest {
+
+ @Test
+ public void testCopyWithSparseChanges_identical() throws Exception {
+ int size = 3;
+ Integer[] input = new Integer[size];
+ for (int i = 0; i < size; i++) {
+ input[i] = i;
+ }
+ Integer[] output =
+ ArrayUtils.copyWithSparseChanges(Integer[].class, input, new Int2IntArrayMap());
+ assertNotEquals(input, output);
+ for (int i = 0; i < size; i++) {
+ assertTrue(i == output[i]);
+ }
+ }
+
+ @Test
+ public void testCopyWithSparseChanges_oneChangeAtBeginning() throws Exception {
+ int size = 3;
+ Integer[] input = new Integer[size];
+ for (int i = 0; i < size; i++) {
+ input[i] = i;
+ }
+ Map<Integer, Integer> changes = new Int2IntArrayMap();
+ changes.put(0, size);
+ Integer[] output = ArrayUtils.copyWithSparseChanges(Integer[].class, input, changes);
+ assertNotEquals(input, output);
+ assertTrue(size == output[0]);
+ for (int i = 1; i < size; i++) {
+ assertTrue(i == output[i]);
+ }
+ }
+
+ @Test
+ public void testCopyWithSparseChanges_oneChangeInMiddle() throws Exception {
+ int size = 3;
+ Integer[] input = new Integer[size];
+ for (int i = 0; i < size; i++) {
+ input[i] = i;
+ }
+ Map<Integer, Integer> changes = new Int2IntArrayMap();
+ changes.put(1, size);
+ Integer[] output = ArrayUtils.copyWithSparseChanges(Integer[].class, input, changes);
+ assertNotEquals(input, output);
+ assertTrue(size == output[1]);
+ for (int i = 0; i < size; i++) {
+ if (i == 1) {
+ continue;
+ }
+ assertTrue(i == output[i]);
+ }
+ }
+
+ @Test
+ public void testCopyWithSparseChanges_oneChangeAtEnd() throws Exception {
+ int size = 3;
+ Integer[] input = new Integer[size];
+ for (int i = 0; i < size; i++) {
+ input[i] = i;
+ }
+ Map<Integer, Integer> changes = new Int2IntArrayMap();
+ changes.put(2, size);
+ Integer[] output = ArrayUtils.copyWithSparseChanges(Integer[].class, input, changes);
+ assertNotEquals(input, output);
+ assertTrue(size == output[2]);
+ for (int i = 0; i < size - 1; i++) {
+ assertTrue(i == output[i]);
+ }
+ }
+
+ @Test
+ public void testCopyWithSparseChanges_twoChangesAtEnds() throws Exception {
+ int size = 3;
+ Integer[] input = new Integer[size];
+ for (int i = 0; i < size; i++) {
+ input[i] = i;
+ }
+ Map<Integer, Integer> changes = new Int2IntArrayMap();
+ changes.put(0, size);
+ changes.put(2, size);
+ Integer[] output = ArrayUtils.copyWithSparseChanges(Integer[].class, input, changes);
+ assertNotEquals(input, output);
+ assertTrue(size == output[0]);
+ assertFalse(size == output[1]);
+ assertTrue(size == output[2]);
+ }
+
+}
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 612043efe..36e496192 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -58,12 +58,11 @@ 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.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.MemberNaming;
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.ProguardMapReader;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -111,7 +110,7 @@ public class DexInspector {
throws IOException, ExecutionException {
ExecutorService executor = Executors.newSingleThreadExecutor();
if (mappingFile != null) {
- this.mapping = ProguardMapReader.mapperFromFile(Paths.get(mappingFile));
+ this.mapping = ClassNameMapper.mapperFromFile(Paths.get(mappingFile));
originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
} else {
this.mapping = null;
@@ -166,7 +165,7 @@ public class DexInspector {
}
public ClassSubject clazz(String name) {
- ClassNaming naming = null;
+ ClassNamingForNameMapper naming = null;
if (mapping != null) {
String obfuscated = originalToObfuscatedMapping.get(name);
if (obfuscated != null) {
@@ -183,7 +182,7 @@ public class DexInspector {
public void forAllClasses(Consumer<FoundClassSubject> inspection) {
forAll(application.classes(), clazz -> {
- ClassNaming naming = null;
+ ClassNamingForNameMapper naming = null;
if (mapping != null) {
String obfuscated = originalToObfuscatedMapping.get(clazz.type.toSourceString());
if (obfuscated != null) {
@@ -360,9 +359,9 @@ public class DexInspector {
public class FoundClassSubject extends ClassSubject {
private final DexClass dexClass;
- private final ClassNaming naming;
+ private final ClassNamingForNameMapper naming;
- private FoundClassSubject(DexClass dexClass, ClassNaming naming) {
+ private FoundClassSubject(DexClass dexClass, ClassNamingForNameMapper naming) {
this.dexClass = dexClass;
this.naming = naming;
}