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? {
|
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 (compiledData, isCompiledDataFromCache) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory)
|
||||||
val classLoaderRef = loadClassesSafely(context, compiledData.classes)
|
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)
|
var analysisResult = checkForErrors(codeFragment, debugProcess)
|
||||||
|
|
||||||
if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) {
|
if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) {
|
||||||
@@ -211,7 +212,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
|||||||
|
|
||||||
val moduleDescriptor = analysisResult.moduleDescriptor
|
val moduleDescriptor = analysisResult.moduleDescriptor
|
||||||
|
|
||||||
val result = CodeFragmentCompiler.compile(codeFragment, bindingContext, moduleDescriptor)
|
val result = CodeFragmentCompiler(evaluationContext).compile(codeFragment, bindingContext, moduleDescriptor)
|
||||||
return CompiledDataDescriptor.from(result, sourcePosition)
|
return CompiledDataDescriptor.from(result, sourcePosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
|
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.backend.common.output.OutputFile
|
||||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
@@ -42,7 +43,7 @@ import org.jetbrains.kotlin.types.SimpleType
|
|||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
object CodeFragmentCompiler {
|
class CodeFragmentCompiler(val evaluationContext: EvaluationContextImpl) {
|
||||||
data class CompilationResult(
|
data class CompilationResult(
|
||||||
val classes: List<ClassToLoad>,
|
val classes: List<ClassToLoad>,
|
||||||
val parameterInfo: CodeFragmentParameterInfo,
|
val parameterInfo: CodeFragmentParameterInfo,
|
||||||
@@ -77,7 +78,7 @@ object CodeFragmentCompiler {
|
|||||||
bindingContext, listOf(codeFragment), compilerConfiguration
|
bindingContext, listOf(codeFragment), compilerConfiguration
|
||||||
).build()
|
).build()
|
||||||
|
|
||||||
val parameterInfo = CodeFragmentParameterAnalyzer(codeFragment, bindingContext).analyze()
|
val parameterInfo = CodeFragmentParameterAnalyzer(evaluationContext, codeFragment, bindingContext).analyze()
|
||||||
val (classDescriptor, methodDescriptor) = createDescriptorsForCodeFragment(
|
val (classDescriptor, methodDescriptor) = createDescriptorsForCodeFragment(
|
||||||
codeFragment, Name.identifier(GENERATED_CLASS_NAME), Name.identifier(GENERATED_FUNCTION_NAME),
|
codeFragment, Name.identifier(GENERATED_CLASS_NAME), Name.identifier(GENERATED_FUNCTION_NAME),
|
||||||
parameterInfo, returnType, moduleDescriptorWrapper.packageFragmentForEvaluator
|
parameterInfo, returnType, moduleDescriptorWrapper.packageFragmentForEvaluator
|
||||||
|
|||||||
+42
-8
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
|
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
|
||||||
|
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.CodeFragmentCodegenInfo
|
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.DebuggerFieldPropertyDescriptor
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilation.CodeFragmentParameter.*
|
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.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.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
|
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.
|
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).
|
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 parameters = LinkedHashMap<DeclarationDescriptor, Smart>()
|
||||||
private val crossingBounds = mutableSetOf<Dumb>()
|
private val crossingBounds = mutableSetOf<Dumb>()
|
||||||
|
|
||||||
private val onceUsedChecker = OnceUsedChecker(CodeFragmentParameterAnalyzer::class.java)
|
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 {
|
fun analyze(): CodeFragmentParameterInfo {
|
||||||
onceUsedChecker.trigger()
|
onceUsedChecker.trigger()
|
||||||
|
|
||||||
@@ -112,15 +125,19 @@ class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, pr
|
|||||||
if (dispatchReceiver is ImplicitReceiver) {
|
if (dispatchReceiver is ImplicitReceiver) {
|
||||||
val descriptor = dispatchReceiver.declarationDescriptor
|
val descriptor = dispatchReceiver.declarationDescriptor
|
||||||
val parameter = processReceiver(dispatchReceiver)
|
val parameter = processReceiver(dispatchReceiver)
|
||||||
checkBounds(descriptor, expression, parameter)
|
if (parameter != null) {
|
||||||
processed = true
|
checkBounds(descriptor, expression, parameter)
|
||||||
|
processed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!processed && resolvedCall.resultingDescriptor is SyntheticFieldDescriptor) {
|
if (!processed && resolvedCall.resultingDescriptor is SyntheticFieldDescriptor) {
|
||||||
val descriptor = resolvedCall.resultingDescriptor as SyntheticFieldDescriptor
|
val descriptor = resolvedCall.resultingDescriptor as SyntheticFieldDescriptor
|
||||||
val parameter = processSyntheticFieldVariable(descriptor)
|
val parameter = processSyntheticFieldVariable(descriptor)
|
||||||
checkBounds(descriptor, expression, parameter)
|
if (parameter != null) {
|
||||||
processed = true
|
checkBounds(descriptor, expression, parameter)
|
||||||
|
processed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a reference has receivers, we can calculate its value using them, no need to capture
|
// 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? {
|
private fun processDispatchReceiver(descriptor: ClassDescriptor): Smart? {
|
||||||
if (descriptor.kind == ClassKind.OBJECT) {
|
if (descriptor.kind == ClassKind.OBJECT || containingPrimaryConstructor != null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,8 +256,13 @@ class CodeFragmentParameterAnalyzer(private val codeFragment: KtCodeFragment, pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processSimpleNameExpression(target: DeclarationDescriptor): Smart? {
|
private fun processSimpleNameExpression(target: DeclarationDescriptor): Smart? {
|
||||||
if ((target as? DeclarationDescriptorWithVisibility)?.visibility != Visibilities.LOCAL) {
|
val isLocalTarget = (target as? DeclarationDescriptorWithVisibility)?.visibility == Visibilities.LOCAL
|
||||||
// No need to capture non-local declarations
|
|
||||||
|
val isPrimaryConstructorParameter = !isLocalTarget
|
||||||
|
&& target is PropertyDescriptor
|
||||||
|
&& isContainingPrimaryConstructorParameter(target)
|
||||||
|
|
||||||
|
if (!isLocalTarget && !isPrimaryConstructorParameter) {
|
||||||
return null
|
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? {
|
private fun processCoroutineContextCall(target: DeclarationDescriptor): Smart? {
|
||||||
if (target is PropertyDescriptor && target.fqNameSafe == COROUTINE_CONTEXT_1_3_FQ_NAME) {
|
if (target is PropertyDescriptor && target.fqNameSafe == COROUTINE_CONTEXT_1_3_FQ_NAME) {
|
||||||
return parameters.getOrPut(target) {
|
return parameters.getOrPut(target) {
|
||||||
|
|||||||
+7
-1
@@ -40,6 +40,7 @@ fun main(args: Array<String>) {
|
|||||||
C(1)
|
C(1)
|
||||||
D()
|
D()
|
||||||
E(1)
|
E(1)
|
||||||
|
F("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPRESSION: 1 + 1
|
// EXPRESSION: 1 + 1
|
||||||
@@ -53,7 +54,7 @@ class A
|
|||||||
class B()
|
class B()
|
||||||
|
|
||||||
// EXPRESSION: a
|
// EXPRESSION: a
|
||||||
// RESULT: 0: I
|
// RESULT: 1: I
|
||||||
//Breakpoint!
|
//Breakpoint!
|
||||||
class C(val a: Int)
|
class C(val a: Int)
|
||||||
|
|
||||||
@@ -69,3 +70,8 @@ class E {
|
|||||||
//Breakpoint!
|
//Breakpoint!
|
||||||
constructor(i: Int)
|
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:13
|
||||||
LineBreakpoint created at constructors.kt:20
|
LineBreakpoint created at constructors.kt:20
|
||||||
LineBreakpoint created at constructors.kt:28
|
LineBreakpoint created at constructors.kt:28
|
||||||
LineBreakpoint created at constructors.kt:48
|
LineBreakpoint created at constructors.kt:49
|
||||||
LineBreakpoint created at constructors.kt:53
|
LineBreakpoint created at constructors.kt:54
|
||||||
LineBreakpoint created at constructors.kt:58
|
LineBreakpoint created at constructors.kt:59
|
||||||
LineBreakpoint created at constructors.kt:64
|
LineBreakpoint created at constructors.kt:65
|
||||||
LineBreakpoint created at constructors.kt:70
|
LineBreakpoint created at constructors.kt:71
|
||||||
|
LineBreakpoint created at constructors.kt:77
|
||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
constructors.kt:9
|
constructors.kt:9
|
||||||
@@ -17,16 +18,18 @@ constructors.kt:20
|
|||||||
Compile bytecode for p1 + p2
|
Compile bytecode for p1 + p2
|
||||||
constructors.kt:28
|
constructors.kt:28
|
||||||
Compile bytecode for i1
|
Compile bytecode for i1
|
||||||
constructors.kt:48
|
constructors.kt:49
|
||||||
Compile bytecode for 1 + 1
|
Compile bytecode for 1 + 1
|
||||||
constructors.kt:53
|
constructors.kt:54
|
||||||
Compile bytecode for 1 + 2
|
Compile bytecode for 1 + 2
|
||||||
constructors.kt:58
|
constructors.kt:59
|
||||||
Compile bytecode for a
|
Compile bytecode for a
|
||||||
constructors.kt:64
|
constructors.kt:65
|
||||||
Compile bytecode for 1 + 3
|
Compile bytecode for 1 + 3
|
||||||
constructors.kt:70
|
constructors.kt:71
|
||||||
Compile bytecode for i
|
Compile bytecode for i
|
||||||
|
constructors.kt:77
|
||||||
|
Compile bytecode for a
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
Reference in New Issue
Block a user