diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBuilder.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBuilder.java index fb218d4a0a9..60c455df3e0 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBuilder.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBuilder.java @@ -21,6 +21,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.asm4.*; public abstract class ClassBuilder { + private String thisName; + public static class Concrete extends ClassBuilder { private final ClassVisitor v; @@ -78,6 +80,7 @@ public abstract class ClassBuilder { String superName, String[] interfaces ) { + thisName = name; getVisitor().visit(version, access, name, signature, superName, interfaces); } @@ -92,4 +95,8 @@ public abstract class ClassBuilder { public void visitInnerClass(String name, String outerName, String innerName, int access) { getVisitor().visitInnerClass(name, outerName, innerName, access); } + + public String getThisName() { + return thisName; + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 759f7f1b32b..8506bf99b2d 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1899,7 +1899,17 @@ public class ExpressionCodegen extends JetVisitor implem return genClosure(((JetFunctionLiteralExpression) argumentExpression).getFunctionLiteral(), samInterface); } else { - throw new UnsupportedOperationException(); // TODO + JvmClassName className = new SamWrapperCodegen(state, samInterface).genWrapper(expression, argumentExpression); + + v.anew(className.getAsmType()); + v.dup(); + + JetType functionType = ((SimpleFunctionDescriptor) funDescriptor).getValueParameters().get(0).getType(); + gen(argumentExpression, typeMapper.mapType(functionType)); + + v.invokespecial(className.getInternalName(), "", + Type.getMethodDescriptor(Type.VOID_TYPE, typeMapper.mapType(functionType))); + return StackValue.onStack(className.getAsmType()); } } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index d1514793ecc..4c185794a87 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -846,9 +846,7 @@ public class FunctionCodegen extends GenerationStateAware { reg += argTypes[i].getSize(); } - iv.invokevirtual(state.getTypeMapper().mapType( - (ClassDescriptor) owner.getContextDescriptor()).getInternalName(), - jvmSignature.getName(), jvmSignature.getDescriptor()); + iv.invokevirtual(v.getThisName(), jvmSignature.getName(), jvmSignature.getDescriptor()); StackValue.onStack(jvmSignature.getReturnType()).put(overridden.getReturnType(), iv); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index eddc3442a6c..0789ab21398 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -1051,7 +1051,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } if (closure != null) { - List argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, bindingContext, state, closure, classAsmType); + List argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, bindingContext, closure, classAsmType); int k = 1; for (FieldInfo info : argsFromClosure) { k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java new file mode 100644 index 00000000000..38d40e106b1 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java @@ -0,0 +1,139 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.asm4.MethodVisitor; +import org.jetbrains.asm4.Type; +import org.jetbrains.asm4.commons.InstructionAdapter; +import org.jetbrains.jet.codegen.binding.CodegenBinding; +import org.jetbrains.jet.codegen.context.CodegenContext; +import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.codegen.state.GenerationStateAware; +import org.jetbrains.jet.codegen.state.JetTypeMapperMode; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.JetCallExpression; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; +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; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; + +import static org.jetbrains.asm4.Opcodes.*; +import static org.jetbrains.jet.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE; +import static org.jetbrains.jet.codegen.AsmUtil.genStubCode; +import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; + +public class SamWrapperCodegen extends GenerationStateAware { + private static final String FUNCTION_FIELD_NAME = "function"; + + @NotNull private final ClassDescriptor samInterface; + + public SamWrapperCodegen(@NotNull GenerationState state, @NotNull ClassDescriptor samInterface) { + super(state); + this.samInterface = samInterface; + } + + public JvmClassName genWrapper(JetCallExpression callExpression, JetExpression argumentExpression) { + JvmClassName name = bindingContext.get(CodegenBinding.FQN_FOR_SAM_CONSTRUCTOR, callExpression); + assert name != null : "internal class name not found for " + callExpression.getText(); + + JetType functionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression); + assert functionType != null && KotlinBuiltIns.getInstance().isFunctionType(functionType) : + "not a function type of " + argumentExpression.getText() + ": " + functionType; + + ResolvedCall resolvedCall = + bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression()); + assert resolvedCall != null : "couldn't find resolved call for " + callExpression.getText(); + + JetType resultType = resolvedCall.getResultingDescriptor().getReturnType(); + assert resultType != null && resultType.getConstructor() == samInterface.getTypeConstructor() : + "unexpected result type: " + resultType; + + SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamType(resultType); + + ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), callExpression.getContainingFile()); + cv.defineClass(callExpression, + V1_6, + ACC_FINAL, + name.getInternalName(), + null, + JvmClassName.byType(OBJECT_TYPE).getInternalName(), + new String[]{JvmClassName.byClassDescriptor(samInterface).getInternalName()} + ); + cv.visitSource(callExpression.getContainingFile().getName(), null); + + Type functionAsmType = state.getTypeMapper().mapType(functionType, JetTypeMapperMode.VALUE); + cv.newField(null, + ACC_SYNTHETIC | ACC_PRIVATE | ACC_FINAL, + FUNCTION_FIELD_NAME, + functionAsmType.getDescriptor(), + null, + null); + + generateConstructor(name.getAsmType(), functionAsmType, cv); + generateMethod(name.getAsmType(), functionAsmType, cv, interfaceFunction, functionType); + + cv.done(); + + return name; + } + + private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) { + MethodVisitor mv = cv.newMethod(null, NO_FLAG_PACKAGE_PRIVATE, "", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null); + + if (state.getClassBuilderMode() == ClassBuilderMode.STUBS) { + genStubCode(mv); + } + else if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { + mv.visitCode(); + InstructionAdapter iv = new InstructionAdapter(mv); + + // super constructor + iv.load(0, OBJECT_TYPE); + iv.invokespecial(OBJECT_TYPE.getInternalName(), "", "()V"); + + // save parameter to field + iv.load(0, OBJECT_TYPE); + iv.load(1, functionType); + iv.putfield(ownerType.getInternalName(), FUNCTION_FIELD_NAME, functionType.getDescriptor()); + + iv.visitInsn(RETURN); + FunctionCodegen.endVisit(iv, "constructor of SAM wrapper", null); + } + } + + private void generateMethod( + Type ownerType, + Type functionType, + ClassBuilder cv, + SimpleFunctionDescriptor interfaceFunction, + JetType functionJetType + ) { + + // using static context to avoid creating ClassDescriptor and everything else + FunctionCodegen codegen = new FunctionCodegen(CodegenContext.STATIC, cv, state); + + FunctionDescriptor invokeFunction = functionJetType.getMemberScope() + .getFunctions(Name.identifier("invoke")).iterator().next().getOriginal(); + StackValue functionField = StackValue.field(functionType, JvmClassName.byType(ownerType), FUNCTION_FIELD_NAME, false); + codegen.genDelegate(interfaceFunction, invokeFunction, functionField); + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java index 138cda0bb36..ad58397a910 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java @@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; @@ -55,6 +56,30 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { this.bindingContext = bindingTrace.getBindingContext(); } + @Override + public void visitCallExpression(JetCallExpression expression) { + super.visitCallExpression(expression); + + JetExpression callee = expression.getCalleeExpression(); + assert callee != null : "not found callee for " + expression.getText(); + + ResolvedCall resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, callee); + if (resolvedCall == null) { + return; + } + + DeclarationDescriptor funDescriptor = resolvedCall.getResultingDescriptor(); + + if (funDescriptor instanceof SimpleFunctionDescriptor) { + ClassDescriptor samTrait = bindingContext.get( + BindingContext.SAM_CONSTRUCTOR_TO_INTERFACE, ((SimpleFunctionDescriptor) funDescriptor).getOriginal()); + if (samTrait != null) { + String name = inventAnonymousClassName(expression); + bindingTrace.record(FQN_FOR_SAM_CONSTRUCTOR, expression, JvmClassName.byInternalName(name)); + } + } + } + private ClassDescriptor recordClassForFunction(FunctionDescriptor funDescriptor) { ClassDescriptor classDescriptor; int arity = funDescriptor.getValueParameters().size(); @@ -90,11 +115,12 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { if (descriptor == null) { if (declaration instanceof JetFunctionLiteralExpression || declaration instanceof JetNamedFunction || - declaration instanceof JetObjectLiteralExpression) { + declaration instanceof JetObjectLiteralExpression || + declaration instanceof JetCallExpression) { } else { throw new IllegalStateException( - "Class-less declaration which is not JetFunctionLiteralExpression|JetNamedFunction|JetObjectLiteralExpression : " + + "Class-less declaration which is not JetFunctionLiteralExpression|JetNamedFunction|JetObjectLiteralExpression|JetCallExpression : " + declaration.getClass().getName()); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java index d50f60c7977..a9a830f2d02 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java @@ -49,6 +49,8 @@ public class CodegenBinding { public static final WritableSlice FQN = Slices.createSimpleSlice(); + public static final WritableSlice FQN_FOR_SAM_CONSTRUCTOR = Slices.createSimpleSlice(); + public static final WritableSlice SCRIPT_NAMES = Slices.createSimpleSetSlice(); public static final WritableSlice ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice(); 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 fb72b5a5161..d231c2fbf9d 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 @@ -41,7 +41,7 @@ public class SingleAbstractMethodUtils { return false; } - List abstractMembers = getAbstractMembers(klass); + List abstractMembers = getAbstractMembers(klass.getDefaultType()); if (abstractMembers.size() == 1) { CallableMemberDescriptor member = abstractMembers.get(0); if (member instanceof SimpleFunctionDescriptor) { @@ -52,9 +52,9 @@ public class SingleAbstractMethodUtils { } @NotNull - private static List getAbstractMembers(@NotNull ClassDescriptor klass) { + private static List getAbstractMembers(@NotNull JetType type) { List abstractMembers = Lists.newArrayList(); - for (DeclarationDescriptor member : klass.getDefaultType().getMemberScope().getAllDescriptors()) { + for (DeclarationDescriptor member : type.getMemberScope().getAllDescriptors()) { if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) { abstractMembers.add((CallableMemberDescriptor) member); } @@ -88,7 +88,7 @@ public class SingleAbstractMethodUtils { SignaturesUtil.recreateTypeParametersAndReturnMapping(samInterface.getTypeConstructor().getParameters(), result); TypeSubstitutor typeParametersSubstitutor = SignaturesUtil.createSubstitutorForTypeParameters(traitToFunTypeParameters); - JetType parameterTypeUnsubstituted = getFunctionTypeForFunction(getAbstractMethodOfSamInterface(samInterface)); + JetType parameterTypeUnsubstituted = getFunctionTypeForFunction(getAbstractMethodOfSamType(samInterface.getDefaultType())); JetType parameterType = typeParametersSubstitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE); assert parameterType != null : "couldn't substitute type: " + parameterType + ", substitutor = " + typeParametersSubstitutor; ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( @@ -125,10 +125,15 @@ public class SingleAbstractMethodUtils { } @NotNull - public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) { - return (SimpleFunctionDescriptor) getAbstractMembers(samInterface).get(0); + public static SimpleFunctionDescriptor getAbstractMethodOfSamType(@NotNull JetType type) { + return (SimpleFunctionDescriptor) getAbstractMembers(type).get(0); } - + + @NotNull + public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) { + return getAbstractMethodOfSamType(samInterface.getDefaultType()); + } + private SingleAbstractMethodUtils() { } } diff --git a/compiler/testData/codegen/box/sam/nonLiteralComparator.kt b/compiler/testData/codegen/box/sam/nonLiteralComparator.kt new file mode 100644 index 00000000000..200adb816a3 --- /dev/null +++ b/compiler/testData/codegen/box/sam/nonLiteralComparator.kt @@ -0,0 +1,9 @@ +import java.util.* + +fun box(): String { + val list = ArrayList(Arrays.asList(3, 2, 4, 8, 1, 5)) + val expected = ArrayList(Arrays.asList(8, 5, 4, 3, 2, 1)) + val comparatorFun: (Int, Int) -> Int = { a, b -> b - a } + Collections.sort(list, Comparator(comparatorFun)) + return if (list == expected) "OK" else list.toString() +} diff --git a/compiler/testData/codegen/box/sam/nonLiteralFilenameFilter.kt b/compiler/testData/codegen/box/sam/nonLiteralFilenameFilter.kt new file mode 100644 index 00000000000..b53eb885331 --- /dev/null +++ b/compiler/testData/codegen/box/sam/nonLiteralFilenameFilter.kt @@ -0,0 +1,10 @@ +import java.io.* + +fun box(): String { + val f : (File?, String?) -> Boolean = { dir, name -> if (name == null) false else (name as java.lang.String).endsWith(".md") } + val filter = FilenameFilter(f) + val listFiles = File(".").listFiles(filter)!! + if (listFiles.size != 1) return "Wrong size: $listFiles.size" + val name = listFiles[0].getName() + return if (name == "ReadMe.md") "OK" else "Wrong name: $name" +} diff --git a/compiler/testData/codegen/box/sam/nonLiteralRunnable.kt b/compiler/testData/codegen/box/sam/nonLiteralRunnable.kt new file mode 100644 index 00000000000..86b3923ef96 --- /dev/null +++ b/compiler/testData/codegen/box/sam/nonLiteralRunnable.kt @@ -0,0 +1,7 @@ +fun box(): String { + var result = "FAIL" + val f = { result = "OK" } + val r = Runnable(f) + r.run() + return result +} diff --git a/compiler/testData/codegen/box/sam/nonTrivialRunnable.kt b/compiler/testData/codegen/box/sam/nonTrivialRunnable.kt new file mode 100644 index 00000000000..8735e110d6c --- /dev/null +++ b/compiler/testData/codegen/box/sam/nonTrivialRunnable.kt @@ -0,0 +1,11 @@ +var result = "FAIL" + +fun getFun(): () -> Unit { + return { result = "OK" } +} + +fun box(): String { + val r = Runnable(getFun()) + r.run() + return result +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index f806455fadc..6ee00d4f8d5 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -3179,6 +3179,26 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/sam/filenameFilter.kt"); } + @TestMetadata("nonLiteralComparator.kt") + public void testNonLiteralComparator() throws Exception { + doTest("compiler/testData/codegen/box/sam/nonLiteralComparator.kt"); + } + + @TestMetadata("nonLiteralFilenameFilter.kt") + public void testNonLiteralFilenameFilter() throws Exception { + doTest("compiler/testData/codegen/box/sam/nonLiteralFilenameFilter.kt"); + } + + @TestMetadata("nonLiteralRunnable.kt") + public void testNonLiteralRunnable() throws Exception { + doTest("compiler/testData/codegen/box/sam/nonLiteralRunnable.kt"); + } + + @TestMetadata("nonTrivialRunnable.kt") + public void testNonTrivialRunnable() throws Exception { + doTest("compiler/testData/codegen/box/sam/nonTrivialRunnable.kt"); + } + @TestMetadata("runnable.kt") public void testRunnable() throws Exception { doTest("compiler/testData/codegen/box/sam/runnable.kt");