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