Do not generate useless methods inside wrapper for inline class

Fix for test data (inlineFunctionInsideInlineClassesBox.kt) is needed
 to avoid check about "no inline functions".

 This check has two steps: first, names of inline functions from
 the metadata are loaded, then these names are checked that they are
 presented for physical methods in the classfile.

 Because now there are no physical methods in the classfile, we can't pass
 the second check, therefore this fix is needed.

 #KT-24872 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-08-05 05:24:26 +03:00
parent fcbcabd3a2
commit 1497c19dc9
7 changed files with 26 additions and 17 deletions
@@ -196,6 +196,10 @@ public class FunctionCodegen {
return; return;
} }
if (!shouldGenerateMethodInsideInlineClass(origin, functionDescriptor, contextKind, containingDeclaration)) {
return;
}
boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor); boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor);
JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge); JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge);
Method asmMethod = jvmSignature.getAsmMethod(); Method asmMethod = jvmSignature.getAsmMethod();
@@ -255,7 +259,7 @@ public class FunctionCodegen {
origin, functionDescriptor, methodContext, strategy, mv, jvmSignature, asmMethod, flags, staticInCompanionObject origin, functionDescriptor, methodContext, strategy, mv, jvmSignature, asmMethod, flags, staticInCompanionObject
); );
} }
else if (shouldDelegateMethodBodyToInlineClass(origin, functionDescriptor, contextKind, containingDeclaration, bindingContext)) { else if (canDelegateMethodBodyToInlineClass(origin, functionDescriptor, contextKind, containingDeclaration)) {
generateMethodInsideInlineClassWrapper(origin, functionDescriptor, (ClassDescriptor) containingDeclaration, mv, typeMapper); generateMethodInsideInlineClassWrapper(origin, functionDescriptor, (ClassDescriptor) containingDeclaration, mv, typeMapper);
} }
else { else {
@@ -277,12 +281,21 @@ public class FunctionCodegen {
return v.newMethod(origin, access, name, desc, signature, exceptions); return v.newMethod(origin, access, name, desc, signature, exceptions);
} }
private static boolean shouldDelegateMethodBodyToInlineClass( private static boolean shouldGenerateMethodInsideInlineClass(
@NotNull JvmDeclarationOrigin origin, @NotNull JvmDeclarationOrigin origin,
@NotNull FunctionDescriptor functionDescriptor, @NotNull FunctionDescriptor functionDescriptor,
@NotNull OwnerKind contextKind, @NotNull OwnerKind contextKind,
@NotNull DeclarationDescriptor containingDeclaration, @NotNull DeclarationDescriptor containingDeclaration
@NotNull BindingContext bindingContext ) {
return !canDelegateMethodBodyToInlineClass(origin, functionDescriptor, contextKind, containingDeclaration) ||
!functionDescriptor.getOverriddenDescriptors().isEmpty();
}
private static boolean canDelegateMethodBodyToInlineClass(
@NotNull JvmDeclarationOrigin origin,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull OwnerKind contextKind,
@NotNull DeclarationDescriptor containingDeclaration
) { ) {
// special kind / function // special kind / function
if (contextKind == OwnerKind.ERASED_INLINE_CLASS) return false; if (contextKind == OwnerKind.ERASED_INLINE_CLASS) return false;
@@ -11,6 +11,8 @@ inline class A(val x: Int) {
inline fun result(other: A): String = if (other.x == x) "OK" else "fail" inline fun result(other: A): String = if (other.x == x) "OK" else "fail"
} }
inline fun stub() {}
// FILE: 2.kt // FILE: 2.kt
import test.* import test.*
@@ -27,7 +27,6 @@ public final class Foo {
public method equals(p0: java.lang.Object): boolean public method equals(p0: java.lang.Object): boolean
public final method getX(): int public final method getX(): int
public method hashCode(): int public method hashCode(): int
public final method inInlineClass(): void
public method toString(): java.lang.String public method toString(): java.lang.String
public final method unbox(): int public final method unbox(): int
} }
@@ -14,8 +14,6 @@ public final class Foo {
private final field x: int private final field x: int
public method <init>(p0: int): void public method <init>(p0: int): void
public method equals(p0: java.lang.Object): boolean public method equals(p0: java.lang.Object): boolean
public final method getAsThis(): int
public final method getProp(): int
public final method getX(): int public final method getX(): int
public method hashCode(): int public method hashCode(): int
public method toString(): java.lang.String public method toString(): java.lang.String
@@ -14,12 +14,9 @@ public final static class Foo$Erased {
public final class Foo { public final class Foo {
private final field l: long private final field l: long
public method <init>(p0: long): void public method <init>(p0: long): void
public final method empty(): void
public method equals(p0: java.lang.Object): boolean public method equals(p0: java.lang.Object): boolean
public final method extension(@org.jetbrains.annotations.NotNull p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.lang.String): void
public final method getL(): long public final method getL(): long
public method hashCode(): int public method hashCode(): int
public final method param(p0: double): void
public method toString(): java.lang.String public method toString(): java.lang.String
public final method unbox(): long public final method unbox(): long
} }
@@ -12,7 +12,7 @@ inline class Foo(val x: Int) {
} }
} }
// 2 INVOKESTATIC Foo\$Erased.empty \(I\)V // 1 INVOKESTATIC Foo\$Erased.empty \(I\)V
// 2 INVOKESTATIC Foo\$Erased.withParam \(ILjava/lang/String;\)V // 1 INVOKESTATIC Foo\$Erased.withParam \(ILjava/lang/String;\)V
// 2 INVOKESTATIC Foo\$Erased.withInlineClassParam \(II\)V // 1 INVOKESTATIC Foo\$Erased.withInlineClassParam \(II\)V
// 5 INVOKEVIRTUAL // 5 INVOKEVIRTUAL
@@ -1,7 +1,7 @@
// !LANGUAGE: +InlineClasses // !LANGUAGE: +InlineClasses
inline class Foo(val x: Int) { inline class Foo(val x: Int) {
inline fun inlineInc(): Foo = Foo(x + 1) // one actual call inside wrapper class Foo inline fun inlineInc(): Foo = Foo(x + 1)
fun notInlineInc(): Foo = Foo(x + 1) fun notInlineInc(): Foo = Foo(x + 1)
fun foo() { fun foo() {
@@ -11,8 +11,8 @@ inline class Foo(val x: Int) {
fun test(f: Foo) { fun test(f: Foo) {
f.inlineInc().inlineInc().inlineInc() f.inlineInc().inlineInc().inlineInc()
f.notInlineInc() // one here, one inside wrapper class Foo f.notInlineInc() // one here
} }
// 1 INVOKESTATIC Foo\$Erased.inlineInc // 0 INVOKESTATIC Foo\$Erased.inlineInc
// 2 INVOKESTATIC Foo\$Erased.notInlineInc // 1 INVOKESTATIC Foo\$Erased.notInlineInc