Support synthetic accessors for @JvmDefault members
This commit is contained in:
@@ -30,7 +30,8 @@ class CallableMethod(
|
||||
override val extensionReceiverKotlinType: KotlinType?,
|
||||
override val generateCalleeType: Type?,
|
||||
override val returnKotlinType: KotlinType?,
|
||||
private val isInterfaceMethod: Boolean = Opcodes.INVOKEINTERFACE == invokeOpcode
|
||||
private val isInterfaceMethod: Boolean = Opcodes.INVOKEINTERFACE == invokeOpcode,
|
||||
private val isDefaultMethodInInterface: Boolean = false
|
||||
) : Callable {
|
||||
fun getValueParameters(): List<JvmMethodParameterSignature> =
|
||||
signature.valueParameters
|
||||
@@ -67,7 +68,7 @@ class CallableMethod(
|
||||
} else {
|
||||
v.visitMethodInsn(
|
||||
INVOKESTATIC, defaultImplOwner.internalName,
|
||||
method.name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodDesc, false
|
||||
method.name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodDesc, isDefaultMethodInInterface
|
||||
)
|
||||
|
||||
StackValue.coerce(Type.getReturnType(defaultMethodDesc), Type.getReturnType(signature.asmMethod.descriptor), v)
|
||||
|
||||
@@ -400,9 +400,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
generateDelegates(delegationFieldsInfo);
|
||||
|
||||
if (!isInterface(descriptor) || kind == OwnerKind.DEFAULT_IMPLS) {
|
||||
generateSyntheticAccessors();
|
||||
}
|
||||
generateSyntheticAccessors();
|
||||
|
||||
generateEnumMethods();
|
||||
|
||||
|
||||
@@ -695,9 +695,16 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
}
|
||||
}
|
||||
|
||||
protected void generateSyntheticAccessors() {
|
||||
protected final void generateSyntheticAccessors() {
|
||||
for (AccessorForCallableDescriptor<?> accessor : ((CodegenContext<?>) context).getAccessors()) {
|
||||
generateSyntheticAccessor(accessor);
|
||||
boolean hasJvmDefaultAnnotation = CodegenUtilKt.hasJvmDefaultAnnotation(accessor.getCalleeDescriptor());
|
||||
OwnerKind kind = context.getContextKind();
|
||||
|
||||
if (!isInterface(context.getContextDescriptor()) ||
|
||||
(hasJvmDefaultAnnotation && kind == OwnerKind.IMPLEMENTATION) ||
|
||||
(!hasJvmDefaultAnnotation && kind == OwnerKind.DEFAULT_IMPLS)) {
|
||||
generateSyntheticAccessor(accessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -423,4 +423,8 @@ fun MethodNode.textifyMethodNode(): String {
|
||||
return "$sw"
|
||||
}
|
||||
|
||||
fun CallableMemberDescriptor.hasJvmDefaultAnnotation() = getDirectMember(this).annotations.hasAnnotation(JvmDefaultChecker.JVM_DEFAULT_FQ_NAME)
|
||||
fun CallableMemberDescriptor.hasJvmDefaultAnnotation() =
|
||||
getDirectMember(this).annotations.hasAnnotation(JvmDefaultChecker.JVM_DEFAULT_FQ_NAME)
|
||||
|
||||
fun DeclarationDescriptor.isCallableMemberWithJvmDefaultAnnotation() =
|
||||
(this as? CallableMemberDescriptor)?.hasJvmDefaultAnnotation() ?: false
|
||||
|
||||
@@ -569,10 +569,13 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
|
||||
CodegenContext properContext = getFirstCrossInlineOrNonInlineContext();
|
||||
DeclarationDescriptor enclosing = descriptor.getContainingDeclaration();
|
||||
boolean isInliningContext = properContext.isInlineMethodContext();
|
||||
boolean sameJvmDefault = CodegenUtilKt.hasJvmDefaultAnnotation(descriptor) ==
|
||||
CodegenUtilKt.isCallableMemberWithJvmDefaultAnnotation(properContext.contextDescriptor) ||
|
||||
properContext.contextDescriptor instanceof AccessorForCallableDescriptor;
|
||||
if (!isInliningContext && (
|
||||
!properContext.hasThisDescriptor() ||
|
||||
enclosing == properContext.getThisDescriptor() ||
|
||||
enclosing == properContext.getClassOrPackageParentContext().getContextDescriptor())) {
|
||||
((enclosing == properContext.getThisDescriptor()) && sameJvmDefault) ||
|
||||
((enclosing == properContext.getClassOrPackageParentContext().getContextDescriptor()) && sameJvmDefault))) {
|
||||
return descriptor;
|
||||
}
|
||||
return (D) properContext.accessibleDescriptorIfNeeded(descriptor, superCallTarget, isInliningContext);
|
||||
@@ -623,6 +626,11 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
|
||||
if (descriptorContext == null) {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
if (CodegenUtilKt.hasJvmDefaultAnnotation(descriptor) && descriptorContext instanceof DefaultImplsClassContext) {
|
||||
descriptorContext = ((DefaultImplsClassContext) descriptorContext).getInterfaceContext();
|
||||
}
|
||||
|
||||
if (descriptor instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||
int propertyAccessFlag = getVisibilityAccessFlag(descriptor);
|
||||
|
||||
+9
-8
@@ -23,20 +23,21 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
|
||||
class DefaultImplsClassContext(
|
||||
typeMapper: KotlinTypeMapper,
|
||||
contextDescriptor: ClassDescriptor,
|
||||
contextKind: OwnerKind,
|
||||
parentContext: CodegenContext<*>?,
|
||||
localLookup: ((DeclarationDescriptor) -> Boolean)?,
|
||||
private val interfaceContext: ClassContext
|
||||
typeMapper: KotlinTypeMapper,
|
||||
contextDescriptor: ClassDescriptor,
|
||||
contextKind: OwnerKind,
|
||||
parentContext: CodegenContext<*>?,
|
||||
localLookup: ((DeclarationDescriptor) -> Boolean)?,
|
||||
val interfaceContext: ClassContext
|
||||
) : ClassContext(typeMapper, contextDescriptor, contextKind, parentContext, localLookup) {
|
||||
|
||||
override fun getCompanionObjectContext(): CodegenContext<*>? = interfaceContext.companionObjectContext
|
||||
|
||||
override fun getAccessors(): Collection<AccessorForCallableDescriptor<*>> {
|
||||
val accessors = super.getAccessors()
|
||||
val alreadyExistKeys = accessors.map ({ Pair(it.calleeDescriptor, it.superCallTarget) })
|
||||
val filtered = interfaceContext.accessors.associateByTo(linkedMapOf()) { Pair(it.calleeDescriptor, it.superCallTarget) }.apply { keys -= alreadyExistKeys }
|
||||
val alreadyExistKeys = accessors.map({ Pair(it.calleeDescriptor, it.superCallTarget) })
|
||||
val filtered = interfaceContext.accessors.associateByTo(linkedMapOf()) { Pair(it.calleeDescriptor, it.superCallTarget) }
|
||||
.apply { keys -= alreadyExistKeys }
|
||||
return accessors + filtered.values
|
||||
}
|
||||
}
|
||||
@@ -746,7 +746,7 @@ public class KotlinTypeMapper {
|
||||
String defaultImplDesc = mapDefaultMethod(originalDescriptor, OwnerKind.IMPLEMENTATION).getDescriptor();
|
||||
return new CallableMethod(
|
||||
owner, owner, defaultImplDesc, method, INVOKESPECIAL,
|
||||
null, null, null, null, null, originalDescriptor.getReturnType(), false
|
||||
null, null, null, null, null, originalDescriptor.getReturnType(), false, false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -770,6 +770,7 @@ public class KotlinTypeMapper {
|
||||
Type thisClass;
|
||||
KotlinType dispatchReceiverKotlinType;
|
||||
boolean isInterfaceMember = false;
|
||||
boolean isDefaultMethodInInterface = false;
|
||||
|
||||
if (functionParent instanceof ClassDescriptor) {
|
||||
FunctionDescriptor declarationFunctionDescriptor = findAnyDeclaration(functionDescriptor);
|
||||
@@ -784,6 +785,7 @@ public class KotlinTypeMapper {
|
||||
|
||||
baseMethodDescriptor = findBaseDeclaration(functionDescriptor).getOriginal();
|
||||
ClassDescriptor ownerForDefault = (ClassDescriptor) baseMethodDescriptor.getContainingDeclaration();
|
||||
isDefaultMethodInInterface = isJvmInterface(ownerForDefault) && CodegenUtilKt.hasJvmDefaultAnnotation(baseMethodDescriptor);
|
||||
ownerForDefaultImpl =
|
||||
isJvmInterface(ownerForDefault) && !CodegenUtilKt.hasJvmDefaultAnnotation(baseMethodDescriptor) ?
|
||||
mapDefaultImpls(ownerForDefault) : mapClass(ownerForDefault);
|
||||
@@ -803,7 +805,14 @@ public class KotlinTypeMapper {
|
||||
FunctionDescriptor originalDescriptor = descriptor.getOriginal();
|
||||
signature = mapSignatureSkipGeneric(originalDescriptor, OwnerKind.DEFAULT_IMPLS);
|
||||
returnKotlinType = originalDescriptor.getReturnType();
|
||||
owner = mapDefaultImpls(currentOwner);
|
||||
if (descriptor instanceof AccessorForCallableDescriptor &&
|
||||
CodegenUtilKt.hasJvmDefaultAnnotation(((AccessorForCallableDescriptor) descriptor).getCalleeDescriptor())) {
|
||||
owner = mapClass(currentOwner);
|
||||
isInterfaceMember = true;
|
||||
}
|
||||
else {
|
||||
owner = mapDefaultImpls(currentOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -893,7 +902,7 @@ public class KotlinTypeMapper {
|
||||
return new CallableMethod(
|
||||
owner, ownerForDefaultImpl, defaultImplDesc, signature, invokeOpcode,
|
||||
thisClass, dispatchReceiverKotlinType, receiverParameterType, extensionReceiverKotlinType, calleeType, returnKotlinType,
|
||||
isJvm8Target ? isInterfaceMember : invokeOpcode == INVOKEINTERFACE
|
||||
isJvm8Target ? isInterfaceMember : invokeOpcode == INVOKEINTERFACE, isDefaultMethodInInterface
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// !API_VERSION: 1.3
|
||||
// JVM_TARGET: 1.8
|
||||
// WITH_RUNTIME
|
||||
|
||||
var storage = "fail"
|
||||
|
||||
interface Test {
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
private var foo: String
|
||||
get() = storage
|
||||
set(value) {
|
||||
storage = value
|
||||
}
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
private fun bar(): String {
|
||||
return "K"
|
||||
}
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
fun call(): String {
|
||||
return {
|
||||
foo = "O"
|
||||
foo + bar()
|
||||
} ()
|
||||
}
|
||||
}
|
||||
|
||||
class TestClass : Test {
|
||||
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return TestClass().call()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// !API_VERSION: 1.3
|
||||
// JVM_TARGET: 1.8
|
||||
// WITH_RUNTIME
|
||||
|
||||
interface Test {
|
||||
@kotlin.annotations.JvmDefault
|
||||
private val foo: String
|
||||
get() = "O"
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
private fun bar(): String {
|
||||
return "K"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun call(test: Test): String {
|
||||
return test.foo + test.bar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestClass : Test
|
||||
|
||||
fun box(): String {
|
||||
return Test.call(TestClass())
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// !API_VERSION: 1.3
|
||||
// JVM_TARGET: 1.8
|
||||
// WITH_RUNTIME
|
||||
|
||||
interface Test {
|
||||
@kotlin.annotations.JvmDefault
|
||||
private val foo: String
|
||||
get() = "O"
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
private fun bar(): String {
|
||||
return "K"
|
||||
}
|
||||
|
||||
fun call(): String {
|
||||
return { foo + bar()} ()
|
||||
}
|
||||
}
|
||||
|
||||
class TestClass : Test {
|
||||
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return TestClass().call()
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// !API_VERSION: 1.3
|
||||
// JVM_TARGET: 1.8
|
||||
// WITH_RUNTIME
|
||||
|
||||
interface Test {
|
||||
fun test(): String {
|
||||
return privateFun() + privateProp
|
||||
}
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
private fun privateFun(): String {
|
||||
return "O"
|
||||
}
|
||||
|
||||
@kotlin.annotations.JvmDefault
|
||||
private val privateProp: String
|
||||
get() = "K"
|
||||
}
|
||||
|
||||
class TestImpl: Test
|
||||
|
||||
fun box(): String {
|
||||
return TestImpl().test()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// !API_VERSION: 1.3
|
||||
// JVM_TARGET: 1.8
|
||||
// WITH_RUNTIME
|
||||
|
||||
interface Test {
|
||||
@kotlin.annotations.JvmDefault
|
||||
fun test(): String {
|
||||
return privateFun() + privateProp
|
||||
}
|
||||
|
||||
private fun privateFun(): String {
|
||||
return "O"
|
||||
}
|
||||
|
||||
private val privateProp: String
|
||||
get() = "K"
|
||||
}
|
||||
|
||||
class TestImpl: Test
|
||||
|
||||
fun box(): String {
|
||||
return TestImpl().test()
|
||||
}
|
||||
Generated
+30
@@ -337,6 +337,24 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Defaults extends AbstractBlackBoxCodegenTest {
|
||||
@TestMetadata("accessor.kt")
|
||||
public void testAccessor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/accessor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("accessorFromCompanion.kt")
|
||||
public void testAccessorFromCompanion() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/accessorFromCompanion.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("accessorsFromDefaultImpls.kt")
|
||||
public void testAccessorsFromDefaultImpls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/accessorsFromDefaultImpls.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDefaults() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8/defaults"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
@@ -413,6 +431,18 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateDefaultFromDefaultImpl.kt")
|
||||
public void testPrivateDefaultFromDefaultImpl() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/privateDefaultFromDefaultImpl.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateInDefaultImpls.kt")
|
||||
public void testPrivateInDefaultImpls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/privateInDefaultImpls.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleCall.kt")
|
||||
public void testSimpleCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/simpleCall.kt");
|
||||
|
||||
Reference in New Issue
Block a user