KT-2280 Recursive function declared in function results in VerifyError
#KT-2280 Fixed A closure's constructor should not take an instance of itself as an argument, because it would require caller to pass yet uninitialized value. Instead it should initialize a corresponding field with 'this'
This commit is contained in:
@@ -146,7 +146,7 @@ public class ClosureCodegen extends ObjectOrClosureCodegen {
|
|||||||
if (enclosingType == null)
|
if (enclosingType == null)
|
||||||
captureThis = null;
|
captureThis = null;
|
||||||
|
|
||||||
final Method constructor = generateConstructor(funClass, fun);
|
final Method constructor = generateConstructor(funClass, fun, funDescriptor);
|
||||||
|
|
||||||
if (captureThis != null) {
|
if (captureThis != null) {
|
||||||
cv.newField(fun, ACC_FINAL, "this$0", enclosingType.getDescriptor(), null, null);
|
cv.newField(fun, ACC_FINAL, "this$0", enclosingType.getDescriptor(), null, null);
|
||||||
@@ -160,6 +160,9 @@ public class ClosureCodegen extends ObjectOrClosureCodegen {
|
|||||||
|
|
||||||
final GeneratedAnonymousClassDescriptor answer = new GeneratedAnonymousClassDescriptor(name, constructor, captureThis, captureReceiver);
|
final GeneratedAnonymousClassDescriptor answer = new GeneratedAnonymousClassDescriptor(name, constructor, captureThis, captureReceiver);
|
||||||
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
||||||
|
if (descriptor == funDescriptor) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (descriptor instanceof VariableDescriptor) {
|
if (descriptor instanceof VariableDescriptor) {
|
||||||
final EnclosedValueDescriptor valueDescriptor = closure.get(descriptor);
|
final EnclosedValueDescriptor valueDescriptor = closure.get(descriptor);
|
||||||
answer.addArg(valueDescriptor.getOuterValue());
|
answer.addArg(valueDescriptor.getOuterValue());
|
||||||
@@ -253,13 +256,16 @@ public class ClosureCodegen extends ObjectOrClosureCodegen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method generateConstructor(JvmClassName funClass, PsiElement fun) {
|
private Method generateConstructor(JvmClassName funClass, JetExpression fun, FunctionDescriptor funDescriptor) {
|
||||||
int argCount = captureThis != null ? 1 : 0;
|
int argCount = captureThis != null ? 1 : 0;
|
||||||
argCount += (captureReceiver != null ? 1 : 0);
|
argCount += (captureReceiver != null ? 1 : 0);
|
||||||
|
|
||||||
ArrayList<DeclarationDescriptor> variableDescriptors = new ArrayList<DeclarationDescriptor>();
|
ArrayList<DeclarationDescriptor> variableDescriptors = new ArrayList<DeclarationDescriptor>();
|
||||||
|
|
||||||
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
||||||
|
if (descriptor == funDescriptor) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
|
if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
|
||||||
argCount++;
|
argCount++;
|
||||||
variableDescriptors.add(descriptor);
|
variableDescriptors.add(descriptor);
|
||||||
@@ -284,7 +290,13 @@ public class ClosureCodegen extends ObjectOrClosureCodegen {
|
|||||||
argTypes[i++] = captureReceiver;
|
argTypes[i++] = captureReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean putFieldForMyself = false;
|
||||||
|
|
||||||
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
||||||
|
if (descriptor == funDescriptor) {
|
||||||
|
putFieldForMyself = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
|
if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
|
||||||
final Type sharedVarType = state.getInjector().getJetTypeMapper().getSharedVarType(descriptor);
|
final Type sharedVarType = state.getInjector().getJetTypeMapper().getSharedVarType(descriptor);
|
||||||
final Type type;
|
final Type type;
|
||||||
@@ -336,6 +348,14 @@ public class ClosureCodegen extends ObjectOrClosureCodegen {
|
|||||||
StackValue.field(type, name, fieldName, false).store(type, iv);
|
StackValue.field(type, name, fieldName, false).store(type, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (putFieldForMyself) {
|
||||||
|
Type type = name.getAsmType();
|
||||||
|
String fieldName = "$" + funDescriptor.getName();
|
||||||
|
iv.load(0, type);
|
||||||
|
iv.dup();
|
||||||
|
StackValue.field(type, name, fieldName, false).store(type, iv);
|
||||||
|
}
|
||||||
|
|
||||||
iv.visitInsn(RETURN);
|
iv.visitInsn(RETURN);
|
||||||
|
|
||||||
FunctionCodegen.endVisit(iv, "constructor", fun);
|
FunctionCodegen.endVisit(iv, "constructor", fun);
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(s: String): String {
|
||||||
|
fun bar(count: Int): String =
|
||||||
|
if (count == 0) s else bar(count - 1)
|
||||||
|
return bar(239)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String = foo("OK")
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun box(): String {
|
||||||
|
fun rmrf(i: Int) {
|
||||||
|
if (i > 0) rmrf(i - 1)
|
||||||
|
}
|
||||||
|
rmrf(5)
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -66,4 +66,8 @@ public class ClosuresGenTest extends CodegenTestCase {
|
|||||||
public void testKt2151() {
|
public void testKt2151() {
|
||||||
blackBoxFile("regressions/kt2151.kt");
|
blackBoxFile("regressions/kt2151.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRecursiveClosure() {
|
||||||
|
blackBoxFile("classes/recursiveClosure.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,10 @@ public class FunctionGenTest extends CodegenTestCase {
|
|||||||
blackBoxFile("regressions/kt2481.kt");
|
blackBoxFile("regressions/kt2481.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testKt2280() {
|
||||||
|
blackBoxFile("regressions/kt2280.kt");
|
||||||
|
}
|
||||||
|
|
||||||
public static class WithJavaFunctionGenTest extends CodegenTestCase {
|
public static class WithJavaFunctionGenTest extends CodegenTestCase {
|
||||||
private void blackBoxFileWithJava(@NotNull String ktFile) throws Exception {
|
private void blackBoxFileWithJava(@NotNull String ktFile) throws Exception {
|
||||||
File javaClassesTempDirectory = new File(FileUtil.getTempDirectory(), "java-classes");
|
File javaClassesTempDirectory = new File(FileUtil.getTempDirectory(), "java-classes");
|
||||||
|
|||||||
Reference in New Issue
Block a user