Add support for desctructuring of lambda parameters in JVM backend
#KT-5828 In Progress
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||
|
||||
class ClosureGenerationStrategy(
|
||||
state: GenerationState,
|
||||
declaration: KtDeclarationWithBody
|
||||
) : FunctionGenerationStrategy.FunctionDefault(state, declaration) {
|
||||
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
processDestructuringInLambdaParameters(codegen)
|
||||
|
||||
super.doGenerateBody(codegen, signature)
|
||||
}
|
||||
|
||||
private fun processDestructuringInLambdaParameters(codegen: ExpressionCodegen) {
|
||||
val savedIsShouldMarkLineNumbers = codegen.isShouldMarkLineNumbers
|
||||
// Do not write line numbers until destructuring happens
|
||||
// (otherwise destructuring variables will be uninitialized in the beginning of lambda)
|
||||
codegen.isShouldMarkLineNumbers = false
|
||||
|
||||
for (parameterDescriptor in codegen.context.functionDescriptor.valueParameters) {
|
||||
if (parameterDescriptor !is ValueParameterDescriptorImpl.WithDestructuringDeclaration) continue
|
||||
|
||||
for (entry in parameterDescriptor.destructuringVariables) {
|
||||
codegen.myFrameMap.enter(entry, codegen.typeMapper.mapType(entry.type))
|
||||
}
|
||||
|
||||
val destructuringDeclaration =
|
||||
(DescriptorToSourceUtils.descriptorToDeclaration(parameterDescriptor) as? KtParameter)?.destructuringDeclaration
|
||||
?: error("Destructuring declaration for descriptor $parameterDescriptor not found")
|
||||
|
||||
codegen.initializeDestructuringDeclarationVariables(
|
||||
destructuringDeclaration,
|
||||
TransientReceiver(parameterDescriptor.type),
|
||||
codegen.findLocalOrCapturedValue(parameterDescriptor) ?: error("Local var not found for parameter $parameterDescriptor")
|
||||
)
|
||||
}
|
||||
|
||||
codegen.isShouldMarkLineNumbers = savedIsShouldMarkLineNumbers
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -139,7 +139,7 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
}
|
||||
|
||||
if (!state.classBuilderMode.generateBodies) {
|
||||
FunctionCodegen.generateLocalVariablesForParameters(mv, signature, null, Label(), Label(), remainingParameters, isStatic)
|
||||
FunctionCodegen.generateLocalVariablesForParameters(mv, signature, null, Label(), Label(), remainingParameters, isStatic, typeMapper)
|
||||
mv.visitEnd()
|
||||
return
|
||||
}
|
||||
@@ -215,7 +215,7 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
mv.visitLabel(methodEnd)
|
||||
|
||||
FunctionCodegen.generateLocalVariablesForParameters(mv, signature, null, methodBegin, methodEnd,
|
||||
remainingParameters, isStatic)
|
||||
remainingParameters, isStatic, typeMapper)
|
||||
|
||||
FunctionCodegen.endVisit(mv, null, methodElement)
|
||||
}
|
||||
|
||||
@@ -1589,7 +1589,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
|
||||
|
||||
return genClosure(
|
||||
declaration, descriptor, new FunctionGenerationStrategy.FunctionDefault(state, declaration), samType, null, null
|
||||
declaration, descriptor, new ClosureGenerationStrategy(state, declaration), samType, null, null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3872,12 +3872,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
v.store(tempVarIndex, initializerAsmType);
|
||||
StackValue.Local local = StackValue.local(tempVarIndex, initializerAsmType);
|
||||
|
||||
for (KtDestructuringDeclarationEntry variableDeclaration : multiDeclaration.getEntries()) {
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(COMPONENT_RESOLVED_CALL, variableDeclaration);
|
||||
assert resolvedCall != null : "Resolved call is null for " + variableDeclaration.getText();
|
||||
Call call = makeFakeCall(initializerAsReceiver);
|
||||
initializeLocalVariable(variableDeclaration, invokeFunction(call, resolvedCall, local));
|
||||
}
|
||||
initializeDestructuringDeclarationVariables(multiDeclaration, initializerAsReceiver, local);
|
||||
|
||||
if (initializerAsmType.getSort() == Type.OBJECT || initializerAsmType.getSort() == Type.ARRAY) {
|
||||
v.aconst(null);
|
||||
@@ -3888,6 +3883,19 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
public void initializeDestructuringDeclarationVariables(
|
||||
@NotNull KtDestructuringDeclaration destructuringDeclaration,
|
||||
@NotNull ReceiverValue receiver,
|
||||
@NotNull StackValue receiverStackValue
|
||||
) {
|
||||
for (KtDestructuringDeclarationEntry variableDeclaration : destructuringDeclaration.getEntries()) {
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(COMPONENT_RESOLVED_CALL, variableDeclaration);
|
||||
assert resolvedCall != null : "Resolved call is null for " + variableDeclaration.getText();
|
||||
Call call = makeFakeCall(receiver);
|
||||
initializeLocalVariable(variableDeclaration, invokeFunction(call, resolvedCall, receiverStackValue));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StackValue getVariableMetadataValue(VariableDescriptor variableDescriptor) {
|
||||
StackValue value = findLocalOrCapturedValue(getDelegatedLocalVariableMetadata(variableDescriptor, bindingContext));
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.SpecialBuiltinMembers;
|
||||
@@ -207,7 +209,8 @@ public class FunctionCodegen {
|
||||
getThisTypeForFunction(functionDescriptor, methodContext, typeMapper),
|
||||
new Label(),
|
||||
new Label(),
|
||||
contextKind
|
||||
contextKind,
|
||||
typeMapper
|
||||
);
|
||||
|
||||
mv.visitEnd();
|
||||
@@ -395,7 +398,8 @@ public class FunctionCodegen {
|
||||
mv.visitLabel(methodEnd);
|
||||
|
||||
Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);
|
||||
generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind());
|
||||
generateLocalVariableTable(
|
||||
mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), typeMapper);
|
||||
|
||||
if (context.isInlineMethodContext() && functionFakeIndex != -1) {
|
||||
mv.visitLocalVariable(
|
||||
@@ -436,11 +440,12 @@ public class FunctionCodegen {
|
||||
@Nullable Type thisType,
|
||||
@NotNull Label methodBegin,
|
||||
@NotNull Label methodEnd,
|
||||
@NotNull OwnerKind ownerKind
|
||||
@NotNull OwnerKind ownerKind,
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
) {
|
||||
generateLocalVariablesForParameters(mv, jvmMethodSignature, thisType, methodBegin, methodEnd,
|
||||
functionDescriptor.getValueParameters(),
|
||||
AsmUtil.isStaticMethod(ownerKind, functionDescriptor));
|
||||
AsmUtil.isStaticMethod(ownerKind, functionDescriptor), typeMapper);
|
||||
}
|
||||
|
||||
public static void generateLocalVariablesForParameters(
|
||||
@@ -450,7 +455,8 @@ public class FunctionCodegen {
|
||||
@NotNull Label methodBegin,
|
||||
@NotNull Label methodEnd,
|
||||
Collection<ValueParameterDescriptor> valueParameters,
|
||||
boolean isStatic
|
||||
boolean isStatic,
|
||||
KotlinTypeMapper typeMapper
|
||||
) {
|
||||
Iterator<ValueParameterDescriptor> valueParameterIterator = valueParameters.iterator();
|
||||
List<JvmMethodParameterSignature> params = jvmMethodSignature.getValueParameters();
|
||||
@@ -474,7 +480,12 @@ public class FunctionCodegen {
|
||||
|
||||
if (kind == JvmMethodParameterKind.VALUE) {
|
||||
ValueParameterDescriptor parameter = valueParameterIterator.next();
|
||||
parameterName = parameter.getName().asString();
|
||||
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
|
||||
|
||||
parameterName =
|
||||
destructuringVariables == null
|
||||
? parameter.getName().asString()
|
||||
: "$" + joinParameterNames(destructuringVariables);
|
||||
}
|
||||
else {
|
||||
String lowercaseKind = kind.name().toLowerCase();
|
||||
@@ -487,6 +498,26 @@ public class FunctionCodegen {
|
||||
mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, methodEnd, shift);
|
||||
shift += type.getSize();
|
||||
}
|
||||
|
||||
for (ValueParameterDescriptor parameter : valueParameters) {
|
||||
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
|
||||
if (destructuringVariables == null) continue;
|
||||
|
||||
for (VariableDescriptor entry : destructuringVariables) {
|
||||
Type type = typeMapper.mapType(parameter.getType());
|
||||
mv.visitLocalVariable(entry.getName().asString(), type.getDescriptor(), null, methodBegin, methodEnd, shift);
|
||||
shift += type.getSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return descriptor.getName().asString();
|
||||
}
|
||||
}), "_");
|
||||
}
|
||||
|
||||
private static void generateFacadeDelegateMethodBody(
|
||||
|
||||
@@ -571,6 +571,9 @@ public class InlineCodegen extends CallGenerator {
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (expression instanceof KtFunctionLiteral) {
|
||||
strategy = new ClosureGenerationStrategy(state, (KtDeclarationWithBody) expression);
|
||||
}
|
||||
else {
|
||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, (KtDeclarationWithBody) expression);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
data class A(val x: String, val y: String)
|
||||
|
||||
fun foo(a: A, block: (A) -> String): String = block(a)
|
||||
|
||||
fun box() {
|
||||
foo(A("O", "K")) { (x, y) -> x + y }
|
||||
}
|
||||
|
||||
// METHOD : DestructuringInLambdasKt$box$1.invoke(LA;)Ljava/lang/String;
|
||||
// VARIABLE : NAME=this TYPE=LDestructuringInLambdasKt$box$1; INDEX=0
|
||||
// VARIABLE : NAME=$x_y TYPE=LA; INDEX=1
|
||||
// VARIABLE : NAME=x TYPE=LA; INDEX=2
|
||||
// VARIABLE : NAME=y TYPE=LA; INDEX=3
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
class A<T>(val x: String, val y: String, val z: T)
|
||||
|
||||
fun <T> foo(a: A<T>, block: (A<T>) -> String): String = block(a)
|
||||
|
||||
operator fun A<*>.component1() = x
|
||||
|
||||
object B {
|
||||
operator fun A<*>.component2() = y
|
||||
}
|
||||
|
||||
fun B.bar(): String {
|
||||
|
||||
operator fun <R> A<R>.component3() = z
|
||||
|
||||
val x = foo(A("O", "K", 123)) { (x, y, z) -> x + y + z.toString() }
|
||||
if (x != "OK123") return "fail 1: $x"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun box() = B.bar()
|
||||
@@ -0,0 +1,11 @@
|
||||
data class A<T, F>(val x: T, val y: F)
|
||||
|
||||
fun <X, Y> foo(a: A<X, Y>, block: (A<X, Y>) -> String) = block(a)
|
||||
|
||||
fun box(): String {
|
||||
val x = foo(A("OK", 1)) { (x, y) -> x + (y.toString()) }
|
||||
|
||||
if (x != "OK1") return "fail1: $x"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
data class A(val x: String, val y: String)
|
||||
|
||||
inline fun foo(a: A, block: (A) -> String): String = block(a)
|
||||
|
||||
fun box() = foo(A("O", "K")) { (x, y) -> x + y }
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
data class A(val x: String, val y: String)
|
||||
|
||||
fun foo(a: A, block: (Int, A, String) -> String): String = block(1, a, "#")
|
||||
|
||||
fun box(): String {
|
||||
val x = foo(A("O", "K")) { i, (x, y), v -> i.toString() + x + y + v }
|
||||
|
||||
if (x != "1OK#") return "fail 1: $x"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
data class A(val x: String, val y: String)
|
||||
|
||||
fun foo(a: A, block: (A) -> String): String = block(a)
|
||||
|
||||
fun box() = foo(A("O", "K")) { (x, y) -> x + y }
|
||||
@@ -0,0 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
val r1 = listOf("O", "K", "fail").let {
|
||||
(x, y) -> x + y
|
||||
}
|
||||
|
||||
|
||||
if (r1 != "OK") return "fail 1: $r1"
|
||||
|
||||
val r2 = listOf(Pair("O", "K")).map { (x, y) -> x + y }[0]
|
||||
|
||||
if (r2 != "OK") return "fail 2: $r2"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -5657,6 +5657,51 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DestructuringDeclInLambdaParam extends AbstractBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInDestructuringDeclInLambdaParam() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/destructuringDeclInLambdaParam"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionComponents.kt")
|
||||
public void testExtensionComponents() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/extensionComponents.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("generic.kt")
|
||||
public void testGeneric() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/generic.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/inline.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("otherParameters.kt")
|
||||
public void testOtherParameters() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/otherParameters.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("stdlibUsages.kt")
|
||||
public void testStdlibUsages() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam/stdlibUsages.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/diagnostics")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -47,6 +47,12 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("destructuringInLambdas.kt")
|
||||
public void testDestructuringInLambdas() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/checkLocalVariablesTable/destructuringInLambdas.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineLambdaWithItParam.kt")
|
||||
public void testInlineLambdaWithItParam() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt");
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
LineBreakpoint created at destructuringParam.kt:12 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! destructuringParam.DestructuringParamKt
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
destructuringParam.kt:12
|
||||
Compile bytecode for x
|
||||
Compile bytecode for y
|
||||
package destructuringParam
|
||||
data class A(val x: String, val y: String)
|
||||
|
||||
fun foo(a: A, block: (A) -> String): String = block(a)
|
||||
|
||||
fun box() {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
//Breakpoint! (lambdaOrdinal = 1)
|
||||
foo(A("O", "K")) { (x, y) -> x + y }
|
||||
}
|
||||
|
||||
// PRINT_FRAME
|
||||
|
||||
// EXPRESSION: x
|
||||
// RESULT: "O": Ljava/lang/String;
|
||||
|
||||
// EXPRESSION: y
|
||||
// RESULT: "K": Ljava/lang/String;
|
||||
|
||||
frame = invoke:12, DestructuringParamKt$main$1 {destructuringParam}
|
||||
this = this = {destructuringParam.DestructuringParamKt$main$1@uniqueID}Function1<destructuringParam.A, java.lang.String>
|
||||
field = arity: int = 1 (sp = Lambda.!EXT!)
|
||||
local = $x_y: destructuringParam.A = {destructuringParam.A@uniqueID}A(x=O, y=K) (sp = null)
|
||||
field = x: java.lang.String = O (sp = destructuringParam.kt, 2)
|
||||
field = value: char[] = {char[1]@uniqueID} (sp = String.!EXT!)
|
||||
element = 0 = 'O' 79
|
||||
field = hash: int = 0 (sp = String.!EXT!)
|
||||
field = y: java.lang.String = K (sp = destructuringParam.kt, 2)
|
||||
field = value: char[] = {char[1]@uniqueID} (sp = String.!EXT!)
|
||||
element = 0 = 'K' 75
|
||||
field = hash: int = 0 (sp = String.!EXT!)
|
||||
local = x: destructuringParam.A = O (sp = destructuringParam.kt, 12)
|
||||
field = value: char[] = {char[1]@uniqueID} (sp = String.!EXT!)
|
||||
element = 0 = 'O' 79
|
||||
field = hash: int = 0 (sp = String.!EXT!)
|
||||
local = y: destructuringParam.A = K (sp = destructuringParam.kt, 12)
|
||||
field = value: char[] = {char[1]@uniqueID} (sp = String.!EXT!)
|
||||
element = 0 = 'K' 75
|
||||
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
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package destructuringParam
|
||||
data class A(val x: String, val y: String)
|
||||
|
||||
fun foo(a: A, block: (A) -> String): String = block(a)
|
||||
|
||||
fun box() {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
//Breakpoint! (lambdaOrdinal = 1)
|
||||
foo(A("O", "K")) { (x, y) -> x + y }
|
||||
}
|
||||
|
||||
// PRINT_FRAME
|
||||
|
||||
// EXPRESSION: x
|
||||
// RESULT: "O": Ljava/lang/String;
|
||||
|
||||
// EXPRESSION: y
|
||||
// RESULT: "K": Ljava/lang/String;
|
||||
+6
@@ -789,6 +789,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("destructuringParam.kt")
|
||||
public void testDestructuringParam() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/destructuringParam.kt");
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionalExpression.kt")
|
||||
public void testInlineFunctionalExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/lambdas/inlineFunctionalExpression.kt");
|
||||
|
||||
Reference in New Issue
Block a user