diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt index a681b30fbd8..46fe131f777 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType @@ -537,6 +538,50 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { } } + override fun visitLambdaExpression(expression: PsiLambdaExpression) { + val parameters = expression.parameterList + val convertedParameters = ParameterList(parameters.parameters.map { + val paramName = Identifier(it.name!!).assignNoPrototype() + val paramType = if (it.typeElement != null) converter.typeConverter.convertType(it.type) else null + LambdaParameter(paramName, paramType).assignPrototype(it) + }).assignPrototype(parameters) + + val body = expression.body + when (body) { + is PsiExpression -> { + val convertedBody = codeConverter.convertExpression(body).assignPrototype(body) + result = LambdaExpression(convertedParameters, Block(listOf(convertedBody), LBrace().assignNoPrototype(), RBrace().assignNoPrototype())) + } + is PsiCodeBlock -> { + val convertedBlock = codeConverter.withSpecialStatementConverter(object: SpecialStatementConverter { + override fun convertStatement(statement: PsiStatement, codeConverter: CodeConverter): Statement? { + if (statement !is PsiReturnStatement) return null + + val returnValue = statement.returnValue + val methodReturnType = codeConverter.methodReturnType + val expressionForReturn = if (returnValue != null && methodReturnType != null) + codeConverter.convertExpression(returnValue, methodReturnType) + else + codeConverter.convertExpression(returnValue) + + if (body.statements.lastOrNull() == statement) { + return expressionForReturn + } + + val callExpression = expression.getParentOfType(false) + if (callExpression != null) { + return ReturnStatement(expressionForReturn, Identifier(callExpression.methodExpression.text).assignNoPrototype()) + } + + return ReturnStatement(expressionForReturn) + } + + }).convertBlock(body).assignPrototype(body) + result = LambdaExpression(convertedParameters, convertedBlock) + } + } + } + companion object { private val needQualifierNameSet = setOf("java.lang.Byte", "java.lang.Double", "java.lang.Float", "java.lang.Long", "java.lang.Short") } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/StatementConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/StatementConverter.kt index b94ff74b50a..27cb131bb24 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/StatementConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/StatementConverter.kt @@ -222,7 +222,9 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter { var block = converterForBody.convertBlock(tryBlock) var expression: Expression = Expression.Empty for (variable in resourceVariables.reverse()) { - val lambda = LambdaExpression(Identifier.toKotlin(variable.getName()!!), block) + val parameter = LambdaParameter(Identifier(variable.name!!).assignNoPrototype(), null).assignNoPrototype() + val parameterList = ParameterList(listOf(parameter)).assignNoPrototype() + val lambda = LambdaExpression(parameterList, block) expression = MethodCallExpression.build(codeConverter.convertExpression(variable.getInitializer()), "use", listOf(), listOf(), false, lambda) expression.assignNoPrototype() block = Block(listOf(expression), LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype() @@ -252,6 +254,7 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter { codeConverter.convertExpression(returnValue, methodReturnType) else codeConverter.convertExpression(returnValue) + result = ReturnStatement(expression) } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Expressions.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Expressions.kt index 86c7bc7d48f..bf341ec8597 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Expressions.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Expressions.kt @@ -124,7 +124,7 @@ class PolyadicExpression(val expressions: List, val token: String) : } } -class LambdaExpression(val arguments: String?, val block: Block) : Expression() { +class LambdaExpression(val parameterList: ParameterList?, val block: Block) : Expression() { init { assignPrototypesFrom(block) } @@ -132,8 +132,8 @@ class LambdaExpression(val arguments: String?, val block: Block) : Expression() override fun generateCode(builder: CodeBuilder) { builder append block.lBrace append " " - if (arguments != null) { - builder.append(arguments) + if (parameterList != null && !parameterList.parameters.isEmpty()) { + builder.append(parameterList) .append("->") .append(if (block.statements.size() > 1) "\n" else " ") .append(block.statements, "\n") diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Parameter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Parameter.kt index f7780b8fc86..4fabbd3049b 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Parameter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Parameter.kt @@ -18,15 +18,15 @@ package org.jetbrains.kotlin.j2k.ast import org.jetbrains.kotlin.j2k.* -abstract class Parameter(val identifier: Identifier, - val type: Type?): Element() +abstract class Parameter(val identifier: Identifier, val type: Type?) : Element() class FunctionParameter(identifier: Identifier, type: Type, val varVal: FunctionParameter.VarValModifier, val annotations: Annotations, val modifiers: Modifiers, - val defaultValue: DeferredElement? = null) : Parameter(identifier, type) { + val defaultValue: DeferredElement? = null +): Parameter(identifier, type) { public enum class VarValModifier { None, Val, @@ -52,3 +52,13 @@ class FunctionParameter(identifier: Identifier, } } } + +class LambdaParameter(identifier: Identifier, type: Type?) : Parameter(identifier, type) { + override fun generateCode(builder: CodeBuilder) { + builder append identifier + + if (type != null) { + builder append ":" append type + } + } +} \ No newline at end of file diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Statements.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Statements.kt index 58df0adf612..8c6f94c8aed 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Statements.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Statements.kt @@ -43,9 +43,13 @@ class LabeledStatement(val name: Identifier, val statement: Element) : Statement } } -class ReturnStatement(val expression: Expression) : Statement() { +class ReturnStatement(val expression: Expression, val label: Identifier? = null) : Statement() { override fun generateCode(builder: CodeBuilder) { - builder append "return " append expression + builder append "return" + if (label != null) { + builder append "@" append label + } + builder append " " append expression } } diff --git a/j2k/testData/fileOrElement/function/java8Lambdas.java b/j2k/testData/fileOrElement/function/java8Lambdas.java new file mode 100644 index 00000000000..d5fa5874fb6 --- /dev/null +++ b/j2k/testData/fileOrElement/function/java8Lambdas.java @@ -0,0 +1,84 @@ +import kotlin.jvm.functions.Function0; +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; + +public class Java8Class { + public void foo0(Function0 r) { + } + + public void foo1(Function1 r) { + } + + public void foo2(Function2 r) { + } + + public void helper() { + } + + public void foo() { + foo0(() -> "42"); + foo0(() -> { return "42"; }); + foo0(() -> { + helper(); + return "42"; + }); + + foo1((i) -> "42"); + foo1(i -> { return "42"; }); + foo1((Integer i) -> { + helper(); + if (i > 1) { + return "42"; + } + + return "43"; + }); + + foo2((i, j) -> "42"); + foo2((Integer i, Integer j) -> { + helper(); + return "42"; + }); + + Function2 f = (Integer i, Integer k) -> { + helper(); + if (i > 1) { + return "42"; + } + + return "43"; + }; + + Function2 f1 = (Integer i1, Integer k1) -> { + Function2 f2 = (Integer i2, Integer k2) -> { + helper(); + if (i2 > 1) { + return "42"; + } + + return "43"; + }; + if (i1 > 1) { + return f.invoke(i1, k1); + } + return f.invoke(i1, k1); + }; + + Runnable runnable = () -> { }; + + foo1((Integer i) -> { + if (i > 1) { + return "42"; + } + + foo0(() -> { + if (true) { + return "42"; + } + return "43"; + }); + + return "43"; + }); + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/java8Lambdas.kt b/j2k/testData/fileOrElement/function/java8Lambdas.kt new file mode 100644 index 00000000000..86c2392110c --- /dev/null +++ b/j2k/testData/fileOrElement/function/java8Lambdas.kt @@ -0,0 +1,86 @@ +// ERROR: 'return' is not allowed here +// ERROR: Type mismatch: inferred type is kotlin.String but kotlin.Unit was expected +// ERROR: 'return' is not allowed here +// ERROR: Type mismatch: inferred type is kotlin.String but kotlin.Unit was expected +// ERROR: 'return' is not allowed here +// ERROR: Type mismatch: inferred type is kotlin.String but kotlin.Unit was expected +public class Java8Class { + public fun foo0(r: Function0) { + } + + public fun foo1(r: Function1) { + } + + public fun foo2(r: Function2) { + } + + public fun helper() { + } + + public fun foo() { + foo0({ "42" }) + foo0({ "42" }) + foo0({ + helper() + "42" + }) + + foo1({ i -> "42" }) + foo1({ i -> "42" }) + foo1({ i: Int -> + helper() + if (i > 1) { + return@foo1 "42" + } + + "43" + }) + + foo2({ i, j -> "42" }) + foo2({ i: Int, j: Int -> + helper() + "42" + }) + + val f = { i: Int, k: Int -> + helper() + if (i > 1) { + return "42" + } + + "43" + } + + val f1 = { i1: Int, k1: Int -> + val f2 = { i2: Int, k2: Int -> + helper() + if (i2 > 1) { + return "42" + } + + "43" + } + if (i1 > 1) { + return f.invoke(i1, k1) + } + f.invoke(i1, k1) + } + + val runnable = { } + + foo1({ i: Int -> + if (i > 1) { + return@foo1 "42" + } + + foo0({ + if (true) { + return@foo0 "42" + } + "43" + }) + + "43" + }) + } +} \ No newline at end of file diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 34affe05706..69fd68d7840 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -2242,6 +2242,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("java8Lambdas.java") + public void testJava8Lambdas() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/java8Lambdas.java"); + doTest(fileName); + } + @TestMetadata("lineBreaksBetweenParameters.java") public void testLineBreaksBetweenParameters() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/lineBreaksBetweenParameters.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index be55787d206..2982b198bdf 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -2242,6 +2242,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("java8Lambdas.java") + public void testJava8Lambdas() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/java8Lambdas.java"); + doTest(fileName); + } + @TestMetadata("lineBreaksBetweenParameters.java") public void testLineBreaksBetweenParameters() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/lineBreaksBetweenParameters.java");