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 PropertyCodegen propertyCodegen;
private List<PropertyDescriptor> classObjectPropertiesToCopy;
private List<PropertyAndDefaultValue> 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<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.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;
@@ -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");
}
@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");
@@ -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");