Initial support of @JvmDefault

This commit is contained in:
Mikhael Bogdanov
2018-02-20 18:02:54 +01:00
parent e885195ee1
commit fe45eb2a81
50 changed files with 322 additions and 256 deletions
@@ -180,9 +180,9 @@ public class AsmUtil {
return new Method(name, Type.getMethodDescriptor(returnType, parameterTypes));
}
public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind, GenerationState state) {
public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind) {
return (functionDescriptor.getModality() == Modality.ABSTRACT ||
(isJvmInterface(functionDescriptor.getContainingDeclaration()) && !state.isJvm8TargetWithDefaults()))
(isJvmInterface(functionDescriptor.getContainingDeclaration()) && !CodegenUtilKt.hasJvmDefaultAnnotation(functionDescriptor)))
&& !isStaticMethod(kind, functionDescriptor);
}
@@ -226,7 +226,7 @@ public class AsmUtil {
flags |= ACC_STATIC;
}
if (isAbstractMethod(functionDescriptor, kind, state)) {
if (isAbstractMethod(functionDescriptor, kind)) {
flags |= ACC_ABSTRACT;
}
@@ -66,7 +66,6 @@ import java.util.*;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceWithDefaultsMember;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
@@ -173,11 +172,7 @@ public class FunctionCodegen {
) {
OwnerKind contextKind = methodContext.getContextKind();
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
if (isInterface(containingDeclaration) &&
functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
!processInterfaceMember(functionDescriptor, contextKind, state)) {
return;
}
if (isInterface(containingDeclaration) && !processInterfaceMethod(functionDescriptor, contextKind, false)) return;
boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor);
JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge);
@@ -220,7 +215,7 @@ public class FunctionCodegen {
generateBridges(functionDescriptor);
}
if (isJvm8InterfaceWithDefaultsMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()) {
if (CodegenUtilKt.hasJvmDefaultAnnotation(functionDescriptor) && contextKind != OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()) {
generateDelegateForDefaultImpl(functionDescriptor, origin.getElement());
}
@@ -376,7 +371,7 @@ public class FunctionCodegen {
boolean staticInCompanionObject
) {
OwnerKind contextKind = methodContext.getContextKind();
if (!state.getClassBuilderMode().generateBodies || isAbstractMethod(functionDescriptor, contextKind, state)) {
if (!state.getClassBuilderMode().generateBodies || isAbstractMethod(functionDescriptor, contextKind)) {
generateLocalVariableTable(
mv,
jvmSignature,
@@ -597,7 +592,7 @@ public class FunctionCodegen {
generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
methodEnd = new Label();
}
else if (OwnerKind.DEFAULT_IMPLS == context.getContextKind() && isJvm8InterfaceWithDefaultsMember(functionDescriptor, parentCodegen.state)) {
else if (OwnerKind.DEFAULT_IMPLS == context.getContextKind() && CodegenUtilKt.hasJvmDefaultAnnotation(functionDescriptor)) {
int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.DEFAULT_IMPLS, context.getState());
assert (flags & Opcodes.ACC_ABSTRACT) == 0 : "Interface method with body should be non-abstract" + functionDescriptor;
Type type = typeMapper.mapOwner(functionDescriptor);
@@ -1001,7 +996,7 @@ public class FunctionCodegen {
}
}
if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION, state)) {
if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION)) {
CallableDescriptor overridden = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor);
assert overridden != null;
@@ -1073,7 +1068,8 @@ public class FunctionCodegen {
) {
DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();
if (isInterface(contextClass) && !processInterface(contextClass, kind, state)) {
if (isInterface(contextClass) &&
!processInterfaceMethod(functionDescriptor, kind, true)) {
return;
}
@@ -1330,7 +1326,7 @@ public class FunctionCodegen {
iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor(), false);
}
else {
if (isJvm8InterfaceWithDefaultsMember(descriptor, state)) {
if (CodegenUtilKt.hasJvmDefaultAnnotation(descriptor)) {
iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
}
else {
@@ -1489,20 +1485,23 @@ public class FunctionCodegen {
);
}
public static boolean processInterfaceMember(
@NotNull CallableMemberDescriptor function,
public static boolean processInterfaceMethod(
@NotNull CallableMemberDescriptor memberDescriptor,
@NotNull OwnerKind kind,
@NotNull GenerationState state
boolean isDefaultOrSynthetic
) {
return processInterface(function.getContainingDeclaration(), kind, state);
}
DeclarationDescriptor containingDeclaration = memberDescriptor.getContainingDeclaration();
assert isInterface(containingDeclaration) : "'processInterfaceMethod' method should be called only for interfaces, but: " +
containingDeclaration;
public static boolean processInterface(
@NotNull DeclarationDescriptor contextClass,
@NotNull OwnerKind kind,
@NotNull GenerationState state
) {
assert isInterface(contextClass) : "'processInterface' method should be called only for interfaces, but: " + contextClass;
return JvmCodegenUtil.isJvm8InterfaceWithDefaults(contextClass, state) ? kind != OwnerKind.DEFAULT_IMPLS : kind == OwnerKind.DEFAULT_IMPLS;
if (CodegenUtilKt.hasJvmDefaultAnnotation(memberDescriptor)) {
return kind != OwnerKind.DEFAULT_IMPLS;
} else {
switch (kind) {
case DEFAULT_IMPLS: return true;
case IMPLEMENTATION: return !Visibilities.isPrivate(memberDescriptor.getVisibility()) && !isDefaultOrSynthetic;
default: return false;
}
}
}
}
@@ -216,7 +216,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@Override
protected void generateDefaultImplsIfNeeded() {
if (isInterface(descriptor) && !isLocal && (!JvmCodegenUtil.isJvm8InterfaceWithDefaults(descriptor, state) || state.getGenerateDefaultImplsForJvm8())) {
if (isInterface(descriptor) && !isLocal) {
Type defaultImplsType = state.getTypeMapper().mapDefaultImpls(descriptor);
ClassBuilder defaultImplsBuilder =
state.getFactory().newVisitor(JvmDeclarationOriginKt.DefaultImpls(myClass.getPsiOrParent(), descriptor), defaultImplsType, myClass.getContainingKtFile());
@@ -1407,21 +1407,13 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private void generateTraitMethods() {
if (isInterfaceWithoutDefaults(descriptor, state)) return;
List<FunctionDescriptor> restrictedInheritance = new ArrayList<>();
for (Map.Entry<FunctionDescriptor, FunctionDescriptor> entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) {
FunctionDescriptor interfaceFun = entry.getKey();
//skip java 8 default methods
if (!CodegenUtilKt.isDefinitelyNotDefaultImplsMethod(interfaceFun) && !isJvm8InterfaceWithDefaultsMember(interfaceFun, state)) {
if (state.isJvm8TargetWithDefaults() && !JvmCodegenUtil.isJvm8InterfaceWithDefaults(interfaceFun.getContainingDeclaration(), state)) {
restrictedInheritance.add(interfaceFun);
}
else {
generateDelegationToDefaultImpl(interfaceFun, entry.getValue());
}
if (!CodegenUtilKt.isDefinitelyNotDefaultImplsMethod(interfaceFun) && !CodegenUtilKt.hasJvmDefaultAnnotation(interfaceFun)) {
generateDelegationToDefaultImpl(interfaceFun, entry.getValue());
}
}
CodegenUtilKt.reportTarget6InheritanceErrorIfNeeded(descriptor, myClass.getPsiOrParent(), restrictedInheritance, state);
}
private void generateDelegationToDefaultImpl(@NotNull FunctionDescriptor interfaceFun, @NotNull FunctionDescriptor inheritedFun) {
@@ -56,10 +56,13 @@ public class JvmCodegenUtil {
}
public static boolean isInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
return isInterfaceWithoutDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults());
return isInterfaceWithoutDefaults(descriptor, state.isJvm8Target());
}
private static boolean isInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) {
private static boolean isInterfaceWithoutDefaults(
@NotNull DeclarationDescriptor descriptor,
boolean isJvm8PlusTarget
) {
if (!DescriptorUtils.isInterface(descriptor)) return false;
if (descriptor instanceof DeserializedClassDescriptor) {
@@ -68,27 +71,15 @@ public class JvmCodegenUtil {
KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass();
assert binaryClass instanceof FileBasedKotlinClass :
"KotlinJvmBinaryClass should be subclass of FileBasedKotlinClass, but " + binaryClass;
/*TODO need add some flags to compiled code*/
return true || ((FileBasedKotlinClass) binaryClass).getClassVersion() == Opcodes.V1_6;
return ((FileBasedKotlinClass) binaryClass).getClassVersion() == Opcodes.V1_6;
}
}
return !isJvm8TargetWithDefaults;
//we can't determine is interface have default methods or not
//we need inspect all methods for jvm target 1.8+
return !isJvm8PlusTarget;
}
public static boolean isJvm8InterfaceWithDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
return isJvm8InterfaceWithDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults());
}
public static boolean isJvm8InterfaceWithDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) {
return DescriptorUtils.isInterface(descriptor) && !isInterfaceWithoutDefaults(descriptor, isJvm8Target, isJvm8TargetWithDefaults);
}
public static boolean isJvm8InterfaceWithDefaultsMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) {
DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
return isJvm8InterfaceWithDefaults(declaration, state);
}
public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) {
public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor) {
if (!isJvmInterface(descriptor.getContainingDeclaration())) {
return false;
}
@@ -96,7 +87,7 @@ public class JvmCodegenUtil {
return descriptor.getModality() == Modality.ABSTRACT;
}
return !isJvm8InterfaceWithDefaultsMember(descriptor, state);
return !CodegenUtilKt.hasJvmDefaultAnnotation(descriptor);
}
public static boolean isJvmInterface(DeclarationDescriptor descriptor) {
@@ -63,7 +63,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceWithDefaultsMember;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isNonDefaultInterfaceMember;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getInlineName;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
@@ -407,7 +406,8 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
ClassDescriptor classDescriptor = ((ClassContext) outermost).getContextDescriptor();
if (context instanceof MethodContext) {
FunctionDescriptor functionDescriptor = ((MethodContext) context).getFunctionDescriptor();
if (isInterface(functionDescriptor.getContainingDeclaration()) && !isJvm8InterfaceWithDefaultsMember(functionDescriptor, state)) {
if (isInterface(functionDescriptor.getContainingDeclaration()) && !CodegenUtilKt
.hasJvmDefaultAnnotation(functionDescriptor)) {
return typeMapper.mapDefaultImpls(classDescriptor);
}
}
@@ -813,7 +813,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
boolean isJvmStaticInObjectOrClass = CodegenUtilKt.isJvmStaticInObjectOrClassOrInterface(functionDescriptor);
boolean hasDispatchReceiver = !isStaticDeclaration(functionDescriptor) &&
!isNonDefaultInterfaceMember(functionDescriptor, state) &&
!isNonDefaultInterfaceMember(functionDescriptor) &&
!isJvmStaticInObjectOrClass;
boolean accessorIsConstructor = accessorDescriptor instanceof AccessorForConstructorDescriptor;
@@ -351,7 +351,7 @@ public class PropertyCodegen {
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
if (!isInterface(contextDescriptor) ||
(FunctionCodegen.processInterface(contextDescriptor, kind, state) ||
(FunctionCodegen.processInterfaceMethod(descriptor, kind, true) || //TODO delete
(kind == OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()))) {
memberCodegen.generateSyntheticAnnotationsMethod(
descriptor, getSyntheticMethodSignature(descriptor), annotations, AnnotationUseSiteTarget.PROPERTY
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.getDirectMember
import org.jetbrains.kotlin.resolve.DescriptorUtils.isSubclass
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression
@@ -58,6 +59,8 @@ import java.io.StringWriter
import java.io.PrintWriter
import java.util.*
private val JVM_DEFAULT_FQ_NAME = FqName("kotlin.annotations.JvmDefault")
fun generateIsCheck(
v: InstructionAdapter,
kotlinType: KotlinType,
@@ -233,26 +236,6 @@ fun ClassBuilder.generateMethod(
}
}
fun reportTarget6InheritanceErrorIfNeeded(
classDescriptor: ClassDescriptor, classElement: PsiElement, restrictedInheritance: List<FunctionDescriptor>, state: GenerationState
) {
if (!restrictedInheritance.isEmpty()) {
val groupBy = restrictedInheritance.groupBy { descriptor -> descriptor.containingDeclaration as ClassDescriptor }
for ((key, value) in groupBy) {
state.diagnostics.report(
ErrorsJvm.TARGET6_INTERFACE_INHERITANCE.on(
classElement, classDescriptor, key,
value.joinToString(separator = "\n", prefix = "\n") {
Renderers.COMPACT.render(JvmCodegenUtil.getDirectMember(it), RenderingContext.Empty)
}
)
)
}
}
}
fun CallableDescriptor.isJvmStaticInObjectOrClassOrInterface(): Boolean =
isJvmStaticIn {
DescriptorUtils.isNonCompanionObject(it) ||
@@ -440,3 +423,5 @@ fun MethodNode.textifyMethodNode(): String {
text.print(PrintWriter(sw))
return "$sw"
}
fun CallableMemberDescriptor.hasJvmDefaultAnnotation() = getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)
@@ -414,7 +414,7 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
@NotNull D descriptor,
@Nullable ClassDescriptor superCallTarget,
@NotNull GenerationState state) {
if (superCallTarget != null && !isNonDefaultInterfaceMember(descriptor, state)) {
if (superCallTarget != null && !isNonDefaultInterfaceMember(descriptor)) {
CodegenContext afterInline = getFirstCrossInlineOrNonInlineContext();
CodegenContext c = afterInline.findParentContextWithDescriptor(superCallTarget);
assert c != null : "Couldn't find a context for a super-call: " + descriptor;
@@ -83,7 +83,6 @@ public class KotlinTypeMapper {
private final IncompatibleClassTracker incompatibleClassTracker;
private final String moduleName;
private final boolean isJvm8Target;
private final boolean isJvm8TargetWithDefaults;
private final TypeMappingConfiguration<Type> typeMappingConfiguration = new TypeMappingConfiguration<Type>() {
@NotNull
@@ -151,7 +150,6 @@ public class KotlinTypeMapper {
this.incompatibleClassTracker = incompatibleClassTracker;
this.moduleName = moduleName;
this.isJvm8Target = isJvm8Target;
this.isJvm8TargetWithDefaults = isJvm8TargetWithDefaults;
}
@NotNull
@@ -730,8 +728,8 @@ public class KotlinTypeMapper {
descriptor = classCallable;
continue;
}
else if (isSuperCall && !isJvm8TargetWithDefaults && !isInterface(descriptor.getContainingDeclaration())) {
//Don't unwrap fake overrides from class to interface cause substituted override would be implicitly generated for target 1.6
else if (isSuperCall && !CodegenUtilKt.hasJvmDefaultAnnotation(descriptor) && !isInterface(descriptor.getContainingDeclaration())) {
//Don't unwrap fake overrides from class to interface cause substituted override would be implicitly generated
return descriptor;
}
@@ -788,13 +786,13 @@ public class KotlinTypeMapper {
baseMethodDescriptor = findBaseDeclaration(functionDescriptor).getOriginal();
ClassDescriptor ownerForDefault = (ClassDescriptor) baseMethodDescriptor.getContainingDeclaration();
ownerForDefaultImpl =
isJvmInterface(ownerForDefault) && !isJvm8InterfaceWithDefaults(ownerForDefault) ?
isJvmInterface(ownerForDefault) && !CodegenUtilKt.hasJvmDefaultAnnotation(baseMethodDescriptor) ?
mapDefaultImpls(ownerForDefault) : mapClass(ownerForDefault);
if (isInterface && (superCall || descriptor.getVisibility() == Visibilities.PRIVATE || isAccessor(descriptor))) {
thisClass = mapClass(currentOwner);
dispatchReceiverKotlinType = currentOwner.getDefaultType();
if (declarationOwner instanceof JavaClassDescriptor || isJvm8InterfaceWithDefaults(declarationOwner)) {
if (declarationOwner instanceof JavaClassDescriptor || CodegenUtilKt.hasJvmDefaultAnnotation(declarationFunctionDescriptor)) {
invokeOpcode = INVOKESPECIAL;
signature = mapSignatureSkipGeneric(functionDescriptor);
returnKotlinType = functionDescriptor.getReturnType();
@@ -900,11 +898,6 @@ public class KotlinTypeMapper {
);
}
private boolean isJvm8InterfaceWithDefaults(@NotNull ClassDescriptor ownerForDefault) {
return isJvmInterface(ownerForDefault) &&
JvmCodegenUtil.isJvm8InterfaceWithDefaults(ownerForDefault, isJvm8Target, isJvm8TargetWithDefaults);
}
public static boolean isAccessor(@Nullable CallableMemberDescriptor descriptor) {
return descriptor instanceof AccessorForCallableDescriptor<?>;
}