| // 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.internal; |
| |
| import com.android.tools.r8.errors.Unimplemented; |
| import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange; |
| import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName; |
| import com.android.tools.r8.naming.mappinginformation.OutlineCallsiteMappingInformation; |
| import com.android.tools.r8.references.MethodReference; |
| import com.android.tools.r8.retrace.RetraceMethodElement; |
| import com.android.tools.r8.retrace.RetraceMethodResult; |
| import com.android.tools.r8.retrace.RetraceStackTraceContext; |
| import com.android.tools.r8.retrace.RetracedMethodReference; |
| import com.android.tools.r8.retrace.RetracedSourceFile; |
| import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl; |
| import com.android.tools.r8.utils.ListUtils; |
| import com.android.tools.r8.utils.Pair; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.OptionalInt; |
| import java.util.Set; |
| import java.util.stream.Stream; |
| |
| public class RetraceMethodResultImpl implements RetraceMethodResult { |
| |
| private final MethodDefinition methodDefinition; |
| private final RetraceClassResultImpl classResult; |
| private final List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges; |
| private final RetracerImpl retracer; |
| |
| RetraceMethodResultImpl( |
| RetraceClassResultImpl classResult, |
| List<Pair<RetraceClassElementImpl, List<MappedRange>>> mappedRanges, |
| MethodDefinition methodDefinition, |
| RetracerImpl retracer) { |
| this.classResult = classResult; |
| this.mappedRanges = mappedRanges; |
| this.methodDefinition = methodDefinition; |
| this.retracer = retracer; |
| assert classResult != null; |
| assert !mappedRanges.isEmpty(); |
| } |
| |
| @Override |
| public boolean isAmbiguous() { |
| if (mappedRanges.size() > 1) { |
| return true; |
| } |
| List<MappedRange> methodRanges = mappedRanges.get(0).getSecond(); |
| if (methodRanges == null || methodRanges.isEmpty()) { |
| return false; |
| } |
| MappedRange lastRange = methodRanges.get(0); |
| for (MappedRange mappedRange : methodRanges) { |
| if (mappedRange != lastRange |
| && (mappedRange.minifiedRange == null |
| || !mappedRange.minifiedRange.equals(lastRange.minifiedRange))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return mappedRanges == null || mappedRanges.isEmpty(); |
| } |
| |
| @Override |
| public RetraceFrameResultImpl narrowByPosition( |
| RetraceStackTraceContext context, OptionalInt position) { |
| List<Pair<RetraceClassElementImpl, List<MappedRange>>> narrowedRanges = new ArrayList<>(); |
| RetraceStackTraceContextImpl stackTraceContext = null; |
| if (context instanceof RetraceStackTraceContextImpl) { |
| stackTraceContext = (RetraceStackTraceContextImpl) context; |
| } |
| for (Pair<RetraceClassElementImpl, List<MappedRange>> mappedRange : mappedRanges) { |
| if (mappedRange.getSecond() == null) { |
| narrowedRanges.add(new Pair<>(mappedRange.getFirst(), null)); |
| continue; |
| } |
| MappedRangesOfName mappedRangesOfElement = new MappedRangesOfName(mappedRange.getSecond()); |
| List<MappedRange> mappedRangesForPosition = null; |
| boolean hasPosition = position.isPresent() && position.getAsInt() >= 0; |
| if (hasPosition) { |
| mappedRangesForPosition = |
| mappedRangesOfElement.allRangesForLine(position.getAsInt(), false); |
| } |
| if (mappedRangesForPosition == null || mappedRangesForPosition.isEmpty()) { |
| mappedRangesForPosition = |
| hasPosition |
| ? ListUtils.filter( |
| mappedRangesOfElement.getMappedRanges(), range -> range.minifiedRange == null) |
| : mappedRangesOfElement.getMappedRanges(); |
| } |
| if (mappedRangesForPosition != null && !mappedRangesForPosition.isEmpty()) { |
| if (stackTraceContext != null && stackTraceContext.hasRewritePosition()) { |
| List<OutlineCallsiteMappingInformation> outlineCallsiteInformation = |
| ListUtils.last(mappedRangesForPosition).getOutlineCallsiteInformation(); |
| if (!outlineCallsiteInformation.isEmpty()) { |
| assert outlineCallsiteInformation.size() == 1 |
| : "There can only be one outline entry for a line"; |
| return narrowByPosition( |
| stackTraceContext.buildFromThis().clearRewritePosition().build(), |
| OptionalInt.of( |
| outlineCallsiteInformation |
| .get(0) |
| .rewritePosition(stackTraceContext.getRewritePosition()))); |
| } |
| } |
| narrowedRanges.add(new Pair<>(mappedRange.getFirst(), mappedRangesForPosition)); |
| } |
| } |
| return new RetraceFrameResultImpl( |
| classResult, |
| narrowedRanges, |
| methodDefinition, |
| position, |
| retracer, |
| (RetraceStackTraceContextImpl) context); |
| } |
| |
| @Override |
| public Stream<RetraceMethodElement> stream() { |
| return mappedRanges.stream() |
| .flatMap( |
| mappedRangePair -> { |
| RetraceClassElementImpl classElement = mappedRangePair.getFirst(); |
| List<MappedRange> mappedRanges = mappedRangePair.getSecond(); |
| if (mappedRanges == null || mappedRanges.isEmpty()) { |
| return Stream.of( |
| new ElementImpl( |
| this, |
| classElement, |
| RetracedMethodReferenceImpl.create( |
| methodDefinition.substituteHolder( |
| classElement.getRetracedClass().getClassReference())))); |
| } |
| List<ElementImpl> results = new ArrayList<>(); |
| Set<MethodReference> seenMethodReferences = new HashSet<>(); |
| for (MappedRange mappedRange : mappedRanges) { |
| MethodReference methodReference = |
| RetraceUtils.methodReferenceFromMappedRange( |
| mappedRange, classElement.getRetracedClass().getClassReference()); |
| if (seenMethodReferences.add(methodReference)) { |
| results.add( |
| new ElementImpl( |
| this, classElement, RetracedMethodReferenceImpl.create(methodReference))); |
| } |
| } |
| return results.stream(); |
| }); |
| } |
| |
| public static class ElementImpl implements RetraceMethodElement { |
| |
| private final RetracedMethodReferenceImpl methodReference; |
| private final RetraceMethodResultImpl retraceMethodResult; |
| private final RetraceClassElementImpl classElement; |
| |
| private ElementImpl( |
| RetraceMethodResultImpl retraceMethodResult, |
| RetraceClassElementImpl classElement, |
| RetracedMethodReferenceImpl methodReference) { |
| this.classElement = classElement; |
| this.retraceMethodResult = retraceMethodResult; |
| this.methodReference = methodReference; |
| } |
| |
| @Override |
| public boolean isCompilerSynthesized() { |
| throw new Unimplemented("b/172014416"); |
| } |
| |
| @Override |
| public boolean isUnknown() { |
| return methodReference.isUnknown(); |
| } |
| |
| @Override |
| public RetracedMethodReference getRetracedMethod() { |
| return methodReference; |
| } |
| |
| @Override |
| public RetraceMethodResult getRetraceResultContext() { |
| return retraceMethodResult; |
| } |
| |
| @Override |
| public RetraceClassElementImpl getClassElement() { |
| return classElement; |
| } |
| |
| @Override |
| public RetracedSourceFile getSourceFile() { |
| return RetraceUtils.getSourceFileOrLookup( |
| methodReference.getHolderClass(), classElement, retraceMethodResult.retracer); |
| } |
| } |
| } |