Merge "Remove package map distributor."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index dce4879..0b16963 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.naming.Minifier;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.optimize.BridgeMethodAnalysis;
-import com.android.tools.r8.optimize.DebugStripper;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.VisibilityBridgeRemover;
import com.android.tools.r8.shaking.AbstractMethodRemover;
@@ -138,17 +137,6 @@
timing.end();
}
- if (!options.skipDebugInfoOpt && (application.getProguardMap() != null)) {
- try {
- timing.begin("DebugStripper");
- DebugStripper stripper =
- new DebugStripper(application.getProguardMap(), options, appInfo.dexItemFactory);
- application.classes().forEach(stripper::processClass);
- } finally {
- timing.end();
- }
- }
-
if (options.printCfg) {
if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
System.out.print(printer.toString());
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 2053abd..7bae1a1 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -263,7 +263,6 @@
" --min-api # Minimum Android API level compatibility.",
" --pg-conf <file> # Proguard configuration <file> (implies tree",
" # shaking/minification).",
- " --pg-map <file> # Proguard map <file>.",
" --print-mapping <file> # Write name/line mapping to <file>.",
" --no-tree-shaking # Force disable tree shaking of unreachable classes.",
" --no-discarded-checker # Force disable the discarded checker (when tree shaking).",
@@ -350,8 +349,6 @@
builder.setMainDexListOutputPath(Paths.get(args[++i]));
} else if (arg.equals("--pg-conf")) {
builder.addProguardConfigurationFiles(Paths.get(args[++i]));
- } else if (arg.equals("--pg-map")) {
- builder.setProguardMapFile(Paths.get(args[++i]));
} else if (arg.equals("--ignore-missing-classes")) {
builder.setIgnoreMissingClasses(true);
} else if (arg.equals("--print-mapping")) {
diff --git a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
new file mode 100644
index 0000000..b46a2ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.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 com.android.tools.r8.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface SynthesizedClassMap {
+ Class<?>[] value() default {};
+}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 1e70b9b..eba5613 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -133,7 +133,7 @@
// Sort the class members.
// Needed before adding static-value arrays and writing annotation directories and classes.
- sortClassData(mixedSectionOffsets.getClassesWithData());
+ mixedSectionOffsets.getClassesWithData().forEach(DexProgramClass::sortMembers);
// Add the static values for all fields now that we have committed to their sorting.
mixedSectionOffsets.getClassesWithData().forEach(this::addStaticFieldValues);
@@ -254,23 +254,6 @@
return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
}
- private void sortClassData(Collection<DexProgramClass> classesWithData) {
- for (DexProgramClass clazz : classesWithData) {
- sortEncodedFields(clazz.instanceFields());
- sortEncodedFields(clazz.staticFields());
- sortEncodedMethods(clazz.directMethods());
- sortEncodedMethods(clazz.virtualMethods());
- }
- }
-
- private void sortEncodedFields(DexEncodedField[] fields) {
- Arrays.sort(fields, (DexEncodedField a, DexEncodedField b) -> a.field.compareTo(b.field));
- }
-
- private void sortEncodedMethods(DexEncodedMethod[] methods) {
- Arrays.sort(methods, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.compareTo(b.method));
- }
-
private void checkInterfaceMethods() throws ApiLevelException {
for (DexProgramClass clazz : mapping.getClasses()) {
if (clazz.isInterface()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 1afd77e..3a8d56b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -13,7 +13,11 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class DexAnnotation extends DexItem {
public static final int VISIBILITY_BUILD = 0x00;
@@ -228,4 +232,37 @@
private static DexValue toDexValue(String string, DexItemFactory factory) {
return new DexValueString(factory.createString(string));
}
+
+ public static Collection<DexType> readAnnotationSynthesizedClassMap(
+ DexProgramClass programClass,
+ DexItemFactory dexItemFactory) {
+ DexAnnotation foundAnnotation = programClass.annotations.getFirstMatching(
+ dexItemFactory.annotationSynthesizedClassMap);
+ if (foundAnnotation != null) {
+ DexAnnotationElement value = foundAnnotation.annotation.elements[0];
+ assert value.name.toSourceString().equals("value");
+ DexValueArray existingList = (DexValueArray) value.value;
+ Collection<DexType> synthesized = new ArrayList<>(existingList.values.length);
+ for (DexValue element : existingList.getValues()) {
+ synthesized.add(((DexValueType) element).value);
+ }
+ return synthesized;
+ }
+ return Collections.emptyList();
+ }
+
+ public static DexAnnotation createAnnotationSynthesizedClassMap(
+ Set<DexType> synthesized,
+ DexItemFactory dexItemFactory) {
+ DexValueType[] values = synthesized.stream()
+ .map(type -> new DexValueType(type))
+ .toArray(length -> new DexValueType[length]);
+ DexValueArray array = new DexValueArray(values);
+ DexAnnotationElement pair =
+ new DexAnnotationElement(dexItemFactory.createString("value"), array);
+ return new DexAnnotation(
+ VISIBILITY_BUILD,
+ new DexEncodedAnnotation(
+ dexItemFactory.annotationSynthesizedClassMap, new DexAnnotationElement[]{pair}));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 00b99fa..93e103d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -74,8 +74,44 @@
return null;
}
+ public DexAnnotationSet getWithout(DexType annotationType) {
+ int index = 0;
+ for (DexAnnotation annotation : annotations) {
+ if (annotation.annotation.type == annotationType) {
+ DexAnnotation[] reducedArray = new DexAnnotation[annotations.length - 1];
+ System.arraycopy(annotations, 0, reducedArray, 0, index);
+ if (index < reducedArray.length) {
+ System.arraycopy(annotations, index + 1, reducedArray, index, reducedArray.length - index);
+ }
+ return new DexAnnotationSet(reducedArray);
+ }
+ ++index;
+ }
+ return this;
+ }
+
private int sortedHashCode() {
int hashCode = hashCode();
return hashCode == UNSORTED ? 1 : hashCode;
}
+
+ public DexAnnotationSet getWithAddedOrReplaced(DexAnnotation newAnnotation) {
+
+ // Check existing annotation for replacement.
+ int index = 0;
+ for (DexAnnotation annotation : annotations) {
+ if (annotation.annotation.type == newAnnotation.annotation.type) {
+ DexAnnotation[] modifiedArray = annotations.clone();
+ modifiedArray[index] = newAnnotation;
+ return new DexAnnotationSet(modifiedArray);
+ }
+ ++index;
+ }
+
+ // No existing annotation, append.
+ DexAnnotation[] extendedArray = new DexAnnotation[annotations.length + 1];
+ System.arraycopy(annotations, 0, extendedArray, 0, annotations.length);
+ extendedArray[annotations.length] = newAnnotation;
+ return new DexAnnotationSet(extendedArray);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 0d2edcb..294b031 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -315,6 +315,7 @@
DexString highestSortingString;
private byte[] deadCode;
private final Set<DexType> mainDexList = Sets.newIdentityHashSet();
+ private final Collection<DexProgramClass> synthesizedClasses;
public Builder(DexItemFactory dexItemFactory, Timing timing) {
this.programClasses = new ArrayList<>();
@@ -323,6 +324,7 @@
this.deadCode = null;
this.classpathClasses = null;
this.libraryClasses = null;
+ this.synthesizedClasses = new ArrayList<>();
}
public Builder(DexApplication application) {
@@ -335,6 +337,7 @@
dexItemFactory = application.dexItemFactory;
mainDexList.addAll(application.mainDexList);
deadCode = application.deadCode;
+ synthesizedClasses = new ArrayList<>();
}
public synchronized Builder setProguardMap(ClassNameMapper proguardMap) {
@@ -387,6 +390,7 @@
DexProgramClass synthesizedClass, boolean addToMainDexList) {
assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
addProgramClass(synthesizedClass);
+ synthesizedClasses.add(synthesizedClass);
if (addToMainDexList && !mainDexList.isEmpty()) {
mainDexList.add(synthesizedClass.type);
}
@@ -397,6 +401,14 @@
return programClasses;
}
+ public Collection<DexProgramClass> getSynthesizedClasses() {
+ return synthesizedClasses;
+ }
+
+ public Set<DexType> getMainDexList() {
+ return mainDexList;
+ }
+
public Builder addToMainDexList(Collection<DexType> mainDexList) {
this.mainDexList.addAll(mainDexList);
return this;
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 c1dad50..14e5288 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -63,11 +63,6 @@
this.factory = factory;
}
- // Public method for the DebugStripper.
- public void setPosition(int pc, int line) {
- emitDebugPosition(pc, line, null);
- }
-
/** Add events at pc for instruction. */
public void add(int pc, Instruction instruction) {
// Initialize locals state on block entry.
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 527f27b..b7e41fe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -161,6 +161,8 @@
public final DexType annotationSourceDebugExtension = createType(
"Ldalvik/annotation/SourceDebugExtension;");
public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");
+ public final DexType annotationSynthesizedClassMap =
+ createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
public synchronized void clearSubtypeInformation() {
types.values().forEach(DexType::clearSubtypeInformation);
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 d27a3be..4332df8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -9,6 +9,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
@@ -213,6 +214,21 @@
directMethods = newDirectMethods;
}
+ public synchronized void sortMembers() {
+ sortEncodedFields(staticFields);
+ sortEncodedFields(instanceFields);
+ sortEncodedMethods(directMethods);
+ sortEncodedMethods(virtualMethods);
+ }
+
+ private void sortEncodedFields(DexEncodedField[] fields) {
+ Arrays.sort(fields, Comparator.comparing(a -> a.field));
+ }
+
+ private void sortEncodedMethods(DexEncodedMethod[] methods) {
+ Arrays.sort(methods, Comparator.comparing(a -> a.method));
+ }
+
@Override
public DexProgramClass get() {
return this;
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 c67e229..7dca5bd 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
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -39,16 +40,22 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
+import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
public class IRConverter {
@@ -225,9 +232,72 @@
synthesizeLambdaClasses(builder);
desugarInterfaceMethods(builder, ExcludeDexResources);
+ if (options.intermediate) {
+ updateSynthesizedClassMapping(builder);
+ }
+
+ updateMainDexListWithSynthesizedClassMap(builder);
+
+ if (!options.intermediate) {
+ clearSynthesizedClassMapping(builder);
+ }
+
return builder.build();
}
+ private void updateMainDexListWithSynthesizedClassMap(Builder builder) {
+ Set<DexType> inputMainDexList = builder.getMainDexList();
+ if (!inputMainDexList.isEmpty()) {
+ Map<DexType, DexProgramClass> programClasses = builder.getProgramClasses().stream()
+ .collect(Collectors.toMap(
+ programClass -> programClass.type,
+ Function.identity()));
+ Collection<DexType> synthesized = new ArrayList<>();
+ for (DexType dexType : inputMainDexList) {
+ DexProgramClass programClass = programClasses.get(dexType);
+ if (programClass != null) {
+ synthesized.addAll(DexAnnotation.readAnnotationSynthesizedClassMap(
+ programClass, builder.dexItemFactory));
+ }
+ }
+ builder.addToMainDexList(synthesized);
+ }
+ }
+
+ private void clearSynthesizedClassMapping(Builder builder) {
+ for (DexProgramClass programClass : builder.getProgramClasses()) {
+ programClass.annotations =
+ programClass.annotations.getWithout(builder.dexItemFactory.annotationSynthesizedClassMap);
+ }
+ }
+
+ private void updateSynthesizedClassMapping(Builder builder) {
+ ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
+ ArrayListMultimap.create();
+ for (DexProgramClass synthesized : builder.getSynthesizedClasses()) {
+ for (DexProgramClass original : synthesized.getSynthesizedFrom()) {
+ originalToSynthesized.put(original, synthesized);
+ }
+ }
+
+ for (Map.Entry<DexProgramClass, Collection<DexProgramClass>> entry :
+ originalToSynthesized.asMap().entrySet()) {
+ DexProgramClass original = entry.getKey();
+ Set<DexType> synthesized = new HashSet<>();
+ entry.getValue()
+ .stream()
+ .map(dexProgramClass -> dexProgramClass.type)
+ .forEach(synthesized::add);
+ synthesized.addAll(
+ DexAnnotation.readAnnotationSynthesizedClassMap(original, builder.dexItemFactory));
+
+ DexAnnotation updatedAnnotation =
+ DexAnnotation.createAnnotationSynthesizedClassMap(synthesized, builder.dexItemFactory);
+
+ original.annotations = original.annotations.getWithAddedOrReplaced(updatedAnnotation);
+ }
+ }
+
private void convertClassesToDex(Iterable<DexProgramClass> classes,
ExecutorService executor) throws ExecutionException {
List<Future<?>> futures = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
deleted file mode 100644
index 0eda1e5..0000000
--- a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize;
-
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexDebugEntry;
-import com.android.tools.r8.graph.DexDebugEventBuilder;
-import com.android.tools.r8.graph.DexDebugInfo;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNaming;
-import com.android.tools.r8.naming.MemberNaming;
-import com.android.tools.r8.naming.MemberNaming.Range;
-import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.utils.InternalOptions;
-
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
-
-import java.util.List;
-
-public class DebugStripper {
-
- private static final int USED_MORE_THAN_ONCE = 0;
- private static final int USED_ONCE = -1;
-
- private final ClassNameMapper classNameMapper;
- private final InternalOptions options;
- private final DexItemFactory dexItemFactory;
-
- public DebugStripper(
- ClassNameMapper classNameMapper, InternalOptions options, DexItemFactory dexItemFactory) {
- this.classNameMapper = classNameMapper;
- this.options = options;
- this.dexItemFactory = dexItemFactory;
- }
-
- private String descriptorToName(String descriptor) {
- // The format is L<name>; and '/' is used as package separator.
- return descriptor.substring(1, descriptor.length() - 1).replace('/', '.');
- }
-
- private Range findRange(int value, List<Range> ranges, Range defaultRange) {
- for (Range range : ranges) {
- if (range.contains(value)) {
- return range;
- }
- }
- return defaultRange;
- }
-
- private static class NumberedDebugInfo {
-
- final int numberOfEntries;
- final DexDebugInfo info;
-
- public NumberedDebugInfo(int numberOfEntries, DexDebugInfo info) {
- this.numberOfEntries = numberOfEntries;
- this.info = info;
- }
- }
-
- private NumberedDebugInfo processDebugInfo(
- DexEncodedMethod method, DexDebugInfo info, MemberNaming naming, int startLine) {
- if (info == null || naming == null) {
- return new NumberedDebugInfo(0, null);
- }
- List<Range> ranges = naming.getInlineRanges();
- // Maintain line and address but only when entering or leaving a range of line numbers
- // that pertains to a different method body.
- Range currentRange = naming.topLevelRange;
- DexDebugEventBuilder builder = new DexDebugEventBuilder(method, dexItemFactory);
- // Below we insert the start-line at pc-0 except if another entry already defines pc-0.
- int entryCount = 0;
- for (DexDebugEntry entry : info.computeEntries()) {
- boolean addEntry = false;
- // We are in a range, check whether we have left it.
- if (currentRange != null && !currentRange.contains(entry.line)) {
- currentRange = null;
- addEntry = true;
- }
- // We have no range (because we left the old one or never were in a range).
- if (currentRange == null) {
- currentRange = findRange(entry.line, ranges, naming.topLevelRange);
- // We entered a new Range, emit this entry.
- if (currentRange != null) {
- addEntry = true;
- }
- }
- if (addEntry) {
- if (entryCount == 0 && entry.address > 0) {
- ++entryCount;
- builder.setPosition(0, startLine);
- }
- int line = options.skipDebugLineNumberOpt
- ? entry.line
- : startLine + ranges.indexOf(currentRange) + 1;
- builder.setPosition(entry.address, line);
- ++entryCount;
- }
- }
- if (entryCount == 0) {
- ++entryCount;
- builder.setPosition(0, startLine);
- }
- return new NumberedDebugInfo(entryCount, builder.build());
- }
-
- private void processCode(DexEncodedMethod encodedMethod, MemberNaming naming,
- Reference2IntMap<DexString> nameCounts) {
- if (encodedMethod.getCode() == null) {
- return;
- }
- DexCode code = encodedMethod.getCode().asDexCode();
- DexString name = encodedMethod.method.name;
- DexDebugInfo originalInfo = code.getDebugInfo();
- if (originalInfo == null) {
- return;
- }
- int startLine;
- boolean isUsedOnce = false;
- if (options.skipDebugLineNumberOpt) {
- startLine = originalInfo.startLine;
- } else {
- int nameCount = nameCounts.getInt(name);
- if (nameCount == USED_ONCE) {
- isUsedOnce = true;
- startLine = 0;
- } else {
- startLine = nameCount;
- }
- }
-
- NumberedDebugInfo numberedInfo =
- processDebugInfo(encodedMethod, originalInfo, naming, startLine);
- DexDebugInfo newInfo = numberedInfo.info;
- if (!options.skipDebugLineNumberOpt) {
- // Fix up the line information.
- int previousCount = nameCounts.getInt(name);
- nameCounts.put(name, previousCount + numberedInfo.numberOfEntries);
- // If we don't actually need line information and there are no debug entries, throw it away.
- if (newInfo != null && isUsedOnce && newInfo.events.length == 0) {
- newInfo = null;
- } else if (naming != null && newInfo != null) {
- naming.setCollapsedStartLineNumber(startLine);
- // Preserve the line number information we had.
- naming.setOriginalStartLineNumber(originalInfo.startLine);
- }
- }
- code.setDebugInfo(newInfo);
- }
-
- private void processMethod(DexEncodedMethod method, ClassNaming classNaming,
- Reference2IntMap<DexString> nameCounts) {
- MemberNaming naming = null;
- if (classNaming != null) {
- Signature renamedSignature = classNameMapper.getRenamedMethodSignature(method.method);
- naming = classNaming.lookup(renamedSignature);
- }
- processCode(method, naming, nameCounts);
- }
-
- public void processClass(DexProgramClass clazz) {
- if (!clazz.hasMethodsOrFields()) {
- return;
- }
- String name = descriptorToName(clazz.type.toDescriptorString());
- ClassNaming naming = classNameMapper == null ? null : classNameMapper.getClassNaming(name);
- Reference2IntMap<DexString> nameCounts = new Reference2IntOpenHashMap<>();
- clazz.forEachMethod(method -> setIntialNameCounts(nameCounts, method));
- clazz.forEachMethod(method -> processMethod(method, naming, nameCounts));
- }
-
- private void setIntialNameCounts(Reference2IntMap<DexString> nameCounts,
- DexEncodedMethod method) {
- if (nameCounts.containsKey(method.method.name)) {
- nameCounts.put(method.method.name, USED_MORE_THAN_ONCE);
- } else {
- nameCounts.put(method.method.name, USED_ONCE);
- }
- }
-}
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 c03c3d6..a2a0446 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -46,7 +46,6 @@
public boolean printTimes = false;
// Skipping optimizations.
- public boolean skipDebugInfoOpt = false;
public boolean skipDebugLineNumberOpt = false;
public boolean skipClassMerging = true;
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 7a56e3c..a2e64e1 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -6,14 +6,20 @@
import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.OutputMode;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.function.UnaryOperator;
import org.hamcrest.core.CombinableMatcher;
import org.hamcrest.core.IsInstanceOf;
@@ -66,6 +72,10 @@
}
}
+ D8TestRunner withIntermediate(boolean intermediate) {
+ return withBuilderTransformation(builder -> builder.setIntermediate(intermediate));
+ }
+
@Override
D8TestRunner self() {
return this;
@@ -519,6 +529,133 @@
new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex});
}
+
+ @Test
+ public void testLambdaDesugaringWithMainDexList1() throws Throwable {
+ // Minimal case: there are synthesized classes but not form the main dex class.
+ testIntermediateWithMainDexList(
+ "lambdadesugaring",
+ 1,
+ "lambdadesugaring.LambdaDesugaring$I");
+ }
+
+ @Test
+ public void testLambdaDesugaringWithMainDexList2() throws Throwable {
+ // Main dex class has many lambdas.
+ testIntermediateWithMainDexList("lambdadesugaring",
+ 33,
+ "lambdadesugaring.LambdaDesugaring$Refs$B");
+ }
+
+ @Test
+ public void testInterfaceDesugaringWithMainDexList1() throws Throwable {
+ // Main dex interface has one static method.
+ testIntermediateWithMainDexList(
+ "interfacemethods",
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
+ 2,
+ "interfacemethods.I1");
+ }
+
+
+ @Test
+ public void testInterfaceDesugaringWithMainDexList2() throws Throwable {
+ // Main dex interface has one default method.
+ testIntermediateWithMainDexList(
+ "interfacemethods",
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
+ 2,
+ "interfacemethods.I2");
+ }
+
+ private void testIntermediateWithMainDexList(
+ String packageName,
+ int expectedMainDexListSize,
+ String... mainDexClasses)
+ throws Throwable {
+ testIntermediateWithMainDexList(
+ packageName,
+ Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION),
+ expectedMainDexListSize,
+ mainDexClasses);
+ }
+
+ private void testIntermediateWithMainDexList(
+ String packageName,
+ Path input,
+ int expectedMainDexListSize,
+ String... mainDexClasses)
+ throws Throwable {
+ int minApi = ANDROID_K_API;
+
+ // Full build, will be used as reference.
+ TestRunner<?> full =
+ test(packageName + "full", packageName, "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi)
+ .withOptionConsumer(option -> option.minimalMainDex = true)
+ .withMainDexClass(mainDexClasses);
+ Path fullDexes = temp.getRoot().toPath().resolve(packageName + "full" + ZIP_EXTENSION);
+ full.build(input, fullDexes);
+
+ // Builds with intermediate in both output mode.
+ Path dexesThroughIndexedIntermediate =
+ buildDexThroughIntermediate(packageName, input, OutputMode.Indexed, minApi, mainDexClasses);
+ Path dexesThroughFilePerInputClassIntermediate =
+ buildDexThroughIntermediate(packageName, input, OutputMode.FilePerInputClass, minApi,
+ mainDexClasses);
+
+ // Collect main dex types.
+ DexInspector fullInspector = getMainDexInspector(fullDexes);
+ DexInspector indexedIntermediateInspector =
+ getMainDexInspector(dexesThroughIndexedIntermediate);
+ DexInspector filePerInputClassIntermediateInspector =
+ getMainDexInspector(dexesThroughFilePerInputClassIntermediate);
+ Collection<String> fullMainClasses = new HashSet<>();
+ fullInspector.forAllClasses(
+ clazz -> fullMainClasses.add(clazz.getFinalDescriptor()));
+ Collection<String> indexedIntermediateMainClasses = new HashSet<>();
+ indexedIntermediateInspector.forAllClasses(
+ clazz -> indexedIntermediateMainClasses.add(clazz.getFinalDescriptor()));
+ Collection<String> filePerInputClassIntermediateMainClasses = new HashSet<>();
+ filePerInputClassIntermediateInspector.forAllClasses(
+ clazz -> filePerInputClassIntermediateMainClasses.add(clazz.getFinalDescriptor()));
+
+ // Check.
+ Assert.assertEquals(expectedMainDexListSize, fullMainClasses.size());
+ Assert.assertEquals(fullMainClasses, indexedIntermediateMainClasses);
+ Assert.assertEquals(fullMainClasses, filePerInputClassIntermediateMainClasses);
+ }
+
+ private Path buildDexThroughIntermediate(
+ String packageName,
+ Path input,
+ OutputMode outputMode,
+ int minApi,
+ String... mainDexClasses)
+ throws Throwable {
+ TestRunner<?> intermediate =
+ test(packageName + "intermediate", packageName, "N/A")
+ .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withMinApiLevel(minApi)
+ .withOptionConsumer(option -> option.outputMode = outputMode)
+ .withIntermediate(true);
+ Path intermediateDex =
+ temp.getRoot().toPath().resolve(packageName + "intermediate" + ZIP_EXTENSION);
+ intermediate.build(input, intermediateDex);
+
+ TestRunner<?> end =
+ test(packageName + "dex", packageName, "N/A")
+ .withOptionConsumer(option -> option.minimalMainDex = true)
+ .withMainDexClass(mainDexClasses)
+ .withMinApiLevel(minApi);
+
+ Path dexesThroughIntermediate =
+ temp.getRoot().toPath().resolve(packageName + "dex" + ZIP_EXTENSION);
+ end.build(intermediateDex, dexesThroughIntermediate);
+ return dexesThroughIntermediate;
+ }
+
@Override
D8TestRunner test(String testName, String packageName, String mainClass) {
return new D8TestRunner(testName, packageName, mainClass);
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index bbf149b..932a61f 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1888,7 +1888,8 @@
// 1) t03
// java.lang.AssertionError: expected null, but was:<1>
- .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01", any())
+ .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
// 1) t04
// java.lang.AssertionError: reference is not enqueued after 2 sec
@@ -1912,7 +1913,8 @@
// 1) t03
// java.lang.AssertionError: expected null, but was:<[I@1b32f32>
- .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", any())
+ .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
+ match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
// 1) t03
// java.lang.AssertionError: reference is not enqueued after 2 sec
@@ -2730,8 +2732,7 @@
// 2) t05
// java.lang.AssertionError: Should throws IndexOutOfBoundsException: 0
- .put("lang.String.getBytesII_BI.String_getBytes_A02",
- match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ .put("lang.String.getBytesII_BI.String_getBytes_A02", any())
// 1) t01
// java.lang.AssertionError: Expected exception: java.lang.NullPointerException
@@ -4018,6 +4019,11 @@
// 1) t02
// java.lang.AssertionError: Exception is not thrown: field: intPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@94fbd35
+ .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A02",
+ match(runtimes(DexVm.ART_4_4_4)))
+ // 1) t01
+ // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: doublePublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
.put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A05", any())
// 1) t01
// java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -4521,7 +4527,7 @@
// java.lang.AssertionError: no IllegalThreadStateException 1
.put("lang.String.getBytesLjava_lang_String.String_getBytes_A02",
- match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+ match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
// 1) t01(com.google.jctf.test.lib.java.lang.String.getBytesLjava_lang_String.String_getBytes_A02)
// java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.io.UnsupportedEncodingException>
@@ -4787,6 +4793,12 @@
// 1) t02
// java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
+ .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
+ match(runtimes(DexVm.ART_4_4_4)))
+ .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
+ match(runtimes(DexVm.ART_4_4_4)))
+ // Passes or fails randomly. Check that something is enqueued after 2 seconds.
+
.build(); // end of flakyWithArt
public static final Multimap<String, TestCondition> timeoutsWithArt =
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 33f43a6..0e0fd9d 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -12,17 +12,20 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
import com.android.tools.r8.utils.DexInspector.InstructionSubject;
import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -30,10 +33,13 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -94,6 +100,10 @@
return self();
}
+ C withMainDexClass(String... classes) {
+ return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
+ }
+
C withInterfaceMethodDesugaring(OffOrAuto behavior) {
return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
}
@@ -391,4 +401,14 @@
}
}
+ protected DexInspector getMainDexInspector(Path zip)
+ throws ZipException, IOException, ExecutionException {
+ try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+ try (InputStream in =
+ zipFile.getInputStream(zipFile.getEntry(FileUtils.DEFAULT_DEX_FILENAME))) {
+ return new DexInspector(AndroidApp.fromDexProgramData(ByteStreams.toByteArray(in)));
+ }
+ }
+ }
+
}
diff --git a/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java b/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
deleted file mode 100644
index 6609ff2..0000000
--- a/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.CompilationException;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNaming;
-import com.android.tools.r8.naming.MemberNaming;
-import com.android.tools.r8.naming.MemberNaming.InlineInformation;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.naming.ProguardMapReader;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class R8DebugStrippingTest {
-
- private static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
- private static final String EXAMPLE_DEX = "throwing/classes.dex";
- private static final String EXAMPLE_MAP = "throwing/throwing.map";
- private static final String EXAMPLE_CLASS = "throwing.Throwing";
- private static final String EXAMPLE_JAVA = "Throwing.java";
-
- private static final String MAIN_NAME = "main";
- private static final String[] MAIN_PARAMETERS = new String[]{"java.lang.String[]"};
- private static final String VOID_RETURN = "void";
-
- private static final String OTHER_NAME = "throwInAFunctionThatIsNotInlinedAndCalledTwice";
- private static final String[] NO_PARAMETERS = new String[0];
- private static final String INT_RETURN = "int";
-
- private static final String THIRD_NAME = "aFunctionThatCallsAnInlinedMethodThatThrows";
- private static final String[] LIST_PARAMETER = new String[]{"java.util.List"};
-
- private static final String FORTH_NAME = "anotherFunctionThatCallsAnInlinedMethodThatThrows";
- private static final String[] STRING_PARAMETER = new String[]{"java.lang.String"};
-
- private static final Map<String, Signature> SIGNATURE_MAP = ImmutableMap.of(
- MAIN_NAME, new MethodSignature(MAIN_NAME, VOID_RETURN, MAIN_PARAMETERS),
- OTHER_NAME, new MethodSignature(OTHER_NAME, INT_RETURN, NO_PARAMETERS),
- THIRD_NAME, new MethodSignature(THIRD_NAME, INT_RETURN, LIST_PARAMETER),
- FORTH_NAME, new MethodSignature(FORTH_NAME, INT_RETURN, STRING_PARAMETER)
- );
-
- private ClassNameMapper mapper;
-
- @Parameter(0)
- public boolean compressRanges;
-
- @Rule
- public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
- @Before
- public void loadRangeInformation() throws IOException {
- mapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
- }
-
- @Parameters(name = "compressLineNumers={0}")
- public static Object[] parameters() {
- return new Object[]{true, false};
- }
-
- @Test
- public void testStackTraces()
- throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
-
- // Temporary directory for R8 output.
- Path out = temp.getRoot().toPath();
-
- R8Command command =
- R8Command.builder()
- .addProgramFiles(Paths.get(ROOT, EXAMPLE_DEX))
- .setOutputPath(out)
- .setProguardMapFile(Paths.get(ROOT, EXAMPLE_MAP))
- .build();
-
- // Generate R8 processed version.
- AndroidApp result =
- ToolHelper.runR8(command, (options) -> options.skipDebugLineNumberOpt = !compressRanges);
-
- ClassNameMapper classNameMapper;
- try (InputStream is = result.getProguardMap()) {
- classNameMapper = ProguardMapReader.mapperFromInputStream(is);
- }
- if (compressRanges) {
- classNameMapper.forAllClassNamings(this::ensureRangesAreUniquePerClass);
- }
-
- if (!ToolHelper.artSupported()) {
- return;
- }
- // Run art on original.
- String originalOutput =
- ToolHelper.runArtNoVerificationErrors(ROOT + EXAMPLE_DEX, EXAMPLE_CLASS);
- // Run art on R8 processed version.
- String otherOutput =
- ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", EXAMPLE_CLASS);
- // Check that exceptions are in same range
- assertStacktracesMatchRanges(originalOutput, otherOutput, classNameMapper);
-
- // Check that we have debug information in all the places required.
- DexInspector inspector = new DexInspector(out.resolve("classes.dex"));
- BiMap<String, String> obfuscationMap
- = classNameMapper.getObfuscatedToOriginalMapping().inverse();
- ClassSubject overloaded = inspector.clazz(obfuscationMap.get("throwing.Overloaded"));
- assertTrue(overloaded.isPresent());
- ensureDebugInfosExist(overloaded);
- }
-
- private void ensureDebugInfosExist(ClassSubject overloaded) {
- final Map<DexString, Boolean> hasDebugInfo = Maps.newIdentityHashMap();
- overloaded.forAllMethods(method -> {
- if (!method.isAbstract()) {
- DexCode code = method.getMethod().getCode().asDexCode();
- DexString name = method.getMethod().method.name;
- Boolean previous = hasDebugInfo.get(name);
- boolean current = code.getDebugInfo() != null;
- // If we have seen one before, it should be the same as now.
- assertTrue(previous == null || (previous == current));
- hasDebugInfo.put(name, current);
- }
- }
- );
- }
-
- private void ensureRangesAreUniquePerClass(ClassNaming naming) {
- final Map<String, Set<Integer>> rangeMap = new HashMap<>();
- naming.forAllMemberNaming(memberNaming -> {
- if (memberNaming.isMethodNaming()) {
- if (memberNaming.topLevelRange != MemberNaming.fakeZeroRange) {
- int startLine = memberNaming.topLevelRange.from;
- Set<Integer> used = rangeMap
- .computeIfAbsent(memberNaming.getRenamedName(), any -> new HashSet<>());
- assertFalse(used.contains(startLine));
- used.add(startLine);
- }
- }
- });
- }
-
- private String extractRangeIndex(String line, ClassNameMapper mapper) {
- int position = line.lastIndexOf(EXAMPLE_JAVA);
- if (position == -1 && ToolHelper.getDexVm() == DexVm.ART_4_4_4) {
- assert line.contains("dalvik.system.NativeStart.main(Native Method)");
- return "Native Method";
- }
- assertNotSame("Malformed stackframe: " + line, -1, position);
- String numberPart = line.substring(position + EXAMPLE_JAVA.length() + 1, line.lastIndexOf(')'));
- int number = Integer.parseInt(numberPart);
- // Search the signature map for all signatures that actually match. We do this by first looking
- // up the renamed signature and then checking whether it is contained in the line. We prepend
- // the class name to make sure we do not match random characters in the line.
- for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
- MemberNaming naming = mapper.getClassNaming(EXAMPLE_CLASS)
- .lookupByOriginalSignature(entry.getValue());
- if (!line.contains(EXAMPLE_CLASS + "." + naming.getRenamedName())) {
- continue;
- }
- if (naming.topLevelRange.contains(number)) {
- return entry.getKey() + ":" + 0;
- }
- int rangeNo = 1;
- for (InlineInformation inlineInformation : naming.inlineInformation) {
- if (inlineInformation.inlinedRange.contains(number)) {
- return entry.getKey() + ":" + rangeNo;
- }
- rangeNo++;
- }
- }
- fail("Number not in any range " + number);
- return null;
- }
-
- private MemberNaming selectRanges(String line, ClassNameMapper mapper) {
- Signature signature;
- for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
- if (line.contains(entry.getKey())) {
- return mapper.getClassNaming(EXAMPLE_CLASS).lookup(entry.getValue());
- }
- }
- Assert.fail("unknown method in line " + line);
- return null;
- }
-
- private void assertStacktracesMatchRanges(String before, String after,
- ClassNameMapper newMapper) {
- String[] beforeLines = before.split("\n");
- String[] afterLines = after.split("\n");
- assertEquals("Output length differs", beforeLines.length,
- afterLines.length);
- for (int i = 0; i < beforeLines.length; i++) {
- if (!beforeLines[i].startsWith("FRAME:")) {
- continue;
- }
- String beforeLine = beforeLines[i];
- String expected = extractRangeIndex(beforeLine, mapper);
- String afterLine = afterLines[i];
- String generated = extractRangeIndex(afterLine, newMapper);
- assertEquals("Ranges match", expected, generated);
- }
- }
-}