Support closures in codegen for callable references
This commit is contained in:
@@ -107,7 +107,7 @@ public class CodegenUtil {
|
||||
return signatureWriter.makeJvmMethodSignature("invoke");
|
||||
}
|
||||
|
||||
public static boolean isConst(CalculatedClosure closure) {
|
||||
public static boolean isConst(@NotNull CalculatedClosure closure) {
|
||||
return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -2504,8 +2504,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = generateNormalFunctionCall(codegen, fakeResolvedCall,
|
||||
CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments));
|
||||
Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
|
||||
Callable callable = codegen.resolveToCallable(codegen.accessibleFunctionDescriptor(referencedFunction), false);
|
||||
StackValue receiver = generateCallee(codegen, callable);
|
||||
result = codegen.invokeFunctionWithCalleeOnStack(call, receiver, fakeResolvedCall, callable);
|
||||
}
|
||||
|
||||
InstructionAdapter v = codegen.v;
|
||||
@@ -2514,24 +2516,30 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StackValue generateNormalFunctionCall(
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ResolvedCall<CallableDescriptor> fakeResolvedCall,
|
||||
@NotNull Call call
|
||||
) {
|
||||
Callable callable = codegen.resolveToCallable(codegen.accessibleFunctionDescriptor(referencedFunction), false);
|
||||
private StackValue generateCallee(@NotNull ExpressionCodegen codegen, @NotNull Callable callable) {
|
||||
if (!(callable instanceof CallableMethod) || ((CallableMethod) callable).getGenerateCalleeType() == null) {
|
||||
return StackValue.none();
|
||||
}
|
||||
|
||||
StackValue receiver;
|
||||
if (callable instanceof CallableMethod && ((CallableMethod) callable).getGenerateCalleeType() != null) {
|
||||
Type asmType = asmTypeForAnonymousClass(codegen.getBindingContext(), referencedFunction);
|
||||
// If referenced method is a CallableMethod with generateCalleeType != null, this means it's some kind of a closure
|
||||
// (e.g. a local named function) and a non-trivial callee should be generated before calling invoke()
|
||||
|
||||
BindingContext bindingContext = codegen.bindingContext;
|
||||
ClassDescriptor closureClassOfReferenced = bindingContext.get(CLASS_FOR_FUNCTION, referencedFunction);
|
||||
MutableClosure closureOfReferenced = bindingContext.get(CLOSURE, closureClassOfReferenced);
|
||||
assert closureOfReferenced != null :
|
||||
"Function mapped to CallableMethod with generateCalleeType != null must be a closure: " + referencedFunction;
|
||||
if (isConst(closureOfReferenced)) {
|
||||
// This is an optimization: we can obtain an instance of a const closure simply by GETSTATIC ...$instance
|
||||
// (instead of passing this instance to the constructor and storing as a field)
|
||||
Type asmType = asmTypeForAnonymousClass(bindingContext, referencedFunction);
|
||||
codegen.v.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
|
||||
receiver = StackValue.onStack(asmType);
|
||||
return StackValue.onStack(asmType);
|
||||
}
|
||||
else {
|
||||
receiver = StackValue.none();
|
||||
Type asmCallRefType = asmTypeForAnonymousClass(bindingContext, callableDescriptor);
|
||||
return codegen.context.lookupInContext(referencedFunction, StackValue.local(0, asmCallRefType), state, false);
|
||||
}
|
||||
|
||||
return codegen.invokeFunctionWithCalleeOnStack(call, receiver, fakeResolvedCall, callable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
class Outer {
|
||||
val result = "OK"
|
||||
|
||||
inner class Inner {
|
||||
fun foo() = result
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val f = Outer.Inner::foo
|
||||
return Outer().Inner().f()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fun box(): String {
|
||||
var result = "Fail"
|
||||
|
||||
fun changeToOK() { result = "OK" }
|
||||
|
||||
val ok = ::changeToOK
|
||||
ok()
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun box(): String {
|
||||
val result = "OK"
|
||||
|
||||
class Local {
|
||||
fun foo() = result
|
||||
}
|
||||
|
||||
val member = Local::foo
|
||||
val instance = Local()
|
||||
return instance.member()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(until: Int): String {
|
||||
fun bar(x: Int): String =
|
||||
if (x == until) "OK" else bar(x + 1)
|
||||
return (::bar)(0)
|
||||
}
|
||||
|
||||
fun box() = foo(10)
|
||||
@@ -0,0 +1,7 @@
|
||||
fun box(): String {
|
||||
val result = "OK"
|
||||
|
||||
fun foo() = result
|
||||
|
||||
return (::foo)()
|
||||
}
|
||||
@@ -737,11 +737,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/callableReference/local"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("captureOuter.kt")
|
||||
public void testCaptureOuter() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/captureOuter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classMember.kt")
|
||||
public void testClassMember() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/classMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("closureWithSideEffect.kt")
|
||||
public void testClosureWithSideEffect() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/closureWithSideEffect.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/constructor.kt");
|
||||
@@ -762,16 +772,31 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/genericMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localClassMember.kt")
|
||||
public void testLocalClassMember() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/localClassMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localLocal.kt")
|
||||
public void testLocalLocal() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/localLocal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveClosure.kt")
|
||||
public void testRecursiveClosure() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/recursiveClosure.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleClosure.kt")
|
||||
public void testSimpleClosure() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/simpleClosure.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleWithArg.kt")
|
||||
public void testSimpleWithArg() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/callableReference/local/simpleWithArg.kt");
|
||||
|
||||
Reference in New Issue
Block a user