diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index bd8ac50d7b3..b7cc9516fc1 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1931,7 +1931,8 @@ public class ExpressionCodegen extends JetVisitor implem return genClosure(((JetFunctionLiteralExpression) argumentExpression).getFunctionLiteral(), samInterface); } else { - JvmClassName className = new SamWrapperCodegen(state, samInterface).genWrapper(expression, argumentExpression); + JvmClassName className = + state.getSamWrapperClasses().getSamWrapperClass(samInterface, (JetFile) expression.getContainingFile()); v.anew(className.getAsmType()); v.dup(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java new file mode 100644 index 00000000000..874ac019ee8 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperClasses.java @@ -0,0 +1,52 @@ +/* + * 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 com.google.common.collect.Maps; +import com.intellij.openapi.util.Factory; +import com.intellij.openapi.util.Pair; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.JvmClassName; +import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode; + +import java.util.Map; + +public class SamWrapperClasses { + private final GenerationState state; + + private final Map, JvmClassName> samInterfaceToWrapperClass = Maps.newHashMap(); + + public SamWrapperClasses(GenerationState state) { + this.state = state; + } + + @NotNull + public JvmClassName getSamWrapperClass(@NotNull final ClassDescriptorFromJvmBytecode samInterface, @NotNull final JetFile file) { + return ContainerUtil.getOrCreate(samInterfaceToWrapperClass, Pair.create(samInterface, file), + new Factory() { + @Override + public JvmClassName create() { + return new SamWrapperCodegen(state, samInterface).genWrapper(file); + } + }); + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java index a5994abd07e..a5e14687a70 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/SamWrapperCodegen.java @@ -20,25 +20,23 @@ 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.CallableDescriptor; -import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; -import org.jetbrains.jet.lang.psi.JetCallExpression; -import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.JvmClassName; +import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode; import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils; +import org.jetbrains.jet.lang.resolve.name.FqName; 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; @@ -55,33 +53,18 @@ public class SamWrapperCodegen extends GenerationStateAware { this.samInterface = samInterface; } - public JvmClassName genWrapper(JetCallExpression callExpression, JetExpression argumentExpression) { - // Example: we generate SAM constructor call Comparator(f), where f: (Int, Int) -> Int - + public JvmClassName genWrapper(@NotNull JetFile file) { // Name for generated class, in form of whatever$1 - JvmClassName name = bindingContext.get(CodegenBinding.FQN_FOR_SAM_CONSTRUCTOR, callExpression); - assert name != null : "internal class name not found for " + callExpression.getText(); + JvmClassName name = JvmClassName.byInternalName(getWrapperName(file)); - // e.g. (Int, Int) -> Int - JetType functionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression); - assert functionType != null && KotlinBuiltIns.getInstance().isFunctionType(functionType) : - "not a function type of " + argumentExpression.getText() + ": " + functionType; + // e.g. (T, T) -> Int + JetType functionType = samInterface.getFunctionTypeForSamInterface(); + assert functionType != null : samInterface.toString(); + // e.g. compare(T, T) + SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamInterface(samInterface); - // SAM constructor call - ResolvedCall resolvedCall = - bindingContext.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression()); - assert resolvedCall != null : "couldn't find resolved call for " + callExpression.getText(); - - // e.g. Comparator - JetType resultType = resolvedCall.getResultingDescriptor().getReturnType(); - assert resultType != null && resultType.getConstructor() == samInterface.getTypeConstructor() : - "unexpected result type: " + resultType; - - // e.g. compare(Int, Int) - SimpleFunctionDescriptor interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamType(resultType); - - ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), callExpression.getContainingFile()); - cv.defineClass(callExpression, + ClassBuilder cv = state.getFactory().newVisitor(name.getInternalName(), file); + cv.defineClass(file, V1_6, ACC_FINAL, name.getInternalName(), @@ -89,7 +72,7 @@ public class SamWrapperCodegen extends GenerationStateAware { OBJECT_TYPE.getInternalName(), new String[]{JvmClassName.byClassDescriptor(samInterface).getInternalName()} ); - cv.visitSource(callExpression.getContainingFile().getName(), null); + cv.visitSource(file.getName(), null); // e.g. ASM type for Function2 Type functionAsmType = state.getTypeMapper().mapType(functionType, JetTypeMapperMode.VALUE); @@ -149,4 +132,14 @@ public class SamWrapperCodegen extends GenerationStateAware { StackValue functionField = StackValue.field(functionType, JvmClassName.byType(ownerType), FUNCTION_FIELD_NAME, false); codegen.genDelegate(interfaceFunction, invokeFunction, functionField); } + + private String getWrapperName(@NotNull JetFile containingFile) { + NamespaceDescriptor namespace = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, containingFile); + assert namespace != null : "couldn't find namespace for file: " + containingFile.getVirtualFile(); + FqName fqName = DescriptorUtils.getFQName(namespace).toSafe(); + String packageInternalName = JvmClassName.byFqNameWithoutInnerClasses( + PackageClassUtils.getPackageClassFqName(fqName)).getInternalName(); + return packageInternalName + "$sam$" + samInterface.getName().asString() + + "$" + Integer.toHexString(CodegenUtil.getPathHashCode(containingFile)); // TODO add class FQ name hash + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java b/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java index 1ae5760d4a5..671a889855a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java @@ -56,6 +56,9 @@ public class GenerationState { @NotNull private final IntrinsicMethods intrinsics; + @NotNull + private final SamWrapperClasses samWrapperClasses = new SamWrapperClasses(this); + @NotNull private final BindingTrace bindingTrace; @@ -154,6 +157,11 @@ public class GenerationState { return intrinsics; } + @NotNull + public SamWrapperClasses getSamWrapperClasses() { + return samWrapperClasses; + } + public boolean isGenerateNotNullAssertions() { return generateNotNullAssertions; } diff --git a/compiler/testData/codegen/box/sam/sameWrapperClass.kt b/compiler/testData/codegen/box/sam/sameWrapperClass.kt new file mode 100644 index 00000000000..aa83e109082 --- /dev/null +++ b/compiler/testData/codegen/box/sam/sameWrapperClass.kt @@ -0,0 +1,7 @@ +fun box(): String { + val f = { } + val class1 = Runnable(f).getClass() + val class2 = Runnable(f).getClass() + + return if (class1 == class2) "OK" else "$class1 $class2" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/1/wrapped.kt b/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/1/wrapped.kt new file mode 100644 index 00000000000..733fce7706e --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/1/wrapped.kt @@ -0,0 +1,4 @@ +fun getWrapped1(): Runnable { + val f = { } + return Runnable(f) +} diff --git a/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/2/wrapped.kt b/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/2/wrapped.kt new file mode 100644 index 00000000000..4abed42b27f --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/2/wrapped.kt @@ -0,0 +1,4 @@ +fun getWrapped2(): Runnable { + val f = { } + return Runnable(f) +} diff --git a/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/box.kt b/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/box.kt new file mode 100644 index 00000000000..86dca7f11b8 --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles/box.kt @@ -0,0 +1,6 @@ +fun box(): String { + val class1 = getWrapped1().getClass() + val class2 = getWrapped2().getClass() + + return if (class1 != class2) "OK" else "Same class: $class1" +} \ 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 784f1355c8f..ade9d8142e4 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -3756,6 +3756,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/sam/runnableAccessingClosure2.kt"); } + @TestMetadata("sameWrapperClass.kt") + public void testSameWrapperClass() throws Exception { + doTest("compiler/testData/codegen/box/sam/sameWrapperClass.kt"); + } + @TestMetadata("syntheticVsReal.kt") public void testSyntheticVsReal() throws Exception { doTest("compiler/testData/codegen/box/sam/syntheticVsReal.kt"); diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java index 84c13b424a5..533525829da 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java @@ -71,6 +71,11 @@ public class BlackBoxMultiFileCodegenTestGenerated extends AbstractBlackBoxCodeg doTestMultiFile("compiler/testData/codegen/boxMultiFile/nestedPackages"); } + @TestMetadata("samWrappersDifferentFiles") + public void testSamWrappersDifferentFiles() throws Exception { + doTestMultiFile("compiler/testData/codegen/boxMultiFile/samWrappersDifferentFiles"); + } + @TestMetadata("sameFileName") public void testSameFileName() throws Exception { doTestMultiFile("compiler/testData/codegen/boxMultiFile/sameFileName");