From 363fe607fc0fff36686b8cfae3e6973e8995adab Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 17 Dec 2013 23:14:43 +0400 Subject: [PATCH] Support local function references in codegen #KT-3704 In Progress #KT-1183 In Progress --- .../jet/codegen/ExpressionCodegen.java | 54 ++++++++++++---- .../callableReference/local/classMember.kt | 8 +++ .../callableReference/local/constructor.kt | 7 +++ .../local/constructorWithInitializer.kt | 10 +++ .../local/enumExtendsTrait.kt | 11 ++++ .../callableReference/local/genericMember.kt | 8 +++ .../box/callableReference/local/localLocal.kt | 10 +++ .../box/callableReference/local/simple.kt | 4 ++ .../callableReference/local/simpleWithArg.kt | 4 ++ .../local/unitWithSideEffect.kt | 15 +++++ .../BlackBoxCodegenTestGenerated.java | 62 ++++++++++++++++++- 11 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/box/callableReference/local/classMember.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/constructor.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/constructorWithInitializer.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/enumExtendsTrait.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/genericMember.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/localLocal.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/simple.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/simpleWithArg.kt create mode 100644 compiler/testData/codegen/box/callableReference/local/unitWithSideEffect.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 5c1558f1de3..6eeeb845bfe 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1987,9 +1987,8 @@ public class ExpressionCodegen extends JetVisitor implem } } - fd = accessibleFunctionDescriptor(fd); + Callable callable = resolveToCallable(accessibleFunctionDescriptor(fd), superCall); - Callable callable = resolveToCallable(fd, superCall); if (callable instanceof CallableMethod) { CallableMethod callableMethod = (CallableMethod) callable; Type calleeType = callableMethod.getGenerateCalleeType(); @@ -1997,17 +1996,25 @@ public class ExpressionCodegen extends JetVisitor implem assert !callableMethod.isNeedsThis() : "Method should have a receiver: " + resolvedCall.getResultingDescriptor(); gen(call.getCalleeExpression(), calleeType); } + } + return invokeFunctionWithCalleeOnStack(call, receiver, resolvedCall, callable); + } + + @NotNull + private StackValue invokeFunctionWithCalleeOnStack( + @NotNull Call call, + @NotNull StackValue receiver, + @NotNull ResolvedCall resolvedCall, + @NotNull Callable callable + ) { + if (callable instanceof CallableMethod) { + CallableMethod callableMethod = (CallableMethod) callable; invokeMethodWithArguments(callableMethod, resolvedCall, receiver); - - Type callReturnType = callableMethod.getSignature().getAsmMethod().getReturnType(); - if (callReturnType == Type.VOID_TYPE) return StackValue.none(); - - JetType type = fd.getReturnType(); - assert type != null; - Type retType = typeMapper.mapReturnType(type); - StackValue.coerce(callReturnType, retType, v); - return StackValue.onStack(retType); + //noinspection ConstantConditions + Type returnType = typeMapper.mapReturnType(resolvedCall.getResultingDescriptor().getReturnType()); + StackValue.coerce(callableMethod.getSignature().getAsmMethod().getReturnType(), returnType, v); + return StackValue.onStack(returnType); } else { receiver = StackValue.receiver(resolvedCall, receiver, this, null); @@ -2497,8 +2504,8 @@ public class ExpressionCodegen extends JetVisitor implem } } else { - Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments); - result = codegen.invokeFunction(call, StackValue.none(), fakeResolvedCall); + result = generateNormalFunctionCall(codegen, fakeResolvedCall, + CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments)); } InstructionAdapter v = codegen.v; @@ -2506,6 +2513,27 @@ public class ExpressionCodegen extends JetVisitor implem v.areturn(returnType); } + @NotNull + private StackValue generateNormalFunctionCall( + @NotNull ExpressionCodegen codegen, + @NotNull ResolvedCall fakeResolvedCall, + @NotNull Call call + ) { + Callable callable = codegen.resolveToCallable(codegen.accessibleFunctionDescriptor(referencedFunction), false); + + StackValue receiver; + if (callable instanceof CallableMethod && ((CallableMethod) callable).getGenerateCalleeType() != null) { + Type asmType = asmTypeForAnonymousClass(codegen.getBindingContext(), referencedFunction); + codegen.v.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor()); + receiver = StackValue.onStack(asmType); + } + else { + receiver = StackValue.none(); + } + + return codegen.invokeFunctionWithCalleeOnStack(call, receiver, fakeResolvedCall, callable); + } + @NotNull private JetCallExpression constructFakeFunctionCall() { StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall("); diff --git a/compiler/testData/codegen/box/callableReference/local/classMember.kt b/compiler/testData/codegen/box/callableReference/local/classMember.kt new file mode 100644 index 00000000000..343e9015c84 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/classMember.kt @@ -0,0 +1,8 @@ +fun box(): String { + class Local { + fun foo() = "OK" + } + + val ref = Local::foo + return Local().ref() +} diff --git a/compiler/testData/codegen/box/callableReference/local/constructor.kt b/compiler/testData/codegen/box/callableReference/local/constructor.kt new file mode 100644 index 00000000000..291af016082 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/constructor.kt @@ -0,0 +1,7 @@ +fun box(): String { + class A { + val result = "OK" + } + + return (::A)().result +} diff --git a/compiler/testData/codegen/box/callableReference/local/constructorWithInitializer.kt b/compiler/testData/codegen/box/callableReference/local/constructorWithInitializer.kt new file mode 100644 index 00000000000..fd37b2b28d8 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/constructorWithInitializer.kt @@ -0,0 +1,10 @@ +fun box(): String { + class A { + var result: String = "Fail"; + { + result = "OK" + } + } + + return (::A)().result +} diff --git a/compiler/testData/codegen/box/callableReference/local/enumExtendsTrait.kt b/compiler/testData/codegen/box/callableReference/local/enumExtendsTrait.kt new file mode 100644 index 00000000000..7fa5592bdf9 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/enumExtendsTrait.kt @@ -0,0 +1,11 @@ +fun box(): String { + trait Named { + fun name(): String + } + + enum class E : Named { + OK + } + + return E.OK.(Named::name)() +} diff --git a/compiler/testData/codegen/box/callableReference/local/genericMember.kt b/compiler/testData/codegen/box/callableReference/local/genericMember.kt new file mode 100644 index 00000000000..ad0db326cd9 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/genericMember.kt @@ -0,0 +1,8 @@ +fun box(): String { + class Id { + fun invoke(t: T) = t + } + + val ref = Id::invoke + return Id().ref("OK") +} diff --git a/compiler/testData/codegen/box/callableReference/local/localLocal.kt b/compiler/testData/codegen/box/callableReference/local/localLocal.kt new file mode 100644 index 00000000000..a34a24395b6 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/localLocal.kt @@ -0,0 +1,10 @@ +fun box(): String { + fun foo(): String { + fun bar() = "OK" + val ref = ::bar + return ref() + } + + val ref = ::foo + return ref() +} diff --git a/compiler/testData/codegen/box/callableReference/local/simple.kt b/compiler/testData/codegen/box/callableReference/local/simple.kt new file mode 100644 index 00000000000..789703872db --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/simple.kt @@ -0,0 +1,4 @@ +fun box(): String { + fun foo() = "OK" + return (::foo)() +} diff --git a/compiler/testData/codegen/box/callableReference/local/simpleWithArg.kt b/compiler/testData/codegen/box/callableReference/local/simpleWithArg.kt new file mode 100644 index 00000000000..e45fce85e2e --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/simpleWithArg.kt @@ -0,0 +1,4 @@ +fun box(): String { + fun foo(s: String) = s + return (::foo)("OK") +} diff --git a/compiler/testData/codegen/box/callableReference/local/unitWithSideEffect.kt b/compiler/testData/codegen/box/callableReference/local/unitWithSideEffect.kt new file mode 100644 index 00000000000..5da17e9d5e5 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/local/unitWithSideEffect.kt @@ -0,0 +1,15 @@ +var state = 23 + +fun box(): String { + fun incrementState(inc: Int) { + state += inc + } + + val inc = ::incrementState + inc(12) + inc(-5) + inc(27) + inc(-15) + + return if (state == 42) "OK" else "Fail $state" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index e52418019d9..baf9ec25d36 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -525,6 +525,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } @TestMetadata("compiler/testData/codegen/box/callableReference") + @InnerTestClasses({CallableReference.Local.class}) public static class CallableReference extends AbstractBlackBoxCodegenTest { @TestMetadata("abstractClassMember.kt") public void testAbstractClassMember() throws Exception { @@ -715,6 +716,65 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/callableReference/traitMember.kt"); } + @TestMetadata("compiler/testData/codegen/box/callableReference/local") + public static class Local extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInLocal() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/callableReference/local"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("classMember.kt") + public void testClassMember() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/classMember.kt"); + } + + @TestMetadata("constructor.kt") + public void testConstructor() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/constructor.kt"); + } + + @TestMetadata("constructorWithInitializer.kt") + public void testConstructorWithInitializer() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/constructorWithInitializer.kt"); + } + + @TestMetadata("enumExtendsTrait.kt") + public void testEnumExtendsTrait() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/enumExtendsTrait.kt"); + } + + @TestMetadata("genericMember.kt") + public void testGenericMember() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/genericMember.kt"); + } + + @TestMetadata("localLocal.kt") + public void testLocalLocal() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/localLocal.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/simple.kt"); + } + + @TestMetadata("simpleWithArg.kt") + public void testSimpleWithArg() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/simpleWithArg.kt"); + } + + @TestMetadata("unitWithSideEffect.kt") + public void testUnitWithSideEffect() throws Exception { + doTest("compiler/testData/codegen/box/callableReference/local/unitWithSideEffect.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("CallableReference"); + suite.addTestSuite(CallableReference.class); + suite.addTestSuite(Local.class); + return suite; + } } @TestMetadata("compiler/testData/codegen/box/casts") @@ -5073,7 +5133,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { suite.addTestSuite(BinaryOp.class); suite.addTestSuite(Bridges.class); suite.addTestSuite(BuiltinStubMethods.class); - suite.addTestSuite(CallableReference.class); + suite.addTest(CallableReference.innerSuite()); suite.addTestSuite(Casts.class); suite.addTestSuite(Classes.class); suite.addTest(Closures.innerSuite());