diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index a421c248719..b289b047db9 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.j2k import com.intellij.openapi.project.Project import com.intellij.psi.* import com.intellij.psi.CommonClassNames.* -import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.InheritanceUtil import com.intellij.psi.util.PsiMethodUtil import com.intellij.psi.util.PsiTreeUtil @@ -352,7 +351,10 @@ class Converter private constructor( if (propertyInfo.needExplicitGetter) { if (getMethod != null) { val method = convertMethod(getMethod, null, null, null, classKind)!! - getter = PropertyAccessor(AccessorKind.GETTER, method.annotations, Modifiers.Empty, method.parameterList, method.body) + if (method.modifiers.contains(Modifier.EXTERNAL)) + getter = PropertyAccessor(AccessorKind.GETTER, method.annotations, Modifiers(listOf(Modifier.EXTERNAL)).assignNoPrototype(), null, null) + else + getter = PropertyAccessor(AccessorKind.GETTER, method.annotations, Modifiers.Empty, method.parameterList, method.body) getter.assignPrototype(getMethod, CommentsAndSpacesInheritance.NO_SPACES) } else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) { @@ -374,18 +376,22 @@ class Converter private constructor( if (propertyInfo.needExplicitSetter) { val accessorModifiers = Modifiers(propertyInfo.specialSetterAccess.singletonOrEmptyList()).assignNoPrototype() if (setMethod != null && !propertyInfo.isSetMethodBodyFieldAccess) { - val method = setMethod.let { convertMethod(it, null, null, null, classKind)!! } - val convertedParameter = method.parameterList!!.parameters.single() as FunctionParameter - val parameterAnnotations = convertedParameter.annotations - val parameterList = if (method.body != null || !parameterAnnotations.isEmpty) { - val parameter = FunctionParameter(convertedParameter.identifier, null, FunctionParameter.VarValModifier.None, parameterAnnotations, Modifiers.Empty) - .assignPrototypesFrom(convertedParameter, CommentsAndSpacesInheritance.NO_SPACES) - ParameterList.withNoPrototype(listOf(parameter)) - } + val method = convertMethod(setMethod, null, null, null, classKind)!! + if (method.modifiers.contains(Modifier.EXTERNAL)) + setter = PropertyAccessor(AccessorKind.SETTER, method.annotations, accessorModifiers.with(Modifier.EXTERNAL), null, null) else { - null + val convertedParameter = method.parameterList!!.parameters.single() as FunctionParameter + val parameterAnnotations = convertedParameter.annotations + val parameterList = if (method.body != null || !parameterAnnotations.isEmpty) { + val parameter = FunctionParameter(convertedParameter.identifier, null, FunctionParameter.VarValModifier.None, parameterAnnotations, Modifiers.Empty) + .assignPrototypesFrom(convertedParameter, CommentsAndSpacesInheritance.NO_SPACES) + ParameterList.withNoPrototype(listOf(parameter)) + } + else { + null + } + setter = PropertyAccessor(AccessorKind.SETTER, method.annotations, accessorModifiers, parameterList, method.body) } - setter = PropertyAccessor(AccessorKind.SETTER, method.annotations, accessorModifiers, parameterList, method.body) setter.assignPrototype(setMethod, CommentsAndSpacesInheritance.NO_SPACES) } else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) { @@ -726,6 +732,9 @@ class Converter private constructor( modifiers = modifiers.with(Modifier.OPEN) } + if (owner.hasModifierProperty(PsiModifier.NATIVE)) + modifiers = modifiers.with(Modifier.EXTERNAL) + modifiers = modifiers.adaptForContainingClassVisibility(owner.containingClass).adaptProtectedVisibility(owner) } else if (owner is PsiField) { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Modifier.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Modifier.kt index d6577b33272..0fad1c2639a 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Modifier.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Modifier.kt @@ -27,6 +27,7 @@ enum class Modifier(private val str: String) { ABSTRACT("abstract"), OPEN("open"), OVERRIDE("override"), + EXTERNAL("external"), INNER("inner"); fun toKotlin(): String = str diff --git a/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt b/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt index 92e48ffe1f1..a72af8b4115 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt @@ -60,7 +60,12 @@ class PropertyInfo( //TODO: what if annotations are not empty? val needExplicitGetter: Boolean get() { - if (getMethod != null && getMethod.body != null && !isGetMethodBodyFieldAccess) return true + if (getMethod != null) { + if (getMethod.hasModifierProperty(PsiModifier.NATIVE)) + return true + if (getMethod.body != null && !isGetMethodBodyFieldAccess) + return true + } return modifiers.contains(Modifier.OVERRIDE) && this.field == null && !modifiers.contains(Modifier.ABSTRACT) } @@ -69,8 +74,13 @@ class PropertyInfo( get() { if (!isVar) return false if (specialSetterAccess != null) return true - if (setMethod != null && setMethod.body != null && !isSetMethodBodyFieldAccess) return true - return modifiers.contains(Modifier.OVERRIDE) && this.field == null && !modifiers.contains(Modifier.ABSTRACT) + if (setMethod != null) { + if (setMethod.hasModifierProperty(PsiModifier.NATIVE)) + return true + if (setMethod.body != null && !isSetMethodBodyFieldAccess) + return true + } + return modifiers.contains(Modifier.EXTERNAL) || modifiers.contains(Modifier.OVERRIDE) && this.field == null && !modifiers.contains(Modifier.ABSTRACT) } companion object { diff --git a/j2k/testData/fileOrElement/function/nativeMethods.java b/j2k/testData/fileOrElement/function/nativeMethods.java new file mode 100644 index 00000000000..cbbe7bb009d --- /dev/null +++ b/j2k/testData/fileOrElement/function/nativeMethods.java @@ -0,0 +1,6 @@ +public class Foo { + private native final void nativeMethod(); + + public native final int getBar(); + public native final void setBar(int bar); +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/nativeMethods.kt b/j2k/testData/fileOrElement/function/nativeMethods.kt new file mode 100644 index 00000000000..7c98e8c8e56 --- /dev/null +++ b/j2k/testData/fileOrElement/function/nativeMethods.kt @@ -0,0 +1,8 @@ +// ERROR: Property must be initialized or be abstract +class Foo { + private external fun nativeMethod() + + var bar: Int + external get + external set +} \ 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 470f37734ac..d8643390d4e 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -2449,6 +2449,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("nativeMethods.java") + public void testNativeMethods() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/nativeMethods.java"); + doTest(fileName); + } + @TestMetadata("open.java") public void testOpen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/open.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 76ed24158c3..93a71395711 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -2449,6 +2449,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("nativeMethods.java") + public void testNativeMethods() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/nativeMethods.java"); + doTest(fileName); + } + @TestMetadata("open.java") public void testOpen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/function/open.java");