Support local function references in codegen

#KT-3704 In Progress
 #KT-1183 In Progress
This commit is contained in:
Alexander Udalov
2013-12-17 23:14:43 +04:00
parent 07648583ed
commit 363fe607fc
11 changed files with 179 additions and 14 deletions
@@ -1987,9 +1987,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> 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<StackValue, StackValue> 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<? extends CallableDescriptor> 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<StackValue, StackValue> 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<StackValue, StackValue> implem
v.areturn(returnType);
}
@NotNull
private StackValue generateNormalFunctionCall(
@NotNull ExpressionCodegen codegen,
@NotNull ResolvedCall<CallableDescriptor> 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(");
@@ -0,0 +1,8 @@
fun box(): String {
class Local {
fun foo() = "OK"
}
val ref = Local::foo
return Local().ref()
}
@@ -0,0 +1,7 @@
fun box(): String {
class A {
val result = "OK"
}
return (::A)().result
}
@@ -0,0 +1,10 @@
fun box(): String {
class A {
var result: String = "Fail";
{
result = "OK"
}
}
return (::A)().result
}
@@ -0,0 +1,11 @@
fun box(): String {
trait Named {
fun name(): String
}
enum class E : Named {
OK
}
return E.OK.(Named::name)()
}
@@ -0,0 +1,8 @@
fun box(): String {
class Id<T> {
fun invoke(t: T) = t
}
val ref = Id<String>::invoke
return Id<String>().ref("OK")
}
@@ -0,0 +1,10 @@
fun box(): String {
fun foo(): String {
fun bar() = "OK"
val ref = ::bar
return ref()
}
val ref = ::foo
return ref()
}
@@ -0,0 +1,4 @@
fun box(): String {
fun foo() = "OK"
return (::foo)()
}
@@ -0,0 +1,4 @@
fun box(): String {
fun foo(s: String) = s
return (::foo)("OK")
}
@@ -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"
}
@@ -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());