From 835c0f85c7a74e61d4779a1b5f643648da23bc2e Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Wed, 13 Mar 2013 20:57:46 +0400 Subject: [PATCH] Supported SAM constructors in codegen (simplest case). --- .../jetbrains/jet/codegen/ClosureCodegen.java | 35 ++++++++++++++----- .../jet/codegen/ExpressionCodegen.java | 19 +++++++--- .../java/sam/SingleAbstractMethodUtils.java | 7 +++- compiler/testData/codegen/box/sam/runnable.kt | 7 ++++ .../box/sam/runnableAccessingClosure1.kt | 8 +++++ .../box/sam/runnableAccessingClosure2.kt | 11 ++++++ .../BlackBoxCodegenTestGenerated.java | 26 +++++++++++++- 7 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/box/sam/runnable.kt create mode 100644 compiler/testData/codegen/box/sam/runnableAccessingClosure1.kt create mode 100644 compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java index 3aa5cbf7964..fb8b0cd58cb 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.codegen; import com.google.common.collect.Lists; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.asm4.MethodVisitor; import org.jetbrains.asm4.Type; import org.jetbrains.asm4.commons.InstructionAdapter; @@ -38,6 +39,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmClassName; +import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; @@ -53,13 +55,16 @@ import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; public class ClosureCodegen extends GenerationStateAware { private final MutableClosure closure; + @Nullable + private final ClassDescriptor samInterface; Method constructor; JvmClassName name; - public ClosureCodegen(GenerationState state, MutableClosure closure) { + public ClosureCodegen(GenerationState state, MutableClosure closure, @Nullable ClassDescriptor samInterface) { super(state); this.closure = closure; + this.samInterface = samInterface; } public ClosureCodegen gen(JetDeclarationWithBody fun, CodegenContext context, ExpressionCodegen expressionCodegen) { @@ -69,7 +74,13 @@ public class ClosureCodegen extends GenerationStateAware { FunctionDescriptor funDescriptor = bindingContext.get(BindingContext.FUNCTION, fun); assert funDescriptor != null; - Name interfaceFunctionName = Name.identifier("invoke"); + Name interfaceFunctionName; + if (samInterface == null) { + interfaceFunctionName = Name.identifier("invoke"); + } + else { + interfaceFunctionName = SingleAbstractMethodUtils.getAbstractMethodOfFunctionalInterface(samInterface).getName(); + } SignatureWriter signatureWriter = new SignatureWriter(); @@ -83,18 +94,24 @@ public class ClosureCodegen extends GenerationStateAware { appendType(signatureWriter, funDescriptor.getReturnType(), '='); signatureWriter.visitEnd(); + JvmClassName superclass = samInterface == null ? funClass : JvmClassName.byType(OBJECT_TYPE); + String[] superInterfaces = samInterface == null + ? new String[0] + : new String[] {JvmClassName.byClassDescriptor(samInterface).getInternalName()}; cv.defineClass(fun, V1_6, - ACC_PUBLIC|ACC_FINAL/*|ACC_SUPER*/, + ACC_PUBLIC | ACC_FINAL/*|ACC_SUPER*/, name.getInternalName(), null, - funClass.getInternalName(), - new String[0] + superclass.getInternalName(), + superInterfaces ); cv.visitSource(fun.getContainingFile().getName(), null); - generateBridge(interfaceFunctionName, name.getInternalName(), funDescriptor, fun, cv); + if (samInterface == null) { // TODO temporary: if SAM interface is generic, we'll need bridge + generateBridge(interfaceFunctionName, name.getInternalName(), funDescriptor, fun, cv); + } generateBody(interfaceFunctionName, funDescriptor, cv, fun, context, expressionCodegen); constructor = generateConstructor(funClass, fun, cv, closure); @@ -213,8 +230,10 @@ public class ClosureCodegen extends GenerationStateAware { mv.visitCode(); InstructionAdapter iv = new InstructionAdapter(mv); - iv.load(0, funClass.getAsmType()); - iv.invokespecial(funClass.getInternalName(), "", "()V"); + Type superAsmType = samInterface == null ? funClass.getAsmType() : OBJECT_TYPE; + + iv.load(0, superAsmType); + iv.invokespecial(superAsmType.getInternalName(), "", "()V"); int k = 1; for (FieldInfo fieldInfo : args) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index b29eea959cc..38c877d5490 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1230,7 +1230,7 @@ public class ExpressionCodegen extends JetVisitor implem return StackValue.none(); } - StackValue closure = genClosure(function); + StackValue closure = genClosure(function, null); DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function); int index = lookupLocalIndex(descriptor); closure.put(OBJECT_TYPE, v); @@ -1246,18 +1246,18 @@ public class ExpressionCodegen extends JetVisitor implem return gen(expression.getFunctionLiteral().getBodyExpression()); } else { - return genClosure(expression.getFunctionLiteral()); + return genClosure(expression.getFunctionLiteral(), null); } } - private StackValue genClosure(JetDeclarationWithBody declaration) { + private StackValue genClosure(JetDeclarationWithBody declaration, @Nullable ClassDescriptor samInterfaceClass) { FunctionDescriptor descriptor = bindingContext.get(BindingContext.FUNCTION, declaration); ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_FUNCTION, descriptor); //noinspection SuspiciousMethodCalls CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor); - ClosureCodegen closureCodegen = new ClosureCodegen(state, (MutableClosure) closure).gen(declaration, context, this); + ClosureCodegen closureCodegen = new ClosureCodegen(state, (MutableClosure) closure, samInterfaceClass).gen(declaration, context, this); JvmClassName className = closureCodegen.name; Type asmType = className.getAsmType(); @@ -1886,6 +1886,17 @@ public class ExpressionCodegen extends JetVisitor implem return invokeFunction(call, receiver, functionCall); } else { + if (funDescriptor instanceof SimpleFunctionDescriptor) { + ClassDescriptor samTrait = bindingContext.get( + BindingContext.SAM_CONSTRUCTOR_TO_TRAIT, (SimpleFunctionDescriptor) funDescriptor); + + if (samTrait != null) { + JetFunctionLiteralExpression argumentExpression = (JetFunctionLiteralExpression) ((ExpressionValueArgument) resolvedCall.getValueArgumentsByIndex().get(0)).getValueArgument() + .getArgumentExpression(); // TODO this will fail for Runnable(f) expression + + return genClosure(argumentExpression.getFunctionLiteral(), samTrait); + } + } return invokeFunction(call, receiver, resolvedCall); } } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java index dbce610633f..93340e13f37 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java @@ -72,7 +72,7 @@ public class SingleAbstractMethodUtils { CallableMemberDescriptor.Kind.SYNTHESIZED ); - JetType parameterType = getFunctionalTypeForFunction((SimpleFunctionDescriptor) getAbstractMembers(klass).get(0)); + JetType parameterType = getFunctionalTypeForFunction(getAbstractMethodOfFunctionalInterface(klass));; ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( result, 0, Collections.emptyList(), Name.identifier("function"), parameterType, false, null); @@ -90,6 +90,11 @@ public class SingleAbstractMethodUtils { return result; } + @NotNull + public static SimpleFunctionDescriptor getAbstractMethodOfFunctionalInterface(@NotNull ClassDescriptor klass) { + return (SimpleFunctionDescriptor) getAbstractMembers(klass).get(0); + } + private SingleAbstractMethodUtils() { } } diff --git a/compiler/testData/codegen/box/sam/runnable.kt b/compiler/testData/codegen/box/sam/runnable.kt new file mode 100644 index 00000000000..dd973440ff8 --- /dev/null +++ b/compiler/testData/codegen/box/sam/runnable.kt @@ -0,0 +1,7 @@ +var result = "FAIL" + +fun box(): String { + val r = Runnable { result = "OK" } + r.run() + return result +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/sam/runnableAccessingClosure1.kt b/compiler/testData/codegen/box/sam/runnableAccessingClosure1.kt new file mode 100644 index 00000000000..3b7c44991d7 --- /dev/null +++ b/compiler/testData/codegen/box/sam/runnableAccessingClosure1.kt @@ -0,0 +1,8 @@ +fun box(): String { + val o = "O" + var result = "" + + val r = Runnable { result = o + "K" } //capturing local vals and local var + r.run() + return result +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt b/compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt new file mode 100644 index 00000000000..a54080487d0 --- /dev/null +++ b/compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt @@ -0,0 +1,11 @@ +class Box(val s: String) { + fun extract(): String { + var result = "" + Runnable { result = s }.run() // capturing this and local var + return result + } +} + +fun box(): String { + return Box("OK").extract() +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 279a4872133..247f137d722 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.codegen.generated.AbstractBlackBoxCodegenTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/box") -@InnerTestClasses({BlackBoxCodegenTestGenerated.Arrays.class, BlackBoxCodegenTestGenerated.Bridges.class, BlackBoxCodegenTestGenerated.Casts.class, BlackBoxCodegenTestGenerated.Classes.class, BlackBoxCodegenTestGenerated.Closures.class, BlackBoxCodegenTestGenerated.ControlStructures.class, BlackBoxCodegenTestGenerated.DefaultArguments.class, BlackBoxCodegenTestGenerated.Elvis.class, BlackBoxCodegenTestGenerated.Enum.class, BlackBoxCodegenTestGenerated.ExclExcl.class, BlackBoxCodegenTestGenerated.ExtensionFunctions.class, BlackBoxCodegenTestGenerated.ExtensionProperties.class, BlackBoxCodegenTestGenerated.Functions.class, BlackBoxCodegenTestGenerated.InnerNested.class, BlackBoxCodegenTestGenerated.Instructions.class, BlackBoxCodegenTestGenerated.Intrinsics.class, BlackBoxCodegenTestGenerated.Labels.class, BlackBoxCodegenTestGenerated.LocalClasses.class, BlackBoxCodegenTestGenerated.MultiDecl.class, BlackBoxCodegenTestGenerated.Namespace.class, BlackBoxCodegenTestGenerated.Objects.class, BlackBoxCodegenTestGenerated.OperatorConventions.class, BlackBoxCodegenTestGenerated.PrimitiveTypes.class, BlackBoxCodegenTestGenerated.Properties.class, BlackBoxCodegenTestGenerated.SafeCall.class, BlackBoxCodegenTestGenerated.Strings.class, BlackBoxCodegenTestGenerated.Super.class, BlackBoxCodegenTestGenerated.Traits.class, BlackBoxCodegenTestGenerated.TypeInfo.class, BlackBoxCodegenTestGenerated.Unit.class, BlackBoxCodegenTestGenerated.Vararg.class, BlackBoxCodegenTestGenerated.When.class}) +@InnerTestClasses({BlackBoxCodegenTestGenerated.Arrays.class, BlackBoxCodegenTestGenerated.Bridges.class, BlackBoxCodegenTestGenerated.Casts.class, BlackBoxCodegenTestGenerated.Classes.class, BlackBoxCodegenTestGenerated.Closures.class, BlackBoxCodegenTestGenerated.ControlStructures.class, BlackBoxCodegenTestGenerated.DefaultArguments.class, BlackBoxCodegenTestGenerated.Elvis.class, BlackBoxCodegenTestGenerated.Enum.class, BlackBoxCodegenTestGenerated.ExclExcl.class, BlackBoxCodegenTestGenerated.ExtensionFunctions.class, BlackBoxCodegenTestGenerated.ExtensionProperties.class, BlackBoxCodegenTestGenerated.Functions.class, BlackBoxCodegenTestGenerated.InnerNested.class, BlackBoxCodegenTestGenerated.Instructions.class, BlackBoxCodegenTestGenerated.Intrinsics.class, BlackBoxCodegenTestGenerated.Labels.class, BlackBoxCodegenTestGenerated.LocalClasses.class, BlackBoxCodegenTestGenerated.MultiDecl.class, BlackBoxCodegenTestGenerated.Namespace.class, BlackBoxCodegenTestGenerated.Objects.class, BlackBoxCodegenTestGenerated.OperatorConventions.class, BlackBoxCodegenTestGenerated.PrimitiveTypes.class, BlackBoxCodegenTestGenerated.Properties.class, BlackBoxCodegenTestGenerated.SafeCall.class, BlackBoxCodegenTestGenerated.Sam.class, BlackBoxCodegenTestGenerated.Strings.class, BlackBoxCodegenTestGenerated.Super.class, BlackBoxCodegenTestGenerated.Traits.class, BlackBoxCodegenTestGenerated.TypeInfo.class, BlackBoxCodegenTestGenerated.Unit.class, BlackBoxCodegenTestGenerated.Vararg.class, BlackBoxCodegenTestGenerated.When.class}) public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInBox() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/box"), Pattern.compile("^(.+)\\.kt$"), true); @@ -3153,6 +3153,29 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } + @TestMetadata("compiler/testData/codegen/box/sam") + public static class Sam extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSam() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("runnable.kt") + public void testRunnable() throws Exception { + doTest("compiler/testData/codegen/box/sam/runnable.kt"); + } + + @TestMetadata("runnableAccessingClosure1.kt") + public void testRunnableAccessingClosure1() throws Exception { + doTest("compiler/testData/codegen/box/sam/runnableAccessingClosure1.kt"); + } + + @TestMetadata("runnableAccessingClosure2.kt") + public void testRunnableAccessingClosure2() throws Exception { + doTest("compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt"); + } + + } + @TestMetadata("compiler/testData/codegen/box/strings") public static class Strings extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInStrings() throws Exception { @@ -3507,6 +3530,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { suite.addTestSuite(PrimitiveTypes.class); suite.addTestSuite(Properties.class); suite.addTestSuite(SafeCall.class); + suite.addTestSuite(Sam.class); suite.addTestSuite(Strings.class); suite.addTestSuite(Super.class); suite.addTestSuite(Traits.class);