From fc3bf3cca4dd58c1518435a49e20035241dcba17 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 3 Sep 2015 01:33:31 +0300 Subject: [PATCH] Implement lateinit in backend --- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 17 ++--- .../kotlin/codegen/PropertyCodegen.java | 10 ++- .../jetbrains/kotlin/codegen/StackValue.java | 15 ++++- .../box/properties/lateinit/accessor.kt | 20 ++++++ .../properties/lateinit/accessorException.kt | 21 +++++++ .../box/properties/lateinit/exceptionField.kt | 18 ++++++ .../properties/lateinit/exceptionGetter.kt | 13 ++++ .../box/properties/lateinit/override.kt | 21 +++++++ .../properties/lateinit/overrideException.kt | 21 +++++++ .../codegen/box/properties/lateinit/simple.kt | 12 ++++ .../box/properties/lateinit/simpleField.kt | 16 +++++ .../box/properties/lateinit/simpleVar.kt | 9 +++ .../fullJdk/lateinitVisibility.kt | 24 +++++++ .../BlackBoxCodegenTestGenerated.java | 63 +++++++++++++++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 6 ++ 15 files changed, 276 insertions(+), 10 deletions(-) create mode 100644 compiler/testData/codegen/box/properties/lateinit/accessor.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/accessorException.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/exceptionField.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/override.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/overrideException.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/simple.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/simpleField.kt create mode 100644 compiler/testData/codegen/box/properties/lateinit/simpleVar.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/fullJdk/lateinitVisibility.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index cf39bc3857c..3e7eb6ae76e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -620,29 +620,29 @@ public class AsmUtil { } } - public static void genNotNullAssertionForField( + public static boolean genNotNullAssertionForField( @NotNull InstructionAdapter v, @NotNull GenerationState state, @NotNull PropertyDescriptor descriptor ) { - genNotNullAssertion(v, state, descriptor, "checkFieldIsNotNull"); + return genNotNullAssertion(v, state, descriptor, "checkFieldIsNotNull"); } - private static void genNotNullAssertion( + private static boolean genNotNullAssertion( @NotNull InstructionAdapter v, @NotNull GenerationState state, @NotNull CallableDescriptor descriptor, @NotNull String assertMethodToCall ) { // Assertions are generated elsewhere for platform types - if (JvmPackage.getPLATFORM_TYPES()) return; + if (JvmPackage.getPLATFORM_TYPES()) return false; - if (!state.isCallAssertionsEnabled()) return; + if (!state.isCallAssertionsEnabled()) return false; - if (!isDeclaredInJava(descriptor)) return; + if (!isDeclaredInJava(descriptor)) return false; JetType type = descriptor.getReturnType(); - if (type == null || isNullableType(TypesPackage.lowerIfFlexible(type))) return; + if (type == null || isNullableType(TypesPackage.lowerIfFlexible(type))) return false; Type asmType = state.getTypeMapper().mapReturnType(descriptor); if (asmType.getSort() == Type.OBJECT || asmType.getSort() == Type.ARRAY) { @@ -651,7 +651,10 @@ public class AsmUtil { v.visitLdcInsn(descriptor.getName().asString()); v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, assertMethodToCall, "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V", false); + return true; } + + return false; } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index ed60654ed9c..442efa8cee2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -288,7 +288,7 @@ public class PropertyCodegen { modifiers |= ACC_STATIC; } - if (!propertyDescriptor.isVar() || isDelegate) { + if (!propertyDescriptor.isLateInit() && (!propertyDescriptor.isVar() || isDelegate)) { modifiers |= ACC_FINAL; } @@ -302,7 +302,10 @@ public class PropertyCodegen { if (AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor) ) { modifiers |= ACC_STATIC; - if (hasPublicFieldAnnotation && !isDelegate) { + if (propertyDescriptor.isLateInit()) { + modifiers |= getVisibilityAccessFlag(propertyDescriptor); + } + else if (hasPublicFieldAnnotation && !isDelegate) { modifiers |= ACC_PUBLIC; } else { @@ -316,6 +319,9 @@ public class PropertyCodegen { v.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor); } } + else if (propertyDescriptor.isLateInit()) { + modifiers |= getVisibilityAccessFlag(propertyDescriptor); + } else if (!isDelegate && hasPublicFieldAnnotation) { modifiers |= ACC_PUBLIC; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 026b74e6bcf..76ebb889430 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.PrimitiveType; +import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.*; @@ -1024,7 +1025,9 @@ public abstract class StackValue { assert fieldName != null : "Property should have either a getter or a field name: " + descriptor; assert backingFieldOwner != null : "Property should have either a getter or a backingFieldOwner: " + descriptor; v.visitFieldInsn(isStaticPut ? GETSTATIC : GETFIELD, backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor()); - genNotNullAssertionForField(v, state, descriptor); + if (!genNotNullAssertionForField(v, state, descriptor)) { + genNotNullAssertionForLateInitIfNeeded(v); + } coerceTo(type, v); } else { @@ -1033,6 +1036,16 @@ public abstract class StackValue { } } + private void genNotNullAssertionForLateInitIfNeeded(@NotNull InstructionAdapter v) { + if (!descriptor.isLateInit()) return; + + v.dup(); + Label ok = new Label(); + v.ifnonnull(ok); + v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwNpe", "()V", false); + v.mark(ok); + } + @Override public void storeSelector(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) { coerceFrom(topOfStackType, v); diff --git a/compiler/testData/codegen/box/properties/lateinit/accessor.kt b/compiler/testData/codegen/box/properties/lateinit/accessor.kt new file mode 100644 index 00000000000..441bc5ec592 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/accessor.kt @@ -0,0 +1,20 @@ +public class A { + + fun setMyStr() { + str = "OK" + } + + fun getMyStr(): String { + return str + } + + private companion object { + private lateinit var str: String + } +} + +fun box(): String { + val a = A() + a.setMyStr() + return a.getMyStr() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/accessorException.kt b/compiler/testData/codegen/box/properties/lateinit/accessorException.kt new file mode 100644 index 00000000000..b878c563238 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/accessorException.kt @@ -0,0 +1,21 @@ +public class A { + + fun getMyStr(): String { + try { + val a = str + } catch (e: NullPointerException) { + return "OK" + } + + return "FAIL" + } + + private companion object { + private lateinit var str: String + } +} + +fun box(): String { + val a = A() + return a.getMyStr() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/exceptionField.kt b/compiler/testData/codegen/box/properties/lateinit/exceptionField.kt new file mode 100644 index 00000000000..ce5f7e5f9e9 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/exceptionField.kt @@ -0,0 +1,18 @@ +class A { + private lateinit var str: String + + public fun getMyStr(): String { + try { + val a = str + } catch (e: NullPointerException) { + return "OK" + } + + return "FAIL" + } +} + +fun box(): String { + val a = A() + return a.getMyStr() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt b/compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt new file mode 100644 index 00000000000..110502a2e93 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt @@ -0,0 +1,13 @@ +class A { + public lateinit var str: String +} + +fun box(): String { + val a = A() + try { + a.str + } catch (e: NullPointerException) { + return "OK" + } + return "FAIL" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/override.kt b/compiler/testData/codegen/box/properties/lateinit/override.kt new file mode 100644 index 00000000000..02d05d723be --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/override.kt @@ -0,0 +1,21 @@ +interface Intf { + val str: String +} + +class A : Intf { + override lateinit var str: String + + fun setMyStr() { + str = "OK" + } + + fun getMyStr(): String { + return str + } +} + +fun box(): String { + val a = A() + a.setMyStr() + return a.getMyStr() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/overrideException.kt b/compiler/testData/codegen/box/properties/lateinit/overrideException.kt new file mode 100644 index 00000000000..4a822e48fa0 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/overrideException.kt @@ -0,0 +1,21 @@ +interface Intf { + val str: String +} + +class A : Intf { + override lateinit var str: String + + fun getMyStr(): String { + try { + val a = str + } catch (e: NullPointerException) { + return "OK" + } + return "FAIL" + } +} + +fun box(): String { + val a = A() + return a.getMyStr() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/simple.kt b/compiler/testData/codegen/box/properties/lateinit/simple.kt new file mode 100644 index 00000000000..d3598cd88aa --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/simple.kt @@ -0,0 +1,12 @@ +class A { + public lateinit val str: String + + init { + str = "OK" + } +} + +fun box(): String { + val a = A() + return a.str +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/simpleField.kt b/compiler/testData/codegen/box/properties/lateinit/simpleField.kt new file mode 100644 index 00000000000..53ebee98133 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/simpleField.kt @@ -0,0 +1,16 @@ +class A { + private lateinit val str: String + + init { + str = "OK" + } + + public fun getMyStr(): String { + return str + } +} + +fun box(): String { + val a = A() + return a.getMyStr() +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/simpleVar.kt b/compiler/testData/codegen/box/properties/lateinit/simpleVar.kt new file mode 100644 index 00000000000..b5314ead1c8 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/simpleVar.kt @@ -0,0 +1,9 @@ +class A { + public lateinit var str: String +} + +fun box(): String { + val a = A() + a.str = "OK" + return a.str +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/fullJdk/lateinitVisibility.kt b/compiler/testData/codegen/boxWithStdlib/fullJdk/lateinitVisibility.kt new file mode 100644 index 00000000000..739e7f46cb6 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/fullJdk/lateinitVisibility.kt @@ -0,0 +1,24 @@ +import java.lang.reflect.Modifier + +public class A { + private lateinit val privateField: String + protected lateinit var protectedField: String + public lateinit var publicField: String + + fun test(): String { + val clazz = A::class.java + val cond = arrayListOf() + + if (!Modifier.isPrivate(clazz.getDeclaredField("privateField").modifiers)) cond += "NOT_PRIVATE" + if (!Modifier.isProtected(clazz.getDeclaredField("protectedField").modifiers)) cond += "NOT_PROTECTED" + if (!Modifier.isPublic(clazz.getDeclaredField("publicField").modifiers)) cond += "NOT_PUBLIC" + + if (Modifier.isPrivate(clazz.getDeclaredField("privateField").modifiers)) cond += "FINAL" + + return if (cond.isEmpty()) "OK" else cond.joinToString() + } +} + +fun box(): String { + return A().test() +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 63fb37bbb5f..5ea2961c97a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -6534,6 +6534,69 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/twoAnnotatedExtensionPropertiesWithoutBackingFields.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/properties/lateinit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Lateinit extends AbstractBlackBoxCodegenTest { + @TestMetadata("accessor.kt") + public void testAccessor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/accessor.kt"); + doTest(fileName); + } + + @TestMetadata("accessorException.kt") + public void testAccessorException() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/accessorException.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInLateinit() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("exceptionField.kt") + public void testExceptionField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/exceptionField.kt"); + doTest(fileName); + } + + @TestMetadata("exceptionGetter.kt") + public void testExceptionGetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); + doTest(fileName); + } + + @TestMetadata("override.kt") + public void testOverride() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/override.kt"); + doTest(fileName); + } + + @TestMetadata("overrideException.kt") + public void testOverrideException() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/overrideException.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/simple.kt"); + doTest(fileName); + } + + @TestMetadata("simpleField.kt") + public void testSimpleField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/simpleField.kt"); + doTest(fileName); + } + + @TestMetadata("simpleVar.kt") + public void testSimpleVar() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/simpleVar.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/reflection") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 08221691455..64405261e0b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -1510,6 +1510,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("lateinitVisibility.kt") + public void testLateinitVisibility() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/fullJdk/lateinitVisibility.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("platformTypeAssertionStackTrace.kt") public void testPlatformTypeAssertionStackTrace() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/fullJdk/platformTypeAssertionStackTrace.kt");