diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 5c14e8f48b4..a20c9e76182 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -69,6 +69,7 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface; import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KOTLIN_SYNTHETIC_CLASS; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; +import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation; import static org.jetbrains.kotlin.types.TypeUtils.isNullableType; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -239,9 +240,13 @@ public class AsmUtil { if (specialCase != null) { return specialCase; } - Integer defaultMapping = visibilityToAccessFlag.get(descriptor.getVisibility()); + return getDefaultVisibilityFlag(descriptor.getVisibility()); + } + + public static int getDefaultVisibilityFlag(@NotNull Visibility visibility) { + Integer defaultMapping = visibilityToAccessFlag.get(visibility); if (defaultMapping == null) { - throw new IllegalStateException(descriptor.getVisibility() + " is not a valid visibility in backend: " + descriptor); + throw new IllegalStateException(visibility + " is not a valid visibility in backend"); } return defaultMapping; } @@ -374,7 +379,10 @@ public class AsmUtil { } } - if (memberDescriptor instanceof PropertyDescriptor && ((PropertyDescriptor) memberDescriptor).isConst()) return null; + if (memberDescriptor instanceof PropertyDescriptor && + ((PropertyDescriptor) memberDescriptor).isConst() || hasJvmFieldAnnotation(memberDescriptor)) { + return null; + } if (containingDeclaration instanceof PackageFragmentDescriptor) { return ACC_PUBLIC; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 0955ff9f3b1..1b700a0f99d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -104,6 +104,7 @@ import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getRes import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCallWithAssert; import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; +import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.TraitImpl; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -2211,14 +2212,16 @@ public class ExpressionCodegen extends JetVisitor implem propertyDescriptor = context.accessibleDescriptor(propertyDescriptor, superExpression); PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); - if (getter != null) { + if (getter != null && !hasJvmFieldAnnotation(propertyDescriptor)) { callableGetter = typeMapper.mapToCallableMethod(getter, isSuper); } } if (propertyDescriptor.isVar()) { PropertySetterDescriptor setter = propertyDescriptor.getSetter(); - if (setter != null && !couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context)) { + if (setter != null && + !couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context) && + !hasJvmFieldAnnotation(propertyDescriptor)) { callableSetter = typeMapper.mapToCallableMethod(setter, isSuper); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index 30706135fd3..874bec87cd4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -58,9 +58,8 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface; import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface; -import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterfaceCompanionObject; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.PROPERTY_METADATA_TYPE; -import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.findJvmFieldAnnotation; +import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -153,6 +152,8 @@ public class PropertyCodegen { @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor accessor ) { + if (hasJvmFieldAnnotation(descriptor)) return false; + boolean isDefaultAccessor = accessor == null || !accessor.hasBody(); // Don't generate accessors for trait properties with default accessors in TRAIT_IMPL @@ -303,7 +304,7 @@ public class PropertyCodegen { ClassBuilder builder = v; - boolean hasJvmFieldAnnotation = findJvmFieldAnnotation(propertyDescriptor) != null; + boolean hasJvmFieldAnnotation = hasJvmFieldAnnotation(propertyDescriptor); FieldOwnerContext backingFieldContext = context; boolean takeVisibilityFromDescriptor = propertyDescriptor.isLateInit() || propertyDescriptor.isConst(); @@ -314,7 +315,7 @@ public class PropertyCodegen { modifiers |= getVisibilityAccessFlag(propertyDescriptor); } else if (hasJvmFieldAnnotation && !isDelegate) { - modifiers |= ACC_PUBLIC; + modifiers |= getDefaultVisibilityFlag(propertyDescriptor.getVisibility()); } else { modifiers |= getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate); @@ -331,7 +332,7 @@ public class PropertyCodegen { modifiers |= getVisibilityAccessFlag(propertyDescriptor); } else if (!isDelegate && hasJvmFieldAnnotation) { - modifiers |= ACC_PUBLIC; + modifiers |= getDefaultVisibilityFlag(propertyDescriptor.getVisibility()); } else if (kind != OwnerKind.PACKAGE || isDelegate) { modifiers |= ACC_PRIVATE; diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotationUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotationUtil.kt index 7a80f18b954..d9985d8fb5e 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotationUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotationUtil.kt @@ -16,14 +16,32 @@ package org.jetbrains.kotlin.resolve.jvm.annotations +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.DescriptorUtils public fun DeclarationDescriptor.hasJvmOverloadsAnnotation(): Boolean { return getAnnotations().findAnnotation(FqName("kotlin.jvm.JvmOverloads")) != null } public fun DeclarationDescriptor.findJvmFieldAnnotation(): AnnotationDescriptor? { - return annotations.findAnnotation(FqName("kotlin.jvm.JvmField")) + val fqName = FqName("kotlin.jvm.JvmField") + val annotation = annotations.findAnnotation(fqName) + if (annotation != null) { + return annotation; + } + + return annotations.getUseSiteTargetedAnnotations().asSequence().filter { + it.target == AnnotationUseSiteTarget.FIELD + }.map { it.annotation }.firstOrNull { + val descriptor = it.getType().getConstructor().getDeclarationDescriptor() + descriptor is ClassDescriptor && fqName.toUnsafe() == DescriptorUtils.getFqName(descriptor) + } +} + +public fun DeclarationDescriptor.hasJvmFieldAnnotation(): Boolean { + return findJvmFieldAnnotation() != null } \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java b/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java index 6f869b8c056..7417ae71d71 100644 --- a/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java +++ b/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java @@ -1,5 +1,6 @@ public final class C { @kotlin.jvm.JvmField + @org.jetbrains.annotations.NotNull public static final java.lang.String foo = "A"; public static final C.Companion Companion; @@ -8,8 +9,6 @@ public final class C { public static final class Companion { public static final C.Companion INSTANCE; - private final java.lang.String getFoo() { /* compiled code */ } - private Companion() { /* compiled code */ } } } \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/publicField/CompanionObject.kt b/compiler/testData/asJava/lightClasses/publicField/CompanionObject.kt index e453f391ce8..727d11a62b0 100644 --- a/compiler/testData/asJava/lightClasses/publicField/CompanionObject.kt +++ b/compiler/testData/asJava/lightClasses/publicField/CompanionObject.kt @@ -2,6 +2,6 @@ class C { companion object { - @[kotlin.jvm.JvmField] private val foo: String = "A" + @[kotlin.jvm.JvmField] public val foo: String = "A" } } diff --git a/compiler/testData/asJava/lightClasses/publicField/Simple.java b/compiler/testData/asJava/lightClasses/publicField/Simple.java index 333cdc060f6..0b8485d6dc6 100644 --- a/compiler/testData/asJava/lightClasses/publicField/Simple.java +++ b/compiler/testData/asJava/lightClasses/publicField/Simple.java @@ -1,5 +1,6 @@ public final class C { @kotlin.jvm.JvmField + @org.jetbrains.annotations.NotNull public final java.lang.String foo = "A"; public C() { /* compiled code */ } diff --git a/compiler/testData/asJava/lightClasses/publicField/Simple.kt b/compiler/testData/asJava/lightClasses/publicField/Simple.kt index cd81bc19384..726a40a0cf9 100644 --- a/compiler/testData/asJava/lightClasses/publicField/Simple.kt +++ b/compiler/testData/asJava/lightClasses/publicField/Simple.kt @@ -1,5 +1,5 @@ // C class C { - @[kotlin.jvm.JvmField] private val foo: String = "A" + @[kotlin.jvm.JvmField] public val foo: String = "A" } diff --git a/compiler/testData/codegen/boxWithJava/jvmField/simple/simple.kt b/compiler/testData/codegen/boxWithJava/jvmField/simple/simple.kt index 027ee17ad19..55a5b7ad4e3 100644 --- a/compiler/testData/codegen/boxWithJava/jvmField/simple/simple.kt +++ b/compiler/testData/codegen/boxWithJava/jvmField/simple/simple.kt @@ -1,5 +1,5 @@ class C { - @JvmField private val foo: String = "OK" + @JvmField public val foo: String = "OK" } fun box(): String { diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/captureClassFields.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/captureClassFields.kt new file mode 100644 index 00000000000..a9572ef78bc --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/captureClassFields.kt @@ -0,0 +1,16 @@ +open class A { + @JvmField public val publicField = "1"; + @JvmField internal val internalField = "2"; + @JvmField protected val protectedField = "34"; + + fun test(): String { + return { + publicField + internalField + protectedField + }() + } +} + + +fun box(): String { + return if (A().test() == "1234") return "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/capturePackageFields.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/capturePackageFields.kt new file mode 100644 index 00000000000..1438479c804 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/capturePackageFields.kt @@ -0,0 +1,13 @@ +@JvmField public val publicField = "1"; +@JvmField internal val internalField = "23"; + +fun test(): String { + return { + publicField + internalField + }() +} + + +fun box(): String { + return if (test() == "123") return "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/checkNoAccessors.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/checkNoAccessors.kt new file mode 100644 index 00000000000..d02020609be --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/checkNoAccessors.kt @@ -0,0 +1,35 @@ +import kotlin.test.assertFalse + +@JvmField public val field = "OK"; + +class A { + @JvmField public val field = "OK"; + + companion object { + @JvmField public val cfield = "OK"; + } +} + +object Object { + @JvmField public val field = "OK"; +} + + +fun box(): String { + var result = A().field + + checkNoAccessors(A::class.java) + checkNoAccessors(A.Companion::class.java) + checkNoAccessors(Object::class.java) + checkNoAccessors(Class.forName("CheckNoAccessorsKt")) + + return "OK" +} + +public fun checkNoAccessors(clazz: Class<*>) { + clazz.declaredMethods.forEach { + assertFalse(it.name.startsWith("get") || it.name.startsWith("set"), + "Class ${clazz.name} has accessor '${it.name}'" + ) + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReference.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReference.kt new file mode 100644 index 00000000000..f76bcc7109a --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReference.kt @@ -0,0 +1,38 @@ +package zzz +import java.lang.reflect.Field +import kotlin.reflect.jvm.javaField +import kotlin.reflect.KProperty1 +import kotlin.test.assertEquals + +class A(val s1: String, val s2: String) { + @JvmField public val publicField = s1; + @JvmField internal val internalField = s2; + + fun testAccessors() { + checkAccessor(A::publicField, s1, this) + checkAccessor(A::internalField, s2, this) + } +} + + +class AWithCompanion { + companion object { + @JvmField public val publicField = "1"; + @JvmField internal val internalField = "2"; + + fun testAccessors() { + checkAccessor(AWithCompanion.Companion::publicField, "1", AWithCompanion.Companion) + checkAccessor(AWithCompanion.Companion::internalField, "2", AWithCompanion.Companion) + } + } +} + +fun box(): String { + A("1", "2").testAccessors() + AWithCompanion.testAccessors() + return "OK" +} + +public fun checkAccessor(prop: KProperty1, value: R, receiver: T) { + assertEquals(prop.get(receiver), value, "Property ${prop} has wrong value") +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReflection.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReflection.kt new file mode 100644 index 00000000000..24ebe84280e --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReflection.kt @@ -0,0 +1,44 @@ +package zzz +import java.lang.reflect.Field +import kotlin.reflect.jvm.javaField +import kotlin.reflect.KProperty1 +import kotlin.test.assertEquals + + +import kotlin.reflect.KMutableProperty1 +import kotlin.test.assertEquals + +class A(val s1: String, val s2: String) { + @JvmField public var publicField = s1; + @JvmField internal var internalField = s2; + + fun testAccessors() { + checkAccessor(A::class.members.firstOrNull { it.name == "publicField" } as KMutableProperty1, s1, "3", this) + checkAccessor(A::class.members.firstOrNull { it.name == "internalField" } as KMutableProperty1, s2, "4", this) + } +} + + +class AWithCompanion { + companion object { + @JvmField public var publicField = "1"; + @JvmField internal var internalField = "2"; + + fun testAccessors() { + checkAccessor(AWithCompanion.Companion::class.members.firstOrNull { it.name == "publicField" } as KMutableProperty1, "1", "3", AWithCompanion.Companion) + checkAccessor(AWithCompanion.Companion::class.members.firstOrNull { it.name == "internalField" } as KMutableProperty1, "2", "4", AWithCompanion.Companion) + } + } +} + +fun box(): String { + A("1", "2").testAccessors() + AWithCompanion.testAccessors() + return "OK" +} + +public fun checkAccessor(prop: KMutableProperty1, value: R, newValue: R, receiver: T) { + assertEquals(prop.get(receiver), value, "Property ${prop} has wrong value") + prop.set(receiver, newValue) + assertEquals(prop.get(receiver), newValue, "Property ${prop} has wrong value") +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/publicField.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/publicField.kt new file mode 100644 index 00000000000..813d451fbca --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/publicField.kt @@ -0,0 +1,23 @@ +class A { + @JvmField public val field = "OK"; + + companion object { + @JvmField public val cfield = "OK"; + } +} + +object Object { + @JvmField public val field = "OK"; +} + + +fun box(): String { + var result = A().field + + if (result != "OK") return "fail 1: $result" + if (A.cfield != "OK") return "fail 2: ${A.cfield}" + if (Object.field != "OK") return "fail 3: ${Object.field}" + + return "OK" + +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/superCall.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/superCall.kt new file mode 100644 index 00000000000..d98a7d1758b --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/superCall.kt @@ -0,0 +1,17 @@ +open class A { + @JvmField public val publicField = "1"; + @JvmField internal val internalField = "2"; + @JvmField protected val protectedfield = "3"; +} + + +class B : A() { + fun test(): String { + return super.publicField + super.internalField + super.protectedfield + } +} + + +fun box(): String { + return if (B().test() == "123") return "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/superCall2.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/superCall2.kt new file mode 100644 index 00000000000..502967ce22c --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/superCall2.kt @@ -0,0 +1,20 @@ +open class A { + @JvmField public val publicField = "1"; + @JvmField internal val internalField = "2"; + @JvmField protected val protectedfield = "3"; +} + +open class B : A() { + +} + +open class C : B() { + fun test(): String { + return super.publicField + super.internalField + super.protectedfield + } +} + + +fun box(): String { + return if (C().test() == "123") return "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReference.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReference.kt new file mode 100644 index 00000000000..6a6ad8e3aaa --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReference.kt @@ -0,0 +1,24 @@ +package zzz +import java.lang.reflect.Field +import kotlin.reflect.jvm.javaField +import kotlin.test.assertEquals +import kotlin.reflect.KProperty0 + +@JvmField public val publicField = "1"; +@JvmField internal val internalField = "2"; + +fun testAccessors() { + val kProperty: KProperty0 = ::publicField + checkAccessor(kProperty, "1") + checkAccessor(::internalField, "2") +} + + +fun box(): String { + testAccessors() + return "OK" +} + +public fun checkAccessor(prop: KProperty0, value: R) { + assertEquals(prop.get(), value, "Property ${prop} has wrong value") +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReflection.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReflection.kt new file mode 100644 index 00000000000..a7dc0da54b8 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReflection.kt @@ -0,0 +1,27 @@ +package test + +import kotlin.reflect.KMutableProperty0 +import kotlin.reflect.jvm.kotlinProperty +import kotlin.test.assertEquals + +public var publicField = "1" +internal var internalField = "2" + +fun testAccessors() { + val packageClass = Class.forName("test.TopLevelFieldReflectionKt") + packageClass.getDeclaredField("publicField").kotlinProperty + checkAccessor(packageClass.getDeclaredField("publicField").kotlinProperty as KMutableProperty0, "1", "3") + checkAccessor(packageClass.getDeclaredField("internalField").kotlinProperty as KMutableProperty0, "2", "4") +} + + +fun box(): String { + testAccessors() + return "OK" +} + +public fun < R> checkAccessor(prop: KMutableProperty0, value: R, newValue: R) { + assertEquals(prop.get(), value, "Property ${prop} has wrong value") + prop.set(newValue) + assertEquals(prop.get(), newValue, "Property ${prop} has wrong value") +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/visibility.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/visibility.kt new file mode 100644 index 00000000000..7f8dfb892ea --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/visibility.kt @@ -0,0 +1,64 @@ +// FULL_JDK + +import java.lang.reflect.Field +import kotlin.reflect.jvm.javaField +import kotlin.test.assertNotEquals +import java.lang.reflect.Modifier + +@JvmField public val publicField = "OK"; +@JvmField internal val internalField = "OK"; + +fun testVisibilities() { + checkVisibility(::publicField.javaField!!, Modifier.PUBLIC) + checkVisibility(::internalField.javaField!!, Modifier.PUBLIC) +} + +class A { + @JvmField public val publicField = "OK"; + @JvmField internal val internalField = "OK"; + @JvmField protected val protectedfield = "OK"; + + fun testVisibilities() { + checkVisibility(A::publicField.javaField!!, Modifier.PUBLIC) + checkVisibility(A::internalField.javaField!!, Modifier.PUBLIC) + checkVisibility(A::protectedfield.javaField!!, Modifier.PROTECTED) + } +} + + +class AWithCompanion { + companion object { + @JvmField public val publicField = "OK"; + @JvmField internal val internalField = "OK"; + @JvmField protected val protectedfield = "OK"; + + fun testVisibilities() { + checkVisibility(AWithCompanion.Companion::publicField.javaField!!, Modifier.PUBLIC) + checkVisibility(AWithCompanion.Companion::internalField.javaField!!, Modifier.PUBLIC) + checkVisibility(AWithCompanion.Companion::protectedfield.javaField!!, Modifier.PROTECTED) + } + } +} + +object Object { + @JvmField public val publicField = "OK"; + @JvmField internal val internalField = "OK"; + @JvmField protected val protectedfield = "OK"; + + fun testVisibilities() { + checkVisibility(Object::publicField.javaField!!, Modifier.PUBLIC) + checkVisibility(Object::internalField.javaField!!, Modifier.PUBLIC) + checkVisibility(Object::protectedfield.javaField!!, Modifier.PROTECTED) + } +} + +fun box(): String { + A().testVisibilities() + AWithCompanion.testVisibilities() + Object.testVisibilities() + return "OK" +} + +public fun checkVisibility(field: Field, visibility: Int) { + assertNotEquals(field.modifiers and visibility, 0, "Field ${field} has wrong visibility") +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/jvmField/writeFieldReference.kt b/compiler/testData/codegen/boxWithStdlib/jvmField/writeFieldReference.kt new file mode 100644 index 00000000000..2e38cf00e14 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmField/writeFieldReference.kt @@ -0,0 +1,25 @@ +package zzz +import kotlin.reflect.KMutableProperty1 +import kotlin.test.assertEquals + +class A(val s1: String, val s2: String) { + @JvmField public var publicField = s1; + @JvmField internal var internalField = s2; + + fun testAccessors() { + val kMutableProperty: KMutableProperty1 = A::publicField + checkAccessor(kMutableProperty, s1, "3", this) + checkAccessor(A::internalField, s2, "4", this) + } +} + +fun box(): String { + A("1", "2").testAccessors() + return "OK" +} + +public fun checkAccessor(prop: KMutableProperty1, value: R, newValue: R, receiver: T) { + assertEquals(prop.get(receiver), value, "Property ${prop} has wrong value") + prop.set(receiver, newValue) + assertEquals(prop.get(receiver), newValue, "Property ${prop} has wrong value") +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstKotlin/jvmField.A.kt b/compiler/testData/compileKotlinAgainstKotlin/jvmField.A.kt new file mode 100644 index 00000000000..4232fdb442c --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/jvmField.A.kt @@ -0,0 +1,9 @@ +open class A { + @JvmField public val publicField = "1"; + @JvmField internal val internalField = "2"; + @JvmField protected val protectedfield = "3"; +} + +open class B : A() { + +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstKotlin/jvmField.B.kt b/compiler/testData/compileKotlinAgainstKotlin/jvmField.B.kt new file mode 100644 index 00000000000..375138cbd7e --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/jvmField.B.kt @@ -0,0 +1,9 @@ +open class C : B() { + fun test(): String { + return super.publicField + super.internalField + super.protectedfield + } +} + +fun main(args: Array) { + C().test() +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 7a878d4372e..c7f0c494013 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2006,6 +2006,87 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode } } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/jvmField") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmField extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInJvmField() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/jvmField"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("captureClassFields.kt") + public void testCaptureClassFields() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/captureClassFields.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("capturePackageFields.kt") + public void testCapturePackageFields() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/capturePackageFields.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("checkNoAccessors.kt") + public void testCheckNoAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/checkNoAccessors.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("classFieldReference.kt") + public void testClassFieldReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReference.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("classFieldReflection.kt") + public void testClassFieldReflection() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/classFieldReflection.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("publicField.kt") + public void testPublicField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/publicField.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("superCall.kt") + public void testSuperCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/superCall.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("superCall2.kt") + public void testSuperCall2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/superCall2.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("topLevelFieldReference.kt") + public void testTopLevelFieldReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReference.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("topLevelFieldReflection.kt") + public void testTopLevelFieldReflection() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/topLevelFieldReflection.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("visibility.kt") + public void testVisibility() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/visibility.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("writeFieldReference.kt") + public void testWriteFieldReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmField/writeFieldReference.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java index 36b65fa7137..99b64eeff34 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java @@ -101,6 +101,12 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl doTest(fileName); } + @TestMetadata("jvmField.A.kt") + public void testJvmField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/jvmField.A.kt"); + doTest(fileName); + } + @TestMetadata("JvmNameOnAccessor.A.kt") public void testJvmNameOnAccessor() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/JvmNameOnAccessor.A.kt");