Support closures in codegen for callable references

This commit is contained in:
Alexander Udalov
2013-12-19 17:59:08 +04:00
parent 596b1622a3
commit d3a811aa7e
8 changed files with 95 additions and 16 deletions
@@ -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");