Introduce new retrace api for general consumption
This is the main CL for introducing a new retrace API that is no
longer tied to stack-traces. Since the entry-points are defined in
terms of references, users can abstract away from string
representations of classes and members.
Bug: 144151634
Change-Id: I8d46e485e9e725d63732634ffdf592422f037ba8
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 226a5f9..1a1d4ef 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -88,7 +88,7 @@
public static class MappedRangesOfName {
private final List<MappedRange> mappedRanges;
- MappedRangesOfName(List<MappedRange> mappedRanges) {
+ public MappedRangesOfName(List<MappedRange> mappedRanges) {
this.mappedRanges = mappedRanges;
}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 054917e..95fbcc8 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -124,6 +124,22 @@
return name.indexOf(JAVA_PACKAGE_SEPARATOR) != -1;
}
+ public boolean isMethodSignature() {
+ return false;
+ }
+
+ public boolean isFieldSignature() {
+ return false;
+ }
+
+ public MethodSignature asMethodSignature() {
+ return null;
+ }
+
+ public FieldSignature asFieldSignature() {
+ return null;
+ }
+
@Override
public String toString() {
try {
@@ -206,6 +222,16 @@
writer.append(' ');
writer.append(name);
}
+
+ @Override
+ public boolean isFieldSignature() {
+ return true;
+ }
+
+ @Override
+ public FieldSignature asFieldSignature() {
+ return this;
+ }
}
public static class MethodSignature extends Signature {
@@ -265,7 +291,7 @@
return name.substring(name.lastIndexOf(JAVA_PACKAGE_SEPARATOR) + 1);
}
- public String toUnqualifiedHolder() {
+ public String toHolderFromQualified() {
assert isQualified();
return name.substring(0, name.lastIndexOf(JAVA_PACKAGE_SEPARATOR));
}
@@ -348,5 +374,15 @@
sb.append(javaTypeToDescriptor(type));
return sb.toString();
}
+
+ @Override
+ public boolean isMethodSignature() {
+ return true;
+ }
+
+ @Override
+ public MethodSignature asMethodSignature() {
+ return this;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/references/FieldReference.java b/src/main/java/com/android/tools/r8/references/FieldReference.java
index d4d74fd..6f27753 100644
--- a/src/main/java/com/android/tools/r8/references/FieldReference.java
+++ b/src/main/java/com/android/tools/r8/references/FieldReference.java
@@ -13,15 +13,19 @@
* type of the field.
*/
@Keep
-public final class FieldReference {
+public class FieldReference {
private final ClassReference holderClass;
private final String fieldName;
private final TypeReference fieldType;
+ boolean isUnknown() {
+ return false;
+ }
+
FieldReference(ClassReference holderClass, String fieldName, TypeReference fieldType) {
assert holderClass != null;
assert fieldName != null;
- assert fieldType != null;
+ assert fieldType != null || isUnknown();
this.holderClass = holderClass;
this.fieldName = fieldName;
this.fieldType = fieldType;
@@ -65,4 +69,29 @@
public String toString() {
return getHolderClass().toString() + getFieldName() + ":" + getFieldType().getDescriptor();
}
+
+ public static final class UnknownFieldReference extends FieldReference {
+
+ private final ClassReference holderClass;
+ private final String fieldName;
+
+ public UnknownFieldReference(ClassReference holderClass, String fieldName) {
+ super(holderClass, fieldName, null);
+ this.holderClass = holderClass;
+ this.fieldName = fieldName;
+ }
+
+ public ClassReference getHolderClass() {
+ return holderClass;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ @Override
+ boolean isUnknown() {
+ return true;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/references/MethodReference.java b/src/main/java/com/android/tools/r8/references/MethodReference.java
index 0a89715..b4a8116 100644
--- a/src/main/java/com/android/tools/r8/references/MethodReference.java
+++ b/src/main/java/com/android/tools/r8/references/MethodReference.java
@@ -17,7 +17,7 @@
* full list of formal parameters.
*/
@Keep
-public final class MethodReference {
+public class MethodReference {
private final ClassReference holderClass;
private final String methodName;
private final List<TypeReference> formalTypes;
@@ -30,13 +30,17 @@
TypeReference returnType) {
assert holderClass != null;
assert methodName != null;
- assert formalTypes != null;
+ assert formalTypes != null || isUnknown();
this.holderClass = holderClass;
this.methodName = methodName;
this.formalTypes = formalTypes;
this.returnType = returnType;
}
+ public boolean isUnknown() {
+ return false;
+ }
+
public ClassReference getHolderClass() {
return holderClass;
}
@@ -86,4 +90,29 @@
public String toString() {
return getHolderClass().toString() + getMethodName() + getMethodDescriptor();
}
+
+ public static final class UnknownMethodReference extends MethodReference {
+
+ private final ClassReference holderClass;
+ private final String methodName;
+
+ @Override
+ public boolean isUnknown() {
+ return true;
+ }
+
+ public UnknownMethodReference(ClassReference holderClass, String methodName) {
+ super(holderClass, methodName, null, null);
+ this.holderClass = holderClass;
+ this.methodName = methodName;
+ }
+
+ public ClassReference getHolderClass() {
+ return holderClass;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/references/Reference.java b/src/main/java/com/android/tools/r8/references/Reference.java
index 94d4ea0..dcf8c68 100644
--- a/src/main/java/com/android/tools/r8/references/Reference.java
+++ b/src/main/java/com/android/tools/r8/references/Reference.java
@@ -65,6 +65,10 @@
return instance;
}
+ public static TypeReference returnTypeFromDescriptor(String descriptor) {
+ return descriptor.equals("V") ? null : typeFromDescriptor(descriptor);
+ }
+
public static TypeReference typeFromDescriptor(String descriptor) {
switch (descriptor.charAt(0)) {
case 'L':
@@ -76,6 +80,10 @@
}
}
+ public static TypeReference typeFromTypeName(String typeName) {
+ return typeFromDescriptor(DescriptorUtils.javaTypeToDescriptor(typeName));
+ }
+
// Internal helper to convert Class<?> for primitive/array types too.
private static TypeReference typeFromClass(Class<?> clazz) {
return typeFromDescriptor(DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()));
diff --git a/src/main/java/com/android/tools/r8/retrace/Result.java b/src/main/java/com/android/tools/r8/retrace/Result.java
new file mode 100644
index 0000000..a8e3903
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/Result.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, 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.retrace;
+
+import com.android.tools.r8.Keep;
+import java.util.function.Consumer;
+
+@Keep
+public abstract class Result<R, RR extends Result<R, RR>> {
+
+ public abstract RR apply(Consumer<R> resultConsumer);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
new file mode 100644
index 0000000..c5284ea
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2019, 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.retrace;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+
+public interface RetraceBase {
+
+ RetraceMethodResult retrace(MethodReference methodReference);
+
+ RetraceFieldResult retrace(FieldReference fieldReference);
+
+ RetraceClassResult retrace(ClassReference classReference);
+
+ String retraceSourceFile(ClassReference classReference, String sourceFile);
+
+ String retraceSourceFile(
+ ClassReference classReference,
+ String sourceFile,
+ ClassReference retracedClassReference,
+ boolean hasRetraceResult);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
new file mode 100644
index 0000000..19bc557
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2019, 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.retrace;
+
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Box;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import java.util.Set;
+
+public class RetraceBaseImpl implements RetraceBase {
+
+ private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
+ Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source");
+
+ private final ClassNameMapper classNameMapper;
+
+ RetraceBaseImpl(ClassNameMapper classNameMapper) {
+ this.classNameMapper = classNameMapper;
+ }
+
+ @Override
+ public RetraceMethodResult retrace(MethodReference methodReference) {
+ return retrace(methodReference.getHolderClass()).lookupMethod(methodReference.getMethodName());
+ }
+
+ @Override
+ public RetraceFieldResult retrace(FieldReference fieldReference) {
+ return retrace(fieldReference.getHolderClass()).lookupField(fieldReference.getFieldName());
+ }
+
+ @Override
+ public RetraceClassResult retrace(ClassReference classReference) {
+ return RetraceClassResult.create(
+ classReference, classNameMapper.getClassNaming(classReference.getTypeName()));
+ }
+
+ @Override
+ public String retraceSourceFile(ClassReference classReference, String sourceFile) {
+ Box<String> retracedSourceFile = new Box<>();
+ retrace(classReference)
+ .apply(element -> retracedSourceFile.set(element.retraceSourceFile(sourceFile, this)));
+ return retracedSourceFile.get();
+ }
+
+ @Override
+ public String retraceSourceFile(
+ ClassReference obfuscatedClass,
+ String sourceFile,
+ ClassReference retracedClassReference,
+ boolean hasRetraceResult) {
+ boolean fileNameProbablyChanged =
+ hasRetraceResult
+ && !retracedClassReference.getTypeName().startsWith(obfuscatedClass.getTypeName());
+ if (!UNKNOWN_SOURCEFILE_NAMES.contains(sourceFile) && !fileNameProbablyChanged) {
+ // We have no new information, only rewrite filename if it is unknown.
+ // PG-retrace will always rewrite the filename, but that seems a bit to harsh to do.
+ return sourceFile;
+ }
+ if (!hasRetraceResult) {
+ // We have no mapping but but the file name is unknown, so the best we can do is take the
+ // name of the obfuscated clazz.
+ assert obfuscatedClass.getTypeName().equals(retracedClassReference.getTypeName());
+ return getClassSimpleName(obfuscatedClass.getTypeName()) + ".java";
+ }
+ String newFileName = getClassSimpleName(retracedClassReference.getTypeName());
+ String extension = Files.getFileExtension(sourceFile);
+ if (extension.isEmpty()) {
+ extension = "java";
+ }
+ return newFileName + "." + extension;
+ }
+
+ private static String getClassSimpleName(String clazz) {
+ int lastIndexOfPeriod = clazz.lastIndexOf('.');
+ // Check if we can find a subclass separator.
+ int endIndex = clazz.lastIndexOf('$');
+ if (lastIndexOfPeriod > endIndex || endIndex < 0) {
+ endIndex = clazz.length();
+ }
+ return clazz.substring(lastIndexOfPeriod + 1, endIndex);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
new file mode 100644
index 0000000..a0d5076
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2019, 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.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.RetraceClassResult.Element;
+import com.android.tools.r8.utils.Box;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+@Keep
+public class RetraceClassResult extends Result<Element, RetraceClassResult> {
+
+ private final ClassReference obfuscatedReference;
+ private final ClassNamingForNameMapper mapper;
+
+ private RetraceClassResult(ClassReference obfuscatedReference, ClassNamingForNameMapper mapper) {
+ this.obfuscatedReference = obfuscatedReference;
+ this.mapper = mapper;
+ }
+
+ static RetraceClassResult create(
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper) {
+ return new RetraceClassResult(obfuscatedReference, mapper);
+ }
+
+ public RetraceFieldResult lookupField(String fieldName) {
+ return lookup(
+ fieldName,
+ (mapper, name) -> {
+ List<MemberNaming> memberNamings = mapper.mappedNamingsByName.get(name);
+ if (memberNamings == null || memberNamings.isEmpty()) {
+ return null;
+ }
+ return memberNamings;
+ },
+ RetraceFieldResult::new);
+ }
+
+ public RetraceMethodResult lookupMethod(String methodName) {
+ return lookup(
+ methodName,
+ (mapper, name) -> {
+ MappedRangesOfName mappedRanges = mapper.mappedRangesByRenamedName.get(name);
+ if (mappedRanges == null || mappedRanges.getMappedRanges().isEmpty()) {
+ return null;
+ }
+ return mappedRanges;
+ },
+ RetraceMethodResult::new);
+ }
+
+ private <T, R> R lookup(
+ String name,
+ BiFunction<ClassNamingForNameMapper, String, T> lookupFunction,
+ ResultConstructor<T, R> constructor) {
+ Box<R> elementBox = new Box<>();
+ apply(
+ element -> {
+ assert !elementBox.isSet();
+ T mappedRangesForT = null;
+ if (element.mapper != null) {
+ mappedRangesForT = lookupFunction.apply(element.mapper, name);
+ }
+ elementBox.set(constructor.create(element, mappedRangesForT, name));
+ });
+ return elementBox.get();
+ }
+
+ private boolean hasRetraceResult() {
+ return mapper != null;
+ }
+
+ @Override
+ public RetraceClassResult apply(Consumer<Element> resultConsumer) {
+ resultConsumer.accept(
+ new Element(
+ this,
+ mapper == null ? obfuscatedReference : Reference.classFromTypeName(mapper.originalName),
+ mapper));
+ return this;
+ }
+
+ private interface ResultConstructor<T, R> {
+ R create(Element element, T mappings, String obfuscatedName);
+ }
+
+ public static class Element {
+
+ private final RetraceClassResult classResult;
+ private final ClassReference classReference;
+ private final ClassNamingForNameMapper mapper;
+
+ public Element(
+ RetraceClassResult classResult,
+ ClassReference classReference,
+ ClassNamingForNameMapper mapper) {
+ this.classResult = classResult;
+ this.classReference = classReference;
+ this.mapper = mapper;
+ }
+
+ public ClassReference getClassReference() {
+ return classReference;
+ }
+
+ public RetraceClassResult getRetraceClassResult() {
+ return classResult;
+ }
+
+ public String retraceSourceFile(String fileName, RetraceBase retraceBase) {
+ return retraceBase.retraceSourceFile(
+ classResult.obfuscatedReference, fileName, classReference, mapper != null);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCore.java b/src/main/java/com/android/tools/r8/retrace/RetraceCore.java
index 8db2ace..d895d8d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCore.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCore.java
@@ -386,7 +386,7 @@
String mappedClazz = retraceClazz;
String mappedMethod = mappedRange.signature.name;
if (mappedRange.signature.isQualified()) {
- mappedClazz = mappedRange.signature.toUnqualifiedHolder();
+ mappedClazz = mappedRange.signature.toHolderFromQualified();
mappedMethod = mappedRange.signature.toUnqualifiedName();
}
int retracedLinePosition = linePosition;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
new file mode 100644
index 0000000..6e0d232
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2019, 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.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.FieldReference.UnknownFieldReference;
+import com.android.tools.r8.references.Reference;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+@Keep
+public class RetraceFieldResult extends Result<RetraceFieldResult.Element, RetraceFieldResult> {
+
+ private final RetraceClassResult.Element classElement;
+ private final List<MemberNaming> memberNamings;
+ private final String obfuscatedName;
+
+ RetraceFieldResult(
+ RetraceClassResult.Element classElement,
+ List<MemberNaming> memberNamings,
+ String obfuscatedName) {
+ this.classElement = classElement;
+ this.memberNamings = memberNamings;
+ this.obfuscatedName = obfuscatedName;
+ assert classElement != null;
+ assert memberNamings == null
+ || (!memberNamings.isEmpty() && memberNamings.stream().allMatch(Objects::nonNull));
+ }
+
+ private boolean hasRetraceResult() {
+ return memberNamings != null;
+ }
+
+ private boolean isAmbiguous() {
+ if (!hasRetraceResult()) {
+ return false;
+ }
+ assert memberNamings != null;
+ return memberNamings.size() > 1;
+ }
+
+ @Override
+ public RetraceFieldResult apply(Consumer<Element> resultConsumer) {
+ if (hasRetraceResult()) {
+ assert !memberNamings.isEmpty();
+ for (MemberNaming memberNaming : memberNamings) {
+ assert memberNaming.isFieldNaming();
+ FieldSignature fieldSignature = memberNaming.getOriginalSignature().asFieldSignature();
+ resultConsumer.accept(
+ new Element(
+ this,
+ classElement,
+ Reference.field(
+ classElement.getClassReference(),
+ fieldSignature.name,
+ Reference.typeFromTypeName(fieldSignature.type))));
+ }
+ } else {
+ resultConsumer.accept(
+ new Element(
+ this,
+ classElement,
+ new UnknownFieldReference(classElement.getClassReference(), obfuscatedName)));
+ }
+ return this;
+ }
+
+ public static class Element {
+
+ private final FieldReference fieldReference;
+ private final RetraceFieldResult retraceFieldResult;
+ private final RetraceClassResult.Element classElement;
+
+ private Element(
+ RetraceFieldResult retraceFieldResult,
+ RetraceClassResult.Element classElement,
+ FieldReference fieldReference) {
+ this.classElement = classElement;
+ this.fieldReference = fieldReference;
+ this.retraceFieldResult = retraceFieldResult;
+ }
+
+ public FieldReference getFieldReference() {
+ return fieldReference;
+ }
+
+ public RetraceFieldResult getRetraceFieldResult() {
+ return getRetraceFieldResult();
+ }
+
+ public RetraceClassResult.Element getClassElement() {
+ return classElement;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
new file mode 100644
index 0000000..e727a6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
@@ -0,0 +1,159 @@
+// Copyright (c) 2019, 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.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.Range;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.MethodReference.UnknownMethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Keep
+public class RetraceMethodResult extends Result<RetraceMethodResult.Element, RetraceMethodResult> {
+
+ private final String obfuscatedName;
+ private final RetraceClassResult.Element classElement;
+ private final MappedRangesOfName mappedRanges;
+ private Boolean isAmbiguousCached = null;
+
+ RetraceMethodResult(
+ RetraceClassResult.Element classElement,
+ MappedRangesOfName mappedRanges,
+ String obfuscatedName) {
+ this.classElement = classElement;
+ this.mappedRanges = mappedRanges;
+ this.obfuscatedName = obfuscatedName;
+ assert classElement != null;
+ }
+
+ private boolean hasRetraceResult() {
+ return mappedRanges != null && mappedRanges.getMappedRanges().size() > 0;
+ }
+
+ public boolean isAmbiguous() {
+ if (isAmbiguousCached != null) {
+ return isAmbiguousCached;
+ }
+ if (!hasRetraceResult()) {
+ return false;
+ }
+ assert mappedRanges != null;
+ Range minifiedRange = null;
+ boolean seenNull = false;
+ for (MappedRange mappedRange : mappedRanges.getMappedRanges()) {
+ if (minifiedRange != null && !minifiedRange.equals(mappedRange.minifiedRange)) {
+ isAmbiguousCached = true;
+ return true;
+ } else if (minifiedRange == null) {
+ if (seenNull) {
+ isAmbiguousCached = true;
+ return true;
+ }
+ seenNull = true;
+ }
+ minifiedRange = mappedRange.minifiedRange;
+ }
+ isAmbiguousCached = false;
+ return false;
+ }
+
+ public RetraceMethodResult narrowByLine(int linePosition) {
+ if (!hasRetraceResult()) {
+ return this;
+ }
+ List<MappedRange> narrowedRanges = this.mappedRanges.allRangesForLine(linePosition, false);
+ if (narrowedRanges.isEmpty()) {
+ narrowedRanges = new ArrayList<>();
+ for (MappedRange mappedRange : this.mappedRanges.getMappedRanges()) {
+ if (mappedRange.minifiedRange == null) {
+ narrowedRanges.add(mappedRange);
+ }
+ }
+ }
+ return new RetraceMethodResult(
+ classElement, new MappedRangesOfName(narrowedRanges), obfuscatedName);
+ }
+
+ @Override
+ public RetraceMethodResult apply(Consumer<Element> resultConsumer) {
+ if (hasRetraceResult()) {
+ for (MappedRange mappedRange : mappedRanges.getMappedRanges()) {
+ MethodSignature signature = mappedRange.signature;
+ ClassReference holder =
+ mappedRange.signature.isQualified()
+ ? Reference.classFromDescriptor(
+ DescriptorUtils.javaTypeToDescriptor(
+ mappedRange.signature.toHolderFromQualified()))
+ : classElement.getClassReference();
+ List<TypeReference> formalTypes = new ArrayList<>(signature.parameters.length);
+ for (String parameter : signature.parameters) {
+ formalTypes.add(Reference.typeFromTypeName(parameter));
+ }
+ TypeReference returnType =
+ Reference.returnTypeFromDescriptor(
+ DescriptorUtils.javaTypeToDescriptor(signature.type));
+ MethodReference retracedMethod =
+ Reference.method(
+ holder,
+ signature.isQualified() ? signature.toUnqualifiedName() : signature.name,
+ formalTypes,
+ returnType);
+ resultConsumer.accept(new Element(this, classElement, retracedMethod, mappedRange));
+ }
+ } else {
+ resultConsumer.accept(
+ new Element(
+ this,
+ classElement,
+ new UnknownMethodReference(classElement.getClassReference(), obfuscatedName),
+ null));
+ }
+ return this;
+ }
+
+ public static class Element {
+
+ private final MethodReference methodReference;
+ private final RetraceMethodResult retraceMethodResult;
+ private final RetraceClassResult.Element classElement;
+ private final MappedRange mappedRange;
+
+ private Element(
+ RetraceMethodResult retraceMethodResult,
+ RetraceClassResult.Element classElement,
+ MethodReference methodReference,
+ MappedRange mappedRange) {
+ this.classElement = classElement;
+ this.retraceMethodResult = retraceMethodResult;
+ this.methodReference = methodReference;
+ this.mappedRange = mappedRange;
+ }
+
+ public MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ public RetraceMethodResult getRetraceMethodResult() {
+ return retraceMethodResult;
+ }
+
+ public RetraceClassResult.Element getClassElement() {
+ return classElement;
+ }
+
+ public int getOriginalLineNumber(int linePosition) {
+ return mappedRange != null ? mappedRange.getOriginalLineNumber(linePosition) : linePosition;
+ }
+ }
+}