Support single-underscore named variables in JVM backend

There are mainly two kind of changes:
- skipping 'componentX' calls for destructuring entries named _
- fixing local variable table for them
 - skip entries for destructuring entries named _
 - use $noName_<i> format for lambda parameters named _

 #KT-3824 Fixed
 #KT-2783 Fixed
This commit is contained in:
Denis Zharkov
2016-10-20 17:23:42 +03:00
parent 544d8f5b66
commit a9fcee098d
18 changed files with 355 additions and 7 deletions
@@ -44,7 +44,7 @@ class ClosureGenerationStrategy(
for (parameterDescriptor in codegen.context.functionDescriptor.valueParameters) {
if (parameterDescriptor !is ValueParameterDescriptorImpl.WithDestructuringDeclaration) continue
for (entry in parameterDescriptor.destructuringVariables) {
for (entry in parameterDescriptor.destructuringVariables.filterOutDescriptorsWithSpecialNames()) {
codegen.myFrameMap.enter(entry, codegen.typeMapper.mapType(entry.type))
}
@@ -775,9 +775,19 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
private void generateDestructuringDeclaration(@NotNull KtDestructuringDeclaration destructuringDeclaration) {
final Label destructuringStartLabel = new Label();
for (KtDestructuringDeclarationEntry variableDeclaration : destructuringDeclaration.getEntries()) {
final VariableDescriptor componentDescriptor = bindingContext.get(VARIABLE, variableDeclaration);
List<VariableDescriptor> componentDescriptors =
CollectionsKt.map(
destructuringDeclaration.getEntries(),
new Function1<KtDestructuringDeclarationEntry, VariableDescriptor>() {
@Override
public VariableDescriptor invoke(KtDestructuringDeclarationEntry entry) {
return getVariableDescriptorNotNull(entry);
}
}
);
for (final VariableDescriptor componentDescriptor : CodegenUtilKt.filterOutDescriptorsWithSpecialNames(componentDescriptors)) {
@SuppressWarnings("ConstantConditions") final Type componentAsmType = asmType(componentDescriptor.getReturnType());
final int componentVarIndex = myFrameMap.enter(componentDescriptor, componentAsmType);
scheduleLeaveVariable(new Runnable() {
@@ -1957,8 +1967,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
private void putLocalVariableIntoFrameMap(@NotNull KtVariableDeclaration statement) {
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, statement);
assert variableDescriptor != null : "Couldn't find variable declaration in binding context " + statement.getText();
VariableDescriptor variableDescriptor = getVariableDescriptorNotNull(statement);
// Do not modify local variables table for variables like _ in val (_, y) = pair
// They always will have special name
if (variableDescriptor.getName().isSpecial()) return;
Type type = getVariableType(variableDescriptor);
int index = myFrameMap.enter(variableDescriptor, type);
@@ -2004,6 +2016,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
) {
final VariableDescriptor variableDescriptor = getVariableDescriptorNotNull(statement);
// Do not modify local variables table for variables like _ in val (_, y) = pair
// They always will have special name
if (variableDescriptor.getName().isSpecial()) return;
final Type type = getVariableType(variableDescriptor);
final Label scopeStart = new Label();
@@ -3902,6 +3918,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(COMPONENT_RESOLVED_CALL, variableDeclaration);
assert resolvedCall != null : "Resolved call is null for " + variableDeclaration.getText();
Call call = makeFakeCall(receiver);
VariableDescriptor variableDescriptor = getVariableDescriptorNotNull(variableDeclaration);
// Do not call `componentX` for destructuring entry called _
if (variableDescriptor.getName().isSpecial()) continue;
initializeLocalVariable(variableDeclaration, invokeFunction(call, resolvedCall, receiverStackValue));
}
}
@@ -46,9 +46,11 @@ import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtFunction;
import org.jetbrains.kotlin.psi.KtNamedFunction;
import org.jetbrains.kotlin.psi.KtParameter;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.KClassValue;
@@ -563,7 +565,7 @@ public class FunctionCodegen {
parameterName =
destructuringVariables == null
? parameter.getName().asString()
? computeParameterName(i, parameter)
: "$" + joinParameterNames(destructuringVariables);
}
else {
@@ -583,7 +585,7 @@ public class FunctionCodegen {
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
if (destructuringVariables == null) continue;
for (VariableDescriptor entry : destructuringVariables) {
for (VariableDescriptor entry : CodegenUtilKt.filterOutDescriptorsWithSpecialNames(destructuringVariables)) {
Type type = typeMapper.mapType(entry.getType());
mv.visitLocalVariable(entry.getName().asString(), type.getDescriptor(), null, methodBegin, methodEnd, shift);
shift += type.getSize();
@@ -591,10 +593,21 @@ public class FunctionCodegen {
}
}
private static String computeParameterName(int i, ValueParameterDescriptor parameter) {
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(parameter);
if (element instanceof KtParameter && UnderscoreUtilKt.isSingleUnderscore((KtParameter) element)) {
return "$noName_" + i;
}
return parameter.getName().asString();
}
private static String joinParameterNames(@NotNull List<VariableDescriptor> variables) {
return org.jetbrains.kotlin.utils.StringsKt.join(CollectionsKt.map(variables, new Function1<VariableDescriptor, String>() {
@Override
public String invoke(VariableDescriptor descriptor) {
// stub for anonymous destructuring declaration entry
if (descriptor.getName().isSpecial()) return "$_$";
return descriptor.getName().asString();
}
}), "_");
@@ -255,3 +255,5 @@ private fun CallableDescriptor.isJvmStaticIn(predicate: (DeclarationDescriptor)
}
else -> predicate(containingDeclaration) && hasJvmStaticAnnotation()
}
fun Collection<VariableDescriptor>.filterOutDescriptorsWithSpecialNames() = filterNot { it.name.isSpecial }
@@ -0,0 +1,34 @@
data class A(val x: Double = 1.0, val y: String = "", val z: Char = '0')
fun foo(a: A, block: (A, String, Int) -> String): String = block(a, "", 1)
val arrayOfA: Array<A> = Array(1) { A() }
fun box() {
foo(A()) {
(x, _, y), _, w ->
val (a, _, c) = A()
val (_, `_`, d) = A()
for ((_, q) in arrayOfA) {
}
""
}
}
// METHOD : UnderscoreNamesKt$box$1.invoke(LA;Ljava/lang/String;I)Ljava/lang/String;
// VARIABLE : NAME=q TYPE=Ljava/lang/String; INDEX=15
// VARIABLE : NAME=d TYPE=C INDEX=11
// VARIABLE : NAME=_ TYPE=Ljava/lang/String; INDEX=10
// VARIABLE : NAME=c TYPE=C INDEX=9
// VARIABLE : NAME=a TYPE=D INDEX=7
// VARIABLE : NAME=this TYPE=LUnderscoreNamesKt$box$1; INDEX=0
// VARIABLE : NAME=$x_$_$_y TYPE=LA; INDEX=1
// VARIABLE : NAME=$noName_1 TYPE=Ljava/lang/String; INDEX=2
// VARIABLE : NAME=w TYPE=I INDEX=3
// VARIABLE : NAME=x TYPE=D INDEX=4
// VARIABLE : NAME=y TYPE=C INDEX=6
@@ -0,0 +1,3 @@
fun foo(block: (String, String, String) -> String): String = block("O", "fail", "K")
fun box() = foo { x, _, y -> x + y }
@@ -0,0 +1,9 @@
class A {
operator fun component1() = "O"
operator fun component2(): String = throw RuntimeException("fail 0")
operator fun component3() = "K"
}
fun foo(a: A, block: (A) -> String): String = block(a)
fun box() = foo(A()) { (x, _, y) -> x + y }
@@ -0,0 +1,3 @@
fun foo(block: (String, String, String) -> String): String = block("O", "fail", "K")
fun box() = foo(fun(x: String, _: String, y: String) = x + y)
@@ -0,0 +1,14 @@
class A {
operator fun component1() = 1
operator fun component2() = 2
}
fun box() : String {
val (_, b) = A()
val (a, _) = A()
val (`_`, c) = A()
return if (a == 1 && b == 2 && _ == 1 && c == 2) "OK" else "fail"
}
@@ -0,0 +1,43 @@
class Range(val from : C, val to: C) {
operator fun iterator() = It(from, to)
}
class It(val from: C, val to: C) {
var c = from.i
operator fun next(): C {
val next = C(c)
c++
return next
}
operator fun hasNext(): Boolean = c <= to.i
}
class C(val i : Int) {
operator fun component1() = i + 1
operator fun component2() = i + 2
operator fun rangeTo(c: C) = Range(this, c)
}
fun doTest(): String {
var s = ""
for ((a, _) in C(0)..C(2)) {
s += "$a;"
}
for ((_, b) in C(1)..C(3)) {
s += "$b;"
}
for ((_, `_`) in C(2)..C(4)) {
s += "$_;"
}
return s
}
fun box(): String {
val s = doTest()
return if (s == "1;2;3;3;4;5;4;5;6;") "OK" else "fail: $s"
}
@@ -0,0 +1,15 @@
class A {
operator fun component1() = "O"
operator fun component2(): String = throw RuntimeException("fail 0")
operator fun component3() = "K"
}
fun box(): String {
val aA = Array(1) { A() }
for ((x, _, z) in aA) {
return x + z
}
return "OK"
}
@@ -3445,6 +3445,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("underscoreParameters.kt")
public void testUnderscoreParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/underscoreParameters.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/closures/captureOuterProperty")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -5776,6 +5782,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("underscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/underscoreNames.kt");
doTest(fileName);
}
@TestMetadata("withIndexed.kt")
public void testWithIndexed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/withIndexed.kt");
@@ -7238,6 +7250,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt");
doTest(fileName);
}
@TestMetadata("underscoreParameters.kt")
public void testUnderscoreParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionExpression/underscoreParameters.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/functions/invoke")
@@ -9043,6 +9061,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("UnderscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/UnderscoreNames.kt");
doTest(fileName);
}
@TestMetadata("ValCapturedInFunctionLiteral.kt")
public void testValCapturedInFunctionLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/ValCapturedInFunctionLiteral.kt");
@@ -9189,6 +9213,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("UnderscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/forRange/UnderscoreNames.kt");
doTest(fileName);
}
@TestMetadata("UnderscoreNamesDontCallComponent.kt")
public void testUnderscoreNamesDontCallComponent() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/forRange/UnderscoreNamesDontCallComponent.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/multiDecl/forRange/explicitRangeTo")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -3445,6 +3445,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("underscoreParameters.kt")
public void testUnderscoreParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/underscoreParameters.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/closures/captureOuterProperty")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -5776,6 +5782,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("underscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/underscoreNames.kt");
doTest(fileName);
}
@TestMetadata("withIndexed.kt")
public void testWithIndexed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/withIndexed.kt");
@@ -7238,6 +7250,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt");
doTest(fileName);
}
@TestMetadata("underscoreParameters.kt")
public void testUnderscoreParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionExpression/underscoreParameters.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/functions/invoke")
@@ -9043,6 +9061,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("UnderscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/UnderscoreNames.kt");
doTest(fileName);
}
@TestMetadata("ValCapturedInFunctionLiteral.kt")
public void testValCapturedInFunctionLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/ValCapturedInFunctionLiteral.kt");
@@ -9189,6 +9213,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("UnderscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/forRange/UnderscoreNames.kt");
doTest(fileName);
}
@TestMetadata("UnderscoreNamesDontCallComponent.kt")
public void testUnderscoreNamesDontCallComponent() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multiDecl/forRange/UnderscoreNamesDontCallComponent.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/multiDecl/forRange/explicitRangeTo")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -118,4 +118,10 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/checkLocalVariablesTable/localFun.kt");
doTest(fileName);
}
@TestMetadata("underscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
doTest(fileName);
}
}
+68
View File
@@ -0,0 +1,68 @@
LineBreakpoint created at underscoreNames.kt:18 lambdaOrdinal = 1
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! underscoreNames.UnderscoreNamesKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
underscoreNames.kt:18
Compile bytecode for x
Compile bytecode for y
package underscoreNames
data class A(val x: Double = 1.0, val y: String = "", val z: Char = '0')
fun foo(a: A, block: (A, String, Int) -> String): String = block(a, "", 1)
val arrayOfA: Array<A> = Array(1) { A() }
fun main(args: Array<String>) {
foo(A()) {
(x, _, y), _, w ->
val (a, _, c) = A()
val (_, `_`, d) = A()
for ((_, q) in arrayOfA) {
//Breakpoint! (lambdaOrdinal = 1)
println(q)
}
""
}
}
// PRINT_FRAME
// EXPRESSION: x
// RESULT: 1.0: D
// EXPRESSION: y
// RESULT: 48: C
Compile bytecode for arrayOfA
frame = invoke:18, UnderscoreNamesKt$main$1 {underscoreNames}
this = this = {underscoreNames.UnderscoreNamesKt$main$1@uniqueID}Function3<underscoreNames.A, java.lang.String, java.lang.Integer, java.lang.String>
field = arity: int = 3 (sp = Lambda.!EXT!)
local = $x_$_$_y: underscoreNames.A = {underscoreNames.A@uniqueID}A(x=1.0, y=, z=0) (sp = null)
field = x: double = 1.0 (sp = underscoreNames.kt, 2)
field = y: java.lang.String = (sp = underscoreNames.kt, 2)
field = value: char[] = {char[0]@uniqueID} (sp = String.!EXT!)
field = hash: int = 0 (sp = String.!EXT!)
field = z: char = '0' 48 (sp = underscoreNames.kt, 2)
local = $noName_1: java.lang.String = (sp = null)
field = value: char[] = {char[0]@uniqueID} (sp = String.!EXT!)
field = hash: int = 0 (sp = String.!EXT!)
local = w: int = 1 (sp = underscoreNames.kt, 11)
local = x: double = 1.0 (sp = underscoreNames.kt, 11)
local = y: char = '0' 48 (sp = underscoreNames.kt, 11)
local = a: double = 1.0 (sp = underscoreNames.kt, 13)
local = c: char = '0' 48 (sp = underscoreNames.kt, 13)
local = _: java.lang.String = (sp = underscoreNames.kt, 14)
field = value: char[] = {char[0]@uniqueID} (sp = String.!EXT!)
field = hash: int = 0 (sp = String.!EXT!)
local = d: char = '0' 48 (sp = underscoreNames.kt, 14)
local = q: java.lang.String = (sp = underscoreNames.kt, 16)
field = value: char[] = {char[0]@uniqueID} (sp = String.!EXT!)
field = hash: int = 0 (sp = String.!EXT!)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,32 @@
package underscoreNames
data class A(val x: Double = 1.0, val y: String = "", val z: Char = '0')
fun foo(a: A, block: (A, String, Int) -> String): String = block(a, "", 1)
val arrayOfA: Array<A> = Array(1) { A() }
fun main(args: Array<String>) {
foo(A()) {
(x, _, y), _, w ->
val (a, _, c) = A()
val (_, `_`, d) = A()
for ((_, q) in arrayOfA) {
//Breakpoint! (lambdaOrdinal = 1)
println(q)
}
""
}
}
// PRINT_FRAME
// EXPRESSION: x
// RESULT: 1.0: D
// EXPRESSION: y
// RESULT: 48: C
@@ -842,6 +842,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/twoLambdasOnOneLineSecond.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("underscoreNames.kt")
public void testUnderscoreNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/underscoreNames.kt");
doSingleBreakpointTest(fileName);
}
}
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/renderer")
@@ -52,4 +52,10 @@ public class FunctionExpressionTestGenerated extends AbstractFunctionExpressionT
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionExpression/functionLiteralExpression.kt");
doTest(fileName);
}
@TestMetadata("underscoreParameters.kt")
public void testUnderscoreParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionExpression/underscoreParameters.kt");
doTest(fileName);
}
}