提交 22142ee4 编写于 作者: M Michael Bogdanov

Initial support of JvmField annotation

上级 6cf9bfdb
......@@ -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;
......
......@@ -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<StackValue, StackValue> 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);
}
}
......
......@@ -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;
......
......@@ -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
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
......@@ -2,6 +2,6 @@
class C {
companion object {
@[kotlin.jvm.JvmField] private val foo: String = "A"
@[kotlin.jvm.JvmField] public val foo: String = "A"
}
}
public final class C {
@kotlin.jvm.JvmField
@org.jetbrains.annotations.NotNull
public final java.lang.String foo = "A";
public C() { /* compiled code */ }
......
// C
class C {
@[kotlin.jvm.JvmField] private val foo: String = "A"
@[kotlin.jvm.JvmField] public val foo: String = "A"
}
class C {
@JvmField private val foo: String = "OK"
@JvmField public val foo: String = "OK"
}
fun box(): String {
......
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
@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
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
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 <T, R> checkAccessor(prop: KProperty1<T, R>, value: R, receiver: T) {
assertEquals(prop.get(receiver), value, "Property ${prop} has wrong value")
}
\ No newline at end of file
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<A, String>, s1, "3", this)
checkAccessor(A::class.members.firstOrNull { it.name == "internalField" } as KMutableProperty1<A, String>, 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<AWithCompanion.Companion, String>, "1", "3", AWithCompanion.Companion)
checkAccessor(AWithCompanion.Companion::class.members.firstOrNull { it.name == "internalField" } as KMutableProperty1<AWithCompanion.Companion, String>, "2", "4", AWithCompanion.Companion)
}
}
}
fun box(): String {
A("1", "2").testAccessors()
AWithCompanion.testAccessors()
return "OK"
}
public fun <T, R> checkAccessor(prop: KMutableProperty1<T, R>, 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
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
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
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
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<String> = ::publicField
checkAccessor(kProperty, "1")
checkAccessor(::internalField, "2")
}
fun box(): String {
testAccessors()
return "OK"
}
public fun <T, R> checkAccessor(prop: KProperty0<T>, value: R) {
assertEquals(prop.get(), value, "Property ${prop} has wrong value")
}
\ No newline at end of file
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<String>, "1", "3")
checkAccessor(packageClass.getDeclaredField("internalField").kotlinProperty as KMutableProperty0<String>, "2", "4")
}
fun box(): String {
testAccessors()
return "OK"
}
public fun < R> checkAccessor(prop: KMutableProperty0<R>, 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
// 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
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, String> = A::publicField
checkAccessor(kMutableProperty, s1, "3", this)
checkAccessor(A::internalField, s2, "4", this)
}
}
fun box(): String {
A("1", "2").testAccessors()
return "OK"
}
public fun <T, R> checkAccessor(prop: KMutableProperty1<T, R>, 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
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
open class C : B() {
fun test(): String {
return super.publicField + super.internalField + super.protectedfield
}
}
fun main(args: Array<String>) {
C().test()
}
\ No newline at end of file
......@@ -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)
......
......@@ -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");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册