Partial fix for KT-3698: properly write field initializer

This commit is contained in:
Mikhael Bogdanov
2013-06-19 15:27:11 +04:00
parent 935c5ec16d
commit 132d74200b
11 changed files with 154 additions and 12 deletions
@@ -84,7 +84,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private final FunctionCodegen functionCodegen; private final FunctionCodegen functionCodegen;
private final PropertyCodegen propertyCodegen; private final PropertyCodegen propertyCodegen;
private List<PropertyDescriptor> classObjectPropertiesToCopy; private List<PropertyAndDefaultValue> classObjectPropertiesToCopy;
public ImplementationBodyCodegen( public ImplementationBodyCodegen(
@NotNull JetClassOrObject aClass, @NotNull JetClassOrObject aClass,
@@ -952,11 +952,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private void generateClassObjectBackingFieldCopies() { private void generateClassObjectBackingFieldCopies() {
if (classObjectPropertiesToCopy != null) { 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(); ExpressionCodegen codegen = createOrGetClInitCodegen();
int classObjectIndex = putClassObjectInLocalVar(codegen); int classObjectIndex = putClassObjectInLocalVar(codegen);
StackValue.local(classObjectIndex, OBJECT_TYPE).put(OBJECT_TYPE, codegen.v); StackValue.local(classObjectIndex, OBJECT_TYPE).put(OBJECT_TYPE, codegen.v);
@@ -1625,6 +1629,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
propValue.store(type, codegen.v); 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( public static boolean shouldInitializeProperty(
@NotNull JetProperty property, @NotNull JetProperty property,
@NotNull JetTypeMapper typeMapper @NotNull JetTypeMapper typeMapper
@@ -1638,6 +1651,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) typeMapper.getBindingContext().get(BindingContext.VARIABLE, property); PropertyDescriptor propertyDescriptor = (PropertyDescriptor) typeMapper.getBindingContext().get(BindingContext.VARIABLE, property);
assert propertyDescriptor != null; assert propertyDescriptor != null;
//TODO: OPTIMIZATION: don't initialize static final fields
Object value = compileTimeValue.getValue(); Object value = compileTimeValue.getValue();
JetType jetType = getPropertyOrDelegateType(typeMapper.getBindingContext(), property, propertyDescriptor); JetType jetType = getPropertyOrDelegateType(typeMapper.getBindingContext(), property, propertyDescriptor);
Type type = typeMapper.mapType(jetType); Type type = typeMapper.mapType(jetType);
@@ -1655,7 +1670,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
return descriptor.getType(); 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 (isPrimitive(type)) {
if (!propertyDescriptor.getType().isNullable() && value instanceof Number) { if (!propertyDescriptor.getType().isNullable() && value instanceof Number) {
if (type == Type.INT_TYPE && ((Number) value).intValue() == 0) { if (type == Type.INT_TYPE && ((Number) value).intValue() == 0) {
@@ -1771,11 +1786,25 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
return r; return r;
} }
public void addClassObjectPropertyToCopy(PropertyDescriptor descriptor) { public void addClassObjectPropertyToCopy(PropertyDescriptor descriptor, Object defaultValue) {
if (classObjectPropertiesToCopy == null) { if (classObjectPropertiesToCopy == null) {
classObjectPropertiesToCopy = new ArrayList<PropertyDescriptor>(); classObjectPropertiesToCopy = new ArrayList<PropertyAndDefaultValue>();
} }
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;
} }
} }
@@ -25,7 +25,6 @@ import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.CodegenContext;
import org.jetbrains.jet.codegen.context.FieldOwnerContext; 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.JvmMethodSignature;
import org.jetbrains.jet.codegen.signature.JvmPropertyAccessorSignature; import org.jetbrains.jet.codegen.signature.JvmPropertyAccessorSignature;
import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter; import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter;
@@ -157,7 +156,7 @@ public class PropertyCodegen extends GenerationStateAware {
if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) { if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen); ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen);
parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor); parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue);
} }
String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
@@ -178,8 +177,9 @@ public class PropertyCodegen extends GenerationStateAware {
private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) { private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
Object value = null; 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) { if (initializer != null) {
CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer); CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer);
value = compileTimeValue != null ? compileTimeValue.getValue() : null; value = compileTimeValue != null ? compileTimeValue.getValue() : null;
@@ -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"
}
@@ -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 {
}
@@ -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
}
}
@@ -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 {
}
@@ -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
}
}
@@ -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);
}
}
@@ -0,0 +1,5 @@
class KotlinClass {
class object {
val FOO: Int = 10
}
}
@@ -3105,6 +3105,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/namespace/namespaceQualifiedMethod.kt"); 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") @TestMetadata("privateTopLevelPropAndVarInInner.kt")
public void testPrivateTopLevelPropAndVarInInner() throws Exception { public void testPrivateTopLevelPropAndVarInInner() throws Exception {
doTest("compiler/testData/codegen/box/namespace/privateTopLevelPropAndVarInInner.kt"); doTest("compiler/testData/codegen/box/namespace/privateTopLevelPropAndVarInInner.kt");
@@ -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); 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") @TestMetadata("staticClassProperty.kt")
public void testStaticClassProperty() throws Exception { public void testStaticClassProperty() throws Exception {
doTest("compiler/testData/compileJavaAgainstKotlin/staticFields/staticClassProperty.kt"); doTest("compiler/testData/compileJavaAgainstKotlin/staticFields/staticClassProperty.kt");