Supported SAM constructors in codegen (simplest case).

This commit is contained in:
Evgeny Gerashchenko
2013-03-13 20:57:46 +04:00
parent 8f34dea4c8
commit 835c0f85c7
7 changed files with 99 additions and 14 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.jet.codegen;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.MethodVisitor; import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Type; import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter; 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.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmClassName; 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.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType; 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 { public class ClosureCodegen extends GenerationStateAware {
private final MutableClosure closure; private final MutableClosure closure;
@Nullable
private final ClassDescriptor samInterface;
Method constructor; Method constructor;
JvmClassName name; JvmClassName name;
public ClosureCodegen(GenerationState state, MutableClosure closure) { public ClosureCodegen(GenerationState state, MutableClosure closure, @Nullable ClassDescriptor samInterface) {
super(state); super(state);
this.closure = closure; this.closure = closure;
this.samInterface = samInterface;
} }
public ClosureCodegen gen(JetDeclarationWithBody fun, CodegenContext context, ExpressionCodegen expressionCodegen) { 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); FunctionDescriptor funDescriptor = bindingContext.get(BindingContext.FUNCTION, fun);
assert funDescriptor != null; 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(); SignatureWriter signatureWriter = new SignatureWriter();
@@ -83,18 +94,24 @@ public class ClosureCodegen extends GenerationStateAware {
appendType(signatureWriter, funDescriptor.getReturnType(), '='); appendType(signatureWriter, funDescriptor.getReturnType(), '=');
signatureWriter.visitEnd(); 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, cv.defineClass(fun,
V1_6, V1_6,
ACC_PUBLIC|ACC_FINAL/*|ACC_SUPER*/, ACC_PUBLIC | ACC_FINAL/*|ACC_SUPER*/,
name.getInternalName(), name.getInternalName(),
null, null,
funClass.getInternalName(), superclass.getInternalName(),
new String[0] superInterfaces
); );
cv.visitSource(fun.getContainingFile().getName(), null); 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); generateBody(interfaceFunctionName, funDescriptor, cv, fun, context, expressionCodegen);
constructor = generateConstructor(funClass, fun, cv, closure); constructor = generateConstructor(funClass, fun, cv, closure);
@@ -213,8 +230,10 @@ public class ClosureCodegen extends GenerationStateAware {
mv.visitCode(); mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv); InstructionAdapter iv = new InstructionAdapter(mv);
iv.load(0, funClass.getAsmType()); Type superAsmType = samInterface == null ? funClass.getAsmType() : OBJECT_TYPE;
iv.invokespecial(funClass.getInternalName(), "<init>", "()V");
iv.load(0, superAsmType);
iv.invokespecial(superAsmType.getInternalName(), "<init>", "()V");
int k = 1; int k = 1;
for (FieldInfo fieldInfo : args) { for (FieldInfo fieldInfo : args) {
@@ -1230,7 +1230,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return StackValue.none(); return StackValue.none();
} }
StackValue closure = genClosure(function); StackValue closure = genClosure(function, null);
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function); DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
int index = lookupLocalIndex(descriptor); int index = lookupLocalIndex(descriptor);
closure.put(OBJECT_TYPE, v); closure.put(OBJECT_TYPE, v);
@@ -1246,18 +1246,18 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return gen(expression.getFunctionLiteral().getBodyExpression()); return gen(expression.getFunctionLiteral().getBodyExpression());
} }
else { 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); FunctionDescriptor descriptor = bindingContext.get(BindingContext.FUNCTION, declaration);
ClassDescriptor classDescriptor = ClassDescriptor classDescriptor =
bindingContext.get(CLASS_FOR_FUNCTION, descriptor); bindingContext.get(CLASS_FOR_FUNCTION, descriptor);
//noinspection SuspiciousMethodCalls //noinspection SuspiciousMethodCalls
CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor); 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; JvmClassName className = closureCodegen.name;
Type asmType = className.getAsmType(); Type asmType = className.getAsmType();
@@ -1886,6 +1886,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return invokeFunction(call, receiver, functionCall); return invokeFunction(call, receiver, functionCall);
} }
else { 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); return invokeFunction(call, receiver, resolvedCall);
} }
} }
@@ -72,7 +72,7 @@ public class SingleAbstractMethodUtils {
CallableMemberDescriptor.Kind.SYNTHESIZED CallableMemberDescriptor.Kind.SYNTHESIZED
); );
JetType parameterType = getFunctionalTypeForFunction((SimpleFunctionDescriptor) getAbstractMembers(klass).get(0)); JetType parameterType = getFunctionalTypeForFunction(getAbstractMethodOfFunctionalInterface(klass));;
ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
result, 0, Collections.<AnnotationDescriptor>emptyList(), Name.identifier("function"), parameterType, false, null); result, 0, Collections.<AnnotationDescriptor>emptyList(), Name.identifier("function"), parameterType, false, null);
@@ -90,6 +90,11 @@ public class SingleAbstractMethodUtils {
return result; return result;
} }
@NotNull
public static SimpleFunctionDescriptor getAbstractMethodOfFunctionalInterface(@NotNull ClassDescriptor klass) {
return (SimpleFunctionDescriptor) getAbstractMembers(klass).get(0);
}
private SingleAbstractMethodUtils() { private SingleAbstractMethodUtils() {
} }
} }
@@ -0,0 +1,7 @@
var result = "FAIL"
fun box(): String {
val r = Runnable { result = "OK" }
r.run()
return result
}
@@ -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
}
@@ -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()
}
@@ -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 */ /** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all") @SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/box") @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 class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBox() throws Exception { 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); 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") @TestMetadata("compiler/testData/codegen/box/strings")
public static class Strings extends AbstractBlackBoxCodegenTest { public static class Strings extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInStrings() throws Exception { public void testAllFilesPresentInStrings() throws Exception {
@@ -3507,6 +3530,7 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
suite.addTestSuite(PrimitiveTypes.class); suite.addTestSuite(PrimitiveTypes.class);
suite.addTestSuite(Properties.class); suite.addTestSuite(Properties.class);
suite.addTestSuite(SafeCall.class); suite.addTestSuite(SafeCall.class);
suite.addTestSuite(Sam.class);
suite.addTestSuite(Strings.class); suite.addTestSuite(Strings.class);
suite.addTestSuite(Super.class); suite.addTestSuite(Super.class);
suite.addTestSuite(Traits.class); suite.addTestSuite(Traits.class);