Generate debug information for 'when' subject variable

NB debugger tests currently don't support language version or individual
language feature settings.
This commit is contained in:
Dmitry Petrov
2018-06-19 15:57:22 +03:00
parent a7492e91c9
commit a4b5d74ae3
7 changed files with 98 additions and 7 deletions
@@ -4513,11 +4513,12 @@ The "returned" value of try expression with no finally is either the last expres
int subjectLocal; int subjectLocal;
Type subjectType; Type subjectType;
VariableDescriptor subjectVariableDescriptor = null;
if (subjectVariable != null) { if (subjectVariable != null) {
VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, subjectVariable); subjectVariableDescriptor = bindingContext.get(BindingContext.VARIABLE, subjectVariable);
assert variableDescriptor != null : "Unresolved subject variable: " + subjectVariable.getName(); assert subjectVariableDescriptor != null : "Unresolved subject variable: " + subjectVariable.getName();
subjectType = asmType(variableDescriptor.getType()); subjectType = asmType(subjectVariableDescriptor.getType());
subjectLocal = myFrameMap.enter(variableDescriptor, subjectType); subjectLocal = myFrameMap.enter(subjectVariableDescriptor, subjectType);
visitProperty(subjectVariable, null); visitProperty(subjectVariable, null);
tempVariables.put(subjectExpression, StackValue.local(subjectLocal, subjectType)); tempVariables.put(subjectExpression, StackValue.local(subjectLocal, subjectType));
} }
@@ -4533,6 +4534,9 @@ The "returned" value of try expression with no finally is either the last expres
subjectType = Type.VOID_TYPE; subjectType = Type.VOID_TYPE;
} }
Label begin = new Label();
v.mark(begin);
Label end = new Label(); Label end = new Label();
boolean hasElse = KtPsiUtil.checkWhenExpressionHasSingleElse(expression); boolean hasElse = KtPsiUtil.checkWhenExpressionHasSingleElse(expression);
@@ -4572,7 +4576,16 @@ The "returned" value of try expression with no finally is either the last expres
markLineNumber(expression, isStatement); markLineNumber(expression, isStatement);
v.mark(end); v.mark(end);
myFrameMap.leaveTemp(subjectType); if (subjectVariableDescriptor != null) {
myFrameMap.leave(subjectVariableDescriptor);
v.visitLocalVariable(
subjectVariableDescriptor.getName().asString(), subjectType.getDescriptor(), null,
begin, end, subjectLocal
);
}
else {
myFrameMap.leaveTemp(subjectType);
}
tempVariables.remove(subjectExpression); tempVariables.remove(subjectExpression);
return null; return null;
}); });
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen.`when`
import org.jetbrains.kotlin.cfg.WhenChecker import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.ConstantValue
@@ -69,6 +70,9 @@ abstract class SwitchCodegen(
generateSubjectValue() generateSubjectValue()
generateSubjectValueToIndex() generateSubjectValueToIndex()
val beginLabel = Label()
v.mark(beginLabel)
generateSwitchInstructionByTransitionsTable() generateSwitchInstructionByTransitionsTable()
generateEntries() generateEntries()
@@ -81,6 +85,13 @@ abstract class SwitchCodegen(
codegen.markLineNumber(expression, isStatement) codegen.markLineNumber(expression, isStatement)
v.mark(endLabel) v.mark(endLabel)
subjectVariableDescriptor?.let {
v.visitLocalVariable(
it.name.asString(), subjectType.descriptor, null,
beginLabel, endLabel, subjectLocal
)
}
} }
/** /**
@@ -115,19 +126,23 @@ abstract class SwitchCodegen(
} }
} }
private var subjectVariableDescriptor: VariableDescriptor? = null
/** /**
* Generates subject value on top of the stack. * Generates subject value on top of the stack.
* If the subject is a variable, it's stored and loaded. * If the subject is a variable, it's stored and loaded.
*/ */
private fun generateSubjectValue() { private fun generateSubjectValue() {
if (subjectVariable != null) { if (subjectVariable != null) {
val variableDescriptor = bindingContext[BindingContext.VARIABLE, subjectVariable] val mySubjectVariable = bindingContext[BindingContext.VARIABLE, subjectVariable]
?: throw AssertionError("Unresolved subject variable: $expression") ?: throw AssertionError("Unresolved subject variable: $expression")
subjectLocal = codegen.frameMap.enter(variableDescriptor, subjectType) subjectLocal = codegen.frameMap.enter(mySubjectVariable, subjectType)
codegen.visitProperty(subjectVariable, null) codegen.visitProperty(subjectVariable, null)
StackValue.local(subjectLocal, subjectType).put(subjectType, codegen.v) StackValue.local(subjectLocal, subjectType).put(subjectType, codegen.v)
subjectVariableDescriptor = mySubjectVariable
} else { } else {
codegen.gen(subjectExpression, subjectType) codegen.gen(subjectExpression, subjectType)
subjectVariableDescriptor = null
} }
} }
@@ -0,0 +1,10 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
fun test(a: Any) =
when (val subject = a) {
is Int -> "Int $subject"
is String -> "String ${subject.length}"
else -> "other"
}
// 1 LOCALVARIABLE subject Ljava/lang/Object;
@@ -0,0 +1,12 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
enum class X { A, B, C }
fun test(a: X) =
when (val subject = a) {
X.A -> 0
X.B -> 1
X.C -> 2
}
// 1 LOCALVARIABLE subject
@@ -0,0 +1,11 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
fun test(a: Int) =
when (val subject = a) {
1 -> 0
2 -> 1
3 -> 2
else -> -1
}
// 1 LOCALVARIABLE subject
@@ -0,0 +1,10 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
fun test(a: String) =
when (val subject = a) {
"" -> 0
"a" -> 1
else -> -1
}
// 1 LOCALVARIABLE subject
@@ -2811,6 +2811,26 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/when/simpleConstValsInsideWhen.kt"); runTest("compiler/testData/codegen/bytecodeText/when/simpleConstValsInsideWhen.kt");
} }
@TestMetadata("subjectValHasLocalVariableSlot.kt")
public void testSubjectValHasLocalVariableSlot() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/when/subjectValHasLocalVariableSlot.kt");
}
@TestMetadata("subjectValInEnumWhenHasLocalVariableSlot.kt")
public void testSubjectValInEnumWhenHasLocalVariableSlot() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/when/subjectValInEnumWhenHasLocalVariableSlot.kt");
}
@TestMetadata("subjectValInIntWhenHasLocalVariableSlot.kt")
public void testSubjectValInIntWhenHasLocalVariableSlot() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/when/subjectValInIntWhenHasLocalVariableSlot.kt");
}
@TestMetadata("subjectValInStringWhenHasLocalVariableSlot.kt")
public void testSubjectValInStringWhenHasLocalVariableSlot() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/when/subjectValInStringWhenHasLocalVariableSlot.kt");
}
@TestMetadata("whenNull.kt") @TestMetadata("whenNull.kt")
public void testWhenNull() throws Exception { public void testWhenNull() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/when/whenNull.kt"); runTest("compiler/testData/codegen/bytecodeText/when/whenNull.kt");