From d851c8679ad7b677789ab1c3864d1615cf14a51c Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 24 May 2018 00:47:08 +0300 Subject: [PATCH] Fix a number of potential exceptions in debugger (EA-119717) --- .../src/org/jetbrains/eval4j/jdi/jdiEval.kt | 16 ++++-- .../idea/debugger/KotlinPositionManager.kt | 14 +++--- .../breakpoints/KotlinFieldBreakpoint.kt | 3 +- .../kotlin/idea/debugger/debuggerUtil.kt | 49 +++++++++++++++---- .../KotlinSyntheticTypeComponentProvider.kt | 3 +- .../DelegatedPropertyFieldDescriptor.kt | 14 ++++-- .../stepping/KotlinSteppingCommandProvider.kt | 10 ++-- 7 files changed, 78 insertions(+), 31 deletions(-) diff --git a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt index a255ea582dc..0ca63c3b92d 100644 --- a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt +++ b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt @@ -357,7 +357,17 @@ class JDIEval( } private fun shouldInvokeMethodWithReflection(method: Method, args: List): Boolean { - return !method.isVarArgs && args.zip(method.argumentTypes()).any { isArrayOfInterfaces(it.first?.type(), it.second) } + if (method.isVarArgs) { + return false + } + + val argumentTypes = try { + method.argumentTypes() + } catch (e: ClassNotLoadedException) { + return false + } + + return args.zip(argumentTypes).any { isArrayOfInterfaces(it.first?.type(), it.second) } } private fun isArrayOfInterfaces(valueType: jdi_Type?, expectedType: jdi_Type?): Boolean { @@ -413,8 +423,8 @@ class JDIEval( } - private fun mapArguments(arguments: List, expecetedTypes: List): List { - return arguments.zip(expecetedTypes).map { + private fun mapArguments(arguments: List, expectedTypes: List): List { + return arguments.zip(expectedTypes).map { val (arg, expectedType) = it arg.asJdiValue(vm, expectedType.asType()) } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt index 7952a3779eb..0faf001d068 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt @@ -102,7 +102,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq if (location == null) throw NoDataException.INSTANCE val fileName = location.safeSourceName ?: throw NoDataException.INSTANCE - val lineNumber = location.safeLineNumber + val lineNumber = location.safeLineNumber() if (lineNumber < 0) { throw NoDataException.INSTANCE } @@ -136,7 +136,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq if (psiFile !is KtFile) throw NoDataException.INSTANCE - val sourceLineNumber = location.safeSourceLineNumber + val sourceLineNumber = location.safeSourceLineNumber() if (sourceLineNumber < 0) { throw NoDataException.INSTANCE } @@ -156,7 +156,10 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq return SourcePosition.createFromLine(ktFile ?: psiFile, line - 1) } - val sameLineLocations = location.safeMethod?.safeAllLineLocations?.filter { it.safeLineNumber == lineNumber && it.safeSourceName == fileName } + val sameLineLocations = location.safeMethod()?.safeAllLineLocations()?.filter { + it.safeLineNumber() == lineNumber && it.safeSourceName == fileName + } + if (sameLineLocations != null) { // There're several locations for same source line. If same source position would be created for all of them, // breakpoints at this line will stop on every location. @@ -262,11 +265,6 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq } } - private val Location.safeLineNumber: Int get() = DebuggerUtilsEx.getLineNumber(this, false) - private val Location.safeSourceLineNumber: Int get() = DebuggerUtilsEx.getLineNumber(this, true) - private val Location.safeMethod: Method? get() = DebuggerUtilsEx.getMethod(this) - private val Method.safeAllLineLocations: MutableList? get() = DebuggerUtilsEx.allLineLocations(this) - private fun getPsiFileByLocation(location: Location): PsiFile? { val sourceName = location.safeSourceName ?: return null diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt index cc55a9b55f8..12e8472cc78 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.debugger.safeAllLineLocations import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.psi.KtCallableDeclaration @@ -210,7 +211,7 @@ class KotlinFieldBreakpoint( private fun createMethodBreakpoint(debugProcess: DebugProcessImpl, refType: ReferenceType, accessor: Method) { val manager = debugProcess.requestsManager - val line = accessor.allLineLocations().firstOrNull() + val line = accessor.safeAllLineLocations().firstOrNull() if (line != null) { val request = manager.createBreakpointRequest(this, line) debugProcess.requestsManager.enableRequest(request) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt index 4897b0a73c8..e5ead557a39 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt @@ -9,6 +9,7 @@ import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebuggerManagerThreadImpl import com.intellij.debugger.engine.events.DebuggerCommandImpl import com.intellij.debugger.impl.DebuggerContextImpl +import com.intellij.debugger.impl.DebuggerUtilsEx import com.intellij.debugger.jdi.LocalVariableProxyImpl import com.intellij.psi.PsiElement import com.sun.jdi.* @@ -28,6 +29,42 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.inline.InlineUtil import java.util.* +fun Method.safeAllLineLocations(): List { + return DebuggerUtilsEx.allLineLocations(this) ?: emptyList() +} + +fun ReferenceType.safeAllLineLocations(): List { + return DebuggerUtilsEx.allLineLocations(this) ?: emptyList() +} + +fun Method.safeLocationsOfLine(line: Int): List { + return try { + locationsOfLine(line) + } catch (e: AbsentInformationException) { + emptyList() + } +} + +fun Method.safeVariables(): List { + return try { + variables() + } catch (e: AbsentInformationException) { + emptyList() + } +} + +fun Location.safeLineNumber(): Int { + return DebuggerUtilsEx.getLineNumber(this, false) +} + +fun Location.safeSourceLineNumber(): Int { + return DebuggerUtilsEx.getLineNumber(this, true) +} + +fun Location.safeMethod(): Method? { + return DebuggerUtilsEx.getMethod(this) +} + fun isInsideInlineFunctionBody(visibleVariables: List): Boolean { return visibleVariables.any { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) } } @@ -108,7 +145,7 @@ private class MockStackFrame(private val location: Location, private val vm: Vir private fun createVisibleVariables() { if (visibleVariables == null) { - val allVariables = location.method().variables() + val allVariables = location.method().safeVariables() val map = HashMap(allVariables.size) for (allVariable in allVariables) { @@ -176,13 +213,7 @@ fun isOnSuspendReturnOrReenter(location: Location): Boolean { } fun isLastLineLocationInMethod(location: Location): Boolean { - val allLineLocations = try { - location.method().allLineLocations() - } catch (e: AbsentInformationException) { - return false - } - - val knownLines = allLineLocations.map { it.lineNumber() }.filter { it != -1 } + val knownLines = location.method().safeAllLineLocations().map { it.lineNumber() }.filter { it != -1 } if (knownLines.isEmpty()) { return false } @@ -191,7 +222,7 @@ fun isLastLineLocationInMethod(location: Location): Boolean { } fun isOneLineMethod(location: Location): Boolean { - val allLineLocations = location.method().allLineLocations() + val allLineLocations = location.method().safeAllLineLocations() val firstLine = allLineLocations.firstOrNull()?.lineNumber() val lastLine = allLineLocations.lastOrNull()?.lineNumber() diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/filter/KotlinSyntheticTypeComponentProvider.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/filter/KotlinSyntheticTypeComponentProvider.kt index 4850ac662a6..f6bc78d415d 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/filter/KotlinSyntheticTypeComponentProvider.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/filter/KotlinSyntheticTypeComponentProvider.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.debugger.filter import com.intellij.debugger.engine.SyntheticTypeComponentProvider import com.sun.jdi.* +import org.jetbrains.kotlin.idea.debugger.safeAllLineLocations import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.org.objectweb.asm.Opcodes @@ -71,7 +72,7 @@ class KotlinSyntheticTypeComponentProvider: SyntheticTypeComponentProvider { } private fun Method.isDelegateToDefaultInterfaceImpl(): Boolean { - if (allLineLocations().size != 1) return false + if (safeAllLineLocations().size != 1) return false if (!virtualMachine().canGetBytecodes()) return false if (!hasOnlyInvokeStatic(this)) return false diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt index 86f477151de..32ae48f1f80 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt @@ -22,10 +22,7 @@ import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl import com.intellij.openapi.project.Project import com.intellij.psi.PsiExpression -import com.sun.jdi.Field -import com.sun.jdi.Method -import com.sun.jdi.ObjectReference -import com.sun.jdi.Value +import com.sun.jdi.* import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name @@ -76,6 +73,13 @@ class DelegatedPropertyFieldDescriptor( } override fun getDeclaredType(): String? { - return findGetterForDelegatedProperty()?.returnType()?.name() + val getter = findGetterForDelegatedProperty() ?: return null + val returnType = try { + getter.returnType() + } catch (e: ClassNotLoadedException) { + // Behavior copied from LocalVariableDescriptorImpl (in platform) + return "" + } + return returnType?.name() } } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt index 0617773cb55..a59848ae344 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -409,7 +409,7 @@ fun getStepOverAction( // this filter locations with too big code indexes manually val returnCodeIndex: Long = if (isDexDebug) { val method = location.method() - val locationsOfLine = method.locationsOfLine(range.last) + val locationsOfLine = method.safeLocationsOfLine(range.last) if (locationsOfLine.isNotEmpty()) { locationsOfLine.map { it.codeIndex() }.max() ?: -1L } @@ -439,7 +439,7 @@ fun getStepOutAction( ): Action { val computedReferenceType = location.declaringType() ?: return Action.STEP_OUT() - val locations = computedReferenceType.allLineLocations() + val locations = computedReferenceType.safeAllLineLocations() val nextLineLocations = locations .dropWhile { it != location } .drop(1) @@ -531,8 +531,10 @@ private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? { } private fun findReturnFromDexBytecode(method: Method): Long { - val methodLocations = method.allLineLocations() - if (methodLocations.isEmpty()) return -1L + val methodLocations = method.safeAllLineLocations() + if (methodLocations.isEmpty()) { + return -1L + } var lastMethodCodeIndex = methodLocations.last().codeIndex() // Continue while it's possible to get location