Support lateinit local vars in JVM BE

This commit is contained in:
Dmitry Petrov
2017-05-31 16:03:04 +03:00
parent 53961e8df0
commit c5b9d500bc
13 changed files with 363 additions and 17 deletions
@@ -1828,10 +1828,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Type sharedVarType = typeMapper.getSharedVarType(descriptor);
Type varType = getVariableTypeNoSharing(variableDescriptor);
if (sharedVarType != null) {
return StackValue.shared(index, varType);
return StackValue.shared(index, varType, variableDescriptor);
}
else {
return adjustVariableValue(StackValue.local(index, varType), variableDescriptor);
return adjustVariableValue(StackValue.local(index, varType, variableDescriptor), variableDescriptor);
}
}
else {
@@ -3496,6 +3496,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
else if (delegateExpression != null) {
initializeLocalVariable(property, gen(delegateExpression));
}
else if (property.hasModifier(KtTokens.LATEINIT_KEYWORD)) {
initializeLocalVariable(property, null);
}
return StackValue.none();
}
@@ -3592,7 +3595,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
private void initializeLocalVariable(
@NotNull KtVariableDeclaration variableDeclaration,
@NotNull StackValue initializer
@Nullable StackValue initializer
) {
LocalVariableDescriptor variableDescriptor = (LocalVariableDescriptor) getVariableDescriptorNotNull(variableDeclaration);
@@ -3612,6 +3615,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
StackValue storeTo = sharedVarType == null ? StackValue.local(index, varType) : StackValue.shared(index, varType);
storeTo.putReceiver(v, false);
if (variableDescriptor.isLateInit()) {
assert initializer == null : "Initializer should be null for lateinit var " + variableDescriptor + ": " + initializer;
v.aconst(null);
storeTo.storeSelector(storeTo.type, v);
return;
}
assert initializer != null : "Initializer should be not null for " + variableDescriptor;
initializer.put(initializer.type, v);
markLineNumber(variableDeclaration, false);
@@ -146,6 +146,11 @@ public abstract class StackValue {
return new Local(index, type);
}
@NotNull
public static Local local(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
return new Local(index, type, descriptor.isLateInit(), descriptor.getName().asString());
}
@NotNull
public static Delegate delegate(
@NotNull Type type,
@@ -162,6 +167,11 @@ public abstract class StackValue {
return new Shared(index, type);
}
@NotNull
public static StackValue shared(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
return new Shared(index, type, descriptor.isLateInit(), descriptor.getName().asString());
}
@NotNull
public static StackValue onStack(@NotNull Type type) {
return type == Type.VOID_TYPE ? none() : new OnStack(type);
@@ -434,16 +444,18 @@ public abstract class StackValue {
@NotNull Type localType,
@NotNull Type classType,
@NotNull String fieldName,
@NotNull Field refWrapper
@NotNull Field refWrapper,
@NotNull VariableDescriptor variableDescriptor
) {
return new FieldForSharedVar(localType, classType, fieldName, refWrapper);
return new FieldForSharedVar(localType, classType, fieldName, refWrapper,
variableDescriptor.isLateInit(), variableDescriptor.getName().asString());
}
@NotNull
public static FieldForSharedVar fieldForSharedVar(@NotNull FieldForSharedVar field, @NotNull StackValue newReceiver) {
Field oldReceiver = (Field) field.receiver;
Field newSharedVarReceiver = field(oldReceiver, newReceiver);
return new FieldForSharedVar(field.type, field.owner, field.name, newSharedVarReceiver);
return new FieldForSharedVar(field.type, field.owner, field.name, newSharedVarReceiver, field.isLateinit, field.variableName);
}
public static StackValue coercion(@NotNull StackValue value, @NotNull Type castType) {
@@ -623,19 +635,35 @@ public abstract class StackValue {
public static class Local extends StackValue {
public final int index;
private final boolean isLateinit;
private final String name;
private Local(int index, Type type) {
private Local(int index, Type type, boolean isLateinit, String name) {
super(type, false);
this.index = index;
if (index < 0) {
throw new IllegalStateException("local variable index must be non-negative");
}
if (isLateinit && name == null) {
throw new IllegalArgumentException("Lateinit local variable should have name: #" + index + " " + type.getDescriptor());
}
this.index = index;
this.isLateinit = isLateinit;
this.name = name;
}
private Local(int index, Type type) {
this(index, type, false, null);
}
@Override
public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
v.load(index, this.type);
if (isLateinit) {
StackValue.genNonNullAssertForLateinit(v, name);
}
coerceTo(type, v);
// TODO unbox
}
@@ -1258,12 +1286,7 @@ public abstract class StackValue {
private void genNotNullAssertionForLateInitIfNeeded(@NotNull InstructionAdapter v) {
if (!descriptor.isLateInit()) return;
v.dup();
Label ok = new Label();
v.ifnonnull(ok);
v.visitLdcInsn(descriptor.getName().asString());
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwUninitializedPropertyAccessException", "(Ljava/lang/String;)V", false);
v.mark(ok);
StackValue.genNonNullAssertForLateinit(v, descriptor.getName().asString());
}
@Override
@@ -1327,6 +1350,15 @@ public abstract class StackValue {
}
}
private static void genNonNullAssertForLateinit(@NotNull InstructionAdapter v, @NotNull String name) {
v.dup();
Label ok = new Label();
v.ifnonnull(ok);
v.visitLdcInsn(name);
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwUninitializedPropertyAccessException", "(Ljava/lang/String;)V", false);
v.mark(ok);
}
private static class Expression extends StackValue {
private final KtExpression expression;
private final ExpressionCodegen generator;
@@ -1345,10 +1377,23 @@ public abstract class StackValue {
public static class Shared extends StackValueWithSimpleReceiver {
private final int index;
private final boolean isLateinit;
private final String name;
public Shared(int index, Type type) {
public Shared(int index, Type type, boolean isLateinit, String name) {
super(type, false, false, local(index, OBJECT_TYPE), false);
this.index = index;
if (isLateinit && name == null) {
throw new IllegalArgumentException("Lateinit shared local variable should have name: #" + index + " " + type.getDescriptor());
}
this.isLateinit = isLateinit;
this.name = name;
}
public Shared(int index, Type type) {
this(index, type, false, null);
}
public int getIndex() {
@@ -1360,6 +1405,9 @@ public abstract class StackValue {
Type refType = refType(this.type);
Type sharedType = sharedTypeForType(this.type);
v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
if (isLateinit) {
StackValue.genNonNullAssertForLateinit(v, name);
}
coerceFrom(refType, v);
coerceTo(type, v);
}
@@ -1397,11 +1445,23 @@ public abstract class StackValue {
public static class FieldForSharedVar extends StackValueWithSimpleReceiver {
final Type owner;
final String name;
final boolean isLateinit;
final String variableName;
public FieldForSharedVar(Type type, Type owner, String name, StackValue.Field receiver) {
public FieldForSharedVar(
Type type, Type owner, String name, StackValue.Field receiver,
boolean isLateinit, String variableName
) {
super(type, false, false, receiver, receiver.canHaveSideEffects());
if (isLateinit && variableName == null) {
throw new IllegalArgumentException("variableName should be non-null for captured lateinit variable " + name);
}
this.owner = owner;
this.name = name;
this.isLateinit = isLateinit;
this.variableName = variableName;
}
@Override
@@ -1409,6 +1469,9 @@ public abstract class StackValue {
Type sharedType = sharedTypeForType(this.type);
Type refType = refType(this.type);
v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
if (isLateinit) {
StackValue.genNonNullAssertForLateinit(v, variableName);
}
coerceFrom(refType, v);
coerceTo(type, v);
}
@@ -70,7 +70,7 @@ public interface LocalLookup {
EnclosedValueDescriptor enclosedValueDescriptor;
if (sharedVarType != null) {
StackValue.Field wrapperValue = StackValue.receiverWithRefWrapper(localType, classType, fieldName, thiz, vd);
innerValue = StackValue.fieldForSharedVar(localType, classType, fieldName, wrapperValue);
innerValue = StackValue.fieldForSharedVar(localType, classType, fieldName, wrapperValue, vd);
enclosedValueDescriptor = new EnclosedValueDescriptor(fieldName, d, innerValue, wrapperValue, type);
}
else {