diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 00a0ac9515e..be3ff10f670 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2317,7 +2317,7 @@ public class ExpressionCodegen extends KtVisitor impleme } final Type asmType = - state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), getParentCodegen()); + state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), this); return StackValue.operation(asmType, new Function1() { @Override diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperClasses.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperClasses.java deleted file mode 100644 index b79e81039fb..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperClasses.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2010-2015 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.kotlin.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.kotlin.codegen.state.GenerationState; -import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.org.objectweb.asm.Type; - -import java.util.Map; - -public class SamWrapperClasses { - private final GenerationState state; - - private final Map, Type> samInterfaceToWrapperClass = Maps.newHashMap(); - - public SamWrapperClasses(@NotNull GenerationState state) { - this.state = state; - } - - @NotNull - public Type getSamWrapperClass(@NotNull final SamType samType, @NotNull final KtFile file, @NotNull final MemberCodegen parentCodegen) { - return ContainerUtil.getOrCreate(samInterfaceToWrapperClass, Pair.create(samType, file), - new Factory() { - @Override - public Type create() { - return new SamWrapperCodegen(state, samType, parentCodegen).genWrapper(file); - } - }); - } -} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperClasses.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperClasses.kt new file mode 100644 index 00000000000..527f185574d --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperClasses.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2015 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.kotlin.codegen + +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.inline.InlineUtil +import org.jetbrains.org.objectweb.asm.Type + +class SamWrapperClasses(private val state: GenerationState) { + + private data class WrapperKey(val samType: SamType, val file: KtFile, val insideInline: Boolean) + + private val samInterfaceToWrapperClass = hashMapOf() + + fun getSamWrapperClass(samType: SamType, file: KtFile, expressionCodegen: ExpressionCodegen): Type { + val isInsideInline = InlineUtil.isInlineOrContainingInline(expressionCodegen.context.contextDescriptor) + return samInterfaceToWrapperClass.getOrPut(WrapperKey(samType, file, isInsideInline)) { + SamWrapperCodegen(state, samType, expressionCodegen.parentCodegen, isInsideInline).genWrapper(file) + } + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperCodegen.java index 5d193662fc6..26f5166c60a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamWrapperCodegen.java @@ -49,17 +49,27 @@ public class SamWrapperCodegen { private static final String FUNCTION_FIELD_NAME = "function"; private final GenerationState state; + private final boolean isInsideInline; private final KotlinTypeMapper typeMapper; private final SamType samType; private final MemberCodegen parentCodegen; + private final int visibility; - public SamWrapperCodegen(@NotNull GenerationState state, @NotNull SamType samType, @NotNull MemberCodegen parentCodegen) { + public SamWrapperCodegen( + @NotNull GenerationState state, + @NotNull SamType samType, + @NotNull MemberCodegen parentCodegen, + boolean isInsideInline + ) { this.state = state; + this.isInsideInline = isInsideInline; this.typeMapper = state.getTypeMapper(); this.samType = samType; this.parentCodegen = parentCodegen; + visibility = isInsideInline ? ACC_PUBLIC : NO_FLAG_PACKAGE_PRIVATE; } + @NotNull public Type genWrapper(@NotNull KtFile file) { // Name for generated class, in form of whatever$1 FqName fqName = getWrapperName(file); @@ -88,7 +98,7 @@ public class SamWrapperCodegen { ClassBuilder cv = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(erasedInterfaceFunction), asmType, file); cv.defineClass(file, V1_6, - ACC_FINAL | ACC_SUPER, + ACC_FINAL | ACC_SUPER | visibility, asmType.getInternalName(), null, OBJECT_TYPE.getInternalName(), @@ -118,7 +128,7 @@ public class SamWrapperCodegen { private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) { MethodVisitor mv = cv.newMethod(JvmDeclarationOriginKt.OtherOrigin(samType.getJavaClassDescriptor()), - NO_FLAG_PACKAGE_PRIVATE, "", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null); + visibility, "", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null); if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { mv.visitCode(); @@ -177,9 +187,10 @@ public class SamWrapperCodegen { int hash = PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 + DescriptorUtils.getFqNameSafe(descriptor).hashCode(); String shortName = String.format( - "%s$sam$%s$%08x", + "%s$sam$%s%s$%08x", fileClassFqName.shortName().asString(), descriptor.getName().asString(), + (isInsideInline ? "$i" : ""), hash ); return fileClassFqName.parent().child(Name.identifier(shortName)); diff --git a/compiler/testData/codegen/boxInline/anonymousObject/sam.kt b/compiler/testData/codegen/boxInline/anonymousObject/sam.kt new file mode 100644 index 00000000000..5402c9e48b1 --- /dev/null +++ b/compiler/testData/codegen/boxInline/anonymousObject/sam.kt @@ -0,0 +1,19 @@ +// FILE: 1.kt +package test + +inline fun makeRunnable(noinline lambda: ()->Unit) : Runnable { + return Runnable(lambda) +} + +// FILE: 2.kt + +import test.* + +fun box(): String { + var result = "fail" + + makeRunnable { result = "OK" }.run() + + return result +} + diff --git a/compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.kt b/compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.kt new file mode 100644 index 00000000000..30bb55ba822 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.kt @@ -0,0 +1,23 @@ +package test + +inline fun makeRunnable(noinline lambda: ()->Unit) : Runnable { + return Runnable(lambda) +} + +inline fun makeRunnable2(noinline lambda: ()->Unit) : Runnable { + return Runnable(lambda) +} + + +fun noInline(lambda: ()->Unit) : Runnable { + return Runnable(lambda) +} + + +fun noInline2(lambda: ()->Unit) : Runnable { + return Runnable(lambda) +} + +// 1 final class test/_1Kt\$sam\$Runnable\$89f9321c +// 1 public final class test/_1Kt\$sam\$Runnable\$i\$89f9321c +// 2 class test/_1Kt\$sam\$ \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.txt b/compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.txt new file mode 100644 index 00000000000..a8edacd3495 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.txt @@ -0,0 +1,21 @@ +@kotlin.Metadata +final class test/SamAdapterAndInlinedOneKt$sam$Runnable$82391ccd { + private synthetic final field function: kotlin.jvm.functions.Function0 + method (p0: kotlin.jvm.functions.Function0): void + public synthetic final method run(): void +} + +@kotlin.Metadata +public final class test/SamAdapterAndInlinedOneKt$sam$Runnable$i$82391ccd { + private synthetic final field function: kotlin.jvm.functions.Function0 + public method (p0: kotlin.jvm.functions.Function0): void + public synthetic final method run(): void +} + +@kotlin.Metadata +public final class test/SamAdapterAndInlinedOneKt { + private final static method makeRunnable(p0: kotlin.jvm.functions.Function0): java.lang.Runnable + public final static @org.jetbrains.annotations.NotNull method makeRunnable2(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): java.lang.Runnable + public final static @org.jetbrains.annotations.NotNull method noInline(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): java.lang.Runnable + public final static @org.jetbrains.annotations.NotNull method noInline2(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): java.lang.Runnable +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index c41ef3aa61f..df3cda8c680 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -175,6 +175,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo doTest(fileName); } + @TestMetadata("sam.kt") + public void testSam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index dd481d7f398..58747d3a60e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -59,6 +59,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { doTest(fileName); } + @TestMetadata("samAdapterAndInlinedOne.kt") + public void testSamAdapterAndInlinedOne() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/samAdapterAndInlinedOne.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/bytecodeListing/annotations") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index f459fed4f6c..a0ae2d93302 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -175,6 +175,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi doTest(fileName); } + @TestMetadata("sam.kt") + public void testSam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)