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) {
|
if (callable instanceof CallableMethod) {
|
||||||
CallableMethod callableMethod = (CallableMethod) callable;
|
CallableMethod callableMethod = (CallableMethod) callable;
|
||||||
Type calleeType = callableMethod.getGenerateCalleeType();
|
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();
|
assert !callableMethod.isNeedsThis() : "Method should have a receiver: " + resolvedCall.getResultingDescriptor();
|
||||||
gen(call.getCalleeExpression(), calleeType);
|
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);
|
invokeMethodWithArguments(callableMethod, resolvedCall, receiver);
|
||||||
|
//noinspection ConstantConditions
|
||||||
Type callReturnType = callableMethod.getSignature().getAsmMethod().getReturnType();
|
Type returnType = typeMapper.mapReturnType(resolvedCall.getResultingDescriptor().getReturnType());
|
||||||
if (callReturnType == Type.VOID_TYPE) return StackValue.none();
|
StackValue.coerce(callableMethod.getSignature().getAsmMethod().getReturnType(), returnType, v);
|
||||||
|
return StackValue.onStack(returnType);
|
||||||
JetType type = fd.getReturnType();
|
|
||||||
assert type != null;
|
|
||||||
Type retType = typeMapper.mapReturnType(type);
|
|
||||||
StackValue.coerce(callReturnType, retType, v);
|
|
||||||
return StackValue.onStack(retType);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
receiver = StackValue.receiver(resolvedCall, receiver, this, null);
|
receiver = StackValue.receiver(resolvedCall, receiver, this, null);
|
||||||
@@ -2497,8 +2504,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
|
result = generateNormalFunctionCall(codegen, fakeResolvedCall,
|
||||||
result = codegen.invokeFunction(call, StackValue.none(), fakeResolvedCall);
|
CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionAdapter v = codegen.v;
|
InstructionAdapter v = codegen.v;
|
||||||
@@ -2506,6 +2513,27 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
v.areturn(returnType);
|
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
|
@NotNull
|
||||||
private JetCallExpression constructFakeFunctionCall() {
|
private JetCallExpression constructFakeFunctionCall() {
|
||||||
StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
|
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")
|
@TestMetadata("compiler/testData/codegen/box/callableReference")
|
||||||
|
@InnerTestClasses({CallableReference.Local.class})
|
||||||
public static class CallableReference extends AbstractBlackBoxCodegenTest {
|
public static class CallableReference extends AbstractBlackBoxCodegenTest {
|
||||||
@TestMetadata("abstractClassMember.kt")
|
@TestMetadata("abstractClassMember.kt")
|
||||||
public void testAbstractClassMember() throws Exception {
|
public void testAbstractClassMember() throws Exception {
|
||||||
@@ -715,6 +716,65 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest("compiler/testData/codegen/box/callableReference/traitMember.kt");
|
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")
|
@TestMetadata("compiler/testData/codegen/box/casts")
|
||||||
@@ -5073,7 +5133,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
suite.addTestSuite(BinaryOp.class);
|
suite.addTestSuite(BinaryOp.class);
|
||||||
suite.addTestSuite(Bridges.class);
|
suite.addTestSuite(Bridges.class);
|
||||||
suite.addTestSuite(BuiltinStubMethods.class);
|
suite.addTestSuite(BuiltinStubMethods.class);
|
||||||
suite.addTestSuite(CallableReference.class);
|
suite.addTest(CallableReference.innerSuite());
|
||||||
suite.addTestSuite(Casts.class);
|
suite.addTestSuite(Casts.class);
|
||||||
suite.addTestSuite(Classes.class);
|
suite.addTestSuite(Classes.class);
|
||||||
suite.addTest(Closures.innerSuite());
|
suite.addTest(Closures.innerSuite());
|
||||||
|
|||||||
Reference in New Issue
Block a user