From 132d74200b420ea642719b888cee431ecaa1cae4 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Wed, 19 Jun 2013 15:27:11 +0400 Subject: [PATCH] Partial fix for KT-3698: properly write field initializer --- .../codegen/ImplementationBodyCodegen.java | 45 +++++++++++++++---- .../jet/codegen/PropertyCodegen.java | 8 ++-- .../nullablePrimitiveNoFieldInitializer.kt | 11 +++++ .../staticFields/AnnotationClass.java | 10 +++++ .../staticFields/AnnotationClass.kt | 23 ++++++++++ .../staticFields/AnnotationTrait.java | 10 +++++ .../staticFields/AnnotationTrait.kt | 23 ++++++++++ .../staticFields/kt3698.java | 11 +++++ .../staticFields/kt3698.kt | 5 +++ .../BlackBoxCodegenTestGenerated.java | 5 +++ ...CompileJavaAgainstKotlinTestGenerated.java | 15 +++++++ 11 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/codegen/box/namespace/nullablePrimitiveNoFieldInitializer.kt create mode 100644 compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java create mode 100644 compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt create mode 100644 compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java create mode 100644 compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt create mode 100644 compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.java create mode 100644 compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 952a8768944..bcdd7a43ffe 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -84,7 +84,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private final FunctionCodegen functionCodegen; private final PropertyCodegen propertyCodegen; - private List classObjectPropertiesToCopy; + private List classObjectPropertiesToCopy; public ImplementationBodyCodegen( @NotNull JetClassOrObject aClass, @@ -952,11 +952,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateClassObjectBackingFieldCopies() { if (classObjectPropertiesToCopy != null) { - for (PropertyDescriptor propertyDescriptor : classObjectPropertiesToCopy) { + for (PropertyAndDefaultValue propertyInfo : classObjectPropertiesToCopy) { + PropertyDescriptor propertyDescriptor = propertyInfo.propertyDescriptor; - v.newField(null, ACC_STATIC | ACC_FINAL | ACC_PUBLIC, context.getFieldName(propertyDescriptor), typeMapper.mapType(propertyDescriptor).getDescriptor(), null, null); + v.newField(null, ACC_STATIC | ACC_FINAL | ACC_PUBLIC, context.getFieldName(propertyDescriptor), + typeMapper.mapType(propertyDescriptor).getDescriptor(), null, propertyInfo.defaultValue); - if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { + //This field are always static and final so if it has constant initializer don't do anything in clinit, + //field would be initialized via default value in v.newField(...) - see JVM SPEC Ch.4 + if (state.getClassBuilderMode() == ClassBuilderMode.FULL && propertyInfo.defaultValue == null) { ExpressionCodegen codegen = createOrGetClInitCodegen(); int classObjectIndex = putClassObjectInLocalVar(codegen); StackValue.local(classObjectIndex, OBJECT_TYPE).put(OBJECT_TYPE, codegen.v); @@ -1625,6 +1629,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { propValue.store(type, codegen.v); } + public static boolean shouldWriteFieldInitializer(PropertyDescriptor descriptor, JetTypeMapper mapper) { + //final field of primitive or String type + if (!descriptor.isVar()) { + Type type = mapper.mapType(descriptor.getType()); + return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName()); + } + return false; + } + public static boolean shouldInitializeProperty( @NotNull JetProperty property, @NotNull JetTypeMapper typeMapper @@ -1638,6 +1651,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { PropertyDescriptor propertyDescriptor = (PropertyDescriptor) typeMapper.getBindingContext().get(BindingContext.VARIABLE, property); assert propertyDescriptor != null; + //TODO: OPTIMIZATION: don't initialize static final fields + Object value = compileTimeValue.getValue(); JetType jetType = getPropertyOrDelegateType(typeMapper.getBindingContext(), property, propertyDescriptor); Type type = typeMapper.mapType(jetType); @@ -1655,7 +1670,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { return descriptor.getType(); } - private static boolean skipDefaultValue(PropertyDescriptor propertyDescriptor, Object value, Type type) { + private static boolean skipDefaultValue(@NotNull PropertyDescriptor propertyDescriptor, Object value, @NotNull Type type) { if (isPrimitive(type)) { if (!propertyDescriptor.getType().isNullable() && value instanceof Number) { if (type == Type.INT_TYPE && ((Number) value).intValue() == 0) { @@ -1771,11 +1786,25 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { return r; } - public void addClassObjectPropertyToCopy(PropertyDescriptor descriptor) { + public void addClassObjectPropertyToCopy(PropertyDescriptor descriptor, Object defaultValue) { if (classObjectPropertiesToCopy == null) { - classObjectPropertiesToCopy = new ArrayList(); + classObjectPropertiesToCopy = new ArrayList(); } - classObjectPropertiesToCopy.add(descriptor); + classObjectPropertiesToCopy.add(new PropertyAndDefaultValue(descriptor, defaultValue)); + } + + static class PropertyAndDefaultValue { + + PropertyAndDefaultValue(PropertyDescriptor propertyDescriptor, Object defaultValue) { + this.propertyDescriptor = propertyDescriptor; + this.defaultValue = defaultValue; + } + + private PropertyDescriptor propertyDescriptor; + + private Object defaultValue; + + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java index 60b33ddbeb4..c30a10423c6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java @@ -25,7 +25,6 @@ import org.jetbrains.asm4.Type; import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.FieldOwnerContext; -import org.jetbrains.jet.codegen.context.MethodContext; import org.jetbrains.jet.codegen.signature.JvmMethodSignature; import org.jetbrains.jet.codegen.signature.JvmPropertyAccessorSignature; import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter; @@ -157,7 +156,7 @@ public class PropertyCodegen extends GenerationStateAware { if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) { ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen); - parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor); + parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue); } String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); @@ -178,8 +177,9 @@ public class PropertyCodegen extends GenerationStateAware { private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) { Object value = null; - if (p instanceof JetProperty && !ImplementationBodyCodegen.shouldInitializeProperty((JetProperty) p, typeMapper)) { - JetExpression initializer = ((JetProperty) p).getInitializer(); + + if (ImplementationBodyCodegen.shouldWriteFieldInitializer(propertyDescriptor, typeMapper)) { + JetExpression initializer = p instanceof JetProperty ? ((JetProperty) p).getInitializer() : null; if (initializer != null) { CompileTimeConstant compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer); value = compileTimeValue != null ? compileTimeValue.getValue() : null; diff --git a/compiler/testData/codegen/box/namespace/nullablePrimitiveNoFieldInitializer.kt b/compiler/testData/codegen/box/namespace/nullablePrimitiveNoFieldInitializer.kt new file mode 100644 index 00000000000..8eef11e6156 --- /dev/null +++ b/compiler/testData/codegen/box/namespace/nullablePrimitiveNoFieldInitializer.kt @@ -0,0 +1,11 @@ +val zint : Int? = 1 +val zlong : Long? = 2 +val zbyte : Byte? = 3 +val zshort : Short? = 4 +val zchar : Char? = 'c' +val zdouble : Double? = 1.0 +val zfloat : Float? = 2.0 + +fun box(): String { + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java new file mode 100644 index 00000000000..ef003db815d --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java @@ -0,0 +1,10 @@ +@AString(value = Test.vstring) +@AStringNullable(value = Test.vstringNullable) +@AChar(value = Test.vchar) +@AInt(value = Test.vint) +@AByte(value = Test.vbyte) +@ALong(value = Test.vlong) +@ADouble(value = Test.vdouble) +@AFloat(value = Test.vfloat) +public class AnnotationClass { +} diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt new file mode 100644 index 00000000000..0d2f88007c1 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt @@ -0,0 +1,23 @@ +annotation class AString(val value: String) +annotation class AStringNullable(val value: String?) +annotation class AChar(val value: Char) +annotation class AInt(val value: Int) +annotation class AByte(val value: Byte) +annotation class ALong(val value: Long) +annotation class ADouble(val value: Double) +annotation class AFloat(val value: Float) + +class Test { + + class object { + val vstring: String = "Test" + val vstringNullable: String? = "Test" + val vchar: Char = 'c' + val vint: Int = 10 + val vbyte: Byte = 11 + val vlong: Long = 12 + val vdouble: Double = 1.2 + val vfloat: Float = 1.3 + } + +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java new file mode 100644 index 00000000000..20cf3bb2061 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java @@ -0,0 +1,10 @@ +@AString(value = Test.vstring) +@AStringNullable(value = Test.vstringNullable) +@AChar(value = Test.vchar) +@AInt(value = Test.vint) +@AByte(value = Test.vbyte) +@ALong(value = Test.vlong) +@ADouble(value = Test.vdouble) +@AFloat(value = Test.vfloat) +public class AnnotationTrait { +} diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt new file mode 100644 index 00000000000..d90d88de6f3 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt @@ -0,0 +1,23 @@ +annotation class AString(val value: String) +annotation class AStringNullable(val value: String?) +annotation class AChar(val value: Char) +annotation class AInt(val value: Int) +annotation class AByte(val value: Byte) +annotation class ALong(val value: Long) +annotation class ADouble(val value: Double) +annotation class AFloat(val value: Float) + +trait Test { + + class object { + val vstring: String = "Test" + val vstringNullable: String? = "Test" + val vchar: Char = 'c' + val vint: Int = 10 + val vbyte: Byte = 11 + val vlong: Long = 12 + val vdouble: Double = 1.2 + val vfloat: Float = 1.3 + } + +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.java b/compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.java new file mode 100644 index 00000000000..936d97acf31 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.java @@ -0,0 +1,11 @@ +public class kt3698 { + + @interface Foo { + int value(); + } + + @Foo(KotlinClass.FOO) // Error here + public static void main(String[] args) { + System.out.println(KotlinClass.FOO); + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.kt b/compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.kt new file mode 100644 index 00000000000..8e836926c81 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.kt @@ -0,0 +1,5 @@ +class KotlinClass { + class object { + val FOO: Int = 10 + } +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 544069b7f3a..42001930d01 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -3105,6 +3105,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/namespace/namespaceQualifiedMethod.kt"); } + @TestMetadata("nullablePrimitiveNoFieldInitializer.kt") + public void testNullablePrimitiveNoFieldInitializer() throws Exception { + doTest("compiler/testData/codegen/box/namespace/nullablePrimitiveNoFieldInitializer.kt"); + } + @TestMetadata("privateTopLevelPropAndVarInInner.kt") public void testPrivateTopLevelPropAndVarInInner() throws Exception { doTest("compiler/testData/codegen/box/namespace/privateTopLevelPropAndVarInInner.kt"); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java index 107b0044382..ff7efe887b7 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java @@ -189,6 +189,21 @@ public class CompileJavaAgainstKotlinTestGenerated extends AbstractCompileJavaAg JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/compileJavaAgainstKotlin/staticFields"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("AnnotationClass.kt") + public void testAnnotationClass() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt"); + } + + @TestMetadata("AnnotationTrait.kt") + public void testAnnotationTrait() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt"); + } + + @TestMetadata("kt3698.kt") + public void testKt3698() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/staticFields/kt3698.kt"); + } + @TestMetadata("staticClassProperty.kt") public void testStaticClassProperty() throws Exception { doTest("compiler/testData/compileJavaAgainstKotlin/staticFields/staticClassProperty.kt");