Debugger: Remove obsolete hacks needed for old Android dex
This commit is contained in:
-5
@@ -138,11 +138,6 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
|
|||||||
return SourcePosition.createFromElement(elementInDeclaration)
|
return SourcePosition.createFromElement(elementInDeclaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceLineNumber > psiFile.getLineCount() && myDebugProcess.isDexDebug()) {
|
|
||||||
val (line, ktFile) = ktLocationInfo(location, true, myDebugProcess.project, false, psiFile)
|
|
||||||
return SourcePosition.createFromLine(ktFile ?: psiFile, line - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val sameLineLocations = location.safeMethod()?.safeAllLineLocations()?.filter {
|
val sameLineLocations = location.safeMethod()?.safeAllLineLocations()?.filter {
|
||||||
it.safeLineNumber() == lineNumber && it.safeSourceName() == fileName
|
it.safeLineNumber() == lineNumber && it.safeSourceName() == fileName
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-9
@@ -37,7 +37,6 @@ import com.sun.jdi.request.StepRequest;
|
|||||||
import one.util.streamex.StreamEx;
|
import one.util.streamex.StreamEx;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.idea.debugger.NoStrataPositionManagerHelperKt;
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinSuspendCallStepOverFilter;
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinSuspendCallStepOverFilter;
|
||||||
import org.jetbrains.kotlin.psi.KtFunctionLiteral;
|
import org.jetbrains.kotlin.psi.KtFunctionLiteral;
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction;
|
import org.jetbrains.kotlin.psi.KtNamedFunction;
|
||||||
@@ -56,17 +55,11 @@ public class DebuggerSteppingHelper {
|
|||||||
return debugProcess.new ResumeCommand(suspendContext) {
|
return debugProcess.new ResumeCommand(suspendContext) {
|
||||||
@Override
|
@Override
|
||||||
public void contextAction() {
|
public void contextAction() {
|
||||||
boolean isDexDebug = NoStrataPositionManagerHelperKt.isDexDebug(suspendContext.getDebugProcess());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
|
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
|
||||||
if (frameProxy != null) {
|
if (frameProxy != null) {
|
||||||
Action action = KotlinSteppingCommandProviderKt.getStepOverAction(
|
Action action = KotlinSteppingCommandProviderKt
|
||||||
frameProxy.location(),
|
.getStepOverAction(frameProxy.location(), kotlinSourcePosition, frameProxy);
|
||||||
kotlinSourcePosition,
|
|
||||||
frameProxy,
|
|
||||||
isDexDebug
|
|
||||||
);
|
|
||||||
|
|
||||||
createStepRequest(
|
createStepRequest(
|
||||||
suspendContext, getContextThread(),
|
suspendContext, getContextThread(),
|
||||||
|
|||||||
+14
-109
@@ -18,7 +18,6 @@ import com.intellij.xdebugger.impl.XSourcePositionImpl
|
|||||||
import com.sun.jdi.AbsentInformationException
|
import com.sun.jdi.AbsentInformationException
|
||||||
import com.sun.jdi.LocalVariable
|
import com.sun.jdi.LocalVariable
|
||||||
import com.sun.jdi.Location
|
import com.sun.jdi.Location
|
||||||
import com.sun.jdi.Method
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME
|
import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME
|
||||||
@@ -30,12 +29,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||||
import org.jetbrains.kotlin.idea.debugger.*
|
import org.jetbrains.kotlin.idea.debugger.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.GOTO
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.MOVE
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN_OBJECT
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN_VOID
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN_WIDE
|
|
||||||
import org.jetbrains.kotlin.idea.core.util.getLineNumber
|
import org.jetbrains.kotlin.idea.core.util.getLineNumber
|
||||||
import org.jetbrains.kotlin.idea.core.util.getLineStartOffset
|
import org.jetbrains.kotlin.idea.core.util.getLineStartOffset
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinStepOverInlineFilter
|
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinStepOverInlineFilter
|
||||||
@@ -46,12 +39,10 @@ import org.jetbrains.kotlin.load.java.JvmAbi
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@@ -281,19 +272,14 @@ interface KotlinMethodFilter : MethodFilter {
|
|||||||
fun locationMatches(context: SuspendContextImpl, location: Location): Boolean
|
fun locationMatches(context: SuspendContextImpl, location: Location): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStepOverAction(
|
fun getStepOverAction(location: Location, kotlinSourcePosition: KotlinSourcePosition, frameProxy: StackFrameProxyImpl): Action {
|
||||||
location: Location,
|
|
||||||
kotlinSourcePosition: KotlinSourcePosition,
|
|
||||||
frameProxy: StackFrameProxyImpl,
|
|
||||||
isDexDebug: Boolean
|
|
||||||
): Action {
|
|
||||||
val inlineArgumentsToSkip = runReadAction {
|
val inlineArgumentsToSkip = runReadAction {
|
||||||
getInlineCallFunctionArgumentsIfAny(kotlinSourcePosition.sourcePosition)
|
getInlineCallFunctionArgumentsIfAny(kotlinSourcePosition.sourcePosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getStepOverAction(
|
return getStepOverAction(
|
||||||
location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange,
|
location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange,
|
||||||
inlineArgumentsToSkip, frameProxy, isDexDebug
|
inlineArgumentsToSkip, frameProxy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,39 +288,27 @@ fun getStepOverAction(
|
|||||||
sourceFile: KtFile,
|
sourceFile: KtFile,
|
||||||
range: IntRange,
|
range: IntRange,
|
||||||
inlineFunctionArguments: List<KtElement>,
|
inlineFunctionArguments: List<KtElement>,
|
||||||
frameProxy: StackFrameProxyImpl,
|
frameProxy: StackFrameProxyImpl
|
||||||
isDexDebug: Boolean
|
|
||||||
): Action {
|
): Action {
|
||||||
location.declaringType() ?: return Action.StepOver()
|
location.declaringType() ?: return Action.StepOver()
|
||||||
|
|
||||||
val project = sourceFile.project
|
|
||||||
|
|
||||||
val methodLocations = location.method().safeAllLineLocations()
|
val methodLocations = location.method().safeAllLineLocations()
|
||||||
if (methodLocations.isEmpty()) {
|
if (methodLocations.isEmpty()) {
|
||||||
return Action.StepOver()
|
return Action.StepOver()
|
||||||
}
|
}
|
||||||
|
|
||||||
val locationsLineAndFile = methodLocations.keysToMap { ktLocationInfo(it, isDexDebug, project, true) }
|
|
||||||
|
|
||||||
fun Location.ktLineNumber(): Int = (locationsLineAndFile[this] ?: ktLocationInfo(this, isDexDebug, project, true)).first
|
|
||||||
fun Location.ktFileName(): String {
|
|
||||||
val ktFile = (locationsLineAndFile[this] ?: ktLocationInfo(this, isDexDebug, project, true)).second
|
|
||||||
// File is not null only for inlined locations. Get file name from debugger information otherwise.
|
|
||||||
return ktFile?.name ?: this.sourceName(KOTLIN_STRATA_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isThisMethodLocation(nextLocation: Location): Boolean {
|
fun isThisMethodLocation(nextLocation: Location): Boolean {
|
||||||
if (nextLocation.method() != location.method()) {
|
if (nextLocation.method() != location.method()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val ktLineNumber = nextLocation.ktLineNumber()
|
val ktLineNumber = nextLocation.lineNumber()
|
||||||
if (ktLineNumber !in range) {
|
if (ktLineNumber !in range) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
nextLocation.ktFileName() == sourceFile.name
|
nextLocation.sourceName(KOTLIN_STRATA_NAME) == sourceFile.name
|
||||||
} catch (e: AbsentInformationException) {
|
} catch (e: AbsentInformationException) {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -346,22 +320,22 @@ fun getStepOverAction(
|
|||||||
.dropWhile { it != location }
|
.dropWhile { it != location }
|
||||||
.drop(1)
|
.drop(1)
|
||||||
.filter(::isThisMethodLocation)
|
.filter(::isThisMethodLocation)
|
||||||
.dropWhile { it.ktLineNumber() == location.ktLineNumber() }
|
.dropWhile { it.lineNumber() == location.lineNumber() }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
return previousSuitableLocation != null && previousSuitableLocation.ktLineNumber() > location.ktLineNumber()
|
return previousSuitableLocation != null && previousSuitableLocation.lineNumber() > location.lineNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
val patchedLocation = if (isBackEdgeLocation()) {
|
val patchedLocation = if (isBackEdgeLocation()) {
|
||||||
// Pretend we had already done a backing step
|
// Pretend we had already done a backing step
|
||||||
methodLocations
|
methodLocations
|
||||||
.filter(::isThisMethodLocation)
|
.filter(::isThisMethodLocation)
|
||||||
.firstOrNull { it.ktLineNumber() == location.ktLineNumber() } ?: location
|
.firstOrNull { it.lineNumber() == location.lineNumber() } ?: location
|
||||||
} else {
|
} else {
|
||||||
location
|
location
|
||||||
}
|
}
|
||||||
|
|
||||||
val patchedLineNumber = patchedLocation.ktLineNumber()
|
val patchedLineNumber = patchedLocation.lineNumber()
|
||||||
|
|
||||||
val lambdaArgumentRanges = runReadAction {
|
val lambdaArgumentRanges = runReadAction {
|
||||||
inlineFunctionArguments.map {
|
inlineFunctionArguments.map {
|
||||||
@@ -384,34 +358,17 @@ fun getStepOverAction(
|
|||||||
val stepOverLocations = methodLocations
|
val stepOverLocations = methodLocations
|
||||||
.dropWhile { it != patchedLocation }
|
.dropWhile { it != patchedLocation }
|
||||||
.drop(1)
|
.drop(1)
|
||||||
.dropWhile { it.ktLineNumber() == patchedLineNumber }
|
.dropWhile { it.lineNumber() == patchedLineNumber }
|
||||||
.takeWhile { loc ->
|
.takeWhile { loc ->
|
||||||
!isThisMethodLocation(loc) || lambdaArgumentRanges.any { loc.ktLineNumber() in it } || loc.ktLineNumber() == patchedLineNumber
|
!isThisMethodLocation(loc) || lambdaArgumentRanges.any { loc.lineNumber() in it } || loc.lineNumber() == patchedLineNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stepOverLocations.isNotEmpty()) {
|
if (stepOverLocations.isNotEmpty()) {
|
||||||
// Some Kotlin inlined methods with 'for' (and maybe others) generates bytecode that, being dex-processed, have a strange artifact.
|
|
||||||
// GOTO instructions are moved to the end of method and as they don't have proper line, line is obtained from the previous
|
|
||||||
// instruction. It might be method return or previous GOTO from the inlining. Simple stepping over such function is really
|
|
||||||
// terrible. On each iteration position jumps to the method end or some previous inline call and then returns back. To prevent
|
|
||||||
// this filter locations with too big code indexes manually
|
|
||||||
val returnCodeIndex: Long = if (isDexDebug) {
|
|
||||||
val method = location.method()
|
|
||||||
val locationsOfLine = method.safeLocationsOfLine(range.last)
|
|
||||||
if (locationsOfLine.isNotEmpty()) {
|
|
||||||
locationsOfLine.map { it.codeIndex() }.max() ?: -1L
|
|
||||||
} else {
|
|
||||||
findReturnFromDexBytecode(location.method())
|
|
||||||
}
|
|
||||||
} else -1L
|
|
||||||
|
|
||||||
return Action.StepOverInlined(
|
return Action.StepOverInlined(
|
||||||
StepOverFilterData(
|
StepOverFilterData(
|
||||||
patchedLineNumber,
|
patchedLineNumber,
|
||||||
stepOverLocations.map { it.ktLineNumber() }.toSet(),
|
stepOverLocations.map { it.lineNumber() }.toSet(),
|
||||||
inlineRangeVariables,
|
inlineRangeVariables
|
||||||
isDexDebug,
|
|
||||||
returnCodeIndex
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -513,56 +470,4 @@ private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? {
|
|||||||
if (!InlineUtil.isInlinedArgument(functionLiteralExpression.functionLiteral, context, false)) return null
|
if (!InlineUtil.isInlinedArgument(functionLiteralExpression.functionLiteral, context, false)) return null
|
||||||
|
|
||||||
return functionLiteralExpression.functionLiteral
|
return functionLiteralExpression.functionLiteral
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findReturnFromDexBytecode(method: Method): Long {
|
|
||||||
val methodLocations = method.safeAllLineLocations()
|
|
||||||
if (methodLocations.isEmpty()) {
|
|
||||||
return -1L
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastMethodCodeIndex = methodLocations.last().codeIndex()
|
|
||||||
// Continue while it's possible to get location
|
|
||||||
while (true) {
|
|
||||||
if (method.locationOfCodeIndex(lastMethodCodeIndex + 1) != null) {
|
|
||||||
lastMethodCodeIndex++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var returnIndex = lastMethodCodeIndex + 1
|
|
||||||
|
|
||||||
val bytecode = method.bytecodes()
|
|
||||||
var i = bytecode.size
|
|
||||||
|
|
||||||
while (i >= 2) {
|
|
||||||
// Can step only through two-byte instructions and abort on any unknown one
|
|
||||||
i -= 2
|
|
||||||
returnIndex -= 1
|
|
||||||
|
|
||||||
val instruction = bytecode[i].toInt()
|
|
||||||
|
|
||||||
if (instruction == RETURN_VOID || instruction == RETURN || instruction == RETURN_WIDE || instruction == RETURN_OBJECT) {
|
|
||||||
// Instruction found
|
|
||||||
return returnIndex
|
|
||||||
} else if (instruction == MOVE || instruction == GOTO) {
|
|
||||||
// proceed
|
|
||||||
} else {
|
|
||||||
// Don't know the instruction and it's length. Abort.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1L
|
|
||||||
}
|
|
||||||
|
|
||||||
object DexBytecode {
|
|
||||||
const val RETURN_VOID = 0x0e
|
|
||||||
const val RETURN = 0x0f
|
|
||||||
const val RETURN_WIDE = 0x10
|
|
||||||
const val RETURN_OBJECT = 0x11
|
|
||||||
|
|
||||||
const val GOTO = 0x28
|
|
||||||
const val MOVE = 0x01
|
|
||||||
}
|
|
||||||
+2
-11
@@ -22,29 +22,20 @@ import com.intellij.openapi.project.Project
|
|||||||
import com.intellij.util.Range
|
import com.intellij.util.Range
|
||||||
import com.sun.jdi.LocalVariable
|
import com.sun.jdi.LocalVariable
|
||||||
import com.sun.jdi.Location
|
import com.sun.jdi.Location
|
||||||
import org.jetbrains.kotlin.idea.debugger.ktLocationInfo
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinMethodFilter
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinMethodFilter
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.getInlineRangeLocalVariables
|
import org.jetbrains.kotlin.idea.debugger.stepping.getInlineRangeLocalVariables
|
||||||
|
|
||||||
class StepOverFilterData(
|
class StepOverFilterData(
|
||||||
val lineNumber: Int,
|
val lineNumber: Int,
|
||||||
val stepOverLines: Set<Int>,
|
val stepOverLines: Set<Int>,
|
||||||
val inlineRangeVariables: List<LocalVariable>,
|
val inlineRangeVariables: List<LocalVariable>
|
||||||
val isDexDebug: Boolean,
|
|
||||||
val skipAfterCodeIndex: Long = -1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class KotlinStepOverInlineFilter(val project: Project, val data: StepOverFilterData) : KotlinMethodFilter {
|
class KotlinStepOverInlineFilter(val project: Project, val data: StepOverFilterData) : KotlinMethodFilter {
|
||||||
private fun Location.ktLineNumber() = ktLocationInfo(this, data.isDexDebug, project).first
|
|
||||||
|
|
||||||
override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean {
|
override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean {
|
||||||
val frameProxy = context.frameProxy ?: return true
|
val frameProxy = context.frameProxy ?: return true
|
||||||
|
|
||||||
if (data.skipAfterCodeIndex != -1L && location.codeIndex() > data.skipAfterCodeIndex) {
|
val currentLine = location.lineNumber()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentLine = location.ktLineNumber()
|
|
||||||
if (!(data.stepOverLines.contains(currentLine))) {
|
if (!(data.stepOverLines.contains(currentLine))) {
|
||||||
return currentLine != data.lineNumber
|
return currentLine != data.lineNumber
|
||||||
}
|
}
|
||||||
|
|||||||
-74
@@ -63,52 +63,6 @@ fun readBytecodeInfo(
|
|||||||
return KotlinDebuggerCaches.getOrReadDebugInfoFromBytecode(project, jvmName, file)
|
return KotlinDebuggerCaches.getOrReadDebugInfoFromBytecode(project, jvmName, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ktLocationInfo(
|
|
||||||
location: Location, isDexDebug: Boolean, project: Project,
|
|
||||||
preferInlined: Boolean = false, locationFile: KtFile? = null
|
|
||||||
): Pair<Int, KtFile?> {
|
|
||||||
if (isDexDebug && (locationFile == null || location.lineNumber() > locationFile.getLineCount())) {
|
|
||||||
if (!preferInlined) {
|
|
||||||
val thisFunLine = runReadAction { getLastLineNumberForLocation(location, project) }
|
|
||||||
if (thisFunLine != null && thisFunLine != location.lineNumber()) {
|
|
||||||
return thisFunLine to locationFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val inlinePosition = runReadAction { getOriginalPositionOfInlinedLine(location, project) }
|
|
||||||
if (inlinePosition != null) {
|
|
||||||
val (file, line) = inlinePosition
|
|
||||||
return line + 1 to file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return location.lineNumber() to locationFile
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only the first line number is stored for instruction in dex. It can be obtained through location.lineNumber().
|
|
||||||
* This method allows to get last stored linenumber for instruction.
|
|
||||||
*/
|
|
||||||
fun getLastLineNumberForLocation(
|
|
||||||
location: Location,
|
|
||||||
project: Project,
|
|
||||||
searchScope: GlobalSearchScope = GlobalSearchScope.allScope(project)
|
|
||||||
): Int? {
|
|
||||||
val lineNumber = location.lineNumber()
|
|
||||||
val fqName = FqName(location.declaringType().name())
|
|
||||||
val fileName = location.sourceName()
|
|
||||||
|
|
||||||
val method = location.method() ?: return null
|
|
||||||
val name = method.name() ?: return null
|
|
||||||
val signature = method.signature() ?: return null
|
|
||||||
|
|
||||||
val debugInfo =
|
|
||||||
findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?: return null
|
|
||||||
|
|
||||||
val lineMapping = debugInfo.lineTableMapping[BytecodeMethodKey(name, signature)] ?: return null
|
|
||||||
return lineMapping.values.firstOrNull { it.contains(lineNumber) }?.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createWeakBytecodeDebugInfoStorage(): ConcurrentMap<BinaryCacheKey, BytecodeDebugInfo?> {
|
fun createWeakBytecodeDebugInfoStorage(): ConcurrentMap<BinaryCacheKey, BytecodeDebugInfo?> {
|
||||||
return ConcurrentFactoryMap.createWeakMap<BinaryCacheKey, BytecodeDebugInfo?> { key ->
|
return ConcurrentFactoryMap.createWeakMap<BinaryCacheKey, BytecodeDebugInfo?> { key ->
|
||||||
val bytes = readClassFileImpl(key.project, key.jvmName, key.file) ?: return@createWeakMap null
|
val bytes = readClassFileImpl(key.project, key.jvmName, key.file) ?: return@createWeakMap null
|
||||||
@@ -245,34 +199,6 @@ private fun readLineNumberTableMapping(bytes: ByteArray): Map<BytecodeMethodKey,
|
|||||||
return lineNumberMapping
|
return lineNumberMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getOriginalPositionOfInlinedLine(location: Location, project: Project): Pair<KtFile, Int>? {
|
|
||||||
val lineNumber = location.lineNumber()
|
|
||||||
val fqName = FqName(location.declaringType().name())
|
|
||||||
val fileName = location.sourceName()
|
|
||||||
val searchScope = GlobalSearchScope.allScope(project)
|
|
||||||
|
|
||||||
val debugInfo =
|
|
||||||
findAndReadClassFile(fqName, fileName, project, searchScope) { isInlineFunctionLineNumber(it, lineNumber, project) } ?: return null
|
|
||||||
val smapData = debugInfo.smapData ?: return null
|
|
||||||
|
|
||||||
return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind.EXECUTED_LINE, searchScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findAndReadClassFile(
|
|
||||||
fqName: FqName, fileName: String, project: Project, searchScope: GlobalSearchScope,
|
|
||||||
fileFilter: (VirtualFile) -> Boolean
|
|
||||||
): BytecodeDebugInfo? {
|
|
||||||
val internalName = fqName.asString().replace('.', '/')
|
|
||||||
val jvmClassName = JvmClassName.byInternalName(internalName)
|
|
||||||
|
|
||||||
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, jvmClassName, fileName) ?: return null
|
|
||||||
|
|
||||||
val virtualFile = file.virtualFile ?: return null
|
|
||||||
if (!fileFilter(virtualFile)) return null
|
|
||||||
|
|
||||||
return readBytecodeInfo(project, jvmClassName, virtualFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List<Location> {
|
fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List<Location> {
|
||||||
val line = position.line
|
val line = position.line
|
||||||
val file = position.file
|
val file = position.file
|
||||||
|
|||||||
Reference in New Issue
Block a user