Partial fix for KT-3698: properly write field initializer
This commit is contained in:
@@ -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");
|
||||
|
||||
+15
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user