Minor: Reformat evaluator code
This commit is contained in:
-1
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
|
||||
+6
-4
@@ -168,10 +168,12 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
else
|
||||
debuggerContext.frameProxy?.stackFrame
|
||||
|
||||
val visibleVariables = frame?.let {
|
||||
val values = it.getValues(it.visibleVariables())
|
||||
val visibleVariables = if (frame != null) {
|
||||
val values = frame.getValues(frame.visibleVariables())
|
||||
values.filterValues { it != null }
|
||||
} ?: emptyMap()
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
|
||||
frameInfo = FrameInfo(frame?.thisObject(), visibleVariables)
|
||||
} catch (ignored: AbsentInformationException) {
|
||||
@@ -315,7 +317,7 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
// elementAt can be PsiWhiteSpace when codeFragment is created from line start offset (in case of first opening EE window)
|
||||
val lineStartOffset = if (elementAt is PsiWhiteSpace || elementAt is PsiComment) {
|
||||
PsiTreeUtil.skipSiblingsForward(elementAt, PsiWhiteSpace::class.java, PsiComment::class.java)?.textOffset
|
||||
?: elementAt.textOffset
|
||||
?: elementAt.textOffset
|
||||
} else {
|
||||
elementAt.textOffset
|
||||
}
|
||||
|
||||
+61
-47
@@ -59,31 +59,39 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
class KotlinDebuggerCaches(project: Project) {
|
||||
|
||||
private val cachedCompiledData = CachedValuesManager.getManager(project).createCachedValue(
|
||||
{
|
||||
CachedValueProvider.Result<MultiMap<String, CompiledDataDescriptor>>(
|
||||
MultiMap.create(), PsiModificationTracker.MODIFICATION_COUNT)
|
||||
}, false)
|
||||
{
|
||||
CachedValueProvider.Result<MultiMap<String, CompiledDataDescriptor>>(
|
||||
MultiMap.create(), PsiModificationTracker.MODIFICATION_COUNT
|
||||
)
|
||||
}, false
|
||||
)
|
||||
|
||||
private val cachedClassNames = CachedValuesManager.getManager(project).createCachedValue(
|
||||
{
|
||||
CachedValueProvider.Result<MutableMap<PsiElement, List<String>>>(
|
||||
ConcurrentHashMap<PsiElement, List<String>>(),
|
||||
PsiModificationTracker.MODIFICATION_COUNT)
|
||||
}, false)
|
||||
{
|
||||
CachedValueProvider.Result<MutableMap<PsiElement, List<String>>>(
|
||||
ConcurrentHashMap(),
|
||||
PsiModificationTracker.MODIFICATION_COUNT
|
||||
)
|
||||
}, false
|
||||
)
|
||||
|
||||
private val cachedTypeMappers = CachedValuesManager.getManager(project).createCachedValue(
|
||||
{
|
||||
CachedValueProvider.Result<MutableMap<PsiElement, KotlinTypeMapper>>(
|
||||
ConcurrentHashMap<PsiElement, KotlinTypeMapper>(),
|
||||
PsiModificationTracker.MODIFICATION_COUNT)
|
||||
}, false)
|
||||
{
|
||||
CachedValueProvider.Result<MutableMap<PsiElement, KotlinTypeMapper>>(
|
||||
ConcurrentHashMap(),
|
||||
PsiModificationTracker.MODIFICATION_COUNT
|
||||
)
|
||||
}, false
|
||||
)
|
||||
|
||||
private val debugInfoCache = CachedValuesManager.getManager(project).createCachedValue(
|
||||
{
|
||||
CachedValueProvider.Result(
|
||||
createWeakBytecodeDebugInfoStorage(),
|
||||
PsiModificationTracker.MODIFICATION_COUNT)
|
||||
}, false)
|
||||
{
|
||||
CachedValueProvider.Result(
|
||||
createWeakBytecodeDebugInfoStorage(),
|
||||
PsiModificationTracker.MODIFICATION_COUNT
|
||||
)
|
||||
}, false
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getLogger(KotlinDebuggerCaches::class.java)!!
|
||||
@@ -91,10 +99,10 @@ class KotlinDebuggerCaches(project: Project) {
|
||||
fun getInstance(project: Project) = ServiceManager.getService(project, KotlinDebuggerCaches::class.java)!!
|
||||
|
||||
fun getOrCreateCompiledData(
|
||||
codeFragment: KtCodeFragment,
|
||||
sourcePosition: SourcePosition,
|
||||
evaluationContext: EvaluationContextImpl,
|
||||
create: (KtCodeFragment, SourcePosition) -> CompiledDataDescriptor
|
||||
codeFragment: KtCodeFragment,
|
||||
sourcePosition: SourcePosition,
|
||||
evaluationContext: EvaluationContextImpl,
|
||||
create: (KtCodeFragment, SourcePosition) -> CompiledDataDescriptor
|
||||
): CompiledDataDescriptor {
|
||||
val evaluateExpressionCache = getInstance(codeFragment.project)
|
||||
|
||||
@@ -157,8 +165,7 @@ class KotlinDebuggerCaches(project: Project) {
|
||||
|
||||
val newValue = if (!isInLibrary) {
|
||||
createTypeMapperForSourceFile(file)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val element = getElementToCreateTypeMapperForLibraryFile(psiElement)
|
||||
createTypeMapperForLibraryFile(element, file)
|
||||
}
|
||||
@@ -168,40 +175,42 @@ class KotlinDebuggerCaches(project: Project) {
|
||||
}
|
||||
|
||||
fun getOrReadDebugInfoFromBytecode(
|
||||
project: Project,
|
||||
jvmName: JvmClassName,
|
||||
file: VirtualFile): BytecodeDebugInfo? {
|
||||
project: Project,
|
||||
jvmName: JvmClassName,
|
||||
file: VirtualFile
|
||||
): BytecodeDebugInfo? {
|
||||
val cache = getInstance(project)
|
||||
return cache.debugInfoCache.value[BinaryCacheKey(project, jvmName, file)]
|
||||
}
|
||||
|
||||
private fun getElementToCreateTypeMapperForLibraryFile(element: PsiElement?) =
|
||||
runReadAction { element as? KtElement ?: PsiTreeUtil.getParentOfType(element, KtElement::class.java)!! }
|
||||
runReadAction { element as? KtElement ?: PsiTreeUtil.getParentOfType(element, KtElement::class.java)!! }
|
||||
|
||||
private fun createTypeMapperForLibraryFile(element: KtElement, file: KtFile): KotlinTypeMapper =
|
||||
runInReadActionWithWriteActionPriorityWithPCE {
|
||||
createTypeMapper(file, element.analyzeAndGetResult())
|
||||
}
|
||||
runInReadActionWithWriteActionPriorityWithPCE {
|
||||
createTypeMapper(file, element.analyzeAndGetResult())
|
||||
}
|
||||
|
||||
private fun createTypeMapperForSourceFile(file: KtFile): KotlinTypeMapper =
|
||||
runInReadActionWithWriteActionPriorityWithPCE {
|
||||
createTypeMapper(file, file.analyzeWithAllCompilerChecks().apply(AnalysisResult::throwIfError))
|
||||
}
|
||||
runInReadActionWithWriteActionPriorityWithPCE {
|
||||
createTypeMapper(file, file.analyzeWithAllCompilerChecks().apply(AnalysisResult::throwIfError))
|
||||
}
|
||||
|
||||
private fun createTypeMapper(file: KtFile, analysisResult: AnalysisResult): KotlinTypeMapper {
|
||||
val state = GenerationState.Builder(
|
||||
file.project,
|
||||
ClassBuilderFactories.THROW_EXCEPTION,
|
||||
analysisResult.moduleDescriptor,
|
||||
analysisResult.bindingContext,
|
||||
listOf(file),
|
||||
CompilerConfiguration.EMPTY
|
||||
file.project,
|
||||
ClassBuilderFactories.THROW_EXCEPTION,
|
||||
analysisResult.moduleDescriptor,
|
||||
analysisResult.bindingContext,
|
||||
listOf(file),
|
||||
CompilerConfiguration.EMPTY
|
||||
).build()
|
||||
state.beforeCompile()
|
||||
return state.typeMapper
|
||||
}
|
||||
|
||||
@TestOnly fun addTypeMapper(file: KtFile, typeMapper: KotlinTypeMapper) {
|
||||
@TestOnly
|
||||
fun addTypeMapper(file: KtFile, typeMapper: KotlinTypeMapper) {
|
||||
getInstance(file.project).cachedTypeMappers.value[file] = typeMapper
|
||||
}
|
||||
}
|
||||
@@ -216,7 +225,12 @@ class KotlinDebuggerCaches(project: Project) {
|
||||
|
||||
val thisDescriptor = value.asmType.getClassDescriptor(context.debugProcess.searchScope)
|
||||
val superClassDescriptor = jetType.constructor.declarationDescriptor as? ClassDescriptor
|
||||
return@all thisDescriptor != null && superClassDescriptor != null && runReadAction { DescriptorUtils.isSubclass(thisDescriptor, superClassDescriptor) }
|
||||
return@all thisDescriptor != null && superClassDescriptor != null && runReadAction {
|
||||
DescriptorUtils.isSubclass(
|
||||
thisDescriptor,
|
||||
superClassDescriptor
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +244,7 @@ class KotlinDebuggerCaches(project: Project) {
|
||||
data class Parameter(val callText: String, val type: KotlinType, val value: Value? = null, val error: EvaluateException? = null)
|
||||
|
||||
class ComputedClassNames(val classNames: List<String>, val shouldBeCached: Boolean) {
|
||||
@Suppress("FunctionName")
|
||||
companion object {
|
||||
val EMPTY = ComputedClassNames.Cached(emptyList())
|
||||
|
||||
@@ -242,8 +257,7 @@ class KotlinDebuggerCaches(project: Project) {
|
||||
fun distinct() = ComputedClassNames(classNames.distinct(), shouldBeCached)
|
||||
|
||||
operator fun plus(other: ComputedClassNames) = ComputedClassNames(
|
||||
classNames + other.classNames, shouldBeCached && other.shouldBeCached)
|
||||
classNames + other.classNames, shouldBeCached && other.shouldBeCached
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String?.toList() = if (this == null) emptyList() else listOf(this)
|
||||
}
|
||||
+115
-82
@@ -95,12 +95,11 @@ import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
|
||||
internal val THIS_NAME = "this"
|
||||
internal val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluator")
|
||||
internal val GENERATED_FUNCTION_NAME = "generated_for_debugger_fun"
|
||||
internal val GENERATED_CLASS_NAME = "Generated_for_debugger_class"
|
||||
internal const val GENERATED_FUNCTION_NAME = "generated_for_debugger_fun"
|
||||
internal const val GENERATED_CLASS_NAME = "Generated_for_debugger_class"
|
||||
|
||||
private val DEBUG_MODE = false
|
||||
private const val DEBUG_MODE = false
|
||||
|
||||
object KotlinEvaluationBuilder : EvaluatorBuilder {
|
||||
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
|
||||
@@ -117,17 +116,23 @@ object KotlinEvaluationBuilder : EvaluatorBuilder {
|
||||
val document = PsiDocumentManager.getInstance(file.project).getDocument(file)
|
||||
if (document == null || document.lineCount < position.line) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException(
|
||||
"Couldn't evaluate kotlin expression: breakpoint is placed outside the file. " +
|
||||
"It may happen when you've changed source file after starting a debug process.")
|
||||
"Couldn't evaluate kotlin expression: breakpoint is placed outside the file. " +
|
||||
"It may happen when you've changed source file after starting a debug process."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (codeFragment.context !is KtElement) {
|
||||
val attachments = arrayOf(attachmentByPsiFile(position.file),
|
||||
attachmentByPsiFile(codeFragment),
|
||||
Attachment("breakpoint.info", "line: ${position.line}"))
|
||||
val attachments = arrayOf(
|
||||
attachmentByPsiFile(position.file),
|
||||
attachmentByPsiFile(codeFragment),
|
||||
Attachment("breakpoint.info", "line: ${position.line}")
|
||||
)
|
||||
|
||||
LOG.error("Trying to evaluate ${codeFragment::class.java} with context ${codeFragment.context?.javaClass}", mergeAttachments(*attachments))
|
||||
LOG.error(
|
||||
"Trying to evaluate ${codeFragment::class.java} with context ${codeFragment.context?.javaClass}",
|
||||
mergeAttachments(*attachments)
|
||||
)
|
||||
throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression in this context")
|
||||
}
|
||||
|
||||
@@ -147,8 +152,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
|
||||
var isCompiledDataFromCache = true
|
||||
try {
|
||||
val compiledData = KotlinDebuggerCaches.getOrCreateCompiledData(codeFragment, sourcePosition, context) {
|
||||
fragment, position ->
|
||||
val compiledData = KotlinDebuggerCaches.getOrCreateCompiledData(codeFragment, sourcePosition, context) { fragment, position ->
|
||||
isCompiledDataFromCache = false
|
||||
extractAndCompile(fragment, position, context)
|
||||
}
|
||||
@@ -157,8 +161,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
|
||||
val result = if (classLoaderRef != null) {
|
||||
evaluateWithCompilation(context, compiledData, classLoaderRef) ?: runEval4j(context, compiledData, classLoaderRef)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
runEval4j(context, compiledData, classLoaderRef)
|
||||
}
|
||||
|
||||
@@ -172,32 +175,33 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
catch (e: EvaluateException) {
|
||||
} catch (e: EvaluateException) {
|
||||
throw e
|
||||
}
|
||||
catch (e: ProcessCanceledException) {
|
||||
} catch (e: ProcessCanceledException) {
|
||||
exception(e)
|
||||
}
|
||||
catch (e: Eval4JInterpretingException) {
|
||||
} catch (e: Eval4JInterpretingException) {
|
||||
exception(e.cause)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
val isSpecialException = isSpecialException(e)
|
||||
if (isSpecialException) {
|
||||
exception(e)
|
||||
}
|
||||
|
||||
val text = runReadAction { codeFragment.context?.text ?: "null" }
|
||||
val attachments = arrayOf(attachmentByPsiFile(sourcePosition.file),
|
||||
attachmentByPsiFile(codeFragment),
|
||||
Attachment("breakpoint.info", "line: ${runReadAction { sourcePosition.line }}"),
|
||||
Attachment("context.info", text))
|
||||
val attachments = arrayOf(
|
||||
attachmentByPsiFile(sourcePosition.file),
|
||||
attachmentByPsiFile(codeFragment),
|
||||
Attachment("breakpoint.info", "line: ${runReadAction { sourcePosition.line }}"),
|
||||
Attachment("context.info", text)
|
||||
)
|
||||
|
||||
LOG.error(LogMessageEx.createEvent(
|
||||
LOG.error(
|
||||
LogMessageEx.createEvent(
|
||||
"Couldn't evaluate expression",
|
||||
ExceptionUtil.getThrowableText(e),
|
||||
mergeAttachments(*attachments)))
|
||||
mergeAttachments(*attachments)
|
||||
)
|
||||
)
|
||||
|
||||
val cause = if (e.message != null) ": ${e.message}" else ""
|
||||
exception("An exception occurs during Evaluate Expression Action $cause")
|
||||
@@ -223,7 +227,11 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun extractAndCompile(codeFragment: KtCodeFragment, sourcePosition: SourcePosition, context: EvaluationContextImpl): CompiledDataDescriptor {
|
||||
private fun extractAndCompile(
|
||||
codeFragment: KtCodeFragment,
|
||||
sourcePosition: SourcePosition,
|
||||
context: EvaluationContextImpl
|
||||
): CompiledDataDescriptor {
|
||||
var bindingContext = codeFragment.checkForErrors().bindingContext
|
||||
|
||||
if (codeFragment.wrapToStringIfNeeded(bindingContext)) {
|
||||
@@ -234,7 +242,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
val variablesCrossingInlineBounds = ScopeCheckerForEvaluator.checkScopes(bindingContext, codeFragment)
|
||||
|
||||
val extractionResult = getFunctionForExtractedFragment(codeFragment, sourcePosition.file, sourcePosition.line)
|
||||
?: throw IllegalStateException("Code fragment cannot be extracted to function: ${codeFragment.text}")
|
||||
?: throw IllegalStateException("Code fragment cannot be extracted to function: ${codeFragment.text}")
|
||||
val (parametersDescriptor, extractedFunction) = try {
|
||||
extractionResult.getParametersForDebugger(codeFragment, context) to extractionResult.declaration as KtNamedFunction
|
||||
} finally {
|
||||
@@ -253,6 +261,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("Output file generated: ${file.relativePath}")
|
||||
}
|
||||
@Suppress("ConstantConditionIf")
|
||||
if (DEBUG_MODE) {
|
||||
println(file.asText())
|
||||
}
|
||||
@@ -297,7 +306,8 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
|
||||
private val CompiledDataDescriptor.mainClass
|
||||
get() = classes.firstOrNull { it.isMainClass() } ?: error(
|
||||
"Can't find main class for " + sourcePosition.elementAt.getParentOfType<KtDeclaration>(strict = false))
|
||||
"Can't find main class for " + sourcePosition.elementAt.getParentOfType<KtDeclaration>(strict = false)
|
||||
)
|
||||
|
||||
private fun evaluateWithCompilation(
|
||||
context: EvaluationContextImpl,
|
||||
@@ -331,12 +341,12 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
|
||||
val argumentTypes = Type.getArgumentTypes(methodToInvoke.desc)
|
||||
val args = context.getArgumentsForEvaluation(compiledData.parameters, argumentTypes, compiledData)
|
||||
.zip(argumentTypes)
|
||||
.map { (value, type) ->
|
||||
// Make argument type classes prepared for sure
|
||||
eval.loadClassByName(type.className, classLoader)
|
||||
boxOrUnboxArgumentIfNeeded(eval, value, type).asJdiValue(vm, type)
|
||||
}
|
||||
.zip(argumentTypes)
|
||||
.map { (value, type) ->
|
||||
// Make argument type classes prepared for sure
|
||||
eval.loadClassByName(type.className, classLoader)
|
||||
boxOrUnboxArgumentIfNeeded(eval, value, type).asJdiValue(vm, type)
|
||||
}
|
||||
|
||||
|
||||
mainClass.invokeMethod(thread, mainClass.methods().single(), args, invokePolicy)
|
||||
@@ -359,8 +369,15 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
val mainClassBytecode = compiledData.mainClass.bytes
|
||||
|
||||
ClassReader(mainClassBytecode).accept(object : ClassVisitor(API_VERSION) {
|
||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
// Maybe just take the single method from the class, as it is done in 'evaluateWithCompilation'
|
||||
@Suppress("ConvertToStringTemplate")
|
||||
if (name == GENERATED_FUNCTION_NAME || name.startsWith(GENERATED_FUNCTION_NAME + "-")) {
|
||||
val argumentTypes = Type.getArgumentTypes(desc)
|
||||
val args = context.getArgumentsForEvaluation(compiledData.parameters, argumentTypes, compiledData)
|
||||
@@ -368,15 +385,25 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
return object : MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions) {
|
||||
override fun visitEnd() {
|
||||
virtualMachine.executeWithBreakpointsDisabled {
|
||||
val eval = JDIEval(virtualMachine,
|
||||
classLoader ?: context.classLoader,
|
||||
context.suspendContext.thread?.threadReference!!,
|
||||
context.suspendContext.getInvokePolicy())
|
||||
val eval = JDIEval(
|
||||
virtualMachine,
|
||||
classLoader ?: context.classLoader,
|
||||
context.suspendContext.thread?.threadReference!!,
|
||||
context.suspendContext.getInvokePolicy()
|
||||
)
|
||||
|
||||
resultValue = interpreterLoop(
|
||||
this,
|
||||
makeInitialFrame(
|
||||
this,
|
||||
makeInitialFrame(this, args.zip(argumentTypes).map { boxOrUnboxArgumentIfNeeded(eval, it.first, it.second) }),
|
||||
eval
|
||||
args.zip(argumentTypes).map {
|
||||
boxOrUnboxArgumentIfNeeded(
|
||||
eval,
|
||||
it.first,
|
||||
it.second
|
||||
)
|
||||
}),
|
||||
eval
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -387,7 +414,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
}, 0)
|
||||
|
||||
return resultValue ?: throw IllegalStateException("resultValue is null: cannot find method " + GENERATED_FUNCTION_NAME)
|
||||
return resultValue ?: throw IllegalStateException("resultValue is null: cannot find method $GENERATED_FUNCTION_NAME")
|
||||
}
|
||||
|
||||
private inline fun <T> VirtualMachine.executeWithBreakpointsDisabled(block: () -> T): T {
|
||||
@@ -410,8 +437,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
if (parameterType == unboxedType) {
|
||||
return eval.unboxType(argumentValue, parameterType)
|
||||
}
|
||||
}
|
||||
catch(ignored: UnsupportedOperationException) {
|
||||
} catch (ignored: UnsupportedOperationException) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +491,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
override fun visitProperty(property: KtProperty) {
|
||||
val value = property.getUserData(KotlinCodeFragmentFactory.LABEL_VARIABLE_VALUE_KEY)
|
||||
if (value != null) {
|
||||
valuesForLabels.put(property.name?.quoteIfNeeded()!!, value.asValue())
|
||||
valuesForLabels[property.name?.quoteIfNeeded()!!] = value.asValue()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -474,7 +500,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
val parameters = mutableListOf<Parameter>()
|
||||
val receiver = config.descriptor.receiverParameter
|
||||
if (receiver != null) {
|
||||
parameters += Parameter(THIS_NAME + "@" + config.descriptor.name, receiver.getParameterType(true))
|
||||
parameters += Parameter(AsmUtil.THIS + "@" + config.descriptor.name, receiver.getParameterType(true))
|
||||
}
|
||||
|
||||
for (param in config.descriptor.parameters) {
|
||||
@@ -537,10 +563,10 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
|
||||
private fun createClassFileFactory(
|
||||
codeFragment: KtCodeFragment,
|
||||
extractedFunction: KtNamedFunction,
|
||||
context: EvaluationContextImpl,
|
||||
parameters: List<Parameter>
|
||||
codeFragment: KtCodeFragment,
|
||||
extractedFunction: KtNamedFunction,
|
||||
context: EvaluationContextImpl,
|
||||
parameters: List<Parameter>
|
||||
): ClassFileFactory {
|
||||
return runReadAction {
|
||||
val fileForDebugger = createFileForDebugger(codeFragment, extractedFunction)
|
||||
@@ -548,22 +574,28 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
LOG.debug("File for eval4j:\n${runReadAction { fileForDebugger.text }}")
|
||||
}
|
||||
|
||||
val (bindingContext, moduleDescriptor, files) = fileForDebugger.checkForErrors(true, codeFragment.getContextContainingFile())
|
||||
val (bindingContext, moduleDescriptor, files) = fileForDebugger.checkForErrors(
|
||||
true,
|
||||
codeFragment.getContextContainingFile()
|
||||
)
|
||||
|
||||
val generateClassFilter = object : GenerationState.GenerateClassFilter() {
|
||||
override fun shouldGeneratePackagePart(ktFile: KtFile) = ktFile == fileForDebugger
|
||||
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject) = true
|
||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject) = processingClassOrObject.containingKtFile == fileForDebugger
|
||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject) =
|
||||
processingClassOrObject.containingKtFile == fileForDebugger
|
||||
|
||||
override fun shouldGenerateScript(script: KtScript) = false
|
||||
}
|
||||
|
||||
@Suppress("ConstantConditionIf")
|
||||
val state = GenerationState.Builder(
|
||||
fileForDebugger.project,
|
||||
if (!DEBUG_MODE) ClassBuilderFactories.BINARIES else ClassBuilderFactories.TEST,
|
||||
moduleDescriptor,
|
||||
bindingContext,
|
||||
files,
|
||||
CompilerConfiguration.EMPTY
|
||||
fileForDebugger.project,
|
||||
if (!DEBUG_MODE) ClassBuilderFactories.BINARIES else ClassBuilderFactories.TEST,
|
||||
moduleDescriptor,
|
||||
bindingContext,
|
||||
files,
|
||||
CompilerConfiguration.EMPTY
|
||||
).generateDeclaredClassFilter(generateClassFilter).build()
|
||||
|
||||
val variableFinder = VariableFinder.instance(context) ?: error("No stack frame available")
|
||||
@@ -577,15 +609,16 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
|
||||
val valueParameters = extractedFunction.valueParameters
|
||||
var paramIndex = 0
|
||||
for (param in parameters) {
|
||||
val valueParameter = valueParameters[paramIndex++]
|
||||
for ((paramIndex, param) in parameters.withIndex()) {
|
||||
val valueParameter = valueParameters[paramIndex]
|
||||
|
||||
val paramRef = valueParameter.typeReference
|
||||
if (paramRef == null) {
|
||||
LOG.error("Each parameter for extracted function should have a type reference",
|
||||
Attachment("codeFragment.txt", codeFragment.text),
|
||||
Attachment("extractedFunction.txt", extractedFunction.text))
|
||||
LOG.error(
|
||||
"Each parameter for extracted function should have a type reference",
|
||||
Attachment("codeFragment.txt", codeFragment.text),
|
||||
Attachment("extractedFunction.txt", extractedFunction.text)
|
||||
)
|
||||
|
||||
exception("An exception occurs during Evaluate Expression Action")
|
||||
}
|
||||
@@ -631,8 +664,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
return runInReadActionWithWriteActionPriorityWithPCE {
|
||||
try {
|
||||
AnalyzingUtils.checkForSyntacticErrors(this)
|
||||
}
|
||||
catch (e: IllegalArgumentException) {
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException(e.message)
|
||||
}
|
||||
|
||||
@@ -655,18 +687,21 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
if (analyzeInlineFunctions) {
|
||||
val (newBindingContext, files) = DebuggerUtils.analyzeInlinedFunctions(resolutionFacade, this, false)
|
||||
ExtendedAnalysisResult(newBindingContext, analysisResult.moduleDescriptor, files)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ExtendedAnalysisResult(bindingContext, analysisResult.moduleDescriptor, Collections.singletonList(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class ExtendedAnalysisResult(val bindingContext: BindingContext, val moduleDescriptor: ModuleDescriptor, val files: List<KtFile>)
|
||||
private data class ExtendedAnalysisResult(
|
||||
val bindingContext: BindingContext,
|
||||
val moduleDescriptor: ModuleDescriptor,
|
||||
val files: List<KtFile>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val template = """
|
||||
private const val template = """
|
||||
@file:kotlin.jvm.JvmName("$GENERATED_CLASS_NAME")
|
||||
!PACKAGE!
|
||||
|
||||
@@ -675,24 +710,22 @@ private val template = """
|
||||
!FUNCTION!
|
||||
"""
|
||||
|
||||
private fun createFileForDebugger(codeFragment: KtCodeFragment,
|
||||
extractedFunction: KtNamedFunction
|
||||
private fun createFileForDebugger(
|
||||
codeFragment: KtCodeFragment,
|
||||
extractedFunction: KtNamedFunction
|
||||
): KtFile {
|
||||
val containingContextFile = codeFragment.getContextContainingFile()
|
||||
val importsFromContextFile = containingContextFile?.importList?.let { it.text + "\n" } ?: ""
|
||||
|
||||
var fileText = template.replace(
|
||||
"!IMPORT_LIST!",
|
||||
importsFromContextFile + codeFragment.importsToString().split(KtCodeFragment.IMPORT_SEPARATOR).joinToString("\n")
|
||||
"!IMPORT_LIST!",
|
||||
importsFromContextFile + codeFragment.importsToString().split(KtCodeFragment.IMPORT_SEPARATOR).joinToString("\n")
|
||||
)
|
||||
|
||||
val packageFromContextFile = containingContextFile?.packageFqName?.let {
|
||||
if (!it.isRoot) "package ${it.quoteSegmentsIfNeeded()}" else ""
|
||||
} ?: ""
|
||||
fileText = fileText.replace("!PACKAGE!", packageFromContextFile)
|
||||
|
||||
val extractedFunctionText = extractedFunction.text
|
||||
assert(extractedFunctionText != null) { "Text of extracted function shouldn't be null" }
|
||||
fileText = fileText.replace("!FUNCTION!", extractedFunction.text!!)
|
||||
|
||||
val jetFile = codeFragment.createKtFile("debugFile.kt", fileText)
|
||||
@@ -717,7 +750,7 @@ private fun PsiElement.createKtFile(fileName: String, fileText: String): KtFile
|
||||
val virtualFile = LightVirtualFile(fileName, KotlinLanguage.INSTANCE, fileText)
|
||||
virtualFile.charset = CharsetToolkit.UTF8_CHARSET
|
||||
val jetFile = (PsiFileFactory.getInstance(project) as PsiFileFactoryImpl)
|
||||
.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile
|
||||
.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile
|
||||
jetFile.analysisContext = this
|
||||
return jetFile
|
||||
}
|
||||
@@ -733,7 +766,7 @@ fun Type.getClassDescriptor(scope: GlobalSearchScope): ClassDescriptor? {
|
||||
|
||||
// TODO: use the correct built-ins from the module instead of DefaultBuiltIns here
|
||||
JavaToKotlinClassMap.mapJavaToKotlin(jvmName)?.let(
|
||||
DefaultBuiltIns.Instance.builtInsModule::findClassAcrossModuleDependencies
|
||||
DefaultBuiltIns.Instance.builtInsModule::findClassAcrossModuleDependencies
|
||||
)?.let { return it }
|
||||
|
||||
return runReadAction {
|
||||
|
||||
+13
-16
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||
|
||||
import com.intellij.debugger.DebuggerBundle
|
||||
import com.intellij.debugger.DebuggerInvocationUtil
|
||||
import com.intellij.debugger.EvaluatingComputable
|
||||
import com.intellij.debugger.engine.ContextUtil
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
@@ -41,22 +40,19 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type as AsmType
|
||||
|
||||
abstract class KotlinRuntimeTypeEvaluator(
|
||||
editor: Editor?,
|
||||
expression: KtExpression,
|
||||
context: DebuggerContextImpl,
|
||||
indicator: ProgressIndicator
|
||||
editor: Editor?,
|
||||
expression: KtExpression,
|
||||
context: DebuggerContextImpl,
|
||||
indicator: ProgressIndicator
|
||||
) : EditorEvaluationCommand<KotlinType>(editor, expression, context, indicator) {
|
||||
|
||||
override fun threadAction() {
|
||||
var type: KotlinType? = null
|
||||
try {
|
||||
type = evaluate()
|
||||
}
|
||||
catch (ignored: ProcessCanceledException) {
|
||||
}
|
||||
catch (ignored: EvaluateException) {
|
||||
}
|
||||
finally {
|
||||
} catch (ignored: ProcessCanceledException) {
|
||||
} catch (ignored: EvaluateException) {
|
||||
} finally {
|
||||
typeCalculationFinished(type)
|
||||
}
|
||||
}
|
||||
@@ -66,11 +62,12 @@ abstract class KotlinRuntimeTypeEvaluator(
|
||||
override fun evaluate(evaluationContext: EvaluationContextImpl): KotlinType? {
|
||||
val project = evaluationContext.project
|
||||
|
||||
val evaluator = DebuggerInvocationUtil.commitAndRunReadAction<ExpressionEvaluator>(project, EvaluatingComputable {
|
||||
val codeFragment = KtPsiFactory(myElement.project).createExpressionCodeFragment(
|
||||
myElement.text, myElement.containingFile.context)
|
||||
KotlinEvaluationBuilder.build(codeFragment, ContextUtil.getSourcePosition(evaluationContext))
|
||||
})
|
||||
val evaluator = DebuggerInvocationUtil.commitAndRunReadAction<ExpressionEvaluator>(project) {
|
||||
val codeFragment = KtPsiFactory(myElement.project).createExpressionCodeFragment(
|
||||
myElement.text, myElement.containingFile.context
|
||||
)
|
||||
KotlinEvaluationBuilder.build(codeFragment, ContextUtil.getSourcePosition(evaluationContext))
|
||||
}
|
||||
|
||||
val value = evaluator.evaluate(evaluationContext)
|
||||
if (value != null) {
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ object ScopeCheckerForEvaluator {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
currentParent = when (currentParent) {
|
||||
is KtCodeFragment -> currentParent.context
|
||||
else -> currentParent.parent
|
||||
|
||||
+4
-4
@@ -29,15 +29,15 @@ abstract class AbstractAndroidClassLoadingAdapter : ClassLoadingAdapter {
|
||||
protected fun wrapToByteBuffer(bytes: ArrayReference, context: EvaluationContext, process: DebugProcessImpl): ObjectReference {
|
||||
val byteBufferClass = process.findClass(context, "java.nio.ByteBuffer", context.classLoader) as ClassType
|
||||
val wrapMethod = byteBufferClass.concreteMethodByName("wrap", "([B)Ljava/nio/ByteBuffer;")
|
||||
?: error("'wrap' method not found")
|
||||
?: error("'wrap' method not found")
|
||||
|
||||
return process.invokeMethod(context, byteBufferClass, wrapMethod, listOf(bytes)) as ObjectReference
|
||||
}
|
||||
|
||||
protected fun DebugProcessImpl.tryLoadClass(
|
||||
context: EvaluationContextImpl,
|
||||
fqName: String,
|
||||
classLoader: ClassLoaderReference?
|
||||
context: EvaluationContextImpl,
|
||||
fqName: String,
|
||||
classLoader: ClassLoaderReference?
|
||||
): ReferenceType? {
|
||||
return try {
|
||||
loadClass(context, fqName, classLoader)
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@ import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
|
||||
interface AndroidDexer {
|
||||
companion object : ProjectExtensionDescriptor<AndroidDexer>(
|
||||
"org.jetbrains.kotlin.androidDexer", AndroidDexer::class.java)
|
||||
"org.jetbrains.kotlin.androidDexer", AndroidDexer::class.java
|
||||
)
|
||||
|
||||
fun dex(classes: Collection<ClassToLoad>): ByteArray?
|
||||
}
|
||||
+11
-7
@@ -29,11 +29,12 @@ class AndroidOClassLoadingAdapter : AbstractAndroidClassLoadingAdapter() {
|
||||
}
|
||||
|
||||
private fun resolveClassLoaderClass(context: EvaluationContextImpl): ClassType? {
|
||||
try {
|
||||
return context.debugProcess.tryLoadClass(
|
||||
context, "dalvik.system.InMemoryDexClassLoader", context.classLoader) as? ClassType
|
||||
return try {
|
||||
context.debugProcess.tryLoadClass(
|
||||
context, "dalvik.system.InMemoryDexClassLoader", context.classLoader
|
||||
) as? ClassType
|
||||
} catch (e: EvaluateException) {
|
||||
return null
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +42,17 @@ class AndroidOClassLoadingAdapter : AbstractAndroidClassLoadingAdapter() {
|
||||
val process = context.debugProcess
|
||||
val inMemoryClassLoaderClass = resolveClassLoaderClass(context) ?: error("InMemoryDexClassLoader class not found")
|
||||
val constructorMethod = inMemoryClassLoaderClass.concreteMethodByName(
|
||||
JVMNameUtil.CONSTRUCTOR_NAME, "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V") ?: error("Constructor method not found")
|
||||
JVMNameUtil.CONSTRUCTOR_NAME, "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"
|
||||
) ?: error("Constructor method not found")
|
||||
|
||||
val dexBytes = dex(context, classes) ?: error("Can't dex classes")
|
||||
val dexBytesMirror = mirrorOfByteArray(dexBytes, context, process)
|
||||
val dexByteBuffer = wrapToByteBuffer(dexBytesMirror, context, process)
|
||||
|
||||
val newClassLoader = process.newInstance(context, inMemoryClassLoaderClass, constructorMethod,
|
||||
listOf(dexByteBuffer, context.classLoader))
|
||||
val newClassLoader = process.newInstance(
|
||||
context, inMemoryClassLoaderClass, constructorMethod,
|
||||
listOf(dexByteBuffer, context.classLoader)
|
||||
)
|
||||
|
||||
DebuggerUtilsEx.keep(newClassLoader, context)
|
||||
|
||||
|
||||
+28
-28
@@ -73,46 +73,44 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
|
||||
|
||||
val classLoader = try {
|
||||
ClassLoadingUtils.getClassLoader(context, process)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
throw EvaluateException("Error creating evaluation class loader: " + e, e)
|
||||
} catch (e: Exception) {
|
||||
throw EvaluateException("Error creating evaluation class loader: $e", e)
|
||||
}
|
||||
|
||||
val debugProcessVersionString = process.virtualMachineProxy.version()
|
||||
val debugProcessVersion = JavaSdkVersion.fromVersionString(debugProcessVersionString)
|
||||
?: throw EvaluateException("Unable to parse java version from $debugProcessVersionString.")
|
||||
?: throw EvaluateException("Unable to parse java version from $debugProcessVersionString.")
|
||||
|
||||
val ideaJavaVersion = JavaSdkVersion.fromVersionString(SystemInfo.JAVA_RUNTIME_VERSION)
|
||||
?: throw EvaluateException("Unable to parse java version from ${SystemInfo.JAVA_RUNTIME_VERSION}.")
|
||||
?: throw EvaluateException("Unable to parse java version from ${SystemInfo.JAVA_RUNTIME_VERSION}.")
|
||||
|
||||
if (!ideaJavaVersion.isAtLeast(debugProcessVersion)) {
|
||||
throw EvaluateException(
|
||||
"Unable to compile for target level ${debugProcessVersion.description}. " +
|
||||
"Need to run IDEA on java version at least $debugProcessVersion, " +
|
||||
"currently running on $ideaJavaVersion")
|
||||
"Unable to compile for target level ${debugProcessVersion.description}. " +
|
||||
"Need to run IDEA on java version at least $debugProcessVersion, " +
|
||||
"currently running on $ideaJavaVersion"
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
defineClasses(classes, context, process, classLoader)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
throw EvaluateException("Error during classes definition " + e, e)
|
||||
} catch (e: Exception) {
|
||||
throw EvaluateException("Error during classes definition $e", e)
|
||||
}
|
||||
|
||||
return classLoader
|
||||
}
|
||||
|
||||
private fun defineClasses(
|
||||
classes: Collection<ClassToLoad>,
|
||||
context: EvaluationContextImpl,
|
||||
process: DebugProcessImpl,
|
||||
classLoader: ClassLoaderReference
|
||||
classes: Collection<ClassToLoad>,
|
||||
context: EvaluationContextImpl,
|
||||
process: DebugProcessImpl,
|
||||
classLoader: ClassLoaderReference
|
||||
) {
|
||||
val classesToLoad = if (classes.size == 1) {
|
||||
// No need in loading lambda superclass if there're no lambdas
|
||||
classes
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val lambdaSuperclasses = LAMBDA_SUPERCLASSES.map {
|
||||
ClassToLoad(it.name, it.name.replace('.', '/') + ".class", it.bytes)
|
||||
}
|
||||
@@ -125,12 +123,12 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
fun defineClass(
|
||||
name: String,
|
||||
bytes: ByteArray,
|
||||
context: EvaluationContextImpl,
|
||||
process: DebugProcessImpl,
|
||||
classLoader: ClassLoaderReference
|
||||
private fun defineClass(
|
||||
name: String,
|
||||
bytes: ByteArray,
|
||||
context: EvaluationContextImpl,
|
||||
process: DebugProcessImpl,
|
||||
classLoader: ClassLoaderReference
|
||||
) {
|
||||
try {
|
||||
val vm = process.virtualMachineProxy
|
||||
@@ -138,13 +136,15 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
|
||||
val defineMethod = classLoaderType.concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;")
|
||||
val nameObj = vm.mirrorOf(name)
|
||||
|
||||
// Still actual for older platform versions
|
||||
@Suppress("DEPRECATION")
|
||||
DebuggerUtilsEx.keep(nameObj, context)
|
||||
|
||||
process.invokeMethod(
|
||||
context, classLoader, defineMethod,
|
||||
listOf(nameObj, mirrorOfByteArray(bytes, context, process), vm.mirrorOf(0), vm.mirrorOf(bytes.size)))
|
||||
}
|
||||
catch (e: Exception) {
|
||||
context, classLoader, defineMethod,
|
||||
listOf(nameObj, mirrorOfByteArray(bytes, context, process), vm.mirrorOf(0), vm.mirrorOf(bytes.size))
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw EvaluateException("Error during class $name definition: $e", e)
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
|
||||
private class ClassBytes(val name: String) {
|
||||
val bytes: ByteArray by lazy {
|
||||
val inputStream = this::class.java.classLoader.getResourceAsStream(name.replace('.', '/') + ".class")
|
||||
?: throw EvaluateException("Couldn't find $name class in current class loader")
|
||||
?: throw EvaluateException("Couldn't find $name class in current class loader")
|
||||
|
||||
inputStream.use {
|
||||
it.readBytes()
|
||||
|
||||
+43
-34
@@ -44,26 +44,31 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST
|
||||
|
||||
fun getFunctionForExtractedFragment(
|
||||
codeFragment: KtCodeFragment,
|
||||
breakpointFile: PsiFile,
|
||||
breakpointLine: Int
|
||||
codeFragment: KtCodeFragment,
|
||||
breakpointFile: PsiFile,
|
||||
breakpointLine: Int
|
||||
): ExtractionResult? {
|
||||
|
||||
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult, tmpFile: KtFile): String {
|
||||
if (ApplicationManager.getApplication().isInternal) {
|
||||
val attachments = arrayOf(attachmentByPsiFile(tmpFile),
|
||||
attachmentByPsiFile(breakpointFile),
|
||||
attachmentByPsiFile(codeFragment),
|
||||
Attachment("breakpoint.info", "line: $breakpointLine"),
|
||||
Attachment("context.info", codeFragment.context?.text ?: "null"),
|
||||
Attachment("errors.info", analysisResult.messages.joinToString("\n") { "$it: ${it.renderMessage()}" }))
|
||||
LOG.error(LogMessageEx.createEvent(
|
||||
val attachments = arrayOf(
|
||||
attachmentByPsiFile(tmpFile),
|
||||
attachmentByPsiFile(breakpointFile),
|
||||
attachmentByPsiFile(codeFragment),
|
||||
Attachment("breakpoint.info", "line: $breakpointLine"),
|
||||
Attachment("context.info", codeFragment.context?.text ?: "null"),
|
||||
Attachment("errors.info", analysisResult.messages.joinToString("\n") { "$it: ${it.renderMessage()}" })
|
||||
)
|
||||
LOG.error(
|
||||
LogMessageEx.createEvent(
|
||||
"Internal error during evaluate expression",
|
||||
ExceptionUtil.getThrowableText(Throwable("Extract function fails with ${analysisResult.messages.joinToString { it.name }}")),
|
||||
mergeAttachments(*attachments)))
|
||||
mergeAttachments(*attachments)
|
||||
)
|
||||
)
|
||||
}
|
||||
return analysisResult.messages.joinToString(", ") { errorMessage ->
|
||||
val message = when(errorMessage) {
|
||||
val message = when (errorMessage) {
|
||||
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
|
||||
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.name}:$breakpointLine"
|
||||
ErrorMessage.SYNTAX_ERRORS -> "Cannot perform an action due to erroneous code"
|
||||
@@ -93,11 +98,13 @@ fun getFunctionForExtractedFragment(
|
||||
|
||||
val targetSibling = tmpFile.declarations.firstOrNull() ?: return null
|
||||
|
||||
val options = ExtractionOptions(inferUnitTypeForUnusedValues = false,
|
||||
enableListBoxing = true,
|
||||
allowSpecialClassNames = true,
|
||||
captureLocalFunctions = true,
|
||||
canWrapInWith = true)
|
||||
val options = ExtractionOptions(
|
||||
inferUnitTypeForUnusedValues = false,
|
||||
enableListBoxing = true,
|
||||
allowSpecialClassNames = true,
|
||||
captureLocalFunctions = true,
|
||||
canWrapInWith = true
|
||||
)
|
||||
val extractionData = ExtractionData(tmpFile, newDebugExpressions.toRange(), targetSibling, null, options)
|
||||
try {
|
||||
val analysisResult = extractionData.performAnalysis()
|
||||
@@ -107,12 +114,18 @@ fun getFunctionForExtractedFragment(
|
||||
|
||||
val validationResult = analysisResult.descriptor!!.validate()
|
||||
if (!validationResult.conflicts.isEmpty) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().joinToString(",") { it.text }}")
|
||||
throw EvaluateExceptionUtil.createEvaluateException(
|
||||
"Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().joinToString(
|
||||
","
|
||||
) { it.text }}"
|
||||
)
|
||||
}
|
||||
|
||||
val generatorOptions = ExtractionGeneratorOptions(inTempFile = true,
|
||||
dummyName = GENERATED_FUNCTION_NAME,
|
||||
allowExpressionBody = false)
|
||||
val generatorOptions = ExtractionGeneratorOptions(
|
||||
inTempFile = true,
|
||||
dummyName = GENERATED_FUNCTION_NAME,
|
||||
allowExpressionBody = false
|
||||
)
|
||||
return ExtractionGeneratorConfiguration(validationResult.descriptor, generatorOptions).generateDeclaration()
|
||||
} finally {
|
||||
Disposer.dispose(extractionData)
|
||||
@@ -154,7 +167,7 @@ private fun KtCodeFragment.clearContextElement() {
|
||||
}
|
||||
|
||||
private fun KtFile.findContextElement(): KtElement? {
|
||||
return this.findDescendantOfType { it.IS_CONTEXT_ELEMENT == true }
|
||||
return this.findDescendantOfType { it.IS_CONTEXT_ELEMENT }
|
||||
}
|
||||
|
||||
private var PsiElement.DEBUG_SMART_CAST: PsiElement? by CopyablePsiUserDataProperty(Key.create("DEBUG_SMART_CAST"))
|
||||
@@ -167,8 +180,9 @@ private fun KtCodeFragment.markSmartCasts() {
|
||||
val smartCast = bindingContext.get(SMARTCAST, expression)?.defaultType
|
||||
if (smartCast != null) {
|
||||
val smartCastedExpression = factory.createExpressionByPattern(
|
||||
"($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})",
|
||||
expression) as KtParenthesizedExpression
|
||||
"($0 as ${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(smartCast)})",
|
||||
expression
|
||||
) as KtParenthesizedExpression
|
||||
|
||||
expression.DEBUG_SMART_CAST = smartCastedExpression
|
||||
}
|
||||
@@ -194,8 +208,7 @@ private fun addImportsToFile(newImportList: KtImportList?, tmpFile: KtFile) {
|
||||
val packageDirective = tmpFile.packageDirective
|
||||
tmpFile.addAfter(psiFactory.createNewLine(), packageDirective)
|
||||
tmpFile.addAfter(newImportList, tmpFile.packageDirective)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
newImportList.imports.forEach {
|
||||
tmpFileImportList.add(psiFactory.createNewLine())
|
||||
tmpFileImportList.add(it)
|
||||
@@ -293,20 +306,17 @@ private fun findElementBefore(contextElement: PsiElement): PsiElement? {
|
||||
val delegateExpressionOrInitializer = contextElement.delegateExpressionOrInitializer
|
||||
if (delegateExpressionOrInitializer != null) {
|
||||
wrapInLambdaCall(delegateExpressionOrInitializer)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val getter = contextElement.getter
|
||||
val bodyExpression = getter?.bodyExpression
|
||||
|
||||
if (getter != null && bodyExpression != null) {
|
||||
if (!getter.hasBlockBody()) {
|
||||
wrapInLambdaCall(bodyExpression)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
(bodyExpression as KtBlockExpression).statements.first()
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
contextElement
|
||||
}
|
||||
}
|
||||
@@ -343,8 +353,7 @@ private fun findElementBefore(contextElement: PsiElement): PsiElement? {
|
||||
val entryExpression = contextElement.expression
|
||||
if (entryExpression is KtBlockExpression) {
|
||||
entryExpression.statements.firstOrNull() ?: entryExpression.lastChild
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
wrapInLambdaCall(entryExpression!!)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user