KT-5347 VerifyError on local data class with closure

- generated copy() should push captured fields on stack
- refactor context lookup
- handle vars in local classes using instance fields

 #KT-5347 Fixed
This commit is contained in:
dnpetrov
2015-06-19 10:51:14 +03:00
parent c24e6b5698
commit f364d23ee9
7 changed files with 134 additions and 24 deletions
@@ -1528,7 +1528,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
if (sharedVarType == null) {
sharedVarType = typeMapper.mapType((VariableDescriptor) entry.getKey());
}
StackValue capturedVar = entry.getValue().getOuterValue(this);
StackValue capturedVar = lookupOuterValue(entry.getValue());
callGenerator.putCapturedValueOnStack(capturedVar, sharedVarType, paramIndex++);
}
@@ -1551,6 +1551,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return generateBlock(expression.getStatements(), isStatement, null, null);
}
@NotNull
public StackValue lookupOuterValue(EnclosedValueDescriptor d) {
DeclarationDescriptor descriptor = d.getDescriptor();
for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
if (aCase.isCase(descriptor)) {
return aCase.outerValue(d, this);
}
}
throw new IllegalStateException("Can't get outer value in " + this + " for " + d);
}
private StackValue generateBlock(
List<JetExpression> statements,
boolean isStatement,
@@ -727,12 +727,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
MutableClosure closure = ImplementationBodyCodegen.this.context.closure;
if (closure != null) {
ClassDescriptor captureThis = closure.getCaptureThis();
if (captureThis != null) {
iv.load(0, classAsmType);
Type type = typeMapper.mapType(captureThis);
iv.getfield(classAsmType.getInternalName(), CAPTURED_THIS_FIELD, type.getDescriptor());
}
pushCapturedFieldsOnStack(iv, closure);
}
int parameterIndex = 1; // localVariable 0 = this
@@ -747,6 +742,33 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
iv.areturn(thisDescriptorType);
}
private void pushCapturedFieldsOnStack(InstructionAdapter iv, MutableClosure closure) {
ClassDescriptor captureThis = closure.getCaptureThis();
if (captureThis != null) {
iv.load(0, classAsmType);
Type type = typeMapper.mapType(captureThis);
iv.getfield(classAsmType.getInternalName(), CAPTURED_THIS_FIELD, type.getDescriptor());
}
JetType captureReceiver = closure.getCaptureReceiverType();
if (captureReceiver != null) {
iv.load(0, classAsmType);
Type type = typeMapper.mapType(captureReceiver);
iv.getfield(classAsmType.getInternalName(), CAPTURED_RECEIVER_FIELD, type.getDescriptor());
}
for (Map.Entry<DeclarationDescriptor, EnclosedValueDescriptor> entry : closure.getCaptureVariables().entrySet()) {
DeclarationDescriptor declarationDescriptor = entry.getKey();
EnclosedValueDescriptor enclosedValueDescriptor = entry.getValue();
StackValue capturedValue = enclosedValueDescriptor.getInstanceValue();
Type sharedVarType = typeMapper.getSharedVarType(declarationDescriptor);
if (sharedVarType == null) {
sharedVarType = typeMapper.mapType((VariableDescriptor) declarationDescriptor);
}
capturedValue.put(sharedVarType, iv);
}
}
});
functionCodegen.generateDefaultIfNeeded(
@@ -412,15 +412,23 @@ public abstract class StackValue {
return None.INSTANCE;
}
public static FieldForSharedVar fieldForSharedVar(
public static Field receiverWithRefWrapper(
@NotNull Type localType,
@NotNull Type classType,
@NotNull String fieldName,
@NotNull StackValue receiver,
@Nullable DeclarationDescriptor descriptor
) {
Field receiverWithRefWrapper = field(sharedTypeForType(localType), classType, fieldName, false, receiver, descriptor);
return new FieldForSharedVar(localType, classType, fieldName, receiverWithRefWrapper);
return field(sharedTypeForType(localType), classType, fieldName, false, receiver, descriptor);
}
public static FieldForSharedVar fieldForSharedVar(
@NotNull Type localType,
@NotNull Type classType,
@NotNull String fieldName,
@NotNull Field refWrapper
) {
return new FieldForSharedVar(localType, classType, fieldName, refWrapper);
}
@NotNull
@@ -27,6 +27,7 @@ public final class EnclosedValueDescriptor {
private final String fieldName;
private final DeclarationDescriptor descriptor;
private final StackValue.StackValueWithSimpleReceiver innerValue;
private final StackValue instanceValue;
private final Type type;
public EnclosedValueDescriptor(
@@ -38,6 +39,21 @@ public final class EnclosedValueDescriptor {
this.fieldName = fieldName;
this.descriptor = descriptor;
this.innerValue = innerValue;
this.instanceValue = innerValue;
this.type = type;
}
public EnclosedValueDescriptor(
@NotNull String name,
@Nullable DeclarationDescriptor descriptor,
@NotNull StackValue.StackValueWithSimpleReceiver innerValue,
@NotNull StackValue.Field instanceValue,
@NotNull Type type
) {
this.fieldName = name;
this.descriptor = descriptor;
this.innerValue = innerValue;
this.instanceValue = instanceValue;
this.type = type;
}
@@ -57,19 +73,13 @@ public final class EnclosedValueDescriptor {
}
@NotNull
public Type getType() {
return type;
public StackValue getInstanceValue() {
return instanceValue;
}
@NotNull
public StackValue getOuterValue(@NotNull ExpressionCodegen codegen) {
for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
if (aCase.isCase(descriptor)) {
return aCase.outerValue(this, codegen);
}
}
throw new IllegalStateException("Can't get outer value in " + codegen + " for " + this);
public Type getType() {
return type;
}
@Override
@@ -61,12 +61,21 @@ public interface LocalLookup {
String fieldName = "$" + vd.getName();
StackValue.Local thiz = StackValue.LOCAL_0;
StackValue.StackValueWithSimpleReceiver innerValue = sharedVarType != null
? StackValue.fieldForSharedVar(localType, classType, fieldName, thiz, vd)
: StackValue.field(type, classType, fieldName, false, thiz, vd);
StackValue.StackValueWithSimpleReceiver innerValue;
EnclosedValueDescriptor enclosedValueDescriptor;
if (sharedVarType != null) {
StackValue.Field wrapperValue = StackValue.receiverWithRefWrapper(localType, classType, fieldName, thiz, vd);
innerValue = StackValue.fieldForSharedVar(localType, classType, fieldName, wrapperValue);
enclosedValueDescriptor = new EnclosedValueDescriptor(fieldName, d, innerValue, wrapperValue, type);
}
else {
innerValue = StackValue.field(type, classType, fieldName, false, thiz, vd);
enclosedValueDescriptor = new EnclosedValueDescriptor(fieldName, d, innerValue, type);
}
closure.recordField(fieldName, type);
closure.captureVariable(new EnclosedValueDescriptor(fieldName, d, innerValue, type));
closure.captureVariable(enclosedValueDescriptor);
return innerValue;
}
+44
View File
@@ -0,0 +1,44 @@
fun test1(str: String): String {
@data class A(val x: Int) {
fun foo() = str
}
return A(0).copy().foo()
}
class TestClass(val x: String) {
fun foo(): String {
@data class A(val x: Int) {
fun foo() = this@TestClass.x
}
return A(0).copy().foo()
}
}
fun test2(str: String): String = TestClass(str).foo()
fun test3(str: String): String {
var xx = ""
@data class A(val x: Int) {
fun foo(): String { xx = str; return xx }
}
return A(0).copy().foo()
}
fun test4(str: String): String {
var xx = ""
fun bar(s: String): String { xx = s; return xx }
@data class A(val x: Int) {
fun foo(): String = bar(str)
}
return A(0).copy().foo()
}
fun box(): String {
return when {
test1("test1") != "test1" -> "Failed #1 (parameter capture)"
test2("test2") != "test2" -> "Failed #2 ('this' capture)"
test3("test3") != "test3" -> "Failed #3 ('var' capture)"
test4("test4") != "test4" -> "Failed #4 (local function capture)"
else -> "OK"
}
}
@@ -1378,6 +1378,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("kt5347.kt")
public void testKt5347() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt5347.kt");
doTest(fileName);
}
@TestMetadata("kt6136.kt")
public void testKt6136() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt6136.kt");