Merge "Cleanup of access to methods in DexClass."
diff --git a/src/main/java/com/android/tools/r8/BaseOutput.java b/src/main/java/com/android/tools/r8/BaseOutput.java
index 16e8264..b1b9166 100644
--- a/src/main/java/com/android/tools/r8/BaseOutput.java
+++ b/src/main/java/com/android/tools/r8/BaseOutput.java
@@ -8,6 +8,7 @@
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.List;
abstract class BaseOutput {
@@ -39,9 +40,9 @@
* resources.get(N - 1) ~=~ classesN.dex (where N > 0).
* </pre>
*
- * @return list of compiled DEX resources.
+ * @return an immutable list of compiled DEX resources.
*/
- public ImmutableList<Resource> getDexResources() {
+ public List<Resource> getDexResources() {
return ImmutableList.copyOf(app.getDexProgramResources());
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index cbd16fd..6ac5f1e 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -33,6 +33,9 @@
* Builder for constructing a D8Command.
*/
public static class Builder extends BaseCommand.Builder<D8Command, Builder> {
+
+ private boolean intermediate = false;
+
private Builder() {
super(CompilationMode.DEBUG);
}
@@ -44,18 +47,23 @@
/** Add classpath file resources. */
public Builder addClasspathFiles(Path... files) throws IOException {
getAppBuilder().addClasspathFiles(files);
- return this;
+ return self();
}
/** Add classpath file resources. */
public Builder addClasspathFiles(Collection<Path> files) throws IOException {
getAppBuilder().addClasspathFiles(files);
- return this;
+ return self();
}
public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) {
getAppBuilder().addClasspathResourceProvider(provider);
- return this;
+ return self();
+ }
+
+ public Builder setIntermediate(boolean value) {
+ this.intermediate = value;
+ return self();
}
@Override
@@ -74,7 +82,12 @@
validate();
return new D8Command(
- getAppBuilder().build(), getOutputPath(), getOutputMode(), getMode(), getMinApiLevel());
+ getAppBuilder().build(),
+ getOutputPath(),
+ getOutputMode(),
+ getMode(),
+ getMinApiLevel(),
+ intermediate);
}
}
@@ -89,10 +102,13 @@
" --lib <file> # Add <file> as a library resource.",
" --classpath <file> # Add <file> as a classpath resource.",
" --min-api # Minimum Android API level compatibility",
+ " --intermediate # Compile an intermediate result intended for later merging.",
" --file-per-class # Produce a separate dex file per class",
" --version # Print the version of d8.",
" --help # Print this message."));
+ private boolean intermediate = false;
+
public static Builder builder() {
return new Builder();
}
@@ -142,6 +158,8 @@
builder.addClasspathFiles(Paths.get(args[++i]));
} else if (arg.equals("--min-api")) {
builder.setMinApiLevel(Integer.valueOf(args[++i]));
+ } else if (arg.equals("--intermediate")) {
+ builder.setIntermediate(true);
} else {
if (arg.startsWith("--")) {
throw new CompilationException("Unknown option: " + arg);
@@ -160,8 +178,10 @@
Path outputPath,
OutputMode outputMode,
CompilationMode mode,
- int minApiLevel) {
+ int minApiLevel,
+ boolean intermediate) {
super(inputApp, outputPath, outputMode, mode, minApiLevel);
+ this.intermediate = intermediate;
}
private D8Command(boolean printHelp, boolean printVersion) {
@@ -174,6 +194,7 @@
assert !internal.debug;
internal.debug = getMode() == CompilationMode.DEBUG;
internal.minApiLevel = getMinApiLevel();
+ internal.intermediate = intermediate;
// Assert and fixup defaults.
assert !internal.skipMinification;
internal.skipMinification = true;
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
index 9902d3b..3827cae 100644
--- a/src/main/java/com/android/tools/r8/DexSegments.java
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -110,7 +110,7 @@
Map<String, Integer> result = new HashMap<>();
try (Closer closer = Closer.create()) {
for (Resource resource : app.getDexProgramResources()) {
- for (Segment segment: DexFileReader.parseMapFrom(resource.getStream(closer))) {
+ for (Segment segment: DexFileReader.parseMapFrom(closer.register(resource.getStream()))) {
int value = result.computeIfAbsent(segment.typeName(), (key) -> 0);
result.put(segment.typeName(), value + segment.size());
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 3f6fcd4..19fc358 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -68,8 +68,7 @@
private final Timing timing = new Timing("R8");
private final InternalOptions options;
- // TODO(zerny): Refactor tests to go through testing methods and make this private.
- public R8(InternalOptions options) {
+ private R8(InternalOptions options) {
this.options = options;
options.itemFactory.resetSortedIndices();
}
@@ -93,14 +92,25 @@
}
}
- public DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo)
+ static DexApplication optimize(
+ DexApplication application,
+ AppInfoWithSubtyping appInfo,
+ InternalOptions options)
+ throws ProguardRuleParserException, ExecutionException, IOException {
+ return new R8(options).optimize(application, appInfo);
+ }
+
+ private DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo)
throws IOException, ProguardRuleParserException, ExecutionException {
return optimize(application, appInfo, GraphLense.getIdentityLense(),
Executors.newSingleThreadExecutor());
}
- public DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo,
- GraphLense graphLense, ExecutorService executorService)
+ private DexApplication optimize(
+ DexApplication application,
+ AppInfoWithSubtyping appInfo,
+ GraphLense graphLense,
+ ExecutorService executorService)
throws IOException, ProguardRuleParserException, ExecutionException {
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 71a193c..3b88ba8 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -50,7 +50,7 @@
*/
public Builder setTreeShaking(boolean useTreeShaking) {
treeShaking = Optional.of(useTreeShaking);
- return this;
+ return self();
}
/**
@@ -58,7 +58,7 @@
*/
public Builder setMinification(boolean useMinification) {
minification = Optional.of(useMinification);
- return this;
+ return self();
}
/**
@@ -66,7 +66,7 @@
*/
public Builder addMainDexRules(Path... paths) {
Collections.addAll(mainDexRules, paths);
- return this;
+ return self();
}
/**
@@ -74,7 +74,7 @@
*/
public Builder addMainDexRules(List<Path> paths) {
mainDexRules.addAll(paths);
- return this;
+ return self();
}
/**
@@ -85,14 +85,14 @@
*/
public Builder setMinimalMainDex(boolean value) {
minimalMainDex = value;
- return this;
+ return self();
}
/**
* Add proguard configuration file resources.
*/
public Builder addProguardConfigurationFiles(Path... paths) {
Collections.addAll(proguardConfigFiles, paths);
- return this;
+ return self();
}
/**
@@ -100,7 +100,7 @@
*/
public Builder addProguardConfigurationFiles(List<Path> paths) {
proguardConfigFiles.addAll(paths);
- return this;
+ return self();
}
/**
@@ -108,7 +108,7 @@
*/
public Builder setProguardMapFile(Path path) {
getAppBuilder().setProguardMapFile(path);
- return this;
+ return self();
}
/**
@@ -116,7 +116,7 @@
*/
public Builder setPackageDistributionFile(Path path) {
getAppBuilder().setPackageDistributionFile(path);
- return this;
+ return self();
}
/**
@@ -126,7 +126,7 @@
*/
Builder setIgnoreMissingClasses(boolean ignoreMissingClasses) {
this.ignoreMissingClasses = ignoreMissingClasses;
- return this;
+ return self();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index eaabc99..3208e52 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -4,7 +4,6 @@
package com.android.tools.r8;
-import com.google.common.io.Closer;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
@@ -48,7 +47,7 @@
public abstract Set<String> getClassDescriptors();
/** Get the resource as a stream. */
- public abstract InputStream getStream(Closer closer) throws IOException;
+ public abstract InputStream getStream() throws IOException;
/** File based application resource. */
private static class FileResource extends Resource {
@@ -66,8 +65,8 @@
}
@Override
- public InputStream getStream(Closer closer) throws IOException {
- return closer.register(new FileInputStream(file.toFile()));
+ public InputStream getStream() throws IOException {
+ return new FileInputStream(file.toFile());
}
}
@@ -89,8 +88,7 @@
}
@Override
- public InputStream getStream(Closer closer) throws IOException {
- // Note: closing a byte-array input stream is a no-op.
+ public InputStream getStream() throws IOException {
return new ByteArrayInputStream(bytes);
}
}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index c871e75..6bbb406 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -472,8 +472,7 @@
+ "Reduce the input-program size or run with --multi-dex enabled");
}
if (isDexFile(output)) {
- try (Closer closer = Closer.create()) {
- InputStream stream = result.getDexResources().get(0).getStream(closer);
+ try (InputStream stream = result.getDexResources().get(0).getStream()) {
Files.copy(stream, output, StandardCopyOption.REPLACE_EXISTING);
}
return;
@@ -544,7 +543,7 @@
// Add dex files.
List<Resource> dexProgramSources = output.getDexResources();
for (int i = 0; i < dexProgramSources.size(); i++) {
- addEntry(getDexFileName(i), dexProgramSources.get(i).getStream(closer), out);
+ addEntry(getDexFileName(i), closer.register(dexProgramSources.get(i).getStream()), out);
}
}
}
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 36b65d7..355080f 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -183,7 +183,7 @@
List<DexFileReader> fileReaders = new ArrayList<>(dexSources.size());
int computedMinApiLevel = options.minApiLevel;
for (Resource input : dexSources) {
- DexFile file = new DexFile(input.getStream(closer));
+ DexFile file = new DexFile(closer.register(input.getStream()));
computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
fileReaders.add(new DexFileReader(file, classKind, itemFactory));
}
@@ -207,7 +207,7 @@
JarClassFileReader reader = new JarClassFileReader(
application, classKind.bridgeConsumer(classes::add));
for (Resource input : classSources) {
- reader.read(DEFAULT_DEX_FILENAME, classKind, input.getStream(closer));
+ reader.read(DEFAULT_DEX_FILENAME, classKind, closer.register(input.getStream()));
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index c9da10b..6586a57 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -135,9 +135,9 @@
assert packageDistribution == null :
"Cannot combine package distribution definition with file-per-class option.";
distributor = new FilePerClassDistributor(this);
- } else if (options.minApiLevel < Constants.ANDROID_L_API
- && options.mainDexKeepRules.isEmpty()
- && application.mainDexList.isEmpty()) {
+ } else if (!options.canUseMultidex()
+ && options.mainDexKeepRules.isEmpty()
+ && application.mainDexList.isEmpty()) {
if (packageDistribution != null) {
throw new CompilationError("Cannot apply package distribution. Multidex is not"
+ " supported with API level " + options.minApiLevel +"."
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index a5645a1..977d703 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -12,6 +12,7 @@
public static final int ANDROID_N_API = 24;
public static final int ANDROID_L_API = 21;
public static final int ANDROID_K_API = 19;
+ public static final int ANDROID_I_API = 14;
public static final int DEFAULT_ANDROID_API = 1;
/** dex file version number for Android O (API level 26) */
diff --git a/src/main/java/com/android/tools/r8/dex/DexFile.java b/src/main/java/com/android/tools/r8/dex/DexFile.java
index a2c65d3..3441346 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFile.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFile.java
@@ -62,13 +62,13 @@
int version;
switch (versionByte) {
case '8':
- version = 38;
+ version = Constants.ANDROID_O_DEX_VERSION;
break;
case '7':
- version = 37;
+ version = Constants.ANDROID_N_DEX_VERSION;
break;
case '5':
- version = 35;
+ version = Constants.ANDROID_PRE_N_DEX_VERSION;
break;
default:
throw new CompilationError("Dex file has invalid version number: " + name);
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index f336a3d..f309670 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -174,6 +174,35 @@
return isFull(transaction.getNumberOfMethods(), transaction.getNumberOfFields(), MAX_ENTRIES);
}
+ void throwIfFull(boolean multiDexEnabled) {
+ if (!isFull()) {
+ return;
+ }
+ StringBuilder messageBuilder = new StringBuilder();
+ // General message: Cannot fit.
+ messageBuilder.append("Cannot fit requested classes in ");
+ messageBuilder.append(multiDexEnabled ? "the main-" : "a single ");
+ messageBuilder.append("dex file.\n");
+ // Suggest supplying the main-dex list or explicitly mention that main-dex list is too large.
+ if (multiDexEnabled) {
+ messageBuilder.append("The list of classes for the main-dex list is too large.\n");
+ } else {
+ messageBuilder.append("Try supplying a main-dex list.\n");
+ }
+ // Show the numbers of methods and/or fields that exceed the limit.
+ if (transaction.getNumberOfMethods() > MAX_ENTRIES) {
+ messageBuilder.append("# methods: ");
+ messageBuilder.append(transaction.getNumberOfMethods());
+ messageBuilder.append(" > ").append(MAX_ENTRIES).append('\n');
+ }
+ if (transaction.getNumberOfFields() > MAX_ENTRIES) {
+ messageBuilder.append("# fields: ");
+ messageBuilder.append(transaction.getNumberOfFields());
+ messageBuilder.append(" > ").append(MAX_ENTRIES).append('\n');
+ }
+ throw new CompilationError(messageBuilder.toString());
+ }
+
private boolean isFilledEnough(FillStrategy fillStrategy) {
return isFull(
transaction.getNumberOfMethods(),
@@ -202,7 +231,7 @@
protected final ApplicationWriter writer;
protected final Map<Integer, VirtualFile> nameToFileMap = new HashMap<>();
- public Distributor(ApplicationWriter writer) {
+ Distributor(ApplicationWriter writer) {
this.application = writer.application;
this.writer = writer;
}
@@ -212,7 +241,7 @@
public static class FilePerClassDistributor extends Distributor {
- public FilePerClassDistributor(ApplicationWriter writer) {
+ FilePerClassDistributor(ApplicationWriter writer) {
super(writer);
}
@@ -232,7 +261,7 @@
protected Set<DexProgramClass> classes;
protected Map<DexProgramClass, String> originalNames;
- public DistributorBase(ApplicationWriter writer) {
+ DistributorBase(ApplicationWriter writer) {
super(writer);
classes = Sets.newHashSet(application.classes());
@@ -247,9 +276,7 @@
if (clazz != null && clazz.isProgramClass()) {
DexProgramClass programClass = (DexProgramClass) clazz;
mainDexFile.addClass(programClass);
- if (mainDexFile.isFull()) {
- throw new CompilationError("Cannot fit requested classes in main-dex file.");
- }
+ mainDexFile.throwIfFull(true);
classes.remove(programClass);
} else {
System.out.println(
@@ -298,7 +325,7 @@
public static class FillFilesDistributor extends DistributorBase {
private final FillStrategy fillStrategy;
- public FillFilesDistributor(ApplicationWriter writer, boolean minimalMainDex) {
+ FillFilesDistributor(ApplicationWriter writer, boolean minimalMainDex) {
super(writer);
this.fillStrategy = minimalMainDex ? FillStrategy.MINIMAL_MAIN_DEX : FillStrategy.FILL_MAX;
}
@@ -326,7 +353,7 @@
}
public static class MonoDexDistributor extends DistributorBase {
- public MonoDexDistributor(ApplicationWriter writer) {
+ MonoDexDistributor(ApplicationWriter writer) {
super(writer);
}
@@ -337,9 +364,7 @@
for (DexProgramClass programClass : classes) {
mainDexFile.addClass(programClass);
- if (mainDexFile.isFull()) {
- throw new CompilationError("Cannot fit all classes in a single dex file.");
- }
+ mainDexFile.throwIfFull(false);
}
mainDexFile.commitTransaction();
return nameToFileMap;
@@ -350,7 +375,7 @@
private final PackageDistribution packageDistribution;
private final ExecutorService executorService;
- public PackageMapDistributor(
+ PackageMapDistributor(
ApplicationWriter writer,
PackageDistribution packageDistribution,
ExecutorService executorService) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 3619b9c..3601ee9 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -226,17 +226,44 @@
((DexEncodedMethod) dexItem).getCode() != null;
}
- private void checkIfMethodIsAmbiguous(DexItem previousResult, DexItem newResult) {
+ /** Returns if <code>interface1</code> is a super interface of <code>interface2</code> */
+ private boolean isSuperInterfaceOf(DexType interface1, DexType interface2) {
+ assert definitionFor(interface1).isInterface();
+ DexClass holder = definitionFor(interface2);
+ assert holder.isInterface();
+ for (DexType iface : holder.interfaces.values) {
+ if (iface == interface1 || isSuperInterfaceOf(interface1, iface)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private <S extends DexItem> S resolveAmbiguousResult(S previousResult, S newResult) {
+ // For default methods return the item found lowest in the interface hierarchy. Different
+ // implementations can come from different paths in a diamond.
+ // See §9.4.1 in The Java® Language Specification, Java SE 8 Edition.
if (previousResult != null
&& previousResult != newResult
&& isDefaultMethod(previousResult)
&& isDefaultMethod(newResult)) {
+ DexEncodedMethod previousMethod = (DexEncodedMethod) previousResult;
+ DexEncodedMethod newMethod = (DexEncodedMethod) newResult;
+ if (isSuperInterfaceOf(previousMethod.method.getHolder(), newMethod.method.getHolder())) {
+ return newResult;
+ }
+ if (isSuperInterfaceOf(newMethod.method.getHolder(), previousMethod.method.getHolder())) {
+ return previousResult;
+ }
throw new CompilationError("Duplicate default methods named "
+ previousResult.toSourceString()
+ " are inherited from the types "
- + ((DexEncodedMethod) previousResult).method.holder.getName()
+ + previousMethod.method.holder.getName()
+ " and "
- + ((DexEncodedMethod) newResult).method.holder.getName());
+ + newMethod.method.holder.getName());
+ } else {
+ // Return the first item found for everything except default methods.
+ return previousResult != null ? previousResult : newResult;
}
}
@@ -256,21 +283,13 @@
for (DexType iface : holder.interfaces.values) {
S localResult = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup);
if (localResult != null) {
- checkIfMethodIsAmbiguous(result, localResult);
- // Return the first item found, we only continue to detect ambiguous method call.
- if (result == null) {
- result = localResult;
- }
+ result = resolveAmbiguousResult(result, localResult);
}
}
if (holder.superType != null) {
S localResult = lookupTargetAlongInterfaceChain(holder.superType, desc, lookup);
if (localResult != null) {
- checkIfMethodIsAmbiguous(result, localResult);
- // Return the first item found, we only continue to detect ambiguous method call.
- if (result == null) {
- result = localResult;
- }
+ result = resolveAmbiguousResult(result, localResult);
}
}
return result;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index c468613..ffaf42b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.code.DebugLocalsChange;
import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.MoveException;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
@@ -51,12 +52,6 @@
private DexString emittedFile = null;
private Int2ReferenceMap<DebugLocalInfo> emittedLocals;
- // If lastMoveInstructionPc != NO_PC_INFO, then the last pc-advancing instruction was a
- // move-exception at lastMoveInstructionPc. This is needed to maintain the art/dx specific
- // behaviour that the move-exception pc is associated with the catch-declaration line.
- // See debug.ExceptionTest.testStepOnCatch().
- private int lastMoveInstructionPc = NO_PC_INFO;
-
// Emitted events.
private final List<DexDebugEvent> events = new ArrayList<>();
@@ -81,23 +76,21 @@
}
assert pendingLocals != null;
- // If this is a position emit and exit as it always emits events.
if (instruction.isDebugPosition()) {
emitDebugPosition(pc, instruction.asDebugPosition());
- return;
- }
-
- if (instruction.isArgument()) {
+ } else if (instruction.isMoveException()) {
+ MoveException move = instruction.asMoveException();
+ if (move.getPosition() != null) {
+ emitDebugPosition(pc, move.getPosition());
+ }
+ } else if (instruction.isArgument()) {
startArgument(instruction.asArgument());
} else if (instruction.isDebugLocalsChange()) {
updateLocals(instruction.asDebugLocalsChange());
} else if (instruction.getBlock().exit() == instruction) {
- // If this is the end of the block clear out the pending state and exit.
+ // If this is the end of the block clear out the pending state.
pendingLocals = null;
pendingLocalChanges = false;
- return;
- } else if (instruction.isMoveException()) {
- lastMoveInstructionPc = pc;
} else {
// For non-exit / pc-advancing instructions emit any pending changes.
emitLocalChanges(pc);
@@ -111,7 +104,7 @@
if (startLine == NO_LINE_INFO) {
return null;
}
- DexString[] params = new DexString[method.method.proto.parameters.values.length];
+ DexString[] params = new DexString[method.method.getArity()];
if (arguments != null) {
assert params.length == arguments.size();
for (int i = 0; i < arguments.size(); i++) {
@@ -159,7 +152,7 @@
private void startArgument(Argument argument) {
if (arguments == null) {
- arguments = new ArrayList<>(method.method.proto.parameters.values.length);
+ arguments = new ArrayList<>(method.method.getArity());
}
if (!argument.outValue().isThis()) {
arguments.add(argument.getLocalInfo());
@@ -191,18 +184,16 @@
}
private void emitDebugPosition(int pc, int line, DexString file) {
- int emitPc = lastMoveInstructionPc != NO_PC_INFO ? lastMoveInstructionPc : pc;
- lastMoveInstructionPc = NO_PC_INFO;
// The position requires a pc change event and possible events for line, file and local changes.
// Verify that we do not ever produce two subsequent positions at the same pc.
- assert emittedPc != emitPc;
+ assert emittedPc != pc;
if (startLine == NO_LINE_INFO) {
assert emittedLine == NO_LINE_INFO;
startLine = line;
emittedLine = line;
}
- emitAdvancementEvents(emittedPc, emittedLine, emittedFile, emitPc, line, file, events, factory);
- emittedPc = emitPc;
+ emitAdvancementEvents(emittedPc, emittedLine, emittedFile, pc, line, file, events, factory);
+ emittedPc = pc;
emittedLine = line;
emittedFile = file;
if (localsChanged()) {
@@ -215,11 +206,9 @@
private void emitLocalChanges(int pc) {
// If pc advanced since the locals changed and locals indeed have changed, emit the changes.
if (localsChanged()) {
- int emitPc = lastMoveInstructionPc != NO_PC_INFO ? lastMoveInstructionPc : pc;
- lastMoveInstructionPc = NO_PC_INFO;
emitAdvancementEvents(
- emittedPc, emittedLine, emittedFile, emitPc, emittedLine, emittedFile, events, factory);
- emittedPc = emitPc;
+ emittedPc, emittedLine, emittedFile, pc, emittedLine, emittedFile, events, factory);
+ emittedPc = pc;
emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
pendingLocalChanges = false;
assert localsEqual(emittedLocals, pendingLocals);
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 572bea9..400b42f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -144,7 +144,7 @@
public void setCode(
IRCode ir, RegisterAllocator registerAllocator, DexItemFactory dexItemFactory) {
final DexBuilder builder = new DexBuilder(ir, registerAllocator, dexItemFactory);
- code = builder.build(method.proto.parameters.values.length);
+ code = builder.build(method.getArity());
}
// Replaces the dex code in the method by setting code to result of compiling the IR.
@@ -152,7 +152,7 @@
DexItemFactory dexItemFactory, DexString firstJumboString) {
final DexBuilder builder =
new DexBuilder(ir, registerAllocator, dexItemFactory, firstJumboString);
- code = builder.build(method.proto.parameters.values.length);
+ code = builder.build(method.getArity());
}
public String toString() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index a066d24..f8b3c8c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -32,6 +32,10 @@
return "Method " + holder + "." + name + " " + proto.toString();
}
+ public int getArity() {
+ return proto.parameters.size();
+ }
+
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
if (indexedItems.addMethod(this)) {
@@ -132,7 +136,7 @@
builder.append(".");
builder.append(name);
builder.append("(");
- for (int i = 0; i < proto.parameters.values.length; i++) {
+ for (int i = 0; i < getArity(); i++) {
if (i != 0) {
builder.append(", ");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 39226d8..5de71a1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -58,6 +58,10 @@
return values.length == 0;
}
+ public int size() {
+ return values.length;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
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 290a1a2..44721ae 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -378,6 +379,18 @@
return instructions.get(instructions.size() - 1).asJumpInstruction();
}
+ public Instruction exceptionalExit() {
+ assert hasCatchHandlers();
+ ListIterator<Instruction> it = listIterator(instructions.size());
+ while (it.hasPrevious()) {
+ Instruction instruction = it.previous();
+ if (instruction.instructionTypeCanThrow()) {
+ return instruction;
+ }
+ }
+ throw new Unreachable();
+ }
+
public void clearUserInfo() {
phis = null;
instructions.forEach(Instruction::clearUserInfo);
@@ -1120,11 +1133,13 @@
List<BasicBlock> predecessors = this.getPredecessors();
boolean hasMoveException = entry().isMoveException();
MoveException move = null;
+ DebugPosition position = null;
if (hasMoveException) {
// Remove the move-exception instruction.
move = entry().asMoveException();
+ position = move.getPosition();
assert move.getPreviousLocalValue() == null;
- this.getInstructions().remove(0);
+ getInstructions().remove(0);
}
// Create new predecessor blocks.
List<BasicBlock> newPredecessors = new ArrayList<>();
@@ -1140,7 +1155,11 @@
Value value = new Value(
valueNumberGenerator.next(), MoveType.OBJECT, move.getDebugInfo());
values.add(value);
- newBlock.add(new MoveException(value));
+ MoveException newMove = new MoveException(value);
+ newBlock.add(newMove);
+ if (position != null) {
+ newMove.setPosition(new DebugPosition(position.line, position.file));
+ }
}
newBlock.add(new Goto());
newBlock.close(null);
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
index a08b044..0a1cc1b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
@@ -18,6 +18,7 @@
public DebugLocalsChange(
Int2ReferenceMap<DebugLocalInfo> ending, Int2ReferenceMap<DebugLocalInfo> starting) {
super(null);
+ assert !ending.isEmpty() || !starting.isEmpty();
this.ending = ending;
this.starting = starting;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 0b8d876..065d3df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -41,11 +41,13 @@
return field;
}
+ abstract DexEncodedField lookupTarget(DexType type, AppInfo appInfo);
+
@Override
public Constraint inliningConstraint(AppInfo info, DexType holder) {
// Resolve the field if possible and decide whether the instruction can inlined.
DexType fieldHolder = field.getHolder();
- DexEncodedField target = info.lookupInstanceTarget(fieldHolder, field);
+ DexEncodedField target = lookupTarget(fieldHolder, info);
DexClass fieldClass = info.definitionFor(fieldHolder);
if ((target != null) && (fieldClass != null) && !fieldClass.isLibraryClass()) {
DexAccessFlags flags = target.accessFlags;
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 e1e9a99..7c37649 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
@@ -363,7 +363,7 @@
}
}
assert arguments.size()
- == method.method.proto.parameters.values.length + (method.accessFlags.isStatic() ? 0 : 1);
+ == method.method.getArity() + (method.accessFlags.isStatic() ? 0 : 1);
return arguments;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index f38614f..1d9e853 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -13,7 +13,10 @@
import com.android.tools.r8.code.IgetWide;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
public class InstanceGet extends FieldInstruction {
@@ -95,6 +98,11 @@
}
@Override
+ DexEncodedField lookupTarget(DexType type, AppInfo appInfo) {
+ return appInfo.lookupInstanceTarget(type, field);
+ }
+
+ @Override
public boolean isInstanceGet() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 7ec8eaf..2642de1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -13,7 +13,10 @@
import com.android.tools.r8.code.IputWide;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.List;
@@ -96,6 +99,11 @@
}
@Override
+ DexEncodedField lookupTarget(DexType type, AppInfo appInfo) {
+ return appInfo.lookupInstanceTarget(type, field);
+ }
+
+ @Override
public boolean isInstancePut() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 2691eed..554e71f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -9,6 +9,8 @@
public class MoveException extends Instruction {
+ private DebugPosition position = null;
+
public MoveException(Value dest) {
super(dest);
}
@@ -17,6 +19,14 @@
return outValue;
}
+ public DebugPosition getPosition() {
+ return position;
+ }
+
+ public void setPosition(DebugPosition position) {
+ this.position = position;
+ }
+
@Override
public void buildDex(DexBuilder builder) {
int dest = builder.allocatedRegister(dest(), getNumber());
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index ad340c2..bb79d5e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -12,7 +12,10 @@
import com.android.tools.r8.code.SgetWide;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
public class StaticGet extends FieldInstruction {
@@ -91,6 +94,11 @@
}
@Override
+ DexEncodedField lookupTarget(DexType type, AppInfo appInfo) {
+ return appInfo.lookupStaticTarget(type, field);
+ }
+
+ @Override
public String toString() {
return super.toString() + "; field: " + field.toSourceString();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index e8a1fed..6736d7e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -12,7 +12,10 @@
import com.android.tools.r8.code.SputWide;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.DexBuilder;
public class StaticPut extends FieldInstruction {
@@ -93,6 +96,11 @@
}
@Override
+ DexEncodedField lookupTarget(DexType type, AppInfo appInfo) {
+ return appInfo.lookupStaticTarget(type, field);
+ }
+
+ @Override
public String toString() {
return super.toString() + "; field: " + field.toSourceString();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 9b1aa3f..9e07ad6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -234,7 +234,7 @@
}
private List<MoveType> computeArgumentTypes() {
- List<MoveType> types = new ArrayList<>(proto.parameters.values.length);
+ List<MoveType> types = new ArrayList<>(proto.parameters.size());
String shorty = proto.shorty.toString();
for (int i = 1; i < proto.shorty.size; i++) {
MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
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 ba63d80..809e3ac 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
@@ -86,6 +86,8 @@
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntSet;
+import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -241,11 +243,11 @@
private BasicBlock currentBlock = null;
// Mappings for canonicalizing constants of a given type at IR construction time.
- private Map<Long, ConstNumber> intConstants = new HashMap<>();
- private Map<Long, ConstNumber> longConstants = new HashMap<>();
- private Map<Long, ConstNumber> floatConstants = new HashMap<>();
- private Map<Long, ConstNumber> doubleConstants = new HashMap<>();
- private Map<Long, ConstNumber> nullConstants = new HashMap<>();
+ private Long2ObjectMap<ConstNumber> intConstants = new Long2ObjectArrayMap<>();
+ private Long2ObjectMap<ConstNumber> longConstants = new Long2ObjectArrayMap<>();
+ private Long2ObjectMap<ConstNumber> floatConstants = new Long2ObjectArrayMap<>();
+ private Long2ObjectMap<ConstNumber> doubleConstants = new Long2ObjectArrayMap<>();
+ private Long2ObjectMap<ConstNumber> nullConstants = new Long2ObjectArrayMap<>();
private List<BasicBlock> exitBlocks = new ArrayList<>();
private BasicBlock normalExitBlock;
@@ -669,7 +671,7 @@
// to disable constant canonicalization in debug builds to make sure we have separate values
// for separate locals.
private void canonicalizeAndAddConst(
- ConstType type, int dest, long value, Map<Long, ConstNumber> table) {
+ ConstType type, int dest, long value, Long2ObjectMap<ConstNumber> table) {
ConstNumber existing = table.get(value);
if (existing != null) {
currentBlock.writeCurrentDefinition(dest, existing.outValue(), ThrowingInfo.NO_THROW);
@@ -1977,6 +1979,16 @@
if (currentDebugPosition != null) {
DebugPosition position = currentDebugPosition;
currentDebugPosition = null;
+ if (!currentBlock.getInstructions().isEmpty()) {
+ MoveException move = currentBlock.getInstructions().getLast().asMoveException();
+ if (move != null && move.getPosition() == null) {
+ // Set the position on the move-exception instruction.
+ // ART/DX associates the move-exception pc with the catch-declaration line.
+ // See debug.ExceptionTest.testStepOnCatch().
+ move.setPosition(position);
+ return;
+ }
+ }
addInstruction(position);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index d1b1577..a84e082 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -62,8 +62,7 @@
// TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
assert (dexCode.getDebugInfo() == null)
- || (companionMethod.proto.parameters.values.length
- == dexCode.getDebugInfo().parameters.length);
+ || (companionMethod.getArity() == dexCode.getDebugInfo().parameters.length);
companionMethods.add(new DexEncodedMethod(companionMethod,
newFlags, virtual.annotations, virtual.parameterAnnotations, code));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index c2600b4..55e6296 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -459,8 +459,7 @@
DexCode dexCode = newMethod.getCode().asDexCode();
dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
assert (dexCode.getDebugInfo() == null)
- || (callTarget.proto.parameters.values.length
- == dexCode.getDebugInfo().parameters.length);
+ || (callTarget.getArity() == dexCode.getDebugInfo().parameters.length);
directMethods[i] = newMethod;
return true;
}
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 4ede5a8..a2b3ba2 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
@@ -302,9 +302,9 @@
continue;
}
// Ensure the container is compatible with the target.
- if (!forceInline
- && !result.target.isPublicInlining()
- && (method.method.getHolder() != result.target.method.getHolder())) {
+ if (!forceInline
+ && !result.target.isPublicInlining()
+ && (method.method.getHolder() != result.target.method.getHolder())) {
continue;
}
DexType downcast = null;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 0bd7188..6f1a9b2 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -28,11 +28,11 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.HashMultiset;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multiset;
-import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntIterator;
@@ -76,6 +76,7 @@
}
private static class LocalRange implements Comparable<LocalRange> {
+ final Value value;
final DebugLocalInfo local;
final int register;
final int start;
@@ -83,6 +84,7 @@
LocalRange(Value value, int register, int start, int end) {
assert value.getLocalInfo() != null;
+ this.value = value;
this.local = value.getLocalInfo();
this.register = register;
this.start = start;
@@ -253,7 +255,7 @@
// information, we always use the argument register whenever a local corresponds to an
// argument value. That avoids ending and restarting locals whenever we move arguments
// to lower register.
- int register = getRegisterForValue(value, value.isArgument() ? 0 : start);
+ int register = getArgumentOrAllocateRegisterForValue(value, start);
ranges.add(new LocalRange(value, register, start, nextEnd));
Integer nextStart = nextInRange(nextEnd, end, starts);
if (nextStart == null) {
@@ -263,7 +265,8 @@
start = nextStart;
}
if (start >= 0) {
- ranges.add(new LocalRange(value, getRegisterForValue(value, start), start, end));
+ ranges.add(new LocalRange(value, getArgumentOrAllocateRegisterForValue(value, start),
+ start, end));
}
}
}
@@ -307,8 +310,8 @@
}
}
while (nextStartingRange != null && nextStartingRange.start <= index) {
- // If the full range is between the two debug positions ignore it.
- if (nextStartingRange.end > index) {
+ // If the range is live at this index open it.
+ if (index < nextStartingRange.end) {
openRanges.add(nextStartingRange);
assert !currentLocals.containsKey(nextStartingRange.register);
currentLocals.put(nextStartingRange.register, nextStartingRange.local);
@@ -317,18 +320,25 @@
}
nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
}
+
if (blockEntry) {
blockEntry = false;
- block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(currentLocals));
- } else if (localsChanged && shouldEmitChangesAtInstruction(instruction)) {
- DebugLocalsChange change = createLocalsChange(ending, starting);
- if (change != null) {
- if (instruction.isDebugPosition() || instruction.isJumpInstruction()) {
- instructionIterator.previous();
- instructionIterator.add(new DebugLocalsChange(ending, starting));
- instructionIterator.next();
- } else {
- instructionIterator.add(new DebugLocalsChange(ending, starting));
+ if (instruction.isMoveException()) {
+ fixupSpillMovesAtMoveException(block, instructionIterator, openRanges, currentLocals);
+ } else {
+ block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(currentLocals));
+ }
+ } else {
+ if (localsChanged && shouldEmitChangesAtInstruction(instruction)) {
+ DebugLocalsChange change = createLocalsChange(ending, starting);
+ if (change != null) {
+ if (instruction.isDebugPosition() || instruction.isJumpInstruction()) {
+ instructionIterator.previous();
+ instructionIterator.add(change);
+ instructionIterator.next();
+ } else {
+ instructionIterator.add(change);
+ }
}
}
}
@@ -337,13 +347,83 @@
}
}
+ private void fixupSpillMovesAtMoveException(
+ BasicBlock block,
+ ListIterator<Instruction> instructionIterator,
+ List<LocalRange> openRanges,
+ Int2ReferenceMap<DebugLocalInfo> finalLocals) {
+ Int2ReferenceMap<DebugLocalInfo> initialLocals = new Int2ReferenceOpenHashMap<>();
+ int exceptionalIndex = block.getPredecessors().get(0).exceptionalExit().getNumber();
+ for (LocalRange open : openRanges) {
+ int exceptionalRegister = getArgumentOrAllocateRegisterForValue(open.value, exceptionalIndex);
+ initialLocals.put(exceptionalRegister, open.local);
+ }
+ block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(initialLocals));
+ Int2ReferenceMap<DebugLocalInfo> clobberedLocals = new Int2ReferenceOpenHashMap<>();
+ Iterator<Instruction> moveIterator = block.iterator();
+ assert block.entry().isMoveException();
+ int index = block.entry().getNumber();
+ moveIterator.next();
+ while (moveIterator.hasNext()) {
+ Instruction next = moveIterator.next();
+ if (next.getNumber() != -1) {
+ break;
+ }
+ if (clobberedLocals.isEmpty()) {
+ // Advance the iterator so it ends up at the first move that clobbers a local.
+ instructionIterator.next();
+ }
+ if (next.isMove()) {
+ Move move = next.asMove();
+ int dstRegister = getArgumentOrAllocateRegisterForValue(move.dest(), index);
+ DebugLocalInfo dstInitialLocal = initialLocals.get(dstRegister);
+ DebugLocalInfo dstFinalLocal = finalLocals.get(dstRegister);
+ if (dstInitialLocal != null && dstInitialLocal != dstFinalLocal) {
+ initialLocals.remove(dstRegister);
+ clobberedLocals.put(dstRegister, dstInitialLocal);
+ }
+ }
+ }
+ // Add an initial local change for all clobbered locals after the first clobbered local.
+ if (!clobberedLocals.isEmpty()) {
+ instructionIterator.add(new DebugLocalsChange(
+ clobberedLocals, Int2ReferenceMaps.emptyMap()));
+ }
+ // Compute the final change in locals and emit it after all spill moves.
+ while (instructionIterator.hasNext()) {
+ if (instructionIterator.next().getNumber() != -1) {
+ instructionIterator.previous();
+ break;
+ }
+ }
+ Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
+ Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
+ for (Entry<DebugLocalInfo> initialLocal : initialLocals.int2ReferenceEntrySet()) {
+ if (finalLocals.get(initialLocal.getIntKey()) != initialLocal.getValue()) {
+ ending.put(initialLocal.getIntKey(), initialLocal.getValue());
+ }
+ }
+ for (Entry<DebugLocalInfo> finalLocal : finalLocals.int2ReferenceEntrySet()) {
+ if (initialLocals.get(finalLocal.getIntKey()) != finalLocal.getValue()) {
+ starting.put(finalLocal.getIntKey(), finalLocal.getValue());
+ }
+ }
+ DebugLocalsChange change = createLocalsChange(ending, starting);
+ if (change != null) {
+ instructionIterator.add(change);
+ }
+ }
+
private DebugLocalsChange createLocalsChange(
Int2ReferenceMap<DebugLocalInfo> ending, Int2ReferenceMap<DebugLocalInfo> starting) {
+ if (ending.isEmpty() && starting.isEmpty()) {
+ return null;
+ }
if (ending.isEmpty() || starting.isEmpty()) {
return new DebugLocalsChange(ending, starting);
}
IntSet unneeded = new IntArraySet(Math.min(ending.size(), starting.size()));
- for (Int2ReferenceMap.Entry<DebugLocalInfo> entry : ending.int2ReferenceEntrySet()) {
+ for (Entry<DebugLocalInfo> entry : ending.int2ReferenceEntrySet()) {
if (starting.get(entry.getIntKey()) == entry.getValue()) {
unneeded.add(entry.getIntKey());
}
@@ -368,18 +448,6 @@
|| (instruction.isGoto() && instruction.asGoto().getTarget() == code.getNormalExitBlock());
}
- private boolean verifyLocalsEqual(
- ImmutableMap<Integer, DebugLocalInfo> a, Map<Integer, DebugLocalInfo> b) {
- int size = 0;
- for (Map.Entry<Integer, DebugLocalInfo> entry : b.entrySet()) {
- if (entry.getValue() != null) {
- assert a.get(entry.getKey()) == entry.getValue();
- ++size;
- }
- }
- return a.size() == size;
- }
-
private void clearState() {
liveAtEntrySets = null;
liveIntervals = null;
@@ -1128,7 +1196,7 @@
map.add(operandRegister);
}
}
- for (Entry<Integer> entry : Multisets.copyHighestCountFirst(map).entrySet()) {
+ for (Multiset.Entry<Integer> entry : Multisets.copyHighestCountFirst(map).entrySet()) {
int register = entry.getElement();
if (tryHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair,
register)) {
@@ -1664,14 +1732,8 @@
for (Phi phi : successor.getPhis()) {
LiveIntervals toIntervals = phi.getLiveIntervals().getSplitCovering(toInstruction);
Value operand = phi.getOperand(predIndex);
- LiveIntervals fromIntervals = operand.getLiveIntervals();
- if (operand.isPhi() && operand != phi && successor.getPhis().contains(operand)) {
- // If the input to this phi is another phi in this block we want the value after
- // merging which is the value for that phi at the from instruction.
- fromIntervals = fromIntervals.getSplitCovering(fromInstruction);
- } else {
- fromIntervals = fromIntervals.getSplitCovering(fromInstruction);
- }
+ LiveIntervals fromIntervals =
+ operand.getLiveIntervals().getSplitCovering(fromInstruction);
if (fromIntervals != toIntervals && !toIntervals.isArgumentInterval()) {
assert block.getSuccessors().size() == 1;
spillMoves.addPhiMove(fromInstruction - 1, toIntervals, fromIntervals);
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 9c45617..1537f05 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -271,7 +271,7 @@
}
public static MethodSignature fromDexMethod(DexMethod method) {
- String[] paramNames = new String[method.proto.parameters.values.length];
+ String[] paramNames = new String[method.getArity()];
DexType[] values = method.proto.parameters.values;
for (int i = 0; i < values.length; i++) {
paramNames[i] = values[i].toSourceString();
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 1a28332..f538125 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-
import java.util.IdentityHashMap;
import java.util.Map;
@@ -40,8 +39,7 @@
method.getCode().registerReachableDefinitions(targetExtractor);
DexMethod target = targetExtractor.getTarget();
InvokeKind kind = targetExtractor.getKind();
- if (target != null &&
- target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
+ if (target != null && target.getArity() == method.method.getArity()) {
assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
target = lense.lookupMethod(target, method);
if (kind == InvokeKind.STATIC) {
@@ -73,7 +71,6 @@
}
-
private static class BridgeLense extends GraphLense {
private final GraphLense previousLense;
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 d03c74b..e01439d 100644
--- a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
@@ -571,7 +571,7 @@
for (DexMethod method : invokes) {
Int2IntMap positionsMap = seenPositions.get(method.name);
if (positionsMap != null) {
- int arity = method.proto.parameters.values.length;
+ int arity = method.getArity();
int previous = positionsMap.get(arity);
if (previous != NOT_FOUND) {
assert previous != 0;
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 15ca87b..e767030 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -182,7 +182,7 @@
* Get the input stream of the dead-code resource if exists.
*/
public InputStream getDeadCode(Closer closer) throws IOException {
- return deadCode == null ? null : deadCode.getStream(closer);
+ return deadCode == null ? null : closer.register(deadCode.getStream());
}
/**
@@ -196,7 +196,7 @@
* Get the input stream of the proguard-map resource if it exists.
*/
public InputStream getProguardMap(Closer closer) throws IOException {
- return proguardMap == null ? null : proguardMap.getStream(closer);
+ return proguardMap == null ? null : closer.register(proguardMap.getStream());
}
/**
@@ -210,7 +210,7 @@
* Get the input stream of the proguard-seeds resource if it exists.
*/
public InputStream getProguardSeeds(Closer closer) throws IOException {
- return proguardSeeds == null ? null : proguardSeeds.getStream(closer);
+ return proguardSeeds == null ? null : closer.register(proguardSeeds.getStream());
}
/**
@@ -224,7 +224,7 @@
* Get the input stream of the package distribution resource if it exists.
*/
public InputStream getPackageDistribution(Closer closer) throws IOException {
- return packageDistribution == null ? null : packageDistribution.getStream(closer);
+ return packageDistribution == null ? null : closer.register(packageDistribution.getStream());
}
/**
@@ -238,7 +238,7 @@
* Get the input stream of the main dex list resource if it exists.
*/
public InputStream getMainDexList(Closer closer) throws IOException {
- return mainDexList == null ? null : mainDexList.getStream(closer);
+ return mainDexList == null ? null : closer.register(mainDexList.getStream());
}
/**
@@ -271,7 +271,7 @@
if (!Files.exists(filePath.getParent())) {
Files.createDirectories(filePath.getParent());
}
- Files.copy(dexProgramSources.get(i).getStream(closer), filePath, options);
+ Files.copy(closer.register(dexProgramSources.get(i).getStream()), filePath, options);
}
}
}
@@ -307,7 +307,7 @@
List<Resource> dexProgramSources = getDexProgramResources();
for (int i = 0; i < dexProgramSources.size(); i++) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteStreams.copy(dexProgramSources.get(i).getStream(closer), out);
+ ByteStreams.copy(closer.register(dexProgramSources.get(i).getStream()), out);
dex.add(out.toByteArray());
}
// TODO(sgjesse): Add Proguard map and seeds.
@@ -326,7 +326,8 @@
List<Resource> dexProgramSources = getDexProgramResources();
for (int i = 0; i < dexProgramSources.size(); i++) {
ZipEntry zipEntry = new ZipEntry(outputMode.getOutputPath(dexProgramSources.get(i), i));
- byte[] bytes = ByteStreams.toByteArray(dexProgramSources.get(i).getStream(closer));
+ byte[] bytes =
+ ByteStreams.toByteArray(closer.register(dexProgramSources.get(i).getStream()));
zipEntry.setSize(bytes.length);
out.putNextEntry(zipEntry);
out.write(bytes);
diff --git a/src/main/java/com/android/tools/r8/utils/ClassProvider.java b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
index 25abdce..63a847a 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
@@ -100,7 +100,7 @@
try (Closer closer = Closer.create()) {
JarClassFileReader classReader =
new JarClassFileReader(reader, classKind.bridgeConsumer(classConsumer));
- classReader.read(DEFAULT_DEX_FILENAME, classKind, resource.getStream(closer));
+ classReader.read(DEFAULT_DEX_FILENAME, classKind, closer.register(resource.getStream()));
} catch (IOException e) {
throw new CompilationError("Failed to load class: " + descriptor, e);
}
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 d1f8290..eb746d7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -47,6 +47,8 @@
public List<String> methodsFilter = ImmutableList.of();
public int minApiLevel = Constants.DEFAULT_ANDROID_API;
+ // Skipping min_api check and compiling an intermediate result intended for later merging.
+ public boolean intermediate = false;
public List<String> logArgumentsFilter = ImmutableList.of();
// Defines interface method rewriter behavior.
@@ -273,6 +275,14 @@
return minApiLevel >= Constants.ANDROID_N_API;
}
+ public boolean canUsePrivateInterfaceMethods() {
+ return minApiLevel >= Constants.ANDROID_N_API;
+ }
+
+ public boolean canUseMultidex() {
+ return intermediate || minApiLevel >= Constants.ANDROID_L_API;
+ }
+
public boolean canUseLongCompareAndObjectsNonNull() {
return minApiLevel >= Constants.ANDROID_K_API;
}
@@ -281,10 +291,6 @@
return minApiLevel >= Constants.ANDROID_K_API;
}
- public boolean canUsePrivateInterfaceMethods() {
- return minApiLevel >= Constants.ANDROID_N_API;
- }
-
// APIs for accessing parameter names annotations are not available before Android O, thus does
// not emit them to avoid wasting space in Dex files because runtimes before Android O will ignore
// them.
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index d8b9bbc..803d760 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -294,7 +294,7 @@
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[16384];
try (Closer closer = Closer.create()) {
- InputStream stream = resource.getStream(closer);
+ InputStream stream = closer.register(resource.getStream());
int read;
while ((read = stream.read(buffer, 0, buffer.length)) != -1) {
output.write(buffer, 0, read);
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 9aec981..38868b6 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -61,18 +61,16 @@
}
TestRunner withClassCheck(Consumer<FoundClassSubject> check) {
- withDexCheck(inspector -> inspector.forAllClasses(check));
- return this;
+ return withDexCheck(inspector -> inspector.forAllClasses(check));
}
TestRunner withMethodCheck(Consumer<FoundMethodSubject> check) {
- withClassCheck(clazz -> clazz.forAllMethods(check));
- return this;
+ return withClassCheck(clazz -> clazz.forAllMethods(check));
}
- <T extends InstructionSubject> TestRunner
- withInstructionCheck(Predicate<InstructionSubject> filter, Consumer<T> check) {
- withMethodCheck(method -> {
+ <T extends InstructionSubject> TestRunner withInstructionCheck(
+ Predicate<InstructionSubject> filter, Consumer<T> check) {
+ return withMethodCheck(method -> {
if (method.isAbstract()) {
return;
}
@@ -81,7 +79,6 @@
check.accept(iterator.next());
}
});
- return this;
}
TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
@@ -267,7 +264,7 @@
@Test
public void paramNames() throws Throwable {
test("paramnames", "paramnames", "ParameterNames")
- .withMinApiLevel(26)
+ .withMinApiLevel(ANDROID_O_API)
.withOptionConsumer((internalOptions) -> internalOptions.allowParameterName = true)
.run();
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index e01ecc0..d0032cd 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
@@ -56,7 +57,7 @@
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
- private static final int DEFAULT_MIN_SDK = 14;
+ private static final int DEFAULT_MIN_SDK = Constants.ANDROID_I_API;
public enum DexVm {
ART_4_4_4("4.4.4"),
@@ -487,6 +488,14 @@
.build());
}
+ public static DexApplication optimizeWithR8(
+ DexApplication application,
+ AppInfoWithSubtyping appInfo,
+ InternalOptions options)
+ throws ProguardRuleParserException, ExecutionException, IOException {
+ return R8.optimize(application, appInfo, options);
+ }
+
public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
return runD8(app, null);
}
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 be25097..6a5f738 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -325,6 +325,10 @@
return new JUnit3Wrapper.Command.SetLocalCommand(localName, newValue);
}
+ protected final JUnit3Wrapper.Command getLocal(String localName, Consumer<Value> inspector) {
+ return t -> inspector.accept(t.debuggeeState.getLocalValues().get(localName));
+ }
+
@Ignore("Prevents Gradle from running the wrapper as a test.")
static class JUnit3Wrapper extends JDWPTestCase {
@@ -643,7 +647,8 @@
assert valuesCount == 1;
Value localValue = replyPacket.getNextValueAsValue();
- Assert.assertEquals(expectedValue, localValue);
+ Assert.assertEquals("Incorrect value for local '" + localName + "'",
+ expectedValue, localValue);
}
public String getClassName() {
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 6a6a20f..1fd4bc9 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -317,4 +317,35 @@
run());
}
+ @Test
+ public void testInvokeRangeLongThrowOnDiv() throws Throwable {
+ final int initialValueOfX = 21;
+ final long expectedValueOfL = (long) initialValueOfX * 2;
+ runDebugTest("Locals",
+ breakpoint("Locals", "foo"),
+ run(),
+ // Initialize obj to 42 using original value of x.
+ stepOver(),
+ // Set value of x to zero which will cause a div-by-zero arithmetic exception below.
+ checkLocal("x", Value.createInt(initialValueOfX)),
+ setLocal("x", Value.createInt(0)),
+ // Single step until the catch handler triggers.
+ checkLine(SOURCE_FILE, 166), stepOver(),
+ checkLine(SOURCE_FILE, 168), stepOver(),
+ checkLine(SOURCE_FILE, 169), stepOver(),
+ // At the catch handler, inspect the initial state of locals.
+ checkLine(SOURCE_FILE, 172),
+ checkLocal("x", Value.createInt(0)),
+ getLocal("obj", value -> Assert.assertEquals(Tag.OBJECT_TAG, value.getTag())),
+ checkLocal("l", Value.createLong(expectedValueOfL)),
+ // Step onto first line of catch handler and inspect again, including the exception local.
+ stepOver(),
+ checkLine(SOURCE_FILE, 173),
+ getLocal("e", value -> Assert.assertEquals(Tag.OBJECT_TAG, value.getTag())),
+ checkLocal("x", Value.createInt(0)),
+ getLocal("obj", value -> Assert.assertEquals(Tag.OBJECT_TAG, value.getTag())),
+ checkLocal("l", Value.createLong(expectedValueOfL)),
+ run());
+ }
+
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index 8fdcfa5..fc9ee3f 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -62,8 +62,8 @@
List<Resource> files2 = app2.getDexProgramResources();
assertEquals(files1.size(), files2.size());
for (int index = 0; index < files1.size(); index++) {
- InputStream file1 = files1.get(index).getStream(closer);
- InputStream file2 = files2.get(index).getStream(closer);
+ InputStream file1 = closer.register(files1.get(index).getStream());
+ InputStream file2 = closer.register(files2.get(index).getStream());
byte[] bytes1 = ByteStreams.toByteArray(file1);
byte[] bytes2 = ByteStreams.toByteArray(file2);
assertArrayEquals("File index " + index, bytes1, bytes2);
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
index d28c37d..fa193d4 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
@@ -34,7 +34,7 @@
int bytes = 0;
try (Closer closer = Closer.create()) {
for (Resource dex : app.getDexProgramResources()) {
- bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
+ bytes += ByteStreams.toByteArray(closer.register(dex.getStream())).length;
}
}
assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
index b26d1aa..8ee3674 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -36,7 +36,7 @@
int bytes = 0;
try (Closer closer = Closer.create()) {
for (Resource dex : app.getDexProgramResources()) {
- bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
+ bytes += ByteStreams.toByteArray(closer.register(dex.getStream())).length;
}
}
assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
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 e5bbad6..04d84e4 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -132,6 +132,6 @@
protected static DexApplication process(DexApplication app, InternalOptions options)
throws IOException, ProguardRuleParserException, ExecutionException {
- return new R8(options).optimize(app, new AppInfoWithSubtyping(app));
+ return ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), options);
}
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 001883f..a5e2309 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -121,6 +121,10 @@
return generatedApplicationsFolder.getRoot().toPath().resolve("many-classes-stereo.zip");
}
+ private static Path getManyClassesForceMultiDexAppPath() {
+ return generatedApplicationsFolder.getRoot().toPath().resolve("many-classes-stereo-forced.zip");
+ }
+
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -146,8 +150,15 @@
@Test
public void cannotFitBothIntoMainDex() throws Throwable {
- thrown.expect(CompilationError.class);
- verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath(), false);
+ try {
+ verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath(), false);
+ fail("Expect to fail, for there are too many classes for the main-dex list.");
+ } catch (CompilationError e) {
+ // Make sure {@link MonoDexDistributor} was _not_ used.
+ assertFalse(e.getMessage().contains("single dex file"));
+ // Make sure what exceeds the limit is the number of methods.
+ assertTrue(e.getMessage().contains("# methods"));
+ }
}
@Test
@@ -179,8 +190,15 @@
@Test
public void cannotFitAllIntoMainDex() throws Throwable {
- thrown.expect(CompilationError.class);
- verifyMainDexContains(MANY_CLASSES, getManyClassesMultiDexAppPath(), false);
+ try {
+ verifyMainDexContains(MANY_CLASSES, getManyClassesMultiDexAppPath(), false);
+ fail("Expect to fail, for there are too many classes for the main-dex list.");
+ } catch (CompilationError e) {
+ // Make sure {@link MonoDexDistributor} was _not_ used.
+ assertFalse(e.getMessage().contains("single dex file"));
+ // Make sure what exceeds the limit is the number of methods.
+ assertTrue(e.getMessage().contains("# methods"));
+ }
}
@Test
@@ -308,6 +326,35 @@
}
}
+ @Test
+ public void checkIntermediateMultiDex() throws Exception {
+ // Generates an application with many classes, every even in one package and every odd in
+ // another. Add enough methods so the application cannot fit into one dex file.
+ // Notice that this one allows multidex while using lower API.
+ AndroidApp generated = generateApplication(
+ MANY_CLASSES, Constants.ANDROID_K_API, true, MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
+ generated.write(getManyClassesForceMultiDexAppPath(), OutputMode.Indexed);
+ // Make sure the generated app indeed has multiple dex files.
+ assertTrue(generated.getDexProgramResources().size() > 1);
+ }
+
+ @Test
+ public void testMultiDexFailDueToMinApi() throws Exception {
+ // Generates an application with many classes, every even in one package and every odd in
+ // another. Add enough methods so the application cannot fit into one dex file.
+ // Notice that this one fails due to the min API.
+ try {
+ generateApplication(
+ MANY_CLASSES, Constants.ANDROID_K_API, false, MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
+ fail("Expect to fail, for there are many classes while multidex is not enabled.");
+ } catch (CompilationError e) {
+ // Make sure {@link MonoDexDistributor} was used.
+ assertTrue(e.getMessage().contains("single dex file"));
+ // Make sure what exceeds the limit is the number of methods.
+ assertTrue(e.getMessage().contains("# methods"));
+ }
+ }
+
private void addMainListFile(ArrayList<Path> mainLists, List<String> content)
throws IOException {
Path listFile = temp.newFile().toPath();
@@ -393,9 +440,16 @@
public static AndroidApp generateApplication(List<String> classes, int minApi, int methodCount)
throws IOException, ExecutionException {
+ return generateApplication(classes, minApi, false, methodCount);
+ }
+
+ private static AndroidApp generateApplication(
+ List<String> classes, int minApi, boolean intermediate, int methodCount)
+ throws IOException, ExecutionException {
Timing timing = new Timing("MainDexListTests");
InternalOptions options = new InternalOptions();
options.minApiLevel = minApi;
+ options.intermediate = intermediate;
DexItemFactory factory = options.itemFactory;
DexApplication.Builder builder = new DexApplication.Builder(factory, timing);
for (String clazz : classes) {
@@ -439,8 +493,8 @@
}
DexApplication application = builder.build();
AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
- ApplicationWriter writer =
- new ApplicationWriter(application, appInfo, options, null, NamingLens.getIdentityLens(), null);
+ ApplicationWriter writer = new ApplicationWriter(
+ application, appInfo, options, null, NamingLens.getIdentityLens(), null);
ExecutorService executor = ThreadUtils.getExecutorService(options);
try {
return writer.write(null, executor);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 2549ff1..57c8cf6 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.maindexlist;
+import static com.android.tools.r8.dex.Constants.ANDROID_I_API;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
@@ -44,7 +45,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
- 14);
+ ANDROID_I_API);
}
@Test
@@ -55,7 +56,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "main-dex-rules-2.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
- 14);
+ ANDROID_I_API);
}
@Test
@@ -66,7 +67,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
- 14);
+ ANDROID_I_API);
}
@Test
@@ -77,7 +78,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
- 14);
+ ANDROID_I_API);
}
@Test
@@ -88,7 +89,7 @@
EXAMPLE_O_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
- 14);
+ ANDROID_I_API);
}
private void doTest(
@@ -137,9 +138,9 @@
CompilationResult result = ToolHelper.runR8WithFullResult(command, optionsConsumer);
List<String> resultMainDexList =
result.dexApplication.mainDexList.stream()
- .filter(dexType -> isApplicationClass(dexType, result) != null)
- .map(dexType -> dexType.descriptor.toString())
- .collect(Collectors.toList());
+ .filter(dexType -> isApplicationClass(dexType, result))
+ .map(dexType -> dexType.descriptor.toString())
+ .collect(Collectors.toList());
Collections.sort(resultMainDexList);
String[] refList = new String(Files.readAllBytes(
expectedMainDexList), StandardCharsets.UTF_8).split("\n");
@@ -161,7 +162,7 @@
}
}
- private Object isApplicationClass(DexType dexType, CompilationResult result) {
+ private boolean isApplicationClass(DexType dexType, CompilationResult result) {
DexClass clazz = result.appInfo.definitionFor(dexType);
return clazz != null && clazz.isProgramClass();
}
diff --git a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
new file mode 100644
index 0000000..70d4efc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
@@ -0,0 +1,54 @@
+// 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.regress.b63935662;
+
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.AndroidApp;
+import java.nio.file.Path;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Regress63935662 extends TestBase {
+
+ void run(AndroidApp app, Class mainClass) throws Exception {
+ if (!ToolHelper.getDexVm().isNewerThan(DexVm.ART_6_0_1)) {
+ return;
+ }
+ Path proguardConfig = writeTextToTempFile(keepMainProguardConfiguration(mainClass, true, false));
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(app)
+ .addProguardConfigurationFiles(proguardConfig)
+ .setMinApiLevel(Constants.ANDROID_N_API)
+ .build();
+ String resultFromJava = runOnJava(mainClass);
+ app = ToolHelper.runR8(command);
+ String resultFromArt = runOnArt(app, mainClass);
+ Assert.assertEquals(resultFromJava, resultFromArt);
+ }
+
+ @Test
+ public void test() throws Exception {
+ Class mainClass = TestClass.class;
+ AndroidApp app = readClasses(
+ TestClass.Top.class, TestClass.Left.class, TestClass.Right.class, TestClass.Bottom.class,
+ TestClass.X1.class, TestClass.X2.class, TestClass.X3.class, TestClass.X4.class, TestClass.X5.class,
+ mainClass);
+ run(app, mainClass);
+ }
+
+ @Test
+ public void test2() throws Exception {
+ Class mainClass = TestFromBug.class;
+ AndroidApp app = readClasses(
+ TestFromBug.Map.class, TestFromBug.AbstractMap.class,
+ TestFromBug.ConcurrentMap.class, TestFromBug.ConcurrentHashMap.class,
+ mainClass);
+ run(app, mainClass);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b63935662/TestClass.java b/src/test/java/com/android/tools/r8/regress/b63935662/TestClass.java
new file mode 100644
index 0000000..878fcff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b63935662/TestClass.java
@@ -0,0 +1,60 @@
+// 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.regress.b63935662;
+
+public class TestClass {
+
+ interface Top {
+ default String name() { return "unnamed"; }
+ }
+
+ interface Left extends Top {
+ default String name() { return getClass().getName(); }
+ }
+
+ interface Right extends Top {
+ /* No override of default String name() */
+ }
+
+ interface Bottom extends Left, Right {}
+
+ static class X1 implements Bottom {
+ void test() {
+ System.out.println(name());
+ }
+ }
+
+ static class X2 implements Left, Right {
+ void test() {
+ System.out.println(name());
+ }
+ }
+
+ static class X3 implements Right, Left {
+ void test() {
+ System.out.println(name());
+ }
+ }
+
+ static class X4 implements Left, Right, Top {
+ void test() {
+ System.out.println(name());
+ }
+ }
+
+ static class X5 implements Right, Left, Top {
+ void test() {
+ System.out.println(name());
+ }
+ }
+
+ public static void main(String[] args) {
+ new X1().test();
+ new X2().test();
+ new X3().test();
+ new X4().test();
+ new X5().test();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b63935662/TestFromBug.java b/src/test/java/com/android/tools/r8/regress/b63935662/TestFromBug.java
new file mode 100644
index 0000000..9d3efc6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b63935662/TestFromBug.java
@@ -0,0 +1,30 @@
+// 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.regress.b63935662;
+
+import java.util.function.BiConsumer;
+
+public class TestFromBug {
+
+ public interface Map<K, V> {
+ default void forEach(BiConsumer<? super K, ? super V> action) {
+ System.out.println("Map.forEach");
+ }
+ }
+
+ public interface ConcurrentMap<K, V> extends Map<K,V> {
+ @Override
+ default void forEach(BiConsumer<? super K, ? super V> action) {
+ System.out.println("ConcurrentMap.forEach");
+ }
+ }
+
+ public static abstract class AbstractMap<K,V> implements Map<K, V> {}
+ public static class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> {}
+
+ public static void main(String[] args) {
+ new ConcurrentHashMap<String, String>().forEach(null);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 9a12d3e..f070130 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -385,8 +385,7 @@
protected DexApplication processApplication(DexApplication application, InternalOptions options) {
try {
- R8 r8 = new R8(options);
- return r8.optimize(application, new AppInfoWithSubtyping(application));
+ return ToolHelper.optimizeWithR8(application, new AppInfoWithSubtyping(application), options);
} catch (IOException | ProguardRuleParserException | ExecutionException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
index e49ea84..f10add0 100644
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstHigh16;
@@ -284,7 +285,7 @@
" return");
DexApplication app = builder.read();
- app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+ app = ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), new InternalOptions());
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);
@@ -344,7 +345,7 @@
" return");
DexApplication app = builder.read();
- app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+ app = ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), new InternalOptions());
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);
@@ -417,7 +418,7 @@
" return");
DexApplication app = builder.read();
- app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+ app = ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), new InternalOptions());
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);