Don't generate hash in sam wrapper class name

#KT-17091 Fixed
This commit is contained in:
Mikhael Bogdanov
2018-02-06 15:26:52 +01:00
parent ba5cc65792
commit 0954d1ab1b
21 changed files with 286 additions and 27 deletions
@@ -49,6 +49,9 @@ public class SpecialFiles {
excludedFiles.add("primitivesAndArrays.kt");
excludedFiles.add("getDelegateWithoutReflection.kt");
excludedFiles.add("parameterAnnotationInDefaultImpls.kt");
excludedFiles.add("kt17091.kt");
excludedFiles.add("kt17091_2.kt");
excludedFiles.add("kt17091_3.kt");
// Reflection is used to check full class name
excludedFiles.add("native");
@@ -2118,7 +2118,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return genClosure((KtNamedFunction) expression, samType);
}
Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), this);
Type asmType = state.getSamWrapperClasses().getSamWrapperClass(samType, expression.getContainingKtFile(), this, context.getContextDescriptor());
return StackValue.operation(asmType, v -> {
Label afterAll = new Label();
@@ -99,7 +99,7 @@ public class PackageCodegenImpl implements PackageCodegen {
List<KtClassOrObject> classOrObjects = new ArrayList<>();
for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(file)) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
if (isFilePartDeclaration(declaration)) {
generatePackagePart = true;
}
else if (declaration instanceof KtClassOrObject) {
@@ -127,6 +127,10 @@ public class PackageCodegenImpl implements PackageCodegen {
new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate();
}
public static boolean isFilePartDeclaration(KtDeclaration declaration) {
return declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias;
}
@Nullable
private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedPackageFqName) {
SmartList<PackageFragmentDescriptor> fragments = new SmartList<>();
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.annotations.NotNull
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.org.objectweb.asm.Type
@@ -27,10 +29,15 @@ class SamWrapperClasses(private val state: GenerationState) {
private val samInterfaceToWrapperClass = hashMapOf<WrapperKey, Type>()
fun getSamWrapperClass(samType: SamType, file: KtFile, expressionCodegen: ExpressionCodegen): Type {
fun getSamWrapperClass(
samType: SamType,
file: KtFile,
expressionCodegen: ExpressionCodegen,
contextDescriptor: CallableMemberDescriptor
): Type {
val isInsideInline = InlineUtil.isInlineOrContainingInline(expressionCodegen.context.contextDescriptor)
return samInterfaceToWrapperClass.getOrPut(WrapperKey(samType, file, isInsideInline)) {
SamWrapperCodegen(state, samType, expressionCodegen.parentCodegen, isInsideInline).genWrapper(file)
SamWrapperCodegen(state, samType, expressionCodegen.parentCodegen, isInsideInline).genWrapper(file, contextDescriptor)
}
}
}
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.codegen;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
@@ -25,9 +27,9 @@ import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
@@ -70,9 +72,12 @@ public class SamWrapperCodegen {
}
@NotNull
public Type genWrapper(@NotNull KtFile file) {
public Type genWrapper(
@NotNull KtFile file,
@NotNull CallableMemberDescriptor contextDescriptor
) {
// Name for generated class, in form of whatever$1
FqName fqName = getWrapperName(file);
FqName fqName = getWrapperName(file, contextDescriptor);
Type asmType = asmTypeByFqNameWithoutInnerClasses(fqName);
// e.g. (T, T) -> Int
@@ -182,20 +187,30 @@ public class SamWrapperCodegen {
}
@NotNull
private FqName getWrapperName(@NotNull KtFile containingFile) {
FqName fileClassFqName = JvmFileClassUtil.getFileClassInfoNoResolve(containingFile).getFileClassFqName();
JavaClassDescriptor descriptor = samType.getJavaClassDescriptor();
//Change sam wrapper name template carefully cause it's used in inliner:
// see isSamWrapper/isSamWrapperConstructorCall in inlineCodegenUtils.kt
int hash = PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 +
DescriptorUtils.getFqNameSafe(descriptor).hashCode();
private FqName getWrapperName(
@NotNull KtFile containingFile,
CallableMemberDescriptor contextDescriptor
) {
boolean hasPackagePartClass =
CollectionsKt.any(CodegenUtil.getActualDeclarations(containingFile), PackageCodegenImpl::isFilePartDeclaration);
FqName filePartFqName = JvmFileClassUtil.getFileClassInfoNoResolve(containingFile).getFileClassFqName();
FqName outermostOwner;
if (hasPackagePartClass) {
outermostOwner = filePartFqName;
}
else {
ClassifierDescriptor outermostClassifier = DescriptorUtils.getParentOfType(contextDescriptor, ClassDescriptor.class);
if (outermostClassifier == null) throw new IllegalStateException("Can't find outermost parent class for " + contextDescriptor);
outermostOwner = filePartFqName.parent().child(outermostClassifier.getName());
}
String shortName = String.format(
"%s$sam$%s%s$%08x",
fileClassFqName.shortName().asString(),
descriptor.getName().asString(),
"%s$sam%s$%s$0",
outermostOwner.shortName().asString(),
(isInsideInline ? "$i" : ""),
hash
DescriptorUtils.getFqNameSafe(samType.getJavaClassDescriptor()).asString().replace('.', '_')
);
return fileClassFqName.parent().child(Name.identifier(shortName));
return outermostOwner.parent().child(Name.identifier(shortName));
}
}
@@ -185,7 +185,7 @@ class MethodInliner(
}
override fun anew(type: Type) {
if (isSamWrapper(type.internalName) || isAnonymousClass(type.internalName)) {
if (isOldSamWrapper(type.internalName) || isAnonymousClass(type.internalName)) {
handleAnonymousObjectRegeneration()
}
@@ -215,14 +215,25 @@ internal fun isWhenMappingAccess(internalName: String, fieldName: String): Boole
internal fun isAnonymousSingletonLoad(internalName: String, fieldName: String): Boolean =
JvmAbi.INSTANCE_FIELD == fieldName && isAnonymousClass(internalName)
internal fun isSamWrapper(internalName: String) =
/*
* Note that sam wrapper prior to 1.2.30 was generated with next template name (that was included suffix hash):
* int hash = PackagePartClassUtils.getPathHashCode(containingFile.getVirtualFile()) * 31 + DescriptorUtils.getFqNameSafe(descriptor).hashCode();
* String shortName = String.format(
* "%s$sam$%s%s$%08x",
* outermostOwner.shortName().asString(),
* descriptor.getName().asString(),
* (isInsideInline ? "$i" : ""),
* hash
* );
*/
internal fun isOldSamWrapper(internalName: String) =
internalName.contains("\$sam$") && internalName.substringAfter("\$i$", "").run { length == 8 && toLongOrNull(16) != null }
internal fun isSamWrapperConstructorCall(internalName: String, methodName: String) =
isConstructor(methodName) && isSamWrapper(internalName)
isConstructor(methodName) && isOldSamWrapper(internalName)
internal fun isAnonymousClass(internalName: String) =
!isSamWrapper(internalName) &&
!isOldSamWrapper(internalName) &&
internalName.substringAfterLast('/').substringAfterLast("$", "").isInteger()
fun wrapWithMaxLocalCalc(methodNode: MethodNode) =
+30
View File
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: Foo.kt
class A2 {
fun doWork(job: () -> Unit) {
Runnable(job)
}
}
// FILE: kt17091.kt
typealias Z = String
class A {
fun doWork(job: () -> Unit) {
java.lang.Runnable(job).run()
}
}
fun box(): String {
var result = "fail"
A().doWork { result = "OK" }
if (java.lang.Class.forName("Kt17091Kt\$sam\$java_lang_Runnable$0") == null) return "fail: can't find sam wrapper"
if (java.lang.Class.forName("A2\$sam\$java_lang_Runnable$0") == null) return "fail 2: can't find sam wrapper"
return result
}
+37
View File
@@ -0,0 +1,37 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: Foo.kt
@file:JvmName("testXX")
package test
class A2 {
fun doWork(job: () -> Unit) {
Runnable(job)
}
}
// FILE: kt17091_2.kt
@file:JvmMultifileClass
@file:JvmName("testX")
package test
typealias Z = String
class A {
fun doWork(job: () -> Unit) {
Runnable(job).run()
}
}
fun box(): String {
var result = "fail"
A().doWork { result = "OK" }
if (java.lang.Class.forName("test.testX__Kt17091_2Kt\$sam\$java_lang_Runnable$0") == null) return "fail: can't find sam wrapper"
if (java.lang.Class.forName("test.A2\$sam\$java_lang_Runnable$0") == null) return "fail 2: can't find sam wrapper"
return result
}
+28
View File
@@ -0,0 +1,28 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: Foo.kt
@file:JvmMultifileClass
@file:JvmName("testX")
package test
class A2 {
fun doWork(job: () -> Unit) {
Runnable(job)
}
}
// FILE: kt17091_3.kt
fun box(): String {
if (java.lang.Class.forName("Kt17091_3Kt\$sam\$java_util_concurrent_Callable$0") == null) return "fail: can't find sam wrapper"
if (java.lang.Class.forName("test.A2\$sam\$java_lang_Runnable$0") == null) return "fail 2: can't find sam wrapper"
return A().foo().call()
}
class A {
val f = {"OK"}
fun foo() = java.util.concurrent.Callable(f)
}
@@ -0,0 +1,18 @@
// FILE: 1.kt
// FULL_JDK
package test
inline fun foo(value: String, crossinline s: () -> String): String {
val x = { value }
return java.util.concurrent.Callable(x).call() + { s() }()
}
// FILE: 2.kt
import test.*
fun box(): String {
return foo("O") { "K" }
}
@@ -1,14 +1,14 @@
@kotlin.Metadata
final class test/SamAdapterAndInlinedOneKt$sam$Runnable$HASH {
public final class test/SamAdapterAndInlinedOneKt$sam$i$java_lang_Runnable$0 {
private synthetic final field function: kotlin.jvm.functions.Function0
method <init>(p0: kotlin.jvm.functions.Function0): void
public method <init>(p0: kotlin.jvm.functions.Function0): void
public synthetic final method run(): void
}
@kotlin.Metadata
public final class test/SamAdapterAndInlinedOneKt$sam$Runnable$i$HASH {
final class test/SamAdapterAndInlinedOneKt$sam$java_lang_Runnable$0 {
private synthetic final field function: kotlin.jvm.functions.Function0
public method <init>(p0: kotlin.jvm.functions.Function0): void
method <init>(p0: kotlin.jvm.functions.Function0): void
public synthetic final method run(): void
}
@@ -0,0 +1,22 @@
// FILE: A.kt
// FULL_JDK
package test
import java.util.concurrent.Callable
inline fun doWork(noinline job: () -> String): Callable<String> {
return Callable(job)
}
// FILE: B.kt
import test.*
fun box(): String {
val anotherModule = doWork { "K" }
if (anotherModule.javaClass.name != "BKt\$box$\$inlined\$doWork$1") return "class should be regenerated, but ${anotherModule.javaClass.name}"
return "OK"
}
@@ -19185,6 +19185,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt17091_2.kt")
public void testKt17091_2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091_2.kt");
doTest(fileName);
}
@TestMetadata("kt17091_3.kt")
public void testKt17091_3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091_3.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/sam/constructors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -452,6 +452,12 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/anonymousObject/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt21671.kt")
public void testKt21671() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt21671.kt");
@@ -452,6 +452,12 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/anonymousObject/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt21671.kt")
public void testKt21671() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt21671.kt");
@@ -19185,6 +19185,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt17091_2.kt")
public void testKt17091_2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091_2.kt");
doTest(fileName);
}
@TestMetadata("kt17091_3.kt")
public void testKt17091_3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091_3.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/sam/constructors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -452,6 +452,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/anonymousObject/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt21671.kt")
public void testKt21671() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt21671.kt");
@@ -452,6 +452,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/anonymousObject/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt21671.kt")
public void testKt21671() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/sam/kt21671.kt");
@@ -79,6 +79,12 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
doTest(fileName);
}
@TestMetadata("copySamOnInline2.kt")
public void testCopySamOnInline2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/copySamOnInline2.kt");
doTest(fileName);
}
@TestMetadata("coroutinesBinary.kt")
public void testCoroutinesBinary() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/coroutinesBinary.kt");
@@ -19185,6 +19185,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/sam"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt17091.kt")
public void testKt17091() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091.kt");
doTest(fileName);
}
@TestMetadata("kt17091_2.kt")
public void testKt17091_2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091_2.kt");
doTest(fileName);
}
@TestMetadata("kt17091_3.kt")
public void testKt17091_3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/sam/kt17091_3.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/sam/constructors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)