Inline callable references

#KT-6900 Fixed
This commit is contained in:
Michael Bogdanov
2015-04-10 12:20:40 +03:00
parent 991db29731
commit e200b97ca4
19 changed files with 235 additions and 23 deletions
@@ -2793,7 +2793,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}); });
} }
private static class CallableReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> { public static class CallableReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
private final ResolvedCall<?> resolvedCall; private final ResolvedCall<?> resolvedCall;
private final FunctionDescriptor referencedFunction; private final FunctionDescriptor referencedFunction;
@@ -24,7 +24,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.builtins.InlineStrategy; import org.jetbrains.kotlin.builtins.InlineStrategy;
import org.jetbrains.kotlin.builtins.InlineUtil; import org.jetbrains.kotlin.builtins.InlineUtil;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.*; import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.context.CodegenContext; import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
@@ -63,6 +62,7 @@ import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionExpression; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionExpression;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionLiteral; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionLiteral;
import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCallWithAssert;
public class InlineCodegen extends CallGenerator { public class InlineCodegen extends CallGenerator {
private final GenerationState state; private final GenerationState state;
@@ -299,7 +299,7 @@ public class InlineCodegen extends CallGenerator {
} }
private SMAPAndMethodNode generateLambdaBody(LambdaInfo info) { private SMAPAndMethodNode generateLambdaBody(LambdaInfo info) {
JetFunctionLiteral declaration = info.getFunctionLiteral(); JetExpression declaration = info.getFunctionLiteralOrCallableReference();
FunctionDescriptor descriptor = info.getFunctionDescriptor(); FunctionDescriptor descriptor = info.getFunctionDescriptor();
MethodContext parentContext = codegen.getContext(); MethodContext parentContext = codegen.getContext();
@@ -321,27 +321,37 @@ public class InlineCodegen extends CallGenerator {
@NotNull MethodVisitor adapter, @NotNull MethodVisitor adapter,
@NotNull FunctionDescriptor descriptor, @NotNull FunctionDescriptor descriptor,
@NotNull MethodContext context, @NotNull MethodContext context,
@NotNull JetDeclarationWithBody declaration, @NotNull JetExpression expression,
@NotNull JvmMethodSignature jvmMethodSignature, @NotNull JvmMethodSignature jvmMethodSignature,
boolean isLambda boolean isLambda
) { ) {
FakeMemberCodegen parentCodegen = FakeMemberCodegen parentCodegen =
new FakeMemberCodegen(codegen.getParentCodegen(), declaration, new FakeMemberCodegen(codegen.getParentCodegen(), expression,
(FieldOwnerContext) context.getParentContext(), (FieldOwnerContext) context.getParentContext(),
isLambda ? codegen.getParentCodegen().getClassName() : typeMapper.mapOwner(descriptor, false).getInternalName()); isLambda ? codegen.getParentCodegen().getClassName() : typeMapper.mapOwner(descriptor, false).getInternalName());
FunctionGenerationStrategy strategy =
expression instanceof JetCallableReferenceExpression ?
new ExpressionCodegen.CallableReferenceGenerationStrategy(
state,
descriptor,
getResolvedCallWithAssert(((JetCallableReferenceExpression) expression).getCallableReference(),
codegen.getBindingContext()
)) :
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (JetDeclarationWithBody) expression);
FunctionCodegen.generateMethodBody( FunctionCodegen.generateMethodBody(
adapter, descriptor, context, jvmMethodSignature, adapter, descriptor, context, jvmMethodSignature,
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), strategy,
// Wrapping for preventing marking actual parent codegen as containing reifier markers // Wrapping for preventing marking actual parent codegen as containing reifier markers
parentCodegen parentCodegen
); );
return createSMAPWithDefaultMapping(declaration, parentCodegen.getOrCreateSourceMapper().getResultMappings()); return createSMAPWithDefaultMapping(expression, parentCodegen.getOrCreateSourceMapper().getResultMappings());
} }
private static SMAP createSMAPWithDefaultMapping( private static SMAP createSMAPWithDefaultMapping(
@NotNull JetDeclarationWithBody declaration, @NotNull JetExpression declaration,
@NotNull List<FileMapping> mappings @NotNull List<FileMapping> mappings
) { ) {
PsiFile containingFile = declaration.getContainingFile(); PsiFile containingFile = declaration.getContainingFile();
@@ -509,22 +519,28 @@ public class InlineCodegen extends CallGenerator {
} }
} }
public static boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptor) { /*lambda or callable reference*/
public static boolean isInliningParameter(JetExpression expression, ValueParameterDescriptor valueParameterDescriptor) {
//TODO deparenthisise typed //TODO deparenthisise typed
JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression); JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
return deparenthesized instanceof JetFunctionLiteralExpression && return InlineUtil.isInlineLambdaParameter(valueParameterDescriptor) &&
InlineUtil.isInlineLambdaParameter(valueParameterDescriptor); (deparenthesized instanceof JetFunctionLiteralExpression ||
deparenthesized instanceof JetCallableReferenceExpression);
} }
public void rememberClosure(JetExpression expression, Type type) { public void rememberClosure(JetExpression expression, Type type) {
JetFunctionLiteralExpression lambda = (JetFunctionLiteralExpression) JetPsiUtil.deparenthesize(expression); JetExpression lambda = JetPsiUtil.deparenthesize(expression);
assert lambda != null : "Couldn't find lambda in " + expression.getText(); assert lambda instanceof JetCallableReferenceExpression || lambda instanceof JetFunctionLiteralExpression :
"Couldn't find inline expression in " + expression.getText();
String labelNameIfPresent = null; String labelNameIfPresent = null;
PsiElement parent = lambda.getParent(); PsiElement parent = lambda.getParent();
if (parent instanceof JetLabeledExpression) { if (parent instanceof JetLabeledExpression) {
labelNameIfPresent = ((JetLabeledExpression) parent).getLabelName(); labelNameIfPresent = ((JetLabeledExpression) parent).getLabelName();
} }
if (lambda instanceof JetFunctionLiteralExpression) {
lambda = ((JetFunctionLiteralExpression) lambda).getFunctionLiteral();
}
LambdaInfo info = new LambdaInfo(lambda, typeMapper, labelNameIfPresent); LambdaInfo info = new LambdaInfo(lambda, typeMapper, labelNameIfPresent);
ParameterInfo closureInfo = invocationParamBuilder.addNextParameter(type, true, null); ParameterInfo closureInfo = invocationParamBuilder.addNextParameter(type, true, null);
@@ -572,10 +588,10 @@ public class InlineCodegen extends CallGenerator {
@NotNull JetExpression argumentExpression, @NotNull JetExpression argumentExpression,
@NotNull Type parameterType @NotNull Type parameterType
) { ) {
//TODO deparenthisise if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
if (isInliningClosure(argumentExpression, valueParameterDescriptor)) {
rememberClosure(argumentExpression, parameterType); rememberClosure(argumentExpression, parameterType);
} else { }
else {
StackValue value = codegen.gen(argumentExpression); StackValue value = codegen.gen(argumentExpression);
putValueIfNeeded(valueParameterDescriptor, parameterType, value); putValueIfNeeded(valueParameterDescriptor, parameterType, value);
} }
@@ -26,8 +26,7 @@ import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.psi.JetFunctionLiteral; import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.Type;
@@ -41,7 +40,7 @@ import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
public class LambdaInfo implements CapturedParamOwner, LabelOwner { public class LambdaInfo implements CapturedParamOwner, LabelOwner {
public final JetFunctionLiteralExpression expression; public final JetExpression expression;
private final JetTypeMapper typeMapper; private final JetTypeMapper typeMapper;
@@ -60,12 +59,12 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
private final Type closureClassType; private final Type closureClassType;
LambdaInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper, @Nullable String labelName) { LambdaInfo(@NotNull JetExpression expression, @NotNull JetTypeMapper typeMapper, @Nullable String labelName) {
this.expression = expression; this.expression = expression;
this.typeMapper = typeMapper; this.typeMapper = typeMapper;
this.labelName = labelName; this.labelName = labelName;
BindingContext bindingContext = typeMapper.getBindingContext(); BindingContext bindingContext = typeMapper.getBindingContext();
functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression.getFunctionLiteral()); functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression);
assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText(); assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText();
classDescriptor = anonymousClassForFunction(bindingContext, functionDescriptor); classDescriptor = anonymousClassForFunction(bindingContext, functionDescriptor);
@@ -87,8 +86,8 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
return functionDescriptor; return functionDescriptor;
} }
public JetFunctionLiteral getFunctionLiteral() { public JetExpression getFunctionLiteralOrCallableReference() {
return expression.getFunctionLiteral(); return expression;
} }
public ClassDescriptor getClassDescriptor() { public ClassDescriptor getClassDescriptor() {
@@ -0,0 +1,7 @@
import test.*
fun box() : String {
val call = call(A(11), A::calc)
return if (call == 11) "OK" else "fail"
}
@@ -0,0 +1,9 @@
package test
class A(val z: Int) {
fun calc() = z
}
inline fun call(p: A, s: A.() -> Int): Int {
return p.s()
}
@@ -0,0 +1,7 @@
import test.*
fun box() : String {
val call = A(11).test()
return if (call == 11) "OK" else "fail"
}
@@ -0,0 +1,11 @@
package test
class A(val z: Int) {
fun calc() = z
fun test() = call(A(z), ::calc)
}
inline fun call(p: A, s: A.() -> Int): Int {
return p.s()
}
@@ -0,0 +1,7 @@
import test.*
fun box() : String {
val call = call(11, ::A)
return if (call == 11) "OK" else "fail"
}
@@ -0,0 +1,9 @@
package test
class A(val z: Int) {
fun calc() = z
}
inline fun call(p: Int, s: (Int) -> A): Int {
return s(p).z
}
@@ -0,0 +1,5 @@
import test.*
fun box() : String {
return if (call("123", String::length) == 3) "OK" else "fail"
}
@@ -0,0 +1,5 @@
package test
inline fun call(p: String, s: String.() -> Int): Int {
return p.s()
}
@@ -0,0 +1,9 @@
import test.*
fun box() : String {
return if (call(10, ::calc) == 5) "OK" else "fail"
}
fun calc(p: Int) : Int {
return p / 2
}
@@ -0,0 +1,5 @@
package test
inline fun call(p: Int, s: (Int) -> Int): Int {
return s(p)
}
@@ -0,0 +1,9 @@
import test.*
fun box() : String {
return if (call(10, Int::calc) == 100) "OK" else "fail"
}
fun Int.calc(p: Int) : Int {
return p * this
}
@@ -0,0 +1,5 @@
package test
inline fun call(p: Int, s: Int.(Int) -> Int): Int {
return p.s(p)
}
@@ -0,0 +1,13 @@
fun box(): String {
return if (call(10, ::calc) == 5) "OK" else "fail"
}
fun calc(p: Int): Int {
return p / 2
}
inline fun call(p: Int, s: (Int) -> Int): Int {
return s(p)
}
// 0 NEW
@@ -65,6 +65,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("callableReferenceInline.kt")
public void testCallableReferenceInline() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/callableReferenceInline.kt");
doTest(fileName);
}
@TestMetadata("componentEvaluatesOnlyOnce.kt") @TestMetadata("componentEvaluatesOnlyOnce.kt")
public void testComponentEvaluatesOnlyOnce() throws Exception { public void testComponentEvaluatesOnlyOnce() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/componentEvaluatesOnlyOnce.kt"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/componentEvaluatesOnlyOnce.kt");
@@ -89,6 +89,51 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
} }
} }
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CallableReference extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInCallableReference() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/callableReference"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("classLevel.1.kt")
public void testClassLevel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("classLevel2.1.kt")
public void testClassLevel2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("constructor.1.kt")
public void testConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/constructor.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intrinsic.1.kt")
public void testIntrinsic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("topLevel.1.kt")
public void testTopLevel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("topLevelExtension.1.kt")
public void testTopLevelExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/capture") @TestMetadata("compiler/testData/codegen/boxInline/capture")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -89,6 +89,51 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
} }
} }
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CallableReference extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInCallableReference() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/callableReference"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("classLevel.1.kt")
public void testClassLevel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("classLevel2.1.kt")
public void testClassLevel2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("constructor.1.kt")
public void testConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/constructor.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intrinsic.1.kt")
public void testIntrinsic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("topLevel.1.kt")
public void testTopLevel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("topLevelExtension.1.kt")
public void testTopLevelExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt");
doBoxTestWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/capture") @TestMetadata("compiler/testData/codegen/boxInline/capture")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)