Evaluate parameter value instead of backing field in primary constructor
This commit is contained in:
+4
-3
@@ -166,7 +166,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
|
||||
private fun evaluateSafe(context: EvaluationContextImpl): Any? {
|
||||
fun compilerFactory(): CompiledDataDescriptor = compileCodeFragment(context.debugProcess)
|
||||
fun compilerFactory(): CompiledDataDescriptor = compileCodeFragment(context)
|
||||
|
||||
val (compiledData, isCompiledDataFromCache) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory)
|
||||
val classLoaderRef = loadClassesSafely(context, compiledData.classes)
|
||||
@@ -194,7 +194,8 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileCodeFragment(debugProcess: DebugProcessImpl): CompiledDataDescriptor {
|
||||
private fun compileCodeFragment(evaluationContext: EvaluationContextImpl): CompiledDataDescriptor {
|
||||
val debugProcess = evaluationContext.debugProcess
|
||||
var analysisResult = checkForErrors(codeFragment, debugProcess)
|
||||
|
||||
if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) {
|
||||
@@ -211,7 +212,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
|
||||
val moduleDescriptor = analysisResult.moduleDescriptor
|
||||
|
||||
val result = CodeFragmentCompiler.compile(codeFragment, bindingContext, moduleDescriptor)
|
||||
val result = CodeFragmentCompiler(evaluationContext).compile(codeFragment, bindingContext, moduleDescriptor)
|
||||
return CompiledDataDescriptor.from(result, sourcePosition)
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
@@ -42,7 +43,7 @@ import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object CodeFragmentCompiler {
|
||||
class CodeFragmentCompiler(val evaluationContext: EvaluationContextImpl) {
|
||||
data class CompilationResult(
|
||||
val classes: List<ClassToLoad>,
|
||||
val parameterInfo: CodeFragmentParameterInfo,
|
||||
@@ -77,7 +78,7 @@ object CodeFragmentCompiler {
|
||||
bindingContext, listOf(codeFragment), compilerConfiguration
|
||||
).build()
|
||||
|
||||
val parameterInfo = CodeFragmentParameterAnalyzer(codeFragment, bindingContext).analyze()
|
||||
val parameterInfo = CodeFragmentParameterAnalyzer(evaluationContext, codeFragment, bindingContext).analyze()
|
||||
val (classDescriptor, methodDescriptor) = createDescriptorsForCodeFragment(
|
||||
codeFragment, Name.identifier(GENERATED_CLASS_NAME), Name.identifier(GENERATED_FUNCTION_NAME),
|
||||
parameterInfo, returnType, moduleDescriptorWrapper.packageFragmentForEvaluator
|
||||
|
||||
+42
-8
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.CodeFragmentCodegenInfo
|
||||
@@ -15,6 +16,8 @@ import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldPropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilation.CodeFragmentParameter.*
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory.Companion.FAKE_JAVA_CONTEXT_FUNCTION_NAME
|
||||
import org.jetbrains.kotlin.idea.debugger.safeLocation
|
||||
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -61,12 +64,22 @@ class CodeFragmentParameterInfo(
|
||||
The purpose of this class is to figure out what parameters the received code fragment captures.
|
||||
It handles both directly mentioned names such as local variables or parameters and implicit values (dispatch/extension receivers).
|
||||
*/
|
||||
class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, private val bindingContext: BindingContext) {
|
||||
class CodeFragmentParameterAnalyzer(
|
||||
private val evaluationContext: EvaluationContextImpl,
|
||||
private val codeFragment: KtCodeFragment,
|
||||
private val bindingContext: BindingContext
|
||||
) {
|
||||
private val parameters = LinkedHashMap<DeclarationDescriptor, Smart>()
|
||||
private val crossingBounds = mutableSetOf<Dumb>()
|
||||
|
||||
private val onceUsedChecker = OnceUsedChecker(CodeFragmentParameterAnalyzer::class.java)
|
||||
|
||||
private val containingPrimaryConstructor: ConstructorDescriptor? by lazy {
|
||||
evaluationContext.frameProxy?.safeLocation()?.safeMethod()?.takeIf { it.isConstructor } ?: return@lazy null
|
||||
val constructor = codeFragment.context?.getParentOfType<KtPrimaryConstructor>(false) ?: return@lazy null
|
||||
bindingContext[BindingContext.CONSTRUCTOR, constructor]
|
||||
}
|
||||
|
||||
fun analyze(): CodeFragmentParameterInfo {
|
||||
onceUsedChecker.trigger()
|
||||
|
||||
@@ -112,15 +125,19 @@ class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, pr
|
||||
if (dispatchReceiver is ImplicitReceiver) {
|
||||
val descriptor = dispatchReceiver.declarationDescriptor
|
||||
val parameter = processReceiver(dispatchReceiver)
|
||||
checkBounds(descriptor, expression, parameter)
|
||||
processed = true
|
||||
if (parameter != null) {
|
||||
checkBounds(descriptor, expression, parameter)
|
||||
processed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!processed && resolvedCall.resultingDescriptor is SyntheticFieldDescriptor) {
|
||||
val descriptor = resolvedCall.resultingDescriptor as SyntheticFieldDescriptor
|
||||
val parameter = processSyntheticFieldVariable(descriptor)
|
||||
checkBounds(descriptor, expression, parameter)
|
||||
processed = true
|
||||
if (parameter != null) {
|
||||
checkBounds(descriptor, expression, parameter)
|
||||
processed = true
|
||||
}
|
||||
}
|
||||
|
||||
// If a reference has receivers, we can calculate its value using them, no need to capture
|
||||
@@ -177,7 +194,7 @@ class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, pr
|
||||
}
|
||||
|
||||
private fun processDispatchReceiver(descriptor: ClassDescriptor): Smart? {
|
||||
if (descriptor.kind == ClassKind.OBJECT) {
|
||||
if (descriptor.kind == ClassKind.OBJECT || containingPrimaryConstructor != null) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -239,8 +256,13 @@ class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, pr
|
||||
}
|
||||
|
||||
private fun processSimpleNameExpression(target: DeclarationDescriptor): Smart? {
|
||||
if ((target as? DeclarationDescriptorWithVisibility)?.visibility != Visibilities.LOCAL) {
|
||||
// No need to capture non-local declarations
|
||||
val isLocalTarget = (target as? DeclarationDescriptorWithVisibility)?.visibility == Visibilities.LOCAL
|
||||
|
||||
val isPrimaryConstructorParameter = !isLocalTarget
|
||||
&& target is PropertyDescriptor
|
||||
&& isContainingPrimaryConstructorParameter(target)
|
||||
|
||||
if (!isLocalTarget && !isPrimaryConstructorParameter) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -261,6 +283,18 @@ class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, pr
|
||||
}
|
||||
}
|
||||
|
||||
private fun isContainingPrimaryConstructorParameter(target: PropertyDescriptor): Boolean {
|
||||
val primaryConstructor = containingPrimaryConstructor ?: return false
|
||||
for (parameter in primaryConstructor.valueParameters) {
|
||||
val property = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter]
|
||||
if (target == property) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun processCoroutineContextCall(target: DeclarationDescriptor): Smart? {
|
||||
if (target is PropertyDescriptor && target.fqNameSafe == COROUTINE_CONTEXT_1_3_FQ_NAME) {
|
||||
return parameters.getOrPut(target) {
|
||||
|
||||
+7
-1
@@ -40,6 +40,7 @@ fun main(args: Array<String>) {
|
||||
C(1)
|
||||
D()
|
||||
E(1)
|
||||
F("foo")
|
||||
}
|
||||
|
||||
// EXPRESSION: 1 + 1
|
||||
@@ -53,7 +54,7 @@ class A
|
||||
class B()
|
||||
|
||||
// EXPRESSION: a
|
||||
// RESULT: 0: I
|
||||
// RESULT: 1: I
|
||||
//Breakpoint!
|
||||
class C(val a: Int)
|
||||
|
||||
@@ -69,3 +70,8 @@ class E {
|
||||
//Breakpoint!
|
||||
constructor(i: Int)
|
||||
}
|
||||
|
||||
// EXPRESSION: a
|
||||
// RESULT: "foo": Ljava/lang/String;
|
||||
//Breakpoint!
|
||||
class F(val a: String)
|
||||
+13
-10
@@ -2,11 +2,12 @@ LineBreakpoint created at constructors.kt:9
|
||||
LineBreakpoint created at constructors.kt:13
|
||||
LineBreakpoint created at constructors.kt:20
|
||||
LineBreakpoint created at constructors.kt:28
|
||||
LineBreakpoint created at constructors.kt:48
|
||||
LineBreakpoint created at constructors.kt:53
|
||||
LineBreakpoint created at constructors.kt:58
|
||||
LineBreakpoint created at constructors.kt:64
|
||||
LineBreakpoint created at constructors.kt:70
|
||||
LineBreakpoint created at constructors.kt:49
|
||||
LineBreakpoint created at constructors.kt:54
|
||||
LineBreakpoint created at constructors.kt:59
|
||||
LineBreakpoint created at constructors.kt:65
|
||||
LineBreakpoint created at constructors.kt:71
|
||||
LineBreakpoint created at constructors.kt:77
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
constructors.kt:9
|
||||
@@ -17,16 +18,18 @@ constructors.kt:20
|
||||
Compile bytecode for p1 + p2
|
||||
constructors.kt:28
|
||||
Compile bytecode for i1
|
||||
constructors.kt:48
|
||||
constructors.kt:49
|
||||
Compile bytecode for 1 + 1
|
||||
constructors.kt:53
|
||||
constructors.kt:54
|
||||
Compile bytecode for 1 + 2
|
||||
constructors.kt:58
|
||||
constructors.kt:59
|
||||
Compile bytecode for a
|
||||
constructors.kt:64
|
||||
constructors.kt:65
|
||||
Compile bytecode for 1 + 3
|
||||
constructors.kt:70
|
||||
constructors.kt:71
|
||||
Compile bytecode for i
|
||||
constructors.kt:77
|
||||
Compile bytecode for a
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
Reference in New Issue
Block a user