Support local function references in codegen
#KT-3704 In Progress #KT-1183 In Progress
This commit is contained in:
@@ -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"
|
||||
}
|
||||
+61
-1
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user