Implement lateinit in backend

This commit is contained in:
Yan Zhulanow
2015-09-03 01:33:31 +03:00
parent e3967b9fa0
commit fc3bf3cca4
15 changed files with 276 additions and 10 deletions
@@ -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
@@ -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;
}
@@ -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);
@@ -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()
}
@@ -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()
}
@@ -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()
}
@@ -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"
}
@@ -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()
}
@@ -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()
}
@@ -0,0 +1,12 @@
class A {
public lateinit val str: String
init {
str = "OK"
}
}
fun box(): String {
val a = A()
return a.str
}
@@ -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()
}
@@ -0,0 +1,9 @@
class A {
public lateinit var str: String
}
fun box(): String {
val a = A()
a.str = "OK"
return a.str
}
@@ -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<String>()
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()
}
@@ -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")
@@ -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");