Support synthetic accessors for @JvmDefault members

This commit is contained in:
Mikhael Bogdanov
2018-02-23 13:19:48 +01:00
parent 1acd5642ce
commit 308283e52e
13 changed files with 214 additions and 21 deletions
@@ -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);
@@ -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
);
}