Fix a number of potential exceptions in debugger (EA-119717)

This commit is contained in:
Yan Zhulanow
2018-05-24 00:47:08 +03:00
parent 0f15dfdfdc
commit d851c8679a
7 changed files with 78 additions and 31 deletions
+13 -3
View File
@@ -357,7 +357,17 @@ class JDIEval(
} }
private fun shouldInvokeMethodWithReflection(method: Method, args: List<com.sun.jdi.Value?>): Boolean { private fun shouldInvokeMethodWithReflection(method: Method, args: List<com.sun.jdi.Value?>): 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 { private fun isArrayOfInterfaces(valueType: jdi_Type?, expectedType: jdi_Type?): Boolean {
@@ -413,8 +423,8 @@ class JDIEval(
} }
private fun mapArguments(arguments: List<Value>, expecetedTypes: List<jdi_Type>): List<jdi_Value?> { private fun mapArguments(arguments: List<Value>, expectedTypes: List<jdi_Type>): List<jdi_Value?> {
return arguments.zip(expecetedTypes).map { return arguments.zip(expectedTypes).map {
val (arg, expectedType) = it val (arg, expectedType) = it
arg.asJdiValue(vm, expectedType.asType()) arg.asJdiValue(vm, expectedType.asType())
} }
@@ -102,7 +102,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
if (location == null) throw NoDataException.INSTANCE if (location == null) throw NoDataException.INSTANCE
val fileName = location.safeSourceName ?: throw NoDataException.INSTANCE val fileName = location.safeSourceName ?: throw NoDataException.INSTANCE
val lineNumber = location.safeLineNumber val lineNumber = location.safeLineNumber()
if (lineNumber < 0) { if (lineNumber < 0) {
throw NoDataException.INSTANCE throw NoDataException.INSTANCE
} }
@@ -136,7 +136,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
if (psiFile !is KtFile) throw NoDataException.INSTANCE if (psiFile !is KtFile) throw NoDataException.INSTANCE
val sourceLineNumber = location.safeSourceLineNumber val sourceLineNumber = location.safeSourceLineNumber()
if (sourceLineNumber < 0) { if (sourceLineNumber < 0) {
throw NoDataException.INSTANCE throw NoDataException.INSTANCE
} }
@@ -156,7 +156,10 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
return SourcePosition.createFromLine(ktFile ?: psiFile, line - 1) 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) { if (sameLineLocations != null) {
// There're several locations for same source line. If same source position would be created for all of them, // 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. // 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<Location>? get() = DebuggerUtilsEx.allLineLocations(this)
private fun getPsiFileByLocation(location: Location): PsiFile? { private fun getPsiFileByLocation(location: Location): PsiFile? {
val sourceName = location.safeSourceName ?: return null val sourceName = location.safeSourceName ?: return null
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.idea.caches.resolve.analyze 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.idea.util.application.runReadAction
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtCallableDeclaration
@@ -210,7 +211,7 @@ class KotlinFieldBreakpoint(
private fun createMethodBreakpoint(debugProcess: DebugProcessImpl, refType: ReferenceType, accessor: Method) { private fun createMethodBreakpoint(debugProcess: DebugProcessImpl, refType: ReferenceType, accessor: Method) {
val manager = debugProcess.requestsManager val manager = debugProcess.requestsManager
val line = accessor.allLineLocations().firstOrNull() val line = accessor.safeAllLineLocations().firstOrNull()
if (line != null) { if (line != null) {
val request = manager.createBreakpointRequest(this, line) val request = manager.createBreakpointRequest(this, line)
debugProcess.requestsManager.enableRequest(request) debugProcess.requestsManager.enableRequest(request)
@@ -9,6 +9,7 @@ import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.DebuggerManagerThreadImpl import com.intellij.debugger.engine.DebuggerManagerThreadImpl
import com.intellij.debugger.engine.events.DebuggerCommandImpl import com.intellij.debugger.engine.events.DebuggerCommandImpl
import com.intellij.debugger.impl.DebuggerContextImpl import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.intellij.debugger.jdi.LocalVariableProxyImpl import com.intellij.debugger.jdi.LocalVariableProxyImpl
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.sun.jdi.* import com.sun.jdi.*
@@ -28,6 +29,42 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import java.util.* import java.util.*
fun Method.safeAllLineLocations(): List<Location> {
return DebuggerUtilsEx.allLineLocations(this) ?: emptyList()
}
fun ReferenceType.safeAllLineLocations(): List<Location> {
return DebuggerUtilsEx.allLineLocations(this) ?: emptyList()
}
fun Method.safeLocationsOfLine(line: Int): List<Location> {
return try {
locationsOfLine(line)
} catch (e: AbsentInformationException) {
emptyList()
}
}
fun Method.safeVariables(): List<LocalVariable> {
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<LocalVariableProxyImpl>): Boolean { fun isInsideInlineFunctionBody(visibleVariables: List<LocalVariableProxyImpl>): Boolean {
return visibleVariables.any { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) } 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() { private fun createVisibleVariables() {
if (visibleVariables == null) { if (visibleVariables == null) {
val allVariables = location.method().variables() val allVariables = location.method().safeVariables()
val map = HashMap<String, LocalVariable>(allVariables.size) val map = HashMap<String, LocalVariable>(allVariables.size)
for (allVariable in allVariables) { for (allVariable in allVariables) {
@@ -176,13 +213,7 @@ fun isOnSuspendReturnOrReenter(location: Location): Boolean {
} }
fun isLastLineLocationInMethod(location: Location): Boolean { fun isLastLineLocationInMethod(location: Location): Boolean {
val allLineLocations = try { val knownLines = location.method().safeAllLineLocations().map { it.lineNumber() }.filter { it != -1 }
location.method().allLineLocations()
} catch (e: AbsentInformationException) {
return false
}
val knownLines = allLineLocations.map { it.lineNumber() }.filter { it != -1 }
if (knownLines.isEmpty()) { if (knownLines.isEmpty()) {
return false return false
} }
@@ -191,7 +222,7 @@ fun isLastLineLocationInMethod(location: Location): Boolean {
} }
fun isOneLineMethod(location: Location): Boolean { fun isOneLineMethod(location: Location): Boolean {
val allLineLocations = location.method().allLineLocations() val allLineLocations = location.method().safeAllLineLocations()
val firstLine = allLineLocations.firstOrNull()?.lineNumber() val firstLine = allLineLocations.firstOrNull()?.lineNumber()
val lastLine = allLineLocations.lastOrNull()?.lineNumber() val lastLine = allLineLocations.lastOrNull()?.lineNumber()
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.debugger.filter
import com.intellij.debugger.engine.SyntheticTypeComponentProvider import com.intellij.debugger.engine.SyntheticTypeComponentProvider
import com.sun.jdi.* import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.safeAllLineLocations
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
@@ -71,7 +72,7 @@ class KotlinSyntheticTypeComponentProvider: SyntheticTypeComponentProvider {
} }
private fun Method.isDelegateToDefaultInterfaceImpl(): Boolean { private fun Method.isDelegateToDefaultInterfaceImpl(): Boolean {
if (allLineLocations().size != 1) return false if (safeAllLineLocations().size != 1) return false
if (!virtualMachine().canGetBytecodes()) return false if (!virtualMachine().canGetBytecodes()) return false
if (!hasOnlyInvokeStatic(this)) return false if (!hasOnlyInvokeStatic(this)) return false
@@ -22,10 +22,7 @@ import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiExpression import com.intellij.psi.PsiExpression
import com.sun.jdi.Field import com.sun.jdi.*
import com.sun.jdi.Method
import com.sun.jdi.ObjectReference
import com.sun.jdi.Value
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -76,6 +73,13 @@ class DelegatedPropertyFieldDescriptor(
} }
override fun getDeclaredType(): String? { 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 "<unknown>"
}
return returnType?.name()
} }
} }
@@ -409,7 +409,7 @@ fun getStepOverAction(
// this filter locations with too big code indexes manually // this filter locations with too big code indexes manually
val returnCodeIndex: Long = if (isDexDebug) { val returnCodeIndex: Long = if (isDexDebug) {
val method = location.method() val method = location.method()
val locationsOfLine = method.locationsOfLine(range.last) val locationsOfLine = method.safeLocationsOfLine(range.last)
if (locationsOfLine.isNotEmpty()) { if (locationsOfLine.isNotEmpty()) {
locationsOfLine.map { it.codeIndex() }.max() ?: -1L locationsOfLine.map { it.codeIndex() }.max() ?: -1L
} }
@@ -439,7 +439,7 @@ fun getStepOutAction(
): Action { ): Action {
val computedReferenceType = location.declaringType() ?: return Action.STEP_OUT() val computedReferenceType = location.declaringType() ?: return Action.STEP_OUT()
val locations = computedReferenceType.allLineLocations() val locations = computedReferenceType.safeAllLineLocations()
val nextLineLocations = locations val nextLineLocations = locations
.dropWhile { it != location } .dropWhile { it != location }
.drop(1) .drop(1)
@@ -531,8 +531,10 @@ private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? {
} }
private fun findReturnFromDexBytecode(method: Method): Long { private fun findReturnFromDexBytecode(method: Method): Long {
val methodLocations = method.allLineLocations() val methodLocations = method.safeAllLineLocations()
if (methodLocations.isEmpty()) return -1L if (methodLocations.isEmpty()) {
return -1L
}
var lastMethodCodeIndex = methodLocations.last().codeIndex() var lastMethodCodeIndex = methodLocations.last().codeIndex()
// Continue while it's possible to get location // Continue while it's possible to get location