diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index 5c5f7b55f58..670dfd40699 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -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(); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 6eeeb845bfe..0f60952c3ec 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -2504,8 +2504,10 @@ public class ExpressionCodegen extends JetVisitor 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 implem } @NotNull - private StackValue generateNormalFunctionCall( - @NotNull ExpressionCodegen codegen, - @NotNull ResolvedCall 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 diff --git a/compiler/testData/codegen/box/callableReference/local/captureOuter.kt b/compiler/testData/codegen/box/callableReference/local/captureOuter.kt new file mode 100644 index 00000000000..46bec43ff91 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/captureOuter.kt @@ -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() +} diff --git a/compiler/testData/codegen/box/callableReference/local/closureWithSideEffect.kt b/compiler/testData/codegen/box/callableReference/local/closureWithSideEffect.kt new file mode 100644 index 00000000000..e58484c9479 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/closureWithSideEffect.kt @@ -0,0 +1,9 @@ +fun box(): String { + var result = "Fail" + + fun changeToOK() { result = "OK" } + + val ok = ::changeToOK + ok() + return result +} diff --git a/compiler/testData/codegen/box/callableReference/local/localClassMember.kt b/compiler/testData/codegen/box/callableReference/local/localClassMember.kt new file mode 100644 index 00000000000..abe1c70a0bd --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/localClassMember.kt @@ -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() +} diff --git a/compiler/testData/codegen/box/callableReference/local/recursiveClosure.kt b/compiler/testData/codegen/box/callableReference/local/recursiveClosure.kt new file mode 100644 index 00000000000..75ca4009a47 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/recursiveClosure.kt @@ -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) diff --git a/compiler/testData/codegen/box/callableReference/local/simpleClosure.kt b/compiler/testData/codegen/box/callableReference/local/simpleClosure.kt new file mode 100644 index 00000000000..5aeb154fbdd --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/simpleClosure.kt @@ -0,0 +1,7 @@ +fun box(): String { + val result = "OK" + + fun foo() = result + + return (::foo)() +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 593cae5515c..c0fc4c43f90 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -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");