diff --git a/j2k/j2k.iml b/j2k/j2k.iml
index a5e5d67ecd7..2eb792263d9 100644
--- a/j2k/j2k.iml
+++ b/j2k/j2k.iml
@@ -12,6 +12,7 @@
+
diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt
index aa4fd8fd8d8..794881d1478 100644
--- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt
+++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt
@@ -58,7 +58,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
is PsiExpression -> convertExpression(element)
is PsiComment -> Comment(element.getText()!!)
is PsiImportList -> convertImportList(element)
- is PsiImportStatementBase -> convertImport(element)
+ is PsiImportStatementBase -> convertImport(element, false)
is PsiPackageStatement -> PackageStatement(quoteKeywords(element.getPackageName() ?: ""))
is PsiWhiteSpace -> WhiteSpace(element.getText()!!)
else -> null
@@ -615,20 +615,6 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
return Parameter(Identifier(parameter.getName()!!), `type`, varValModifier, modifiers)
}
- public fun convertArguments(expression: PsiCallExpression): List {
- val arguments = expression.getArgumentList()?.getExpressions() ?: array()
- val resolved = expression.resolveMethod()
- val expectedTypes = if (resolved != null)
- resolved.getParameterList().getParameters().map { it.getType() }
- else
- listOf()
-
- return if (arguments.size == expectedTypes.size())
- (0..expectedTypes.lastIndex).map { i -> convertExpression(arguments[i], expectedTypes[i]) }
- else
- arguments.map { convertExpression(it) }
- }
-
public fun convertExpression(argument: PsiExpression?, expectedType: PsiType?): Expression {
if (argument == null) return Identifier.Empty
diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt
index 09aeb915730..e06faa4fa56 100644
--- a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt
+++ b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt
@@ -21,41 +21,51 @@ import org.jetbrains.jet.j2k.*
import com.intellij.psi.PsiImportList
import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap
-import org.jetbrains.jet.lang.resolve.name.isValidJavaFqName
-
+import com.intellij.psi.PsiJavaCodeReferenceElement
+import org.jetbrains.jet.asJava.KotlinLightClassForPackage
class Import(val name: String) : Element {
override fun toKotlin() = "import " + name
}
-class ImportList(val imports: List) : Element {
- val filteredImports = imports.filter {
- !it.name.isEmpty() && it.name !in NOT_NULL_ANNOTATIONS && it.name !in NULLABLE_ANNOTATIONS
- }.filter {
- // If name is invalid, like with star imports, don't try to filter
- if (!isValidJavaFqName(it.name))
- true
- else {
- // If imported class has a kotlin analog, drop the import
- val kotlinAnalogsForClass = JavaToKotlinClassMap.getInstance().mapPlatformClass(FqName(it.name))
- kotlinAnalogsForClass.isEmpty()
- }
- }
-
-
+class ImportList(private val imports: List) : Element {
override val isEmpty: Boolean
- get() = filteredImports.isEmpty()
+ get() = imports.isEmpty()
- override fun toKotlin() = filteredImports.toKotlin("\n")
+ override fun toKotlin() = imports.toKotlin("\n")
}
-fun Converter.convertImportList(importList: PsiImportList): ImportList =
- ImportList(importList.getAllImportStatements() map { convertImport(it) })
+public fun Converter.convertImportList(importList: PsiImportList): ImportList =
+ ImportList(importList.getAllImportStatements().map { convertImport(it, true) }.filterNotNull())
-fun Converter.convertImport(i: PsiImportStatementBase): Import {
- val reference = i.getImportReference()
- if (reference != null) {
- return Import(quoteKeywords(reference.getQualifiedName()!!) + if (i.isOnDemand()) ".*" else "")
+public fun Converter.convertImport(anImport: PsiImportStatementBase, filter: Boolean): Import? {
+ val reference = anImport.getImportReference()
+ if (reference == null) return null
+ val qualifiedName = quoteKeywords(reference.getQualifiedName()!!)
+ if (anImport.isOnDemand()) {
+ return Import(qualifiedName + ".*")
}
- return Import("")
-}
\ No newline at end of file
+ else {
+ return if (filter) {
+ val filteredName = filterImport(qualifiedName, reference)
+ if (filteredName != null) Import(filteredName) else null
+ }
+ else {
+ Import(qualifiedName)
+ }
+ }
+}
+
+private fun filterImport(name: String, ref: PsiJavaCodeReferenceElement): String? {
+ if (name in NOT_NULL_ANNOTATIONS || name in NULLABLE_ANNOTATIONS) return null
+
+ // If imported class has a kotlin analog, drop the import
+ if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(FqName(name)).isEmpty()) return null
+
+ val target = ref.resolve()
+ if (target != null && target is KotlinLightClassForPackage) {
+ return quoteKeywords(target.getFqName().parent().toString()) + ".*"
+ }
+
+ return name
+}
diff --git a/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt
index 93ce24e3765..ef5f3c70b3a 100644
--- a/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt
+++ b/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt
@@ -27,6 +27,11 @@ import org.jetbrains.jet.lang.types.lang.PrimitiveType
import org.jetbrains.jet.j2k.*
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod
import org.jetbrains.jet.lang.psi.JetProperty
+import org.jetbrains.jet.lang.psi.JetFunction
+import org.jetbrains.jet.lang.psi.JetClassOrObject
+import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
+import org.jetbrains.jet.lang.psi.psiUtil.isExtensionDeclaration
+import org.jetbrains.jet.lang.psi.JetPropertyAccessor
open class ExpressionVisitor(protected val converter: Converter,
private val usageReplacementMap: Map = mapOf()) : JavaElementVisitor() {
@@ -138,39 +143,73 @@ open class ExpressionVisitor(protected val converter: Converter,
}
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
+ if (expression.isSuperConstructorCall() && expression.isInsidePrimaryConstructor()) return // skip it
+
val methodExpr = expression.getMethodExpression()
val target = methodExpr.resolve()
if (target is KotlinLightMethod) {
- val property = target.origin as? JetProperty
- if (property != null) {
+ val origin = target.origin
+ val isTopLevel = origin?.getParentByType(javaClass(), true) == null
+ if (origin is JetProperty || origin is JetPropertyAccessor) {
+ val property = if (origin is JetProperty) origin else origin.getParent() as JetProperty
val parameterCount = target.getParameterList().getParameters().size
- val argCount = expression.getArgumentList().getExpressions().size
- if (parameterCount == argCount) {
- val qualifier = converter.convertExpression(methodExpr.getQualifierExpression())
+ val arguments = expression.getArgumentList().getExpressions()
+ if (parameterCount == arguments.size) {
val propertyName = Identifier(property.getName()!!, false)
- val propertyAccess = QualifiedExpression(qualifier, propertyName)
- when(parameterCount) {
+ var isExtension = false
+ val propertyAccess = if (isTopLevel) {
+ if (property.isExtensionDeclaration()) {
+ isExtension = true
+ QualifiedExpression(converter.convertExpression(arguments.firstOrNull()), propertyName)
+ }
+ else {
+ propertyName
+ }
+ }
+ else {
+ QualifiedExpression(converter.convertExpression(methodExpr.getQualifierExpression()), propertyName)
+ }
+
+ when(if (isExtension) parameterCount - 1 else parameterCount) {
0 /* getter */ -> {
result = propertyAccess
return
}
1 /* setter */ -> {
- val argument = converter.convertExpression(expression.getArgumentList().getExpressions().single())
+ val argument = converter.convertExpression(arguments[if (isExtension) 1 else 0])
result = AssignmentExpression(propertyAccess, argument, "=")
return
}
}
}
}
+ else if (origin is JetFunction) {
+ if (isTopLevel) {
+ result = if (origin.isExtensionDeclaration()) {
+ val arguments = expression.getArgumentList().getExpressions()
+ val qualifier = converter.convertExpression(arguments.firstOrNull())
+ MethodCallExpression(QualifiedExpression(qualifier, Identifier(origin.getName()!!, false)),
+ convertArguments(expression, isExtension = true),
+ converter.convertTypes(expression.getTypeArguments()),
+ converter.convertType(expression.getType()).isNullable)
+ }
+ else {
+ MethodCallExpression(Identifier(origin.getName()!!, false),
+ convertArguments(expression),
+ converter.convertTypes(expression.getTypeArguments()),
+ converter.convertType(expression.getType()).isNullable)
+ }
+ return
+ }
+
+ }
}
- if (!expression.isSuperConstructorCall() || !expression.isInsidePrimaryConstructor()) {
- result = MethodCallExpression(converter.convertExpression(methodExpr),
- converter.convertArguments(expression),
- converter.convertTypes(expression.getTypeArguments()),
- converter.convertType(expression.getType()).isNullable)
- }
+ result = MethodCallExpression(converter.convertExpression(methodExpr),
+ convertArguments(expression),
+ converter.convertTypes(expression.getTypeArguments()),
+ converter.convertType(expression.getType()).isNullable)
}
override fun visitNewExpression(expression: PsiNewExpression) {
@@ -194,7 +233,7 @@ open class ExpressionVisitor(protected val converter: Converter,
var arguments = argumentList?.getExpressions() ?: array()
if (constructor == null || constructor.isPrimaryConstructor() || isNotConvertedClass) {
return NewClassExpression(converter.convertElement(classReference),
- converter.convertArguments(expression),
+ convertArguments(expression),
converter.convertExpression(expression.getQualifier()),
if (anonymousClass != null) converter.convertAnonymousClass(anonymousClass) else null)
}
@@ -236,16 +275,15 @@ open class ExpressionVisitor(protected val converter: Converter,
}
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
- val containingConstructor = expression.getContainingConstructor()
- val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
- val addReceiver = insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()
-
val isNullable = converter.convertType(expression.getType(), expression.nullability()).isNullable
val referencedName = expression.getReferenceName()!!
var identifier: Expression = Identifier(referencedName, isNullable)
val qualifier = expression.getQualifierExpression()
- if (addReceiver) {
+ val containingConstructor = expression.getContainingConstructor()
+ val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
+
+ if (insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()) {
identifier = QualifiedExpression(Identifier("__", false), Identifier(referencedName, isNullable))
}
else if (insideSecondaryConstructor && expression.isThisConstructorCall()) {
@@ -269,7 +307,7 @@ open class ExpressionVisitor(protected val converter: Converter,
&& target.getContainingClass() != null
&& PsiTreeUtil.getParentOfType(expression, javaClass()) != target.getContainingClass()
&& !isStaticallyImported(target, expression)) {
- var member = target as PsiMember
+ var member: PsiMember = target
var code = Identifier(referencedName).toKotlin()
while (member.getContainingClass() != null) {
code = Identifier(member.getContainingClass()!!.getName()!!).toKotlin() + "." + code
@@ -285,6 +323,7 @@ open class ExpressionVisitor(protected val converter: Converter,
identifier = Identifier(replacement, isNullable)
}
}
+
}
result = QualifiedExpression(converter.convertExpression(qualifier), identifier)
@@ -332,6 +371,23 @@ open class ExpressionVisitor(protected val converter: Converter,
result = PolyadicExpression(parameters, getOperatorString(expression.getOperationTokenType()))
}
+ private fun convertArguments(expression: PsiCallExpression, isExtension: Boolean = false): List {
+ var arguments = expression.getArgumentList()?.getExpressions()?.toList() ?: listOf()
+ if (isExtension && arguments.isNotEmpty()) {
+ arguments = arguments.drop(1)
+ }
+ val resolved = expression.resolveMethod()
+ val expectedTypes = if (resolved != null)
+ resolved.getParameterList().getParameters().map { it.getType() }
+ else
+ listOf()
+
+ return if (arguments.size == expectedTypes.size())
+ (0..expectedTypes.lastIndex).map { i -> converter.convertExpression(arguments[i], expectedTypes[i]) }
+ else
+ arguments.map { converter.convertExpression(it) }
+ }
+
private fun getOperatorString(tokenType: IElementType): String {
return when(tokenType) {
JavaTokenType.EQEQ -> "=="
diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java
index 3dcbfc9bb01..3650d0e49f9 100644
--- a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java
+++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java
@@ -1556,6 +1556,36 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/kotlinApiAccess"), Pattern.compile("^(.+)\\.java$"), true);
}
+ @TestMetadata("ExtensionFunction.java")
+ public void testExtensionFunction() throws Exception {
+ doTest("j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.java");
+ }
+
+ @TestMetadata("ExtensionProperty.java")
+ public void testExtensionProperty() throws Exception {
+ doTest("j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.java");
+ }
+
+ @TestMetadata("GlobalFunction.java")
+ public void testGlobalFunction() throws Exception {
+ doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.java");
+ }
+
+ @TestMetadata("GlobalGenericFunction.java")
+ public void testGlobalGenericFunction() throws Exception {
+ doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.java");
+ }
+
+ @TestMetadata("GlobalValue1.java")
+ public void testGlobalValue1() throws Exception {
+ doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.java");
+ }
+
+ @TestMetadata("GlobalValue2.java")
+ public void testGlobalValue2() throws Exception {
+ doTest("j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.java");
+ }
+
@TestMetadata("InheritedProperty.java")
public void testInheritedProperty() throws Exception {
doTest("j2k/tests/testData/ast/kotlinApiAccess/InheritedProperty.java");
diff --git a/j2k/tests/testData/KotlinApi.kt b/j2k/tests/testData/KotlinApi.kt
index 2e026a7240e..098ec57d4f6 100644
--- a/j2k/tests/testData/KotlinApi.kt
+++ b/j2k/tests/testData/KotlinApi.kt
@@ -1,5 +1,19 @@
package kotlinApi
public open class KotlinClass {
- public var property: String
-}
\ No newline at end of file
+ public var property: String = ""
+}
+
+public fun globalFunction(s: String): String = s
+public fun globalGenericFunction(t: T): T = t
+
+public fun Int.extensionFunction(): String = toString()
+
+public var globalValue1: Int = 1
+public var globalValue2: Int
+ get() = 0
+ set(value) {}
+
+public var String.extensionProperty: Int
+ get() = 1
+ set(value) {}
\ No newline at end of file
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.java b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.java
new file mode 100644
index 00000000000..7dcded5dde4
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.java
@@ -0,0 +1,8 @@
+//file
+import kotlinApi.KotlinApiPackage;
+
+class C {
+ void foo() {
+ String s = KotlinApiPackage.extensionFunction(1);
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.kt b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.kt
new file mode 100644
index 00000000000..058b88e1dd0
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionFunction.kt
@@ -0,0 +1,7 @@
+import kotlinApi.*
+
+class C() {
+ fun foo() {
+ val s = 1.extensionFunction()
+ }
+}
\ No newline at end of file
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.java b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.java
new file mode 100644
index 00000000000..287bf3b0445
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.java
@@ -0,0 +1,9 @@
+//file
+import kotlinApi.KotlinApiPackage;
+
+class C {
+ int foo() {
+ KotlinApiPackage.setExtensionProperty("a", 1);
+ return KotlinApiPackage.getExtensionProperty("b");
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.kt b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.kt
new file mode 100644
index 00000000000..c6a11c8bddb
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/ExtensionProperty.kt
@@ -0,0 +1,8 @@
+import kotlinApi.*
+
+class C() {
+ fun foo(): Int {
+ "a".extensionProperty = 1
+ return "b".extensionProperty
+ }
+}
\ No newline at end of file
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.java b/j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.java
new file mode 100644
index 00000000000..127088a30a3
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.java
@@ -0,0 +1,8 @@
+//file
+import kotlinApi.KotlinApiPackage;
+
+class C {
+ void foo() {
+ String s = KotlinApiPackage.globalFunction("x");
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.kt b/j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.kt
new file mode 100644
index 00000000000..dfd478dfe49
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalFunction.kt
@@ -0,0 +1,7 @@
+import kotlinApi.*
+
+class C() {
+ fun foo() {
+ val s = globalFunction("x")
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.java b/j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.java
new file mode 100644
index 00000000000..be75de65a3a
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.java
@@ -0,0 +1,8 @@
+//file
+import kotlinApi.KotlinApiPackage;
+
+class C {
+ void foo() {
+ int v = KotlinApiPackage.globalGenericFunction(1);
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.kt b/j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.kt
new file mode 100644
index 00000000000..1962cd451ea
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalGenericFunction.kt
@@ -0,0 +1,7 @@
+import kotlinApi.*
+
+class C() {
+ fun foo() {
+ val v = globalGenericFunction(1)!!
+ }
+}
\ No newline at end of file
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.java b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.java
new file mode 100644
index 00000000000..2135ee3ab95
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.java
@@ -0,0 +1,9 @@
+//file
+import kotlinApi.KotlinApiPackage;
+
+class C {
+ int foo() {
+ KotlinApiPackage.setGlobalValue1(0)
+ return KotlinApiPackage.getGlobalValue1();
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.kt b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.kt
new file mode 100644
index 00000000000..aa88eef973f
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue1.kt
@@ -0,0 +1,8 @@
+import kotlinApi.*
+
+class C() {
+ fun foo(): Int {
+ globalValue1 = 0
+ return globalValue1
+ }
+}
\ No newline at end of file
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.java b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.java
new file mode 100644
index 00000000000..81ebe699755
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.java
@@ -0,0 +1,9 @@
+//file
+import kotlinApi.KotlinApiPackage;
+
+class C {
+ int foo() {
+ KotlinApiPackage.setGlobalValue2(0)
+ return KotlinApiPackage.getGlobalValue2();
+ }
+}
diff --git a/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.kt b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.kt
new file mode 100644
index 00000000000..84e96eceb3e
--- /dev/null
+++ b/j2k/tests/testData/ast/kotlinApiAccess/GlobalValue2.kt
@@ -0,0 +1,8 @@
+import kotlinApi.*
+
+class C() {
+ fun foo(): Int {
+ globalValue2 = 0
+ return globalValue2
+ }
+}
\ No newline at end of file