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 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");
|
||||||
|
|||||||
+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);
|
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");
|
||||||
|
|||||||
Reference in New Issue
Block a user