diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CodeFragmentCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CodeFragmentCodegen.kt index 079c37aafa2..44578467873 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CodeFragmentCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CodeFragmentCodegen.kt @@ -36,6 +36,7 @@ class CodeFragmentCodegenInfo( interface IParameter { val targetDescriptor: DeclarationDescriptor val targetType: KotlinType + val isLValue: Boolean } } @@ -210,7 +211,7 @@ class CodeFragmentCodegen private constructor( val asmType: Type val stackValue: StackValue - val sharedAsmType = getSharedTypeIfApplicable(parameter.targetDescriptor, typeMapper) + val sharedAsmType = getSharedTypeIfApplicable(parameter, typeMapper) if (sharedAsmType != null) { asmType = sharedAsmType val unwrappedType = typeMapper.mapType(parameter.targetType) @@ -229,9 +230,15 @@ class CodeFragmentCodegen private constructor( return CalculatedCodeFragmentCodegenInfo(parameters, methodSignature.returnType) } - fun getSharedTypeIfApplicable(descriptor: DeclarationDescriptor, typeMapper: KotlinTypeMapper): Type? { - return when (descriptor) { - is LocalVariableDescriptor -> typeMapper.getSharedVarType(descriptor) + fun getSharedTypeIfApplicable(parameter: IParameter, typeMapper: KotlinTypeMapper): Type? { + return when (val descriptor = parameter.targetDescriptor) { + is LocalVariableDescriptor -> { + var result = typeMapper.getSharedVarType(descriptor) + if (result == null && parameter.isLValue) { + result = StackValue.sharedTypeForType(typeMapper.mapType(descriptor.type)) + } + result + } else -> null } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 934d670b53d..ed37ce267a9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -110,15 +110,21 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return qualifiedExpression.getOperationSign() == KtTokens.DOT && qualifiedExpression.getReceiverExpression() == KtPsiUtil.deparenthesize(expression); } - if (parent instanceof KtBinaryExpression) { - KtBinaryExpression binaryExpression = (KtBinaryExpression) parent; - if (!OperatorConventions.BINARY_OPERATION_NAMES.containsKey(binaryExpression.getOperationToken()) && - !KtTokens.ALL_ASSIGNMENTS.contains(binaryExpression.getOperationToken())) { - return false; - } - return PsiTreeUtil.isAncestor(binaryExpression.getLeft(), expression, false); + + return isLValue(expression, parent); + } + + public static boolean isLValue(@NotNull KtSimpleNameExpression expression, @Nullable PsiElement parent) { + if (!(parent instanceof KtBinaryExpression)) { + return false; } - return false; + + KtBinaryExpression binaryExpression = (KtBinaryExpression) parent; + if (!OperatorConventions.BINARY_OPERATION_NAMES.containsKey(binaryExpression.getOperationToken()) && + !KtTokens.ALL_ASSIGNMENTS.contains(binaryExpression.getOperationToken())) { + return false; + } + return PsiTreeUtil.isAncestor(binaryExpression.getLeft(), expression, false); } private static boolean isDangerousWithNull(@NotNull KtSimpleNameExpression expression, @NotNull ExpressionTypingContext context) { @@ -1463,7 +1469,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @NotNull private KotlinTypeInfo assignmentIsNotAnExpressionError(KtBinaryExpression expression, ExpressionTypingContext context) { facade.checkStatementType(expression, context); - context.trace.report(ASSIGNMENT_IN_EXPRESSION_CONTEXT.on(expression)); + if (!context.isDebuggerContext) { + context.trace.report(ASSIGNMENT_IN_EXPRESSION_CONTEXT.on(expression)); + } return TypeInfoFactoryKt.noTypeInfo(context); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index a1438caaf98..b50a3a80e34 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -373,7 +373,9 @@ public class DataFlowAnalyzer { public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) { facade.checkStatementType( expression, context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT)); - context.trace.report(EXPRESSION_EXPECTED.on(expression, expression)); + if (!context.isDebuggerContext) { + context.trace.report(EXPRESSION_EXPECTED.on(expression, expression)); + } return TypeInfoFactoryKt.noTypeInfo(context); } diff --git a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluatorBuilder.kt b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluatorBuilder.kt index a784105cb5f..115b62350e4 100644 --- a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluatorBuilder.kt +++ b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluatorBuilder.kt @@ -55,6 +55,7 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.variables.EvaluatorValueConve import org.jetbrains.kotlin.idea.debugger.evaluate.variables.VariableFinder import org.jetbrains.kotlin.idea.debugger.safeLocation import org.jetbrains.kotlin.idea.debugger.safeMethod +import org.jetbrains.kotlin.idea.debugger.safeVisibleVariableByName import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.psi.* @@ -287,18 +288,38 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour for (parameterType in compiledData.mainMethodSignature.parameterTypes) { context.findClass(parameterType, classLoader) } - val args = calculateMainMethodCallArguments(context, compiledData) - block(args) + + val variableFinder = VariableFinder(context) + val args = calculateMainMethodCallArguments(variableFinder, compiledData) + + val result = block(args) + + for (wrapper in variableFinder.refWrappers) { + updateLocalVariableValue(variableFinder.evaluatorValueConverter, wrapper) + } + + return@executeWithBreakpointsDisabled result } } - private fun calculateMainMethodCallArguments(context: ExecutionContext, compiledData: CompiledDataDescriptor): List { + private fun updateLocalVariableValue(converter: EvaluatorValueConverter, ref: VariableFinder.RefWrapper) { + val frameProxy = converter.context.frameProxy + val variable = frameProxy.safeVisibleVariableByName(ref.localVariableName) ?: return + val newValue = converter.unref(ref.wrapper) + + try { + frameProxy.setValue(variable, newValue) + } catch (e: InvalidTypeException) { + LOG.error("Cannot update local variable value: expected type ${variable.type}, actual type ${newValue?.type()}", e) + } + } + + private fun calculateMainMethodCallArguments(variableFinder: VariableFinder, compiledData: CompiledDataDescriptor): List { val asmValueParameters = compiledData.mainMethodSignature.parameterTypes val valueParameters = compiledData.parameters require(asmValueParameters.size == valueParameters.size) val args = valueParameters.zip(asmValueParameters) - val variableFinder = VariableFinder(context) return args.map { (parameter, asmType) -> val result = variableFinder.find(parameter, asmType) @@ -307,7 +328,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour val name = parameter.debugString fun isInsideDefaultInterfaceMethod(): Boolean { - val method = context.frameProxy.safeLocation()?.safeMethod() ?: return false + val method = variableFinder.context.frameProxy.safeLocation()?.safeMethod() ?: return false val desc = method.signature() return method.name().endsWith("\$default") && DEFAULT_METHOD_MARKERS.any { desc.contains("I${it.descriptor})") } } @@ -319,7 +340,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour } else if (parameter.kind == CodeFragmentParameter.Kind.ORDINARY && isInsideDefaultInterfaceMethod()) { evaluationException("Parameter evaluation is not supported for '\$default' methods") } else { - throw VariableFinder.variableNotFound(context, buildString { + throw VariableFinder.variableNotFound(variableFinder.context, buildString { append("Cannot find local variable: name = '").append(name).append("', type = ").append(asmType.className) }) } diff --git a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt index 7be4824b2dd..f8149adf03f 100644 --- a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt +++ b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt @@ -94,7 +94,7 @@ class CodeFragmentCompiler(private val executionContext: ExecutionContext) { val classes = generationState.factory.asList().filterClassFiles() .map { ClassToLoad(it.internalClassName, it.relativePath, it.asByteArray()) } - val methodSignature = getMethodSignature(methodDescriptor, parameterInfo.parameters, generationState) + val methodSignature = getMethodSignature(methodDescriptor, parameterInfo, generationState) val functionSuffixes = getLocalFunctionSuffixes(parameterInfo.parameters, generationState.typeMapper) generationState.destroy() @@ -123,13 +123,14 @@ class CodeFragmentCompiler(private val executionContext: ExecutionContext) { private fun getMethodSignature( methodDescriptor: FunctionDescriptor, - parameters: List, + parameterInfo: CodeFragmentParameterInfo, state: GenerationState ): MethodSignature { val typeMapper = state.typeMapper val asmSignature = typeMapper.mapSignatureSkipGeneric(methodDescriptor) - val asmParameters = parameters.zip(asmSignature.valueParameters).map { (param, sigParam) -> - getSharedTypeIfApplicable(param.targetDescriptor, typeMapper) ?: sigParam.asmType + + val asmParameters = parameterInfo.parameters.zip(asmSignature.valueParameters).map { (param, sigParam) -> + getSharedTypeIfApplicable(param, typeMapper) ?: sigParam.asmType } return MethodSignature(asmParameters, asmSignature.returnType) diff --git a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt index e8dd376e679..c2b33d51151 100644 --- a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt +++ b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.compilation import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.CodeFragmentCodegenInfo import org.jetbrains.kotlin.codegen.getCallLabelForLambdaArgument @@ -34,6 +35,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.expressions.BasicExpressionTypingVisitor import org.jetbrains.kotlin.types.expressions.createFunctionType class CodeFragmentParameterInfo( @@ -140,7 +142,8 @@ class CodeFragmentParameterAnalyzer( private fun processDescriptor(descriptor: DeclarationDescriptor, expression: KtSimpleNameExpression) { val parameter = processDebugLabel(descriptor) ?: processCoroutineContextCall(descriptor) - ?: processSimpleNameExpression(descriptor) + ?: processSimpleNameExpression(descriptor, expression) + checkBounds(descriptor, expression, parameter) } @@ -255,7 +258,7 @@ class CodeFragmentParameterAnalyzer( } } - private fun processSimpleNameExpression(target: DeclarationDescriptor): Smart? { + private fun processSimpleNameExpression(target: DeclarationDescriptor, expression: KtSimpleNameExpression): Smart? { if (target is ValueParameterDescriptor && target.isCrossinline) { throw EvaluateExceptionUtil.createEvaluateException("Evaluation of 'crossinline' lambdas is not supported") } @@ -278,11 +281,14 @@ class CodeFragmentParameterAnalyzer( } } is ValueDescriptor -> { + val parent = PsiTreeUtil.skipParentsOfType(expression, KtParenthesizedExpression::class.java) + val isLValue = BasicExpressionTypingVisitor.isLValue(expression, parent) + parameters.getOrPut(target) { val type = target.type @Suppress("DEPRECATION") val kind = if (target is LocalVariableDescriptor && target.isDelegated) Kind.DELEGATED else Kind.ORDINARY - Smart(Dumb(kind, target.name.asString()), type, target) + Smart(Dumb(kind, target.name.asString()), type, target, isLValue) } } else -> null diff --git a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt index 144e77280f2..1e9b8cfe365 100644 --- a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt +++ b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt @@ -17,7 +17,7 @@ import com.sun.jdi.Type as JdiType import kotlin.jvm.internal.Ref @Suppress("SpellCheckingInspection") -class EvaluatorValueConverter(private val context: ExecutionContext) { +class EvaluatorValueConverter(val context: ExecutionContext) { private companion object { private val UNBOXING_METHOD_NAMES = mapOf( "java/lang/Boolean" to "booleanValue", @@ -228,9 +228,12 @@ private fun unwrap(type: AsmType): AsmType { private val AsmType.isPrimitiveType: Boolean get() = sort != AsmType.OBJECT && sort != AsmType.ARRAY -private val AsmType.isRefType: Boolean +internal val AsmType.isRefType: Boolean get() = sort == AsmType.OBJECT && this in REF_TYPES +internal val Value?.isRefType: Boolean + get() = this is ObjectReference && AsmType.getType(this.referenceType().signature()).isRefType + private val AsmType.isBoxedType: Boolean get() = this in BOXED_TO_PRIMITIVE diff --git a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt index 33997ba7673..27b35f336f0 100644 --- a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt +++ b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt @@ -30,11 +30,12 @@ import kotlin.coroutines.Continuation import org.jetbrains.org.objectweb.asm.Type as AsmType import com.sun.jdi.Type as JdiType -class VariableFinder(private val context: ExecutionContext) { +class VariableFinder(val context: ExecutionContext) { private val frameProxy = context.frameProxy companion object { - private const val USE_UNSAFE_FALLBACK = true + private val USE_UNSAFE_FALLBACK: Boolean + get() = true fun variableNotFound(context: ExecutionContext, message: String): Exception { val frameProxy = context.frameProxy @@ -73,7 +74,14 @@ class VariableFinder(private val context: ExecutionContext) { } } - private val evaluatorValueConverter = EvaluatorValueConverter(context) + val evaluatorValueConverter = EvaluatorValueConverter(context) + + val refWrappers: List + get() = mutableRefWrappers + + private val mutableRefWrappers = mutableListOf() + + class RefWrapper(val localVariableName: String, val wrapper: Value?) sealed class VariableKind(val asmType: AsmType) { abstract fun capturedNameMatches(name: String): Boolean @@ -273,8 +281,26 @@ class VariableFinder(private val context: ExecutionContext) { ): Result? { val inlineDepth = getInlineDepth(variables) + findLocalVariable(variables, kind, inlineDepth, namePredicate)?.let { return it } + + // Try to find variables outside of inline functions as well + if (inlineDepth > 0 && USE_UNSAFE_FALLBACK) { + findLocalVariable(variables, kind, 0, namePredicate)?.let { return it } + } + + return null + } + + private fun findLocalVariable( + variables: List, + kind: VariableKind, + inlineDepth: Int, + namePredicate: (String) -> Boolean + ): Result? { + val actualPredicate: (String) -> Boolean + if (inlineDepth > 0) { - val inlineAwareNamePredicate = fun(name: String): Boolean { + actualPredicate = fun(name: String): Boolean { var endIndex = name.length var depth = 0 @@ -288,21 +314,27 @@ class VariableFinder(private val context: ExecutionContext) { endIndex -= suffixLen } - return namePredicate(name.take(endIndex)) + return namePredicate(name.take(endIndex)) && getInlineDepth(name) == inlineDepth } - - variables.namedEntitySequence() - .filter { inlineAwareNamePredicate(it.name) && getInlineDepth(it.name) == inlineDepth && kind.typeMatches(it.type) } - .mapNotNull { it.unwrapAndCheck(kind) } - .firstOrNull() - ?.let { return it } + } else { + actualPredicate = namePredicate } - variables.namedEntitySequence() - .filter { namePredicate(it.name) && kind.typeMatches(it.type) } - .mapNotNull { it.unwrapAndCheck(kind) } - .firstOrNull() - ?.let { return it } + for (item in variables.namedEntitySequence()) { + if (!actualPredicate(item.name) || !kind.typeMatches(item.type)) { + continue + } + + val rawValue = item.value() + val result = evaluatorValueConverter.coerce(getUnwrapDelegate(kind, rawValue), kind.asmType) ?: continue + + if (!rawValue.isRefType && result.value.isRefType) { + // Local variable was wrapped into a Ref instance + mutableRefWrappers += RefWrapper(item.name, result.value) + } + + return result + } return null } @@ -377,7 +409,7 @@ class VariableFinder(private val context: ExecutionContext) { } private fun findCapturedVariable(kind: VariableKind, parentFactory: () -> Value?): Result? { - val parent = getUnwrapDelegate(kind, parentFactory) + val parent = getUnwrapDelegate(kind, parentFactory()) return findCapturedVariable(kind, parent) } @@ -415,8 +447,7 @@ class VariableFinder(private val context: ExecutionContext) { return null } - private fun getUnwrapDelegate(kind: VariableKind, valueFactory: () -> Value?): Value? { - val rawValue = valueFactory() + private fun getUnwrapDelegate(kind: VariableKind, rawValue: Value?): Value? { if (kind !is VariableKind.Ordinary || !kind.isDelegated) { return rawValue } @@ -443,8 +474,8 @@ class VariableFinder(private val context: ExecutionContext) { return evaluatorValueConverter.typeMatches(asmType, actualType) } - private fun NamedEntity.unwrapAndCheck(kind: VariableKind): Result? { - return evaluatorValueConverter.coerce(getUnwrapDelegate(kind, value), kind.asmType) + private fun NamedEntity.unwrapAndCheck(kind: VariableKind, value: () -> Value? = this.value): Result? { + return evaluatorValueConverter.coerce(getUnwrapDelegate(kind, value()), kind.asmType) } private fun List.namedEntitySequence(owner: ObjectReference): Sequence { diff --git a/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameter.kt b/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameter.kt index db79682ce2b..e798e271884 100644 --- a/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameter.kt +++ b/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameter.kt @@ -22,7 +22,8 @@ interface CodeFragmentParameter { class Smart( val dumb: Dumb, override val targetType: KotlinType, - override val targetDescriptor: DeclarationDescriptor + override val targetDescriptor: DeclarationDescriptor, + override val isLValue: Boolean = false ) : CodeFragmentParameter by dumb, CodeFragmentCodegenInfo.IParameter data class Dumb( diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.kt b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.kt new file mode 100644 index 00000000000..55b2ec5f136 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.kt @@ -0,0 +1,24 @@ +package mutations + +fun main() { + var a = 5 + val b = 3 + + //Breakpoint! + val x = 0 + + //Breakpoint! + val y = 0 + + //Breakpoint! + val z = 0 +} + +// EXPRESSION: a = 100 +// RESULT: VOID_VALUE + +// EXPRESSION: b = 200 +// RESULT: VOID_VALUE + +// EXPRESSION: a + b +// RESULT: 300: I \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.out b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.out new file mode 100644 index 00000000000..1800ff7dc4f --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.out @@ -0,0 +1,14 @@ +LineBreakpoint created at mutations.kt:8 +LineBreakpoint created at mutations.kt:11 +LineBreakpoint created at mutations.kt:14 +Run Java +Connected to the target VM +mutations.kt:8 +Compile bytecode for a = 100 +mutations.kt:11 +Compile bytecode for b = 200 +mutations.kt:14 +Compile bytecode for a + b +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index 358cf65dabb..c4eecbdaccd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -1136,6 +1136,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/multipleBreakpointsAtLine.kt"); } + @TestMetadata("mutations.kt") + public void testMutations() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/mutations.kt"); + } + @TestMetadata("nonCapturedVariables.kt") public void testNonCapturedVariables() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/nonCapturedVariables.kt");