Merge "Fix parsing of -dontwarn flag."
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index ae022a5..aec690d 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -7,8 +7,9 @@
import static com.android.tools.r8.utils.FileUtils.isArchive;
import static com.android.tools.r8.utils.FileUtils.isClassFile;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.io.ByteStreams;
@@ -56,7 +57,7 @@
private ArchiveClassFileProvider(FilteredClassPath archive) throws IOException {
assert isArchive(archive.getPath());
- origin = new Resource.PathOrigin(archive.getPath(), Origin.root());
+ origin = new PathOrigin(archive.getPath(), Origin.root());
zipFile = new ZipFile(archive.getPath().toFile());
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 1c1961d..b0868cc 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.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.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
/**
* Interface for all diagnostic message produced by D8 and R8.
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 8e69699..10f24f5 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.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.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
/**
* A DiagnosticsHandler can be provided to customize handling of diagnostics information.
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 76a6ed1..c1b1096 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -45,6 +45,8 @@
import com.android.tools.r8.utils.AndroidAppOutputSink;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import com.android.tools.r8.utils.LineNumberOptimizer;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -314,6 +316,15 @@
: new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
timing.end();
+ if (options.lineNumberOptimization != LineNumberOptimization.OFF) {
+ timing.begin("Line number remapping");
+ LineNumberOptimizer.run(
+ application,
+ namingLens,
+ options.lineNumberOptimization == LineNumberOptimization.IDENTITY_MAPPING);
+ timing.end();
+ }
+
// If a method filter is present don't produce output since the application is likely partial.
if (options.hasMethodsFilter()) {
System.out.println("Finished compilation with method filter: ");
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index b2bc53d..bbd5954 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -4,161 +4,18 @@
package com.android.tools.r8;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Set;
/** Represents application resources. */
public abstract class Resource {
- /**
- * Origin description of a resource.
- *
- * <p>An origin is a list of parts that describe where a resource originates from. The first part
- * is the most recent part and is associated with the present resource, each successive part is
- * then associated with the context of the previous part.
- *
- * <p>For example, for a class file, say {@code my/class/Foo.class}, that is contained within a
- * jar archive, say {@code myjar.jar}, the Origin of of this resource will be {@code
- * myjar.jar:my/class/Foo.class} where each part is separated by a colon.
- *
- * <p>There are two top-most origins which have no parent: {@code Origin.root()} and {@code
- * Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
- * origin (e.g., for generated resources of raw bytes).
- */
- public abstract static class Origin implements Comparable<Origin> {
-
- private static final Origin ROOT =
- new Origin() {
- @Override
- public String part() {
- return "";
- }
-
- @Override
- List<String> buildParts(int size) {
- return new ArrayList<>(size);
- }
- };
-
- private static final Origin UNKNOWN =
- new Origin() {
- @Override
- public String part() {
- return "<unknown>";
- }
-
- @Override
- List<String> buildParts(int size) {
- List<String> parts = new ArrayList<>(size + 1);
- parts.add(part());
- return parts;
- }
- };
-
- public static Origin root() {
- return ROOT;
- }
-
- public static Origin unknown() {
- return UNKNOWN;
- }
-
- private final Origin parent;
-
- private Origin() {
- this.parent = null;
- }
-
- protected Origin(Origin parent) {
- assert parent != null;
- this.parent = parent;
- }
-
- public abstract String part();
-
- public Origin parent() {
- return parent;
- }
-
- public List<String> parts() {
- return buildParts(0);
- }
-
- List<String> buildParts(int size) {
- List<String> parts = parent().buildParts(size + 1);
- parts.add(part());
- return parts;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (!(obj instanceof Origin)) {
- return false;
- }
- Origin self = this;
- Origin other = (Origin) obj;
- while (self != null && other != null && self.part().equals(other.part())) {
- self = self.parent();
- other = other.parent();
- }
- return self == other;
- }
-
- @Override
- public int compareTo(Origin other) {
- // Lexicographic ordering from root to leaf.
- List<String> thisParts = parts();
- List<String> otherParts = other.parts();
- int len = Math.min(thisParts.size(), otherParts.size());
- for (int i = 0; i < len; i++) {
- int compare = thisParts.get(i).compareTo(otherParts.get(i));
- if (compare != 0) {
- return compare;
- }
- }
- return Integer.compare(thisParts.size(), otherParts.size());
- }
-
- @Override
- public int hashCode() {
- int hash = 1;
- for (String part : parts()) {
- hash = 31 * hash + part.hashCode();
- }
- return hash;
- }
-
- @Override
- public String toString() {
- return String.join(":", parts());
- }
- }
-
- /** Path component in an origin description. */
- public static class PathOrigin extends Origin {
- private final Path path;
-
- public PathOrigin(Path path, Origin parent) {
- super(parent);
- assert path != null;
- this.path = path;
- }
-
- @Override
- public String part() {
- return path.toString();
- }
- }
-
/** Origin of the resource. */
public final Origin origin;
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 e904e53..948178a 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -12,8 +12,8 @@
import com.android.tools.r8.D8Command;
import com.android.tools.r8.D8Output;
import com.android.tools.r8.Resource;
-import com.android.tools.r8.Resource.Origin;
-import com.android.tools.r8.Resource.PathOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.OutputMode;
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 4b0d6e2..17d634b 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -8,8 +8,8 @@
import static com.android.tools.r8.utils.EncodedValueUtils.parseSigned;
import static com.android.tools.r8.utils.EncodedValueUtils.parseUnsigned;
-import com.android.tools.r8.Resource.Origin;
-import com.android.tools.r8.Resource.PathOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.InstructionFactory;
import com.android.tools.r8.graph.ClassAccessFlags;
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index f8c4cbd..a526c3f 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -1,6 +1,6 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.ProgramResource;
import java.util.function.Consumer;
import java.util.function.Predicate;
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 d5fc0be..82cdbe1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 292b368..4094453 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
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 083c865..f998705 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.StringUtils;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import java.util.Arrays;
@@ -162,7 +163,9 @@
@Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
- DexSourceCode source = new DexSourceCode(this, encodedMethod, null);
+ DexSourceCode source =
+ new DexSourceCode(
+ this, encodedMethod, null, options.lineNumberOptimization == LineNumberOptimization.ON);
IRBuilder builder = new IRBuilder(encodedMethod, source, options);
return builder.build();
}
@@ -173,7 +176,12 @@
ValueNumberGenerator valueNumberGenerator,
Position callerPosition)
throws ApiLevelException {
- DexSourceCode source = new DexSourceCode(this, encodedMethod, callerPosition);
+ DexSourceCode source =
+ new DexSourceCode(
+ this,
+ encodedMethod,
+ callerPosition,
+ options.lineNumberOptimization == LineNumberOptimization.ON);
IRBuilder builder = new IRBuilder(encodedMethod, source, options, valueNumberGenerator);
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 9d42c82..58d8473 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -446,7 +446,7 @@
@Override
public String toString() {
- return String.format("DEFAULT %d (dpc %d, %dline %d)", value, getPCDelta(), getLineDelta());
+ return String.format("DEFAULT %d (dpc %d, dline %d)", value, getPCDelta(), getLineDelta());
}
@Override
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 1c71b9b..83e01a2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -29,7 +29,7 @@
*/
public class DexDebugEventBuilder {
- private static final int NO_PC_INFO = -1;
+ public static final int NO_PC_INFO = -1;
private static final int NO_LINE_INFO = -1;
private final DexEncodedMethod method;
@@ -234,7 +234,7 @@
}
}
- private static void emitAdvancementEvents(
+ public static void emitAdvancementEvents(
int previousPc,
Position previousPosition,
int nextPc,
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
index 280b27e..a02e546 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
@@ -17,8 +17,6 @@
* the current state using the getters after a Default event.
*/
public class DexDebugPositionState implements DexDebugEventVisitor {
- private final DexMethod method;
-
private int currentPc = 0;
private int currentLine;
private DexString currentFile = null;
@@ -26,7 +24,6 @@
private Position currentCallerPosition = null;
public DexDebugPositionState(int startLine, DexMethod method) {
- this.method = method;
currentLine = startLine;
currentMethod = method;
}
@@ -44,9 +41,6 @@
@Override
public void visit(SetInlineFrame setInlineFrame) {
- assert (setInlineFrame.caller == null && setInlineFrame.callee == method)
- || (setInlineFrame.caller != null
- && setInlineFrame.caller.getOutermostCaller().method == method);
currentMethod = setInlineFrame.callee;
currentCallerPosition = setInlineFrame.caller;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemBasedString.java b/src/main/java/com/android/tools/r8/graph/DexItemBasedString.java
index 7777147..14fc568 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemBasedString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemBasedString.java
@@ -42,7 +42,8 @@
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
// This instance should not exist when collecting indexed items.
- // {@link IdentifierMinifier} will replace this with an appropriate {@link DexString}.
+ // {@link com.android.tools.r8.naming.IdentifierMinifier} will replace this with an appropriate
+ // {@link DexString}.
throw new Unreachable("Remaining DexItemBasedString: " + this.toString());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index f2e262b..a38c01d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 4d16548..c96ee92 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.utils.ProgramResource;
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 2c573f4..1985869 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -7,7 +7,7 @@
import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
import static org.objectweb.asm.Opcodes.ASM6;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
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 ce2de41..06eeeea 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.ApiLevelException;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index d02863f..a93253a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -194,6 +194,9 @@
@Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
+ if (other == this) {
+ return true;
+ }
if (preciseTypeUnknown()) {
return false;
}
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 eb055f2..ee00724 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
@@ -76,11 +76,14 @@
private List<DexDebugEntry> debugEntries = null;
private Position callerPosition; // In case of inlining the position of the invoke in the caller.
private final DexMethod method;
+ private final boolean preserveCaller;
- public DexSourceCode(DexCode code, DexEncodedMethod method, Position callerPosition) {
+ public DexSourceCode(
+ DexCode code, DexEncodedMethod method, Position callerPosition, boolean preserveCaller) {
this.code = code;
this.proto = method.method.proto;
this.accessFlags = method.accessFlags;
+ this.preserveCaller = preserveCaller;
argumentTypes = computeArgumentTypes();
DexDebugInfo info = code.getDebugInfo();
if (info != null) {
@@ -242,6 +245,9 @@
}
private Position canonicalizeCallerPosition(Position caller) {
+ // We are not supposed to get here from getCanonicalPositionAppendCaller if !preserveCaller.
+ assert preserveCaller;
+
if (caller == null) {
return callerPosition;
}
@@ -257,16 +263,15 @@
private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
// If this instruction has already been inlined then this.method must be the outermost caller.
- assert (entry.callerPosition == null && entry.method == method)
- || (entry.callerPosition != null
- && entry.callerPosition.getOutermostCaller().method == method);
+ assert entry.callerPosition == null
+ || entry.callerPosition.getOutermostCaller().method == method;
return getCanonicalPosition(
new Position(
entry.line,
entry.sourceFile,
entry.method,
- canonicalizeCallerPosition(entry.callerPosition)));
+ preserveCaller ? canonicalizeCallerPosition(entry.callerPosition) : null));
}
@Override
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 b466315..8547206 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
@@ -113,7 +113,7 @@
this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping());
if (appInfo.hasLiveness()) {
this.protoLiteRewriter = new ProtoLitePruner(appInfo.withLiveness());
- if (!appInfo.withLiveness().identifierNameStrings.isEmpty()) {
+ if (!appInfo.withLiveness().identifierNameStrings.isEmpty() && !options.skipMinification) {
this.identifierNameStringMarker = new IdentifierNameStringMarker(appInfo.withLiveness());
} else {
this.identifierNameStringMarker = null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5386163..b94a22b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1339,11 +1339,13 @@
ConstInstruction[] values = new ConstInstruction[size];
int remaining = size;
Set<Instruction> users = newArray.outValue().uniqueUsers();
+ Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet();
// We allow the array instantiations to cross block boundaries as long as it hasn't encountered
// an instruction instance that can throw an exception.
InstructionListIterator it = block.listIterator();
it.nextUntil(i -> i == newArray);
do {
+ visitedBlocks.add(block);
while (it.hasNext()) {
Instruction instruction = it.next();
// If we encounter an instruction that can throw an exception we need to bail out of the
@@ -1377,7 +1379,10 @@
return values;
}
}
- block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
+ block =
+ block.exit().isGoto() && !visitedBlocks.contains(block.exit().asGoto().getTarget())
+ ? block.exit().asGoto().getTarget()
+ : null;
it = block != null ? block.listIterator() : null;
} while (it != null);
return null;
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 cd51b25..98b08d3 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
@@ -370,7 +370,8 @@
assert invokePosition.isNone();
invokePosition = Position.noneWithMethod(method.method, null);
}
- assert invokePosition.getOutermostCaller().method == method.method;
+ assert invokePosition.callerPosition == null
+ || invokePosition.getOutermostCaller().method == method.method;
IRCode inlinee =
result.buildIR(
diff --git a/src/main/java/com/android/tools/r8/origin/Origin.java b/src/main/java/com/android/tools/r8/origin/Origin.java
new file mode 100644
index 0000000..9e3d33e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/origin/Origin.java
@@ -0,0 +1,135 @@
+// 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.origin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Origin description of a resource.
+ *
+ * <p>An origin is a list of parts that describe where a resource originates from. The first part
+ * is the most recent part and is associated with the present resource, each successive part is
+ * then associated with the context of the previous part.
+ *
+ * <p>For example, for a class file, say {@code my/class/Foo.class}, that is contained within a
+ * jar archive, say {@code myjar.jar}, the Origin of of this resource will be {@code
+ * myjar.jar:my/class/Foo.class} where each part is separated by a colon.
+ *
+ * <p>There are two top-most origins which have no parent: {@code Origin.root()} and {@code
+ * Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
+ * origin (e.g., for generated resources of raw bytes).
+ */
+public abstract class Origin implements Comparable<Origin> {
+
+ private static final Origin ROOT =
+ new Origin() {
+ @Override
+ public String part() {
+ return "";
+ }
+
+ @Override
+ List<String> buildParts(int size) {
+ return new ArrayList<>(size);
+ }
+ };
+
+ private static final Origin UNKNOWN =
+ new Origin() {
+ @Override
+ public String part() {
+ return "<unknown>";
+ }
+
+ @Override
+ List<String> buildParts(int size) {
+ List<String> parts = new ArrayList<>(size + 1);
+ parts.add(part());
+ return parts;
+ }
+ };
+
+ public static Origin root() {
+ return ROOT;
+ }
+
+ public static Origin unknown() {
+ return UNKNOWN;
+ }
+
+ private final Origin parent;
+
+ private Origin() {
+ this.parent = null;
+ }
+
+ protected Origin(Origin parent) {
+ assert parent != null;
+ this.parent = parent;
+ }
+
+ public abstract String part();
+
+ public Origin parent() {
+ return parent;
+ }
+
+ public List<String> parts() {
+ return buildParts(0);
+ }
+
+ List<String> buildParts(int size) {
+ List<String> parts = parent().buildParts(size + 1);
+ parts.add(part());
+ return parts;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Origin)) {
+ return false;
+ }
+ Origin self = this;
+ Origin other = (Origin) obj;
+ while (self != null && other != null && self.part().equals(other.part())) {
+ self = self.parent();
+ other = other.parent();
+ }
+ return self == other;
+ }
+
+ @Override
+ public int compareTo(Origin other) {
+ // Lexicographic ordering from root to leaf.
+ List<String> thisParts = parts();
+ List<String> otherParts = other.parts();
+ int len = Math.min(thisParts.size(), otherParts.size());
+ for (int i = 0; i < len; i++) {
+ int compare = thisParts.get(i).compareTo(otherParts.get(i));
+ if (compare != 0) {
+ return compare;
+ }
+ }
+ return Integer.compare(thisParts.size(), otherParts.size());
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 1;
+ for (String part : parts()) {
+ hash = 31 * hash + part.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return String.join(":", parts());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/origin/PathOrigin.java b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
new file mode 100644
index 0000000..919c4f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
@@ -0,0 +1,23 @@
+// 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.origin;
+
+import java.nio.file.Path;
+
+/** Path component in an origin description. */
+public class PathOrigin extends Origin {
+ private final Path path;
+
+ public PathOrigin(Path path, Origin parent) {
+ super(parent);
+ assert path != null;
+ this.path = path;
+ }
+
+ @Override
+ public String part() {
+ return path.toString();
+ }
+}
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 ddac51f..f9f3f01 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.Resource;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.dex.VDexFile;
import com.android.tools.r8.dex.VDexFileReader;
import com.android.tools.r8.errors.CompilationError;
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 0f85cac..62a5e76 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.OutputSink;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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 8bb1514..24df0e4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -4,13 +4,13 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.Resource.Origin;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.graph.DexEncodedMethod;
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.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.google.common.collect.ImmutableList;
@@ -25,6 +25,12 @@
public class InternalOptions {
+ public enum LineNumberOptimization {
+ OFF,
+ ON,
+ IDENTITY_MAPPING
+ }
+
public final DexItemFactory itemFactory;
public final ProguardConfiguration proguardConfiguration;
@@ -125,6 +131,8 @@
public ImmutableList<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
public boolean minimalMainDex;
+ public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.OFF;
+
public static class InvalidParameterAnnotationInfo {
final DexMethod method;
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
new file mode 100644
index 0000000..37e1265
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -0,0 +1,316 @@
+// 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 com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugEventBuilder;
+import com.android.tools.r8.graph.DexDebugEventVisitor;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugPositionState;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+
+public class LineNumberOptimizer {
+
+ // EventFilter is a visitor for DebugEvents, splits events into two sinks:
+ // - Forwards non-positional events unchanged into a BypassedEventReceiver
+ // - Forwards positional events, accumulated into DexDebugPositionStates, into
+ // positionEventReceiver.
+ private static class EventFilter implements DexDebugEventVisitor {
+ private final BypassedEventReceiver bypassedEventReceiver;
+ private final PositionEventReceiver positionEventReceiver;
+
+ private interface BypassedEventReceiver {
+ void receiveBypassedEvent(DexDebugEvent event);
+ }
+
+ private interface PositionEventReceiver {
+ void receivePositionEvent(DexDebugPositionState positionState);
+ }
+
+ private DexDebugPositionState positionState;
+
+ private EventFilter(
+ int startLine,
+ DexMethod method,
+ BypassedEventReceiver bypassedEventReceiver,
+ PositionEventReceiver positionEventReceiver) {
+ positionState = new DexDebugPositionState(startLine, method);
+ this.bypassedEventReceiver = bypassedEventReceiver;
+ this.positionEventReceiver = positionEventReceiver;
+ }
+
+ @Override
+ public void visit(DexDebugEvent.SetPrologueEnd event) {
+ bypassedEventReceiver.receiveBypassedEvent(event);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.SetEpilogueBegin event) {
+ bypassedEventReceiver.receiveBypassedEvent(event);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.StartLocal event) {
+ bypassedEventReceiver.receiveBypassedEvent(event);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.EndLocal event) {
+ bypassedEventReceiver.receiveBypassedEvent(event);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.RestartLocal event) {
+ bypassedEventReceiver.receiveBypassedEvent(event);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.AdvancePC advancePC) {
+ positionState.visit(advancePC);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.AdvanceLine advanceLine) {
+ positionState.visit(advanceLine);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.SetInlineFrame setInlineFrame) {
+ positionState.visit(setInlineFrame);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.Default defaultEvent) {
+ positionState.visit(defaultEvent);
+ positionEventReceiver.receivePositionEvent(positionState);
+ }
+
+ @Override
+ public void visit(DexDebugEvent.SetFile setFile) {
+ positionState.visit(setFile);
+ }
+ }
+
+ // PositionRemapper is a stateful function which takes a position (represented by a
+ // DexDebugPositionState) and returns a remapped Position.
+ private interface PositionRemapper {
+ Position createRemappedPosition(DexDebugPositionState positionState);
+ }
+
+ private static class IdentityPositionRemapper implements PositionRemapper {
+ @Override
+ public Position createRemappedPosition(DexDebugPositionState positionState) {
+ return new Position(
+ positionState.getCurrentLine(),
+ positionState.getCurrentFile(),
+ positionState.getCurrentMethod(),
+ positionState.getCurrentCallerPosition());
+ }
+ }
+
+ private static class OptimizingPositionRemapper implements PositionRemapper {
+ private int nextLineNumber = 1;
+
+ @Override
+ public Position createRemappedPosition(DexDebugPositionState positionState) {
+ Position newPosition =
+ new Position(
+ nextLineNumber,
+ positionState.getCurrentFile(),
+ positionState.getCurrentMethod(),
+ null);
+ ++nextLineNumber;
+ return newPosition;
+ }
+ }
+
+ // PositionEventEmitter is a stateful function which converts a Position into series of
+ // position-related DexDebugEvents and puts them into a processedEvents list.
+ private static class PositionEventEmitter {
+ private final DexItemFactory dexItemFactory;
+ private int startLine = -1;
+ private DexMethod method;
+ private int previousPc = DexDebugEventBuilder.NO_PC_INFO;
+ private Position previousPosition = null;
+ private List<DexDebugEvent> processedEvents;
+
+ private PositionEventEmitter(
+ DexItemFactory dexItemFactory, DexMethod method, List<DexDebugEvent> processedEvents) {
+ this.dexItemFactory = dexItemFactory;
+ this.method = method;
+ this.processedEvents = processedEvents;
+ }
+
+ private void emitPositionEvents(int currentPc, Position currentPosition) {
+ if (previousPosition == null) {
+ startLine = currentPosition.line;
+ previousPosition = new Position(startLine, null, method, null);
+ }
+ DexDebugEventBuilder.emitAdvancementEvents(
+ previousPc,
+ previousPosition,
+ currentPc,
+ currentPosition,
+ processedEvents,
+ dexItemFactory);
+ previousPc = currentPc;
+ previousPosition = currentPosition;
+ }
+
+ private int getStartLine() {
+ assert (startLine >= 0);
+ return startLine;
+ }
+ }
+
+ public static void run(
+ DexApplication application, NamingLens namingLens, boolean identityMapping) {
+ IdentityHashMap<DexString, List<DexProgramClass>> classesOfFiles = new IdentityHashMap<>();
+ // Collect which files contain which classes that need to have their line numbers optimized.
+ for (DexProgramClass clazz : application.classes()) {
+
+ // TODO(tamaskenez) fix b/69356670 and remove the conditional skipping.
+ if (!clazz.getSynthesizedFrom().isEmpty()) {
+ continue;
+ }
+
+ // Group methods by name
+ IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByName =
+ new IdentityHashMap<>(clazz.directMethods().length + clazz.virtualMethods().length);
+ clazz.forEachMethod(
+ method -> {
+ if (doesContainPositions(method)) {
+ methodsByName.compute(
+ method.method.name,
+ (name, methods) -> {
+ if (methods == null) {
+ methods = new ArrayList<>();
+ }
+ methods.add(method);
+ return methods;
+ });
+ }
+ });
+ for (List<DexEncodedMethod> methods : methodsByName.values()) {
+ if (methods.size() > 1) {
+ // If there are multiple methods with the same name (overloaded) then sort them for
+ // deterministic behaviour: the algorithm will assign new line numbers in this order.
+ // Methods with different names can share the same line numbers, that's why they don't
+ // need to be sorted.
+ methods.sort(
+ (lhs, rhs) -> {
+ int startLineDiff =
+ lhs.getCode().asDexCode().getDebugInfo().startLine
+ - rhs.getCode().asDexCode().getDebugInfo().startLine;
+ if (startLineDiff != 0) return startLineDiff;
+ return DexEncodedMethod.slowCompare(lhs, rhs);
+ });
+ }
+
+ PositionRemapper positionRemapper =
+ identityMapping ? new IdentityPositionRemapper() : new OptimizingPositionRemapper();
+
+ for (DexEncodedMethod method : methods) {
+ // Do the actual processing for each method.
+ DexCode dexCode = method.getCode().asDexCode();
+ DexDebugInfo debugInfo = dexCode.getDebugInfo();
+ List<DexDebugEvent> processedEvents = new ArrayList<>();
+
+ // Our pipeline will be:
+ // [debugInfo.events] -> eventFilter -> positionRemapper -> positionEventEmitter ->
+ // [processedEvents]
+ PositionEventEmitter positionEventEmitter =
+ new PositionEventEmitter(application.dexItemFactory, method.method, processedEvents);
+
+ EventFilter eventFilter =
+ new EventFilter(
+ debugInfo.startLine,
+ method.method,
+ processedEvents::add,
+ positionState -> {
+ Position position = positionRemapper.createRemappedPosition(positionState);
+ positionEventEmitter.emitPositionEvents(positionState.getCurrentPc(), position);
+ });
+ for (DexDebugEvent event : debugInfo.events) {
+ event.accept(eventFilter);
+ }
+
+ DexDebugInfo optimizedDebugInfo =
+ new DexDebugInfo(
+ positionEventEmitter.getStartLine(),
+ debugInfo.parameters,
+ processedEvents.toArray(new DexDebugEvent[processedEvents.size()]));
+
+ // TODO(tamaskenez) Remove this as soon as we have external tests testing not only the
+ // remapping but whether the non-positional debug events remain intact.
+ if (identityMapping) {
+ assert (optimizedDebugInfo.startLine == debugInfo.startLine);
+ assert (optimizedDebugInfo.events.length == debugInfo.events.length);
+ for (int i = 0; i < debugInfo.events.length; ++i) {
+ assert optimizedDebugInfo.events[i].equals(debugInfo.events[i]);
+ }
+ }
+ dexCode.setDebugInfo(optimizedDebugInfo);
+ }
+ }
+ }
+ }
+
+ // Return true if any of the methods' debug infos describe a Position which lists the containing
+ // method as the outermost caller.
+ private static boolean checkMethodsForSelfReferenceInPositions(DexEncodedMethod[] methods) {
+ for (DexEncodedMethod method : methods) {
+ if (!doesContainPositions(method)) {
+ continue;
+ }
+ DexDebugInfo debugInfo = method.getCode().asDexCode().getDebugInfo();
+
+ DexDebugPositionState positionState =
+ new DexDebugPositionState(debugInfo.startLine, method.method);
+ for (DexDebugEvent event : debugInfo.events) {
+ event.accept(positionState);
+ if (event instanceof DexDebugEvent.Default) {
+ Position caller = positionState.getCurrentCallerPosition();
+ DexMethod outermostMethod =
+ caller == null
+ ? positionState.getCurrentMethod()
+ : caller.getOutermostCaller().method;
+ if (outermostMethod == method.method) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean doesContainPositions(DexEncodedMethod method) {
+ Code code = method.getCode();
+ if (code == null || !code.isDexCode()) {
+ return false;
+ }
+ DexDebugInfo debugInfo = code.asDexCode().getDebugInfo();
+ if (debugInfo == null) {
+ return false;
+ }
+ for (DexDebugEvent event : debugInfo.events) {
+ if (event instanceof DexDebugEvent.Default) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java b/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
index cc9c1e3..e64ebfb 100644
--- a/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
+++ b/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.Resource;
+import com.android.tools.r8.origin.Origin;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
diff --git a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
index 01ea7ba..ec5fccb 100644
--- a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.Resource;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.HashMap;
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
index 2cce6da..f2290b3 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
@@ -8,8 +8,8 @@
import static com.android.tools.r8.utils.FileUtils.isDexFile;
import com.android.tools.r8.Resource;
-import com.android.tools.r8.Resource.Origin;
-import com.android.tools.r8.Resource.PathOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.shaking.FilteredClassPath;
import com.google.common.io.ByteStreams;
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 d2c6560..cd5bbcd 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.Diagnostic;
-import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.origin.Origin;
public class StringDiagnostic implements Diagnostic {
diff --git a/src/main/java/com/android/tools/r8/utils/VersionProperties.java b/src/main/java/com/android/tools/r8/utils/VersionProperties.java
index 28ac2ef..d384134 100644
--- a/src/main/java/com/android/tools/r8/utils/VersionProperties.java
+++ b/src/main/java/com/android/tools/r8/utils/VersionProperties.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.utils;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@@ -26,8 +25,11 @@
private String releaser;
private static VersionProperties get() {
- try {
- return new VersionProperties(VersionProperties.class.getClassLoader());
+ ClassLoader loader = VersionProperties.class.getClassLoader();
+ try (InputStream resourceStream = loader.getResourceAsStream(RESOURCE_NAME)) {
+ return resourceStream == null
+ ? new VersionProperties()
+ : new VersionProperties(resourceStream);
} catch (IOException e) {
return new VersionProperties();
}
@@ -36,19 +38,9 @@
private VersionProperties() {
}
- private VersionProperties(ClassLoader loader)
- throws IOException {
- try (InputStream resourceStream = loader.getResourceAsStream(RESOURCE_NAME)) {
- if (resourceStream == null) {
- throw new FileNotFoundException(RESOURCE_NAME);
- }
- initWithInputStream(resourceStream);
- }
- }
-
- private void initWithInputStream(InputStream is) throws IOException {
+ private VersionProperties(InputStream resourceStream) throws IOException {
Properties prop = new Properties();
- prop.load(is);
+ prop.load(resourceStream);
long versionFileVersion = Long.parseLong(prop.getProperty(VERSION_CODE_KEY));
assert versionFileVersion >= 1;
diff --git a/src/test/debugTestResources/Inlining2.java b/src/test/debugTestResources/Inlining2.java
index 38f854c..a570ba3 100644
--- a/src/test/debugTestResources/Inlining2.java
+++ b/src/test/debugTestResources/Inlining2.java
@@ -2,6 +2,31 @@
// 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.
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+
public class Inlining2 {
public static void inlineThisFromAnotherFile() {
System.out.println("inlineThisFromAnotherFile");
diff --git a/src/test/debugTestResources/LineNumberOptimization1.java b/src/test/debugTestResources/LineNumberOptimization1.java
new file mode 100644
index 0000000..a97fc6d
--- /dev/null
+++ b/src/test/debugTestResources/LineNumberOptimization1.java
@@ -0,0 +1,24 @@
+// 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.
+
+public class LineNumberOptimization1 {
+ private static void callThisFromSameFile() {
+ System.out.println("callThisFromSameFile");
+ LineNumberOptimization2.callThisFromAnotherFile();
+ }
+
+ private static void callThisFromSameFile(int a) {
+ System.out.println("callThisFromSameFile second overload");
+ }
+
+ private static void callThisFromSameFile(int a, int b) {
+ System.out.println("callThisFromSameFile third overload");
+ }
+
+ public static void main(String[] args) {
+ callThisFromSameFile();
+ callThisFromSameFile(1);
+ callThisFromSameFile(1, 2);
+ }
+}
diff --git a/src/test/debugTestResources/LineNumberOptimization2.java b/src/test/debugTestResources/LineNumberOptimization2.java
new file mode 100644
index 0000000..0f07ae9
--- /dev/null
+++ b/src/test/debugTestResources/LineNumberOptimization2.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.
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+public class LineNumberOptimization2 {
+ public static void callThisFromAnotherFile() {
+ System.out.println("callThisFromAnotherFile");
+ }
+}
diff --git a/src/test/examples/enclosingmethod/OuterClass.java b/src/test/examples/enclosingmethod/OuterClass.java
index d9ab4ba..387f233 100644
--- a/src/test/examples/enclosingmethod/OuterClass.java
+++ b/src/test/examples/enclosingmethod/OuterClass.java
@@ -4,11 +4,34 @@
package enclosingmethod;
public class OuterClass {
+ // Named member class.
public class AClass {
}
+ static {
+ // Named local class. Will have an enclosing-method annotation with a zero method by being
+ // defined in the static initializer.
+ class LocalClass extends AbstractClass {
+
+ @Override
+ public int anInt() {
+ return 7;
+ }
+ }
+
+ // Anonymous inner class. Will have the same zero-method enclosing-method annotation.
+ print(new AbstractClass() {
+ @Override
+ public int anInt() {
+ return 42;
+ }
+ });
+ print(new LocalClass());
+ }
+
public void aMethod() {
+ // Local class with a non-zero-method enclosing-method annotation.
class AnotherClass extends AbstractClass {
@Override
@@ -17,6 +40,7 @@
}
}
+ // Anonymous inner class with a non-zero-method enclosing-method annotation.
print(new AbstractClass() {
@Override
public int anInt() {
diff --git a/src/test/examples/enclosingmethod_proguarded/AbstractClass.java b/src/test/examples/enclosingmethod_proguarded/AbstractClass.java
new file mode 100644
index 0000000..e9fce35
--- /dev/null
+++ b/src/test/examples/enclosingmethod_proguarded/AbstractClass.java
@@ -0,0 +1,8 @@
+// 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 enclosingmethod_proguarded;
+
+public abstract class AbstractClass {
+ public abstract int anInt();
+}
diff --git a/src/test/examples/enclosingmethod_proguarded/Main.java b/src/test/examples/enclosingmethod_proguarded/Main.java
new file mode 100644
index 0000000..93b4c9f
--- /dev/null
+++ b/src/test/examples/enclosingmethod_proguarded/Main.java
@@ -0,0 +1,15 @@
+// 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 enclosingmethod_proguarded;
+
+public class Main {
+ public static void main(String... args) {
+ OuterClass anInstance = new OuterClass();
+ anInstance.aMethod();
+ final Class[] classes = OuterClass.class.getDeclaredClasses();
+ for (Class clazz : classes) {
+ System.out.println("InnerClass " + clazz.getName());
+ }
+ }
+}
diff --git a/src/test/examples/enclosingmethod_proguarded/OuterClass.java b/src/test/examples/enclosingmethod_proguarded/OuterClass.java
new file mode 100644
index 0000000..84deaad
--- /dev/null
+++ b/src/test/examples/enclosingmethod_proguarded/OuterClass.java
@@ -0,0 +1,37 @@
+// 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 enclosingmethod_proguarded;
+
+public class OuterClass {
+ public class AClass {
+
+ }
+
+ public void aMethod() {
+ class AnotherClass extends AbstractClass {
+
+ @Override
+ public int anInt() {
+ return 48;
+ }
+ }
+
+ print(new AbstractClass() {
+ @Override
+ public int anInt() {
+ return 42;
+ }
+ });
+ print(new AnotherClass());
+ }
+
+ private static void print(AbstractClass anInstance) {
+ System.out.println(anInstance.anInt());
+ System.out.println(anInstance.getClass().getEnclosingClass());
+ System.out.println(anInstance.getClass().getEnclosingMethod());
+ System.out.println(anInstance.getClass().isAnonymousClass());
+ System.out.println(anInstance.getClass().isLocalClass());
+ System.out.println(anInstance.getClass().isMemberClass());
+ }
+}
diff --git a/src/test/examples/enclosingmethod/proguard.cfg b/src/test/examples/enclosingmethod_proguarded/proguard.cfg
similarity index 87%
rename from src/test/examples/enclosingmethod/proguard.cfg
rename to src/test/examples/enclosingmethod_proguarded/proguard.cfg
index bc5bd8d..12d9b6b 100644
--- a/src/test/examples/enclosingmethod/proguard.cfg
+++ b/src/test/examples/enclosingmethod_proguarded/proguard.cfg
@@ -2,7 +2,7 @@
# 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 class enclosingmethod.* {
+-keep class enclosingmethod_proguarded.* {
*;
}
diff --git a/src/test/examples/loop/UdpServer.java b/src/test/examples/loop/UdpServer.java
new file mode 100644
index 0000000..463bfde
--- /dev/null
+++ b/src/test/examples/loop/UdpServer.java
@@ -0,0 +1,39 @@
+// 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 loop;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class UdpServer {
+ private static final String PREFIX = "RANDOM_DATA_PREFIX_";
+ public static void main(String[] args) throws Exception {
+ ExecutorService service = Executors.newWorkStealingPool(2);
+ Callable c = new Callable() {
+ @Override
+ public Object call() throws Exception {
+ int counter = 0;
+ byte[] receiveData = new byte[1024];
+ while (true) {
+ // Mimic receiving data via socket. (A use of actual socket is IO blocking.)
+ receiveData = (PREFIX + counter++).getBytes();
+ }
+ }
+ };
+ Future<?> f = service.submit(c);
+ try {
+ f.get(1, TimeUnit.NANOSECONDS);
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ System.out.println(e);
+ } finally {
+ f.cancel(true);
+ service.shutdownNow();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 3485c65..e0aeaeb 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -64,6 +64,7 @@
.put("floating_point_annotations.FloatingPointValuedAnnotationTest", match(R8_COMPILER))
.put("regress_62300145.Regress", match(R8_COMPILER)) // npe
.put("enclosingmethod.Main", match(R8_COMPILER)) // output differs
+ .put("enclosingmethod_proguarded.Main", match(R8_COMPILER)) // output differs
.build();
private static final Set<String> failingCompileCf =
@@ -76,6 +77,10 @@
new ImmutableMap.Builder<String, TestCondition>()
// Traverses stack frames that contain Art specific frames.
.put("throwing.Throwing", TestCondition.any())
+ // DEX inner-classes annotations can't distinguish member classes from local classes.
+ // This results in Class.isLocalClass always being false and Class.isMemberClass always
+ // being true even when the converse is the case when running on the JVM.
+ .put("enclosingmethod.Main", TestCondition.any())
// Early art versions incorrectly print Float.MIN_VALUE.
.put(
"filledarray.FilledArray",
@@ -103,6 +108,7 @@
"invoke.Invoke",
"jumbostring.JumboString",
"loadconst.LoadConst",
+ "loop.UdpServer",
"newarray.NewArray",
"regalloc.RegAlloc",
"returns.Returns",
@@ -129,6 +135,7 @@
"memberrebinding3.Memberrebinding",
"minification.Minification",
"enclosingmethod.Main",
+ "enclosingmethod_proguarded.Main",
"interfaceinlining.Main",
"switchmaps.Switches",
};
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
index 3222734..883a61f 100644
--- a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
+++ b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
@@ -38,8 +38,8 @@
public class CompatDxTests {
private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
- private static final String EXAMPLE_JAR_FILE1 = "build/test/examples/arithmetic.jar";
- private static final String EXAMPLE_JAR_FILE2 = "build/test/examples/barray.jar";
+ private static final String EXAMPLE_JAR_FILE1 = ToolHelper.EXAMPLES_BUILD_DIR + "arithmetic.jar";
+ private static final String EXAMPLE_JAR_FILE2 = ToolHelper.EXAMPLES_BUILD_DIR + "barray.jar";
private static final String NO_LOCALS = "--no-locals";
private static final String NO_POSITIONS = "--positions=none";
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
index 168b794..f6e1ef0 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
@@ -5,36 +5,33 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.CompilationException;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.InvokeStatic;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.smali.SmaliBuilder;
-import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.google.common.collect.ImmutableList;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
-import java.util.concurrent.ExecutionException;
-import org.antlr.runtime.RecognitionException;
import org.junit.Test;
public class ForNameTest extends SmaliTestBase {
+ private final String CLASS_NAME = "Example";
private final static String BOO = "Boo";
@Test
public void forName_renamed() throws Exception {
- SmaliBuilder builder = new SmaliBuilder("Example");
- MethodSignature main = builder.addMainMethod(
+ SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
+ builder.addMainMethod(
1,
"const-string v0, \"" + BOO + "\"",
"invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;",
@@ -44,16 +41,19 @@
builder.addClass(BOO);
List<String> pgConfigs = ImmutableList.of(
- "-keep class Example { *; }",
+ "-keep class " + CLASS_NAME + " { *; }",
"-keep,allowobfuscation class " + BOO,
"-dontshrink",
"-dontoptimize");
- Path processedApp = runCompatProguard(builder, pgConfigs);
- DexEncodedMethod method = getMethod(processedApp, main);
- assertNotNull(method);
+ DexInspector inspector = runCompatProguard(builder, pgConfigs);
- DexCode code = method.getCode().asDexCode();
+ ClassSubject clazz = inspector.clazz(CLASS_NAME);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method(DexInspector.MAIN);
+ assertTrue(method.isPresent());
+
+ DexCode code = method.getMethod().getCode().asDexCode();
// TODO(b/36799092): DeadCodeRemover should be able to remove this instruction.
assertTrue(code.instructions[0] instanceof ConstString);
ConstString constString = (ConstString) code.instructions[0];
@@ -65,20 +65,49 @@
assertTrue(code.instructions[3] instanceof ReturnVoid);
}
- private Path runCompatProguard(SmaliBuilder builder, List<String> proguardConfigurations) {
- try {
- Path dexOutputDir = temp.newFolder().toPath();
- R8Command command =
- new CompatProguardCommandBuilder(true, true)
- .addDexProgramData(builder.compile())
- .setOutputPath(dexOutputDir)
- .addProguardConfiguration(proguardConfigurations)
- .build();
- ToolHelper.runR8(command);
- return dexOutputDir.resolve("classes.dex");
- } catch (CompilationException | IOException | RecognitionException | ExecutionException e) {
- throw new RuntimeException(e);
- }
+ @Test
+ public void forName_noMinification() throws Exception {
+ SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
+ builder.addMainMethod(
+ 1,
+ "const-string v0, \"" + BOO + "\"",
+ "invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;",
+ "move-result-object v0",
+ "return-void");
+
+ builder.addClass(BOO);
+
+ List<String> pgConfigs = ImmutableList.of(
+ "-keep class " + CLASS_NAME +" { *; }",
+ "-keep class " + BOO,
+ "-dontshrink",
+ "-dontoptimize",
+ "-dontobfuscate");
+
+ DexInspector inspector = runCompatProguard(builder, pgConfigs);
+
+ ClassSubject clazz = inspector.clazz(CLASS_NAME);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method(DexInspector.MAIN);
+ assertTrue(method.isPresent());
+
+ DexCode code = method.getMethod().getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof ConstString);
+ ConstString constString = (ConstString) code.instructions[0];
+ assertEquals(BOO, constString.getString().toString());
+ assertTrue(code.instructions[1] instanceof InvokeStatic);
+ assertTrue(code.instructions[2] instanceof ReturnVoid);
}
+ private DexInspector runCompatProguard(SmaliBuilder builder, List<String> proguardConfigurations)
+ throws Exception{
+ Path dexOutputDir = temp.newFolder().toPath();
+ R8Command command =
+ new CompatProguardCommandBuilder(true, true)
+ .addDexProgramData(builder.compile())
+ .setOutputPath(dexOutputDir)
+ .addProguardConfiguration(proguardConfigurations)
+ .build();
+ return new DexInspector(ToolHelper.runR8(command));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
index d8649c7..2d109e3 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.debug;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -12,33 +16,64 @@
public class DebugInfoWhenInliningTest extends DebugTestBase {
public static final String SOURCE_FILE = "Inlining1.java";
- private static DebuggeePath debuggeePath;
+ private static DebuggeePath debuggeePathNotOptimized;
+ private static DebuggeePath debuggeePathOptimized;
+
+ private static DebuggeePath makeDex(LineNumberOptimization lineNumberOptimization)
+ throws Exception {
+ return DebuggeePath.makeDex(
+ compileToDexViaR8(
+ oc -> {
+ oc.lineNumberOptimization = lineNumberOptimization;
+ },
+ null,
+ DEBUGGEE_JAR,
+ Collections.<String>emptyList(),
+ true,
+ CompilationMode.RELEASE));
+ }
@BeforeClass
public static void initDebuggeePath() throws Exception {
- debuggeePath =
- DebuggeePath.makeDex(
- compileToDexViaR8(
- null,
- null,
- DEBUGGEE_JAR,
- Collections.<String>emptyList(),
- true,
- CompilationMode.RELEASE));
+ debuggeePathNotOptimized = makeDex(LineNumberOptimization.OFF);
+ debuggeePathOptimized = makeDex(LineNumberOptimization.ON);
}
@Test
- public void testEachLine() throws Throwable {
+ public void testEachLineNotOptimized() throws Throwable {
+ // The reason why the not-optimized test contains half as many line numbers as the optimized
+ // one:
+ //
+ // In the Java source (Inlining1) each call is duplicated. Since they end up on the same line
+ // (innermost callee) the line numbers are actually 7, 7, 32, 32, ... but even if the positions
+ // are emitted duplicated in the dex code, the debugger stops only when there's a change.
+ int[] lineNumbers = {7, 32, 11, 7};
+ testEachLine(debuggeePathNotOptimized, lineNumbers);
+ }
+
+ @Test
+ public void testEachLineOptimized() throws Throwable {
+ int[] lineNumbers = {1, 2, 3, 4, 5, 6, 7, 8};
+ testEachLine(debuggeePathOptimized, lineNumbers);
+ }
+
+ private void testEachLine(DebuggeePath debuggeePath, int[] lineNumbers) throws Throwable {
final String className = "Inlining1";
- final String methodName = "main";
- final String signature = "([Ljava/lang/String;)V";
- runDebugTest(
- debuggeePath,
- className,
- breakpoint(className, methodName, signature),
- run(),
- checkMethod(className, methodName, signature),
- // TODO(tamaskenez) to be continued as the feature is implemented in class Inliner
- run());
+ final String mainSignature = "([Ljava/lang/String;)V";
+ List<Command> commands = new ArrayList<Command>();
+ commands.add(breakpoint(className, "main", mainSignature));
+ commands.add(run());
+ boolean first = true;
+ for (int i : lineNumbers) {
+ if (first) {
+ first = false;
+ } else {
+ commands.add(stepOver());
+ }
+ commands.add(checkMethod(className, "main", mainSignature));
+ commands.add(checkLine(SOURCE_FILE, i));
+ }
+ commands.add(run());
+ runDebugTest(debuggeePath, className, commands);
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
new file mode 100644
index 0000000..74a46ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -0,0 +1,103 @@
+// 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.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import java.util.Collections;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/** Tests source file and line numbers on inlined methods. */
+public class LineNumberOptimizationTest extends DebugTestBase {
+
+ public static final String SOURCE_FILE = "LineNumberOptimization1.java";
+ private static DebuggeePath debuggeePathOptimized;
+ private static DebuggeePath debuggeePathNotOptimized;
+ private static DebuggeePath debuggeePathIdentityTest;
+
+ private static DebuggeePath makeDex(LineNumberOptimization lineNumberOptimization)
+ throws Exception {
+ return DebuggeePath.makeDex(
+ compileToDexViaR8(
+ oc -> {
+ oc.lineNumberOptimization = lineNumberOptimization;
+ oc.inlineAccessors = false;
+ },
+ null,
+ DEBUGGEE_JAR,
+ Collections.<String>emptyList(),
+ true,
+ CompilationMode.RELEASE));
+ }
+
+ @BeforeClass
+ public static void initDebuggeePath() throws Exception {
+ debuggeePathNotOptimized = makeDex(LineNumberOptimization.OFF);
+ debuggeePathOptimized = makeDex(LineNumberOptimization.ON);
+ debuggeePathIdentityTest = makeDex(LineNumberOptimization.IDENTITY_MAPPING);
+ }
+
+ @Test
+ public void testNotOptimized() throws Throwable {
+ int[] lineNumbers = {20, 7, 8, 28, 8, 20, 21, 12, 21, 22, 16, 22};
+ test(debuggeePathNotOptimized, lineNumbers);
+ }
+
+ @Test
+ public void testOptimized() throws Throwable {
+ int[] lineNumbers = {1, 1, 2, 1, 2, 1, 2, 3, 2, 3, 4, 3};
+ test(debuggeePathOptimized, lineNumbers);
+ }
+
+ private void test(DebuggeePath debuggeePath, int[] lineNumbers) throws Throwable {
+ final String class1 = "LineNumberOptimization1";
+ final String class2 = "LineNumberOptimization2";
+ final String file1 = class1 + ".java";
+ final String file2 = class2 + ".java";
+ final String mainSignature = "([Ljava/lang/String;)V";
+
+ runDebugTest(
+ debuggeePath,
+ class1,
+ breakpoint(class1, "main", mainSignature),
+ run(),
+ checkMethod(class1, "main", mainSignature),
+ checkLine(file1, lineNumbers[0]),
+ stepInto(),
+ checkMethod(class1, "callThisFromSameFile", "()V"),
+ checkLine(file1, lineNumbers[1]),
+ stepOver(),
+ checkMethod(class1, "callThisFromSameFile", "()V"),
+ checkLine(file1, lineNumbers[2]),
+ stepInto(INTELLIJ_FILTER),
+ checkMethod(class2, "callThisFromAnotherFile", "()V"),
+ checkLine(file2, lineNumbers[3]),
+ stepOver(),
+ checkMethod(class1, "callThisFromSameFile", "()V"),
+ checkLine(file1, lineNumbers[4]),
+ stepOver(),
+ checkMethod(class1, "main", mainSignature),
+ checkLine(file1, lineNumbers[5]),
+ stepOver(),
+ checkMethod(class1, "main", mainSignature),
+ checkLine(file1, lineNumbers[6]),
+ stepInto(),
+ checkMethod(class1, "callThisFromSameFile", "(I)V"),
+ checkLine(file1, lineNumbers[7]),
+ stepOver(),
+ checkMethod(class1, "main", mainSignature),
+ checkLine(file1, lineNumbers[8]),
+ stepOver(),
+ checkMethod(class1, "main", mainSignature),
+ checkLine(file1, lineNumbers[9]),
+ stepInto(),
+ checkMethod(class1, "callThisFromSameFile", "(II)V"),
+ checkLine(file1, lineNumbers[10]),
+ stepOver(),
+ checkMethod(class1, "main", mainSignature),
+ checkLine(file1, lineNumbers[11]),
+ run());
+ }
+}
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 94f53f4..23fc147 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -5,8 +5,8 @@
import static com.android.tools.r8.utils.DescriptorUtils.getPathFromDescriptor;
-import com.android.tools.r8.Resource.Origin;
-import com.android.tools.r8.Resource.PathOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -84,6 +84,7 @@
private boolean makeInit = false;
private boolean hasInit = false;
private boolean isInterface = false;
+ private String access = "public";
private ClassBuilder(String name) {
this(name, "java/lang/Object");
@@ -135,6 +136,14 @@
return addMethod("public static", name, argumentTypes, returnType, lines);
}
+ public MethodSignature addPackagePrivateStaticMethod(
+ String name,
+ List<String> argumentTypes,
+ String returnType,
+ String... lines) {
+ return addMethod("static", name, argumentTypes, returnType, lines);
+ }
+
public MethodSignature addMainMethod(String... lines) {
return addStaticMethod("main", ImmutableList.of("[Ljava/lang/String;"), "V", lines);
}
@@ -187,7 +196,7 @@
if (isInterface) {
builder.append(" interface abstract");
}
- builder.append(" public ").append(name).append('\n');
+ builder.append(" ").append(access).append(" ").append(name).append('\n');
builder.append(".super ").append(superName).append('\n');
for (String iface : interfaces) {
builder.append(".implements ").append(iface).append('\n');
@@ -215,6 +224,10 @@
isInterface = true;
}
+ void setAccess(String access) {
+ this.access = access;
+ }
+
public MethodSignature addDefaultConstructor() {
assert !hasInit;
hasInit = true;
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 3fa67af..7576912 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -19,6 +19,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -39,6 +40,17 @@
return ToolHelper.runJavaNoVerify(ImmutableList.of(out.toString()), main);
}
+ protected ProcessResult runOnJavaNoVerifyRaw(JasminBuilder program, JasminBuilder library,
+ String main)
+ throws Exception {
+ Path out = temp.newFolder().toPath();
+ program.writeClassFiles(out);
+ Path libraryOut = temp.newFolder().toPath();
+ library.writeClassFiles(libraryOut);
+ return ToolHelper.runJavaNoVerify(ImmutableList.of(out.toString(), libraryOut.toString()),
+ main);
+ }
+
private String assertNormalExitAndGetStdout(ProcessResult result) {
if (result.exitCode != 0) {
System.out.println("Std out:");
@@ -71,6 +83,11 @@
return runOnArtRaw(compileWithD8(builder), main);
}
+ protected ProcessResult runOnArtD8Raw(JasminBuilder program, JasminBuilder library, String main)
+ throws Exception {
+ return runOnArtRaw(compileWithD8(program), compileWithD8(library), main);
+ }
+
protected AndroidApp compileWithR8(JasminBuilder builder) throws Exception {
return compileWithR8(builder, null);
}
@@ -86,11 +103,34 @@
throws Exception {
R8Command command =
ToolHelper.prepareR8CommandBuilder(builder.build())
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
.addProguardConfiguration(ImmutableList.of(proguardConfig))
.build();
return ToolHelper.runR8(command, optionsConsumer);
}
+ protected AndroidApp compileWithR8(JasminBuilder program, Path library,
+ Consumer<InternalOptions> optionsConsumer)
+ throws Exception {
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(program.build())
+ .addLibraryFiles(library)
+ .build();
+ return ToolHelper.runR8(command, optionsConsumer);
+ }
+
+ protected AndroidApp compileWithR8(JasminBuilder program, Path library,
+ String proguardConfig, Consumer<InternalOptions> optionsConsumer)
+ throws Exception {
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(program.build())
+ .addProguardConfiguration(ImmutableList.of(proguardConfig))
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .addLibraryFiles(library)
+ .build();
+ return ToolHelper.runR8(command, optionsConsumer);
+ }
+
protected String runOnArtR8(JasminBuilder builder, String main) throws Exception {
return runOnArtR8(builder, main, null);
}
@@ -116,13 +156,25 @@
return runOnArtRaw(result, main);
}
- protected ProcessResult runOnArtR8Raw(JasminBuilder builder, String main, String proguardConfig,
- Consumer<InternalOptions> optionsConsumer)
+ protected ProcessResult runOnArtR8Raw(JasminBuilder builder, String main,
+ String proguardConfig, Consumer<InternalOptions> optionsConsumer)
throws Exception {
AndroidApp result = compileWithR8(builder, proguardConfig, optionsConsumer);
return runOnArtRaw(result, main);
}
+ protected ProcessResult runOnArtR8Raw(JasminBuilder program, JasminBuilder library, String main,
+ String proguardConfig, Consumer<InternalOptions> optionsConsumer)
+ throws Exception {
+ Path libraryClasses = temp.newFolder().toPath();
+ library.writeClassFiles(libraryClasses);
+ AndroidApp result = proguardConfig == null
+ ? compileWithR8(program, libraryClasses, optionsConsumer)
+ : compileWithR8(program, libraryClasses, proguardConfig, optionsConsumer);
+ AndroidApp libraryApp = compileWithR8(library);
+ return runOnArtRaw(result, libraryApp, main);
+ }
+
private ProcessResult runDx(JasminBuilder builder, File classes, Path dex) throws Exception {
builder.writeClassFiles(classes.toPath());
List<String> args = new ArrayList<>();
@@ -151,12 +203,33 @@
return ToolHelper.runArtRaw(dex.toString(), main);
}
+ protected ProcessResult runOnArtDxRaw(JasminBuilder program, JasminBuilder library, String main)
+ throws Exception {
+ Path dex = temp.getRoot().toPath().resolve("classes.dex");
+ ProcessResult result = runDx(program, temp.newFolder("classes_for_dx"), dex);
+ assertNormalExitAndGetStdout(result);
+ Path libraryDex = temp.getRoot().toPath().resolve("library.dex");
+ result = runDx(library, temp.newFolder("classes_for_library_dx"), libraryDex);
+ assertNormalExitAndGetStdout(result);
+ return ToolHelper.runArtRaw(ImmutableList.of(dex.toString(), libraryDex.toString()), main, null);
+ }
+
protected ProcessResult runOnArtRaw(AndroidApp app, String main) throws IOException {
Path out = temp.getRoot().toPath().resolve("out.zip");
app.writeToZip(out, OutputMode.Indexed);
return ToolHelper.runArtRaw(out.toString(), main);
}
+ protected ProcessResult runOnArtRaw(AndroidApp program, AndroidApp library, String main)
+ throws IOException {
+ Path out = temp.getRoot().toPath().resolve("out.zip");
+ program.writeToZip(out, OutputMode.Indexed);
+ Path libraryOut = temp.getRoot().toPath().resolve("libraryOut.zip");
+ library.writeToZip(libraryOut, OutputMode.Indexed);
+ return ToolHelper.runArtRaw(ImmutableList.of(out.toString(), libraryOut.toString()), main,
+ null);
+ }
+
protected String runOnArt(AndroidApp app, String main) throws IOException {
Path out = temp.getRoot().toPath().resolve("out.zip");
app.writeToZip(out, OutputMode.Indexed);
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index fb1672f..e122f9b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -201,7 +201,7 @@
" invokespecial SubClass/<init>()V",
" invokevirtual SubClass/aMethod()V",
" return");
- ensureIAEExceptJava(builder);
+ ensureIAE(builder);
}
@Test
@@ -238,7 +238,7 @@
" invokespecial SubClass/<init>()V",
" invokespecial SubClass/aMethod()V",
" return");
- ensureIAEExceptJava(builder);
+ ensureIAE(builder);
}
@Test
@@ -275,7 +275,7 @@
" invokevirtual SubClass/callAMethod()V",
" return");
- ensureIAEExceptJava(builder);
+ ensureIAE(builder);
}
@Test
@@ -352,6 +352,111 @@
ensureICCE(builder);
}
+ @Test
+ public void testRebindVirtualCallToStatic() throws Exception {
+ // Library classes.
+ JasminBuilder libraryBuilder = new JasminBuilder();
+ ClassBuilder classWithStatic = libraryBuilder.addClass("ClassWithStatic");
+ classWithStatic.addDefaultConstructor();
+ classWithStatic.addStaticMethod("aMethod", emptyList(), "V",
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " bipush 42",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " return");
+
+ // Program classes.
+ JasminBuilder programBuilder = new JasminBuilder();
+ programBuilder.addClass("SubClass", "ClassWithStatic")
+ .addDefaultConstructor();
+ programBuilder.addClass("SubSubClass", "SubClass")
+ .addDefaultConstructor();
+ ClassBuilder mainClass = programBuilder.addClass("Main");
+ mainClass.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " new SubSubClass",
+ " dup",
+ " invokespecial SubSubClass/<init>()V",
+ " invokevirtual SubSubClass/aMethod()V",
+ " return");
+
+ ensureICCE(programBuilder, libraryBuilder);
+ }
+
+ @Test
+ public void testRebindVirtualCallToPackagePrivateStatic() throws Exception {
+ // Library classes.
+ JasminBuilder libraryBuilder = new JasminBuilder();
+ ClassBuilder classWithStatic = libraryBuilder.addClass("ClassWithStatic");
+ classWithStatic.addDefaultConstructor();
+ classWithStatic.addPackagePrivateStaticMethod("aMethod", emptyList(), "V",
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " bipush 42",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " return");
+ libraryBuilder.addClass("p/LibSub", "ClassWithStatic")
+ .addDefaultConstructor();
+
+ // Program classes.
+ JasminBuilder programBuilder = new JasminBuilder();
+ programBuilder.addClass("p/SubClass", "p/LibSub")
+ .addDefaultConstructor();
+ programBuilder.addClass("SubSubClass", "p/SubClass")
+ .addDefaultConstructor();
+ ClassBuilder mainClass = programBuilder.addClass("Main");
+ mainClass.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " new SubSubClass",
+ " dup",
+ " invokespecial SubSubClass/<init>()V",
+ " invokevirtual SubSubClass/aMethod()V",
+ " return");
+
+ ensureICCE(programBuilder, libraryBuilder);
+ }
+
+ @Test
+ @Ignore("b/69356146")
+ public void testRebindVirtualCallToStaticInPackagePrivateClass() throws Exception {
+ // Library classes.
+ JasminBuilder libraryBuilder = new JasminBuilder();
+ ClassBuilder classWithStatic = libraryBuilder.addClass("ClassWithStatic");
+ classWithStatic.setAccess("");
+ classWithStatic.addDefaultConstructor();
+ classWithStatic.addStaticMethod("aMethod", emptyList(), "V",
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " bipush 42",
+ " invokevirtual java/io/PrintStream/println(I)V",
+ " return");
+ libraryBuilder.addClass("LibSub", "ClassWithStatic");
+ libraryBuilder.addClass("p/LibSubSub", "LibSub");
+
+ // Program classes.
+ JasminBuilder programBuilder = new JasminBuilder();
+ programBuilder.addClass("p/SubClass", "p/LibSubSub")
+ .addDefaultConstructor();
+ programBuilder.addClass("SubSubClass", "p/SubClass")
+ .addDefaultConstructor();
+ ClassBuilder mainClass = programBuilder.addClass("Main");
+ mainClass.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " new SubSubClass",
+ " dup",
+ " invokespecial SubSubClass/<init>()V",
+ " invokevirtual SubSubClass/aMethod()V",
+ " return");
+
+ ensureICCE(programBuilder, libraryBuilder);
+ }
+
private void ensureSameOutput(JasminBuilder app) throws Exception {
String javaOutput = runOnJava(app, MAIN_CLASS);
String dxOutput = runOnArtDx(app, MAIN_CLASS);
@@ -369,11 +474,18 @@
ensureRuntimeException(app, IncompatibleClassChangeError.class);
}
- private void ensureIAEExceptJava(JasminBuilder app)
- throws Exception {
+ private void ensureICCE(JasminBuilder app, JasminBuilder library) throws Exception {
+ ensureRuntimeException(app, library, IncompatibleClassChangeError.class);
+ }
+
+ private void ensureIAE(JasminBuilder app) throws Exception {
ensureRuntimeException(app, IllegalAccessError.class);
}
+ private void ensureIAE(JasminBuilder app, JasminBuilder library) throws Exception {
+ ensureRuntimeException(app, library, IllegalAccessError.class);
+ }
+
private void ensureRuntimeException(JasminBuilder app, Class exception) throws Exception {
String name = exception.getSimpleName();
ProcessResult dxOutput = runOnArtDxRaw(app, MAIN_CLASS);
@@ -388,4 +500,20 @@
ProcessResult javaOutput = runOnJavaNoVerifyRaw(app, MAIN_CLASS);
Assert.assertTrue(javaOutput.stderr.contains(name));
}
+
+ private void ensureRuntimeException(JasminBuilder app, JasminBuilder library, Class exception)
+ throws Exception {
+ String name = exception.getSimpleName();
+ ProcessResult dxOutput = runOnArtDxRaw(app, library, MAIN_CLASS);
+ Assert.assertTrue(dxOutput.stderr.contains(name));
+ ProcessResult d8Output = runOnArtD8Raw(app, library, MAIN_CLASS);
+ Assert.assertTrue(d8Output.stderr.contains(name));
+ ProcessResult r8Output = runOnArtR8Raw(app, library, MAIN_CLASS, null, null);
+ Assert.assertTrue(r8Output.stderr.contains(name));
+ ProcessResult r8ShakenOutput = runOnArtR8Raw(app, library, MAIN_CLASS,
+ keepMainProguardConfiguration(MAIN_CLASS), null);
+ Assert.assertTrue(r8ShakenOutput.stderr.contains(name));
+ ProcessResult javaOutput = runOnJavaNoVerifyRaw(app, library, MAIN_CLASS);
+ Assert.assertTrue(javaOutput.stderr.contains(name));
+ }
}