From 3c4b2994a970405dc66117c25abcdb796eb988c8 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 7 Sep 2015 13:34:15 +0300 Subject: [PATCH] Fix protected call to super method from lambda Previously JVM back-end had an assumption that if we're calling a method declared in the super class from a lambda via a synthetic accessor, that should be a super call and it must be done with 'invokespecial'. Which is wrong because a method declared in the super class may be open and overridden in the subclass, so 'invokevirtual' should be used. Surprisingly, Java SE verifier allowed both instructions, but on Android only the latter is possible #KT-8899 Fixed #KT-9052 Fixed --- .../AccessorForCallableDescriptor.java | 5 ++ .../AccessorForConstructorDescriptor.kt | 28 +++--- .../AccessorForFunctionDescriptor.java | 13 ++- ...orForPropertyBackingFieldInOuterClass.java | 5 +- .../AccessorForPropertyDescriptor.java | 33 ++++++- .../kotlin/codegen/ExpressionCodegen.java | 72 +++++++-------- .../kotlin/codegen/FunctionCodegen.java | 2 +- .../codegen/ImplementationBodyCodegen.java | 60 ++++++------- .../kotlin/codegen/MemberCodegen.java | 3 +- .../jetbrains/kotlin/codegen/MethodKind.java | 23 ----- .../kotlin/codegen/PlatformStaticGenerator.kt | 28 +++--- .../codegen/context/CodegenContext.java | 87 +++++++++++++++---- .../accessorForProtectedInvokeVirtual/1.kt | 49 +++++++++++ .../accessorForProtectedInvokeVirtual/2.kt | 13 +++ ...BlackBoxMultiFileCodegenTestGenerated.java | 6 ++ 15 files changed, 281 insertions(+), 146 deletions(-) delete mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/MethodKind.java create mode 100644 compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/1.kt create mode 100644 compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/2.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForCallableDescriptor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForCallableDescriptor.java index e9b5c5360a1..2b4077f13b1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForCallableDescriptor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForCallableDescriptor.java @@ -17,9 +17,14 @@ package org.jetbrains.kotlin.codegen; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; +import org.jetbrains.kotlin.psi.JetSuperExpression; public interface AccessorForCallableDescriptor { @NotNull T getCalleeDescriptor(); + + @Nullable + JetSuperExpression getSuperCallExpression(); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForConstructorDescriptor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForConstructorDescriptor.kt index f5bca65b046..f51981ca83f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForConstructorDescriptor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForConstructorDescriptor.kt @@ -17,37 +17,35 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.JetSuperExpression import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.types.JetType -import java.util.* public class AccessorForConstructorDescriptor( private val calleeDescriptor: ConstructorDescriptor, - containingDeclaration: DeclarationDescriptor -) : AbstractAccessorForFunctionDescriptor(containingDeclaration, Name.special("")) - , ConstructorDescriptor - , AccessorForCallableDescriptor { - + containingDeclaration: DeclarationDescriptor, + private val superCallExpression: JetSuperExpression? +) : AbstractAccessorForFunctionDescriptor(containingDeclaration, Name.special("")), + ConstructorDescriptor, + AccessorForCallableDescriptor { override fun getCalleeDescriptor(): ConstructorDescriptor = calleeDescriptor - override fun getContainingDeclaration(): ClassDescriptor = calleeDescriptor.getContainingDeclaration() + override fun getContainingDeclaration(): ClassDescriptor = calleeDescriptor.containingDeclaration override fun isPrimary(): Boolean = false - override fun getReturnType(): JetType = super.getReturnType()!! + override fun getReturnType(): JetType = super.getReturnType()!! + + override fun getSuperCallExpression(): JetSuperExpression? = superCallExpression init { initialize( - DescriptorUtils.getReceiverParameterType(getExtensionReceiverParameter()), - calleeDescriptor.getDispatchReceiverParameter(), + DescriptorUtils.getReceiverParameterType(extensionReceiverParameter), + calleeDescriptor.dispatchReceiverParameter, copyTypeParameters(calleeDescriptor), copyValueParameters(calleeDescriptor), - calleeDescriptor.getReturnType(), + calleeDescriptor.returnType, Modality.FINAL, Visibilities.INTERNAL ) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java index 559463c1e51..8258740a6e0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForFunctionDescriptor.java @@ -17,23 +17,27 @@ package org.jetbrains.kotlin.codegen; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.psi.JetSuperExpression; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage; public class AccessorForFunctionDescriptor extends AbstractAccessorForFunctionDescriptor implements AccessorForCallableDescriptor { - private final FunctionDescriptor calleeDescriptor; + private final JetSuperExpression superCallExpression; public AccessorForFunctionDescriptor( @NotNull FunctionDescriptor descriptor, @NotNull DeclarationDescriptor containingDeclaration, - int index + int index, + @Nullable JetSuperExpression superCallExpression ) { super(containingDeclaration, Name.identifier("access$" + (descriptor instanceof ConstructorDescriptor ? "init" : descriptor.getName()) + "$" + index)); this.calleeDescriptor = descriptor; + this.superCallExpression = superCallExpression; initialize(DescriptorUtils.getReceiverParameterType(descriptor.getExtensionReceiverParameter()), descriptor instanceof ConstructorDescriptor || AnnotationsPackage.isPlatformStaticInObjectOrClass(descriptor) @@ -51,4 +55,9 @@ public class AccessorForFunctionDescriptor extends AbstractAccessorForFunctionDe public FunctionDescriptor getCalleeDescriptor() { return calleeDescriptor; } + + @Override + public JetSuperExpression getSuperCallExpression() { + return superCallExpression; + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyBackingFieldInOuterClass.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyBackingFieldInOuterClass.java index 89569f6e430..b09ba5bfce3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyBackingFieldInOuterClass.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyBackingFieldInOuterClass.java @@ -23,13 +23,12 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor; import org.jetbrains.kotlin.types.JetType; public class AccessorForPropertyBackingFieldInOuterClass extends AccessorForPropertyDescriptor { - public AccessorForPropertyBackingFieldInOuterClass( - @NotNull PropertyDescriptor pd, + @NotNull PropertyDescriptor property, @NotNull DeclarationDescriptor containingDeclaration, int index, @Nullable JetType delegationType ) { - super(pd, delegationType != null ? delegationType : pd.getType(), null, null, containingDeclaration, index); + super(property, delegationType != null ? delegationType : property.getType(), null, null, containingDeclaration, index, null); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java index 81130a4a11b..ad16c00293b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AccessorForPropertyDescriptor.java @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl; import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl; import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl; import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.psi.JetSuperExpression; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.types.JetType; @@ -33,9 +34,16 @@ import java.util.Collections; public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implements AccessorForCallableDescriptor { private final PropertyDescriptor calleeDescriptor; private final int accessorIndex; + private final JetSuperExpression superCallExpression; - public AccessorForPropertyDescriptor(@NotNull PropertyDescriptor pd, @NotNull DeclarationDescriptor containingDeclaration, int index) { - this(pd, pd.getType(), DescriptorUtils.getReceiverParameterType(pd.getExtensionReceiverParameter()), pd.getDispatchReceiverParameter(), containingDeclaration, index); + public AccessorForPropertyDescriptor( + @NotNull PropertyDescriptor property, + @NotNull DeclarationDescriptor containingDeclaration, + int index, + @Nullable JetSuperExpression superCallExpression + ) { + this(property, property.getType(), DescriptorUtils.getReceiverParameterType(property.getExtensionReceiverParameter()), + property.getDispatchReceiverParameter(), containingDeclaration, index, superCallExpression); } protected AccessorForPropertyDescriptor( @@ -44,7 +52,8 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem @Nullable JetType receiverType, @Nullable ReceiverParameterDescriptor dispatchReceiverParameter, @NotNull DeclarationDescriptor containingDeclaration, - int index + int index, + @Nullable JetSuperExpression superCallExpression ) { super(containingDeclaration, null, Annotations.EMPTY, Modality.FINAL, Visibilities.LOCAL, original.isVar(), Name.identifier("access$" + getIndexedAccessorSuffix(original, index)), @@ -52,6 +61,7 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem this.calleeDescriptor = original; this.accessorIndex = index; + this.superCallExpression = superCallExpression; setType(propertyType, Collections.emptyList(), dispatchReceiverParameter, receiverType); initialize(new Getter(this), new Setter(this)); } @@ -69,6 +79,12 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem //noinspection ConstantConditions return ((AccessorForPropertyDescriptor) getCorrespondingProperty()).getCalleeDescriptor().getGetter(); } + + @Nullable + @Override + public JetSuperExpression getSuperCallExpression() { + return ((AccessorForPropertyDescriptor) getCorrespondingProperty()).getSuperCallExpression(); + } } public static class Setter extends PropertySetterDescriptorImpl implements AccessorForCallableDescriptor{ @@ -84,6 +100,12 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem //noinspection ConstantConditions return ((AccessorForPropertyDescriptor) getCorrespondingProperty()).getCalleeDescriptor().getSetter(); } + + @Nullable + @Override + public JetSuperExpression getSuperCallExpression() { + return ((AccessorForPropertyDescriptor) getCorrespondingProperty()).getSuperCallExpression(); + } } @NotNull @@ -92,6 +114,11 @@ public class AccessorForPropertyDescriptor extends PropertyDescriptorImpl implem return calleeDescriptor; } + @Override + public JetSuperExpression getSuperCallExpression() { + return superCallExpression; + } + @NotNull public String getIndexedAccessorSuffix() { return getIndexedAccessorSuffix(calleeDescriptor, accessorIndex); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index fdcbf45c376..0295439095f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -356,11 +356,15 @@ public class ExpressionCodegen extends JetVisitor implem @Override public StackValue visitSuperExpression(@NotNull JetSuperExpression expression, StackValue data) { - return StackValue.thisOrOuter(this, getSuperCallLabelTarget(expression), true, true); + return StackValue.thisOrOuter(this, getSuperCallLabelTarget(context, expression), true, true); } @NotNull - private ClassDescriptor getSuperCallLabelTarget(JetSuperExpression expression) { + public static ClassDescriptor getSuperCallLabelTarget( + @NotNull CodegenContext context, + @NotNull JetSuperExpression expression + ) { + BindingContext bindingContext = context.getState().getBindingContext(); PsiElement labelPsi = bindingContext.get(LABEL_TARGET, expression.getTargetLabel()); ClassDescriptor labelTarget = (ClassDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, labelPsi); DeclarationDescriptor descriptor = bindingContext.get(REFERENCE_TARGET, expression.getInstanceReference()); @@ -2007,7 +2011,7 @@ public class ExpressionCodegen extends JetVisitor implem expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER && contextKind() != OwnerKind.TRAIT_IMPL; JetSuperExpression superExpression = resolvedCall == null ? null : CallResolverUtilPackage.getSuperCallExpression(resolvedCall.getCall()); - propertyDescriptor = context.accessibleDescriptor(propertyDescriptor); + propertyDescriptor = context.accessibleDescriptor(propertyDescriptor, superExpression); if (directToField) { receiver = StackValue.receiverWithoutReceiverArgument(receiver); @@ -2137,14 +2141,14 @@ public class ExpressionCodegen extends JetVisitor implem @Nullable JetSuperExpression superExpression, @NotNull StackValue receiver ) { - return intermediateValueForProperty(propertyDescriptor, forceField, superExpression, MethodKind.GENERAL, receiver); + return intermediateValueForProperty(propertyDescriptor, forceField, superExpression, false, receiver); } public StackValue.Property intermediateValueForProperty( @NotNull PropertyDescriptor propertyDescriptor, boolean forceField, @Nullable JetSuperExpression superExpression, - @NotNull MethodKind methodKind, + boolean skipAccessorsForPrivateFieldInOuterClass, StackValue receiver ) { if (propertyDescriptor instanceof SyntheticJavaPropertyDescriptor) { @@ -2165,56 +2169,54 @@ public class ExpressionCodegen extends JetVisitor implem CallableMethod callableGetter = null; CallableMethod callableSetter = null; - boolean skipPropertyAccessors = forceField && !isBackingFieldInAnotherClass; - - CodegenContext backingFieldContext = context.getParentContext(); - boolean changeOwnerOnTypeMapping = isBackingFieldInAnotherClass; + CodegenContext backingFieldContext; + boolean changeOwnerOnTypeMapping; + boolean skipPropertyAccessors; if (isBackingFieldInAnotherClass && forceField) { backingFieldContext = context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration()); int flags = AsmUtil.getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegatedProperty); - skipPropertyAccessors = - (flags & ACC_PRIVATE) == 0 || methodKind == MethodKind.SYNTHETIC_ACCESSOR || methodKind == MethodKind.INITIALIZER; + skipPropertyAccessors = (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass; if (!skipPropertyAccessors) { - propertyDescriptor = (PropertyDescriptor) backingFieldContext.getAccessor(propertyDescriptor, true, delegateType); - changeOwnerOnTypeMapping = - changeOwnerOnTypeMapping && !(propertyDescriptor instanceof AccessorForPropertyBackingFieldInOuterClass); + //noinspection ConstantConditions + propertyDescriptor = (PropertyDescriptor) backingFieldContext.getAccessor( + propertyDescriptor, true, delegateType, superExpression + ); + changeOwnerOnTypeMapping = !(propertyDescriptor instanceof AccessorForPropertyBackingFieldInOuterClass); } + else { + changeOwnerOnTypeMapping = true; + } + } + else { + backingFieldContext = context.getParentContext(); + changeOwnerOnTypeMapping = isBackingFieldInAnotherClass; + skipPropertyAccessors = forceField; } if (!skipPropertyAccessors) { - if (couldUseDirectAccessToProperty(propertyDescriptor, true, isDelegatedProperty, context)) { - callableGetter = null; - } - else { + if (!couldUseDirectAccessToProperty(propertyDescriptor, true, isDelegatedProperty, context)) { if (isSuper && !isInterface(containingDeclaration)) { - ClassDescriptor owner = getSuperCallLabelTarget(superExpression); + ClassDescriptor owner = getSuperCallLabelTarget(context, superExpression); CodegenContext c = context.findParentContextWithDescriptor(owner); assert c != null : "Couldn't find a context for a super-call: " + propertyDescriptor; if (c != context.getParentContext()) { - propertyDescriptor = (PropertyDescriptor) c.getAccessor(propertyDescriptor); + propertyDescriptor = (PropertyDescriptor) c.getAccessor(propertyDescriptor, superExpression); } } - propertyDescriptor = context.accessibleDescriptor(propertyDescriptor); + propertyDescriptor = context.accessibleDescriptor(propertyDescriptor, superExpression); PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); if (getter != null) { - callableGetter = - typeMapper.mapToCallableMethod(getter, isSuper || MethodKind.SYNTHETIC_ACCESSOR == methodKind, context); + callableGetter = typeMapper.mapToCallableMethod(getter, isSuper, context); } } if (propertyDescriptor.isVar()) { PropertySetterDescriptor setter = propertyDescriptor.getSetter(); - if (setter != null) { - if (couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context)) { - callableSetter = null; - } - else { - callableSetter = - typeMapper.mapToCallableMethod(setter, isSuper || MethodKind.SYNTHETIC_ACCESSOR == methodKind, context); - } + if (setter != null && !couldUseDirectAccessToProperty(propertyDescriptor, false, isDelegatedProperty, context)) { + callableSetter = typeMapper.mapToCallableMethod(setter, isSuper, context); } } } @@ -2329,7 +2331,9 @@ public class ExpressionCodegen extends JetVisitor implem descriptor = originalIfSamAdapter; } // $default method is not private, so you need no accessor to call it - return usesDefaultArguments(resolvedCall) ? descriptor : context.accessibleDescriptor(descriptor); + return usesDefaultArguments(resolvedCall) + ? descriptor + : context.accessibleDescriptor(descriptor, CallResolverUtilPackage.getSuperCallExpression(resolvedCall.getCall())); } private static boolean usesDefaultArguments(@NotNull ResolvedCall resolvedCall) { @@ -2355,11 +2359,11 @@ public class ExpressionCodegen extends JetVisitor implem boolean superCall = superCallExpression != null; if (superCall && !isInterface(fd.getContainingDeclaration())) { - ClassDescriptor owner = getSuperCallLabelTarget(superCallExpression); + ClassDescriptor owner = getSuperCallLabelTarget(context, superCallExpression); CodegenContext c = context.findParentContextWithDescriptor(owner); assert c != null : "Couldn't find a context for a super-call: " + fd; if (c != context.getParentContext()) { - fd = (FunctionDescriptor) c.getAccessor(fd); + fd = (FunctionDescriptor) c.getAccessor(fd, superCallExpression); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index adbf3bd4a0a..a958cdd3952 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -210,7 +210,7 @@ public class FunctionCodegen { mv.visitCode(); FunctionDescriptor staticFunctionDescriptor = PlatformStaticGenerator.createStaticFunctionDescriptor(functionDescriptor); JvmMethodSignature jvmMethodSignature = - typeMapper.mapSignature(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor)); + typeMapper.mapSignature(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null)); Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration()); generateDelegateToMethodBody(false, mv, jvmMethodSignature.getAsmMethod(), owningType.getInternalName()); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 0e7cd2ece5b..1f8fdea3d48 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -857,32 +857,31 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } protected void generateSyntheticAccessors() { - Map accessors = ((CodegenContext) context).getAccessors(); - for (Map.Entry entry : accessors.entrySet()) { - generateSyntheticAccessor(entry); + for (AccessorForCallableDescriptor accessor : ((CodegenContext) context).getAccessors()) { + generateSyntheticAccessor(accessor); } } - private void generateSyntheticAccessor(Map.Entry entry) { - if (entry.getValue() instanceof FunctionDescriptor) { - final FunctionDescriptor bridge = (FunctionDescriptor) entry.getValue(); - final FunctionDescriptor original = (FunctionDescriptor) entry.getKey(); + private void generateSyntheticAccessor(@NotNull AccessorForCallableDescriptor accessorForCallableDescriptor) { + if (accessorForCallableDescriptor instanceof FunctionDescriptor) { + final FunctionDescriptor accessor = (FunctionDescriptor) accessorForCallableDescriptor; + final FunctionDescriptor original = (FunctionDescriptor) accessorForCallableDescriptor.getCalleeDescriptor(); functionCodegen.generateMethod( - Synthetic(null, original), bridge, - new FunctionGenerationStrategy.CodegenBased(state, bridge) { + Synthetic(null, original), accessor, + new FunctionGenerationStrategy.CodegenBased(state, accessor) { @Override public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { markLineNumberForSyntheticFunction(descriptor, codegen.v); - generateMethodCallTo(original, bridge, codegen.v); + generateMethodCallTo(original, accessor, codegen.v); codegen.v.areturn(signature.getReturnType()); } } ); } - else if (entry.getValue() instanceof PropertyDescriptor) { - final PropertyDescriptor bridge = (PropertyDescriptor) entry.getValue(); - final PropertyDescriptor original = (PropertyDescriptor) entry.getKey(); + else if (accessorForCallableDescriptor instanceof AccessorForPropertyDescriptor) { + final AccessorForPropertyDescriptor accessor = (AccessorForPropertyDescriptor) accessorForCallableDescriptor; + final PropertyDescriptor original = accessor.getCalleeDescriptor(); class PropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased { public PropertyAccessorStrategy(@NotNull PropertyAccessorDescriptor callableDescriptor) { @@ -892,10 +891,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { @Override public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { boolean forceField = AsmUtil.isPropertyWithBackingFieldInOuterClass(original) && - !isCompanionObject(bridge.getContainingDeclaration()); - StackValue property = - codegen.intermediateValueForProperty(original, forceField, null, MethodKind.SYNTHETIC_ACCESSOR, - StackValue.none()); + !isCompanionObject(accessor.getContainingDeclaration()); + StackValue property = codegen.intermediateValueForProperty( + original, forceField, accessor.getSuperCallExpression(), true, StackValue.none() + ); InstructionAdapter iv = codegen.v; @@ -920,14 +919,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } - PropertyGetterDescriptor getter = bridge.getGetter(); + PropertyGetterDescriptor getter = accessor.getGetter(); assert getter != null; functionCodegen.generateMethod(Synthetic(null, original.getGetter() != null ? original.getGetter() : original), getter, new PropertyAccessorStrategy(getter)); - if (bridge.isVar()) { - PropertySetterDescriptor setter = bridge.getSetter(); + if (accessor.isVar()) { + PropertySetterDescriptor setter = accessor.getSetter(); assert setter != null; functionCodegen.generateMethod(Synthetic(null, original.getSetter() != null ? original.getSetter() : original), @@ -957,24 +956,25 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateMethodCallTo( @NotNull FunctionDescriptor functionDescriptor, - @Nullable FunctionDescriptor bridgeDescriptor, + @Nullable FunctionDescriptor accessorDescriptor, @NotNull InstructionAdapter iv ) { boolean isConstructor = functionDescriptor instanceof ConstructorDescriptor; - boolean bridgeIsAccessorConstructor = bridgeDescriptor instanceof AccessorForConstructorDescriptor; - boolean callFromAccessor = bridgeIsAccessorConstructor - || (bridgeDescriptor != null && JetTypeMapper.isAccessor(bridgeDescriptor)); + boolean accessorIsConstructor = accessorDescriptor instanceof AccessorForConstructorDescriptor; + + boolean superCall = accessorDescriptor instanceof AccessorForCallableDescriptor && + ((AccessorForCallableDescriptor) accessorDescriptor).getSuperCallExpression() != null; CallableMethod callableMethod = isConstructor ? typeMapper.mapToCallableMethod((ConstructorDescriptor) functionDescriptor) : - typeMapper.mapToCallableMethod(functionDescriptor, callFromAccessor, context); + typeMapper.mapToCallableMethod(functionDescriptor, superCall, context); int reg = 1; - if (isConstructor && !bridgeIsAccessorConstructor) { + if (isConstructor && !accessorIsConstructor) { iv.anew(callableMethod.getOwner()); iv.dup(); reg = 0; } - else if (callFromAccessor) { + else if (accessorIsConstructor || (accessorDescriptor != null && JetTypeMapper.isAccessor(accessorDescriptor))) { if (!AnnotationsPackage.isPlatformStaticInObjectOrClass(functionDescriptor)) { iv.load(0, OBJECT_TYPE); } @@ -989,6 +989,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { reg += argType.getSize(); } } + callableMethod.genInvokeInstruction(iv); } @@ -1074,8 +1075,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateCompanionObjectInitializer(@NotNull ClassDescriptor companionObject) { ExpressionCodegen codegen = createOrGetClInitCodegen(); - FunctionDescriptor constructor = - (FunctionDescriptor) context.accessibleDescriptor(KotlinPackage.single(companionObject.getConstructors())); + FunctionDescriptor constructor = (FunctionDescriptor) context.accessibleDescriptor( + KotlinPackage.single(companionObject.getConstructors()), /* superCallExpression = */ null + ); generateMethodCallTo(constructor, null, codegen.v); codegen.v.dup(); StackValue instance = StackValue.onStack(typeMapper.mapClass(companionObject)); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 8d915a7c4f9..58780d947f4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -360,8 +360,7 @@ public abstract class MemberCodegen { + private val typeMapper = state.typeMapper override fun invoke(codegen: ImplementationBodyCodegen, classBuilder: ClassBuilder) { val staticFunctionDescriptor = createStaticFunctionDescriptor(descriptor) @@ -51,40 +50,37 @@ class PlatformStaticGenerator( frameMap: FrameMap, signature: JvmMethodSignature, context: MethodContext, - parentCodegen: MemberCodegen + parentCodegen: MemberCodegen<*> ) { - val typeMapper = parentCodegen.typeMapper - val iv = InstructionAdapter(mv) - val classDescriptor = descriptor.getContainingDeclaration() as ClassDescriptor + val classDescriptor = descriptor.containingDeclaration as ClassDescriptor val singletonValue = StackValue.singleton(classDescriptor, typeMapper) - singletonValue.put(singletonValue.type, iv); - var index = 0; - val asmMethod = signature.getAsmMethod() - for (paramType in asmMethod.getArgumentTypes()) { - iv.load(index, paramType); - index += paramType.getSize(); + singletonValue.put(singletonValue.type, iv) + var index = 0 + val asmMethod = signature.asmMethod + for (paramType in asmMethod.argumentTypes) { + iv.load(index, paramType) + index += paramType.size } val syntheticOrOriginalMethod = typeMapper.mapToCallableMethod( - codegen.getContext().accessibleDescriptor(descriptor), + codegen.getContext().accessibleDescriptor(descriptor, /* superCallExpression = */ null), false, codegen.getContext() ) syntheticOrOriginalMethod.genInvokeInstruction(iv) - iv.areturn(asmMethod.getReturnType()); + iv.areturn(asmMethod.returnType) } } ) - if (originElement is JetNamedFunction) { codegen.functionCodegen.generateOverloadsWithDefaultValues(originElement, staticFunctionDescriptor, descriptor) } } companion object { - @platformStatic + @JvmStatic public fun createStaticFunctionDescriptor(descriptor: FunctionDescriptor): FunctionDescriptor { val memberDescriptor = if (descriptor is PropertyAccessorDescriptor) descriptor.getCorrespondingProperty() else descriptor val copies = CodegenUtil.copyFunctions( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java index 1be0d8fa702..ed827d3fe16 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.binding.MutableClosure; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.psi.JetSuperExpression; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.storage.LockBasedStorageManager; @@ -47,8 +48,38 @@ public abstract class CodegenContext { private final LocalLookup enclosingLocalLookup; private final NullableLazyValue outerExpression; - private Map accessors; private Map childContexts; + private Map> accessors; + + private static class AccessorKey { + public final DeclarationDescriptor descriptor; + public final ClassDescriptor superCallLabelTarget; + + public AccessorKey(@NotNull DeclarationDescriptor descriptor, @Nullable ClassDescriptor superCallLabelTarget) { + this.descriptor = descriptor; + this.superCallLabelTarget = superCallLabelTarget; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AccessorKey)) return false; + AccessorKey other = (AccessorKey) obj; + return descriptor.equals(other.descriptor) && + (superCallLabelTarget == null + ? other.superCallLabelTarget == null + : superCallLabelTarget.equals(other.superCallLabelTarget)); + } + + @Override + public int hashCode() { + return 31 * descriptor.hashCode() + (superCallLabelTarget == null ? 0 : superCallLabelTarget.hashCode()); + } + + @Override + public String toString() { + return descriptor.toString(); + } + } public CodegenContext( @NotNull T contextDescriptor, @@ -227,20 +258,28 @@ public abstract class CodegenContext { } @NotNull - public D getAccessor(@NotNull D descriptor) { - return getAccessor(descriptor, false, null); + public D getAccessor(@NotNull D descriptor, @Nullable JetSuperExpression superCallExpression) { + return getAccessor(descriptor, false, null, superCallExpression); } @SuppressWarnings("unchecked") @NotNull public D getAccessor( - @NotNull D descriptor, boolean isForBackingFieldInOuterClass, @Nullable JetType delegateType + @NotNull D possiblySubstitutedDescriptor, + boolean isForBackingFieldInOuterClass, + @Nullable JetType delegateType, + @Nullable JetSuperExpression superCallExpression ) { if (accessors == null) { - accessors = new LinkedHashMap(); + accessors = new LinkedHashMap>(); } - descriptor = (D) descriptor.getOriginal(); - DeclarationDescriptor accessor = accessors.get(descriptor); + + D descriptor = (D) possiblySubstitutedDescriptor.getOriginal(); + AccessorKey key = new AccessorKey( + descriptor, superCallExpression == null ? null : ExpressionCodegen.getSuperCallLabelTarget(this, superCallExpression) + ); + + AccessorForCallableDescriptor accessor = accessors.get(key); if (accessor != null) { assert !isForBackingFieldInOuterClass || accessor instanceof AccessorForPropertyBackingFieldInOuterClass : "There is already exists accessor with isForBackingFieldInOuterClass = false in this context"; @@ -249,10 +288,12 @@ public abstract class CodegenContext { int accessorIndex = accessors.size(); if (descriptor instanceof SimpleFunctionDescriptor) { - accessor = new AccessorForFunctionDescriptor((FunctionDescriptor) descriptor, contextDescriptor, accessorIndex); + accessor = new AccessorForFunctionDescriptor( + (FunctionDescriptor) descriptor, contextDescriptor, accessorIndex, superCallExpression + ); } else if (descriptor instanceof ConstructorDescriptor) { - accessor = new AccessorForConstructorDescriptor((ConstructorDescriptor) descriptor, contextDescriptor); + accessor = new AccessorForConstructorDescriptor((ConstructorDescriptor) descriptor, contextDescriptor, superCallExpression); } else if (descriptor instanceof PropertyDescriptor) { if (isForBackingFieldInOuterClass) { @@ -260,13 +301,16 @@ public abstract class CodegenContext { accessorIndex, delegateType); } else { - accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor, accessorIndex); + accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor, + accessorIndex, superCallExpression); } } else { throw new UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor); } - accessors.put(descriptor, accessor); + + accessors.put(key, accessor); + return (D) accessor; } @@ -314,25 +358,29 @@ public abstract class CodegenContext { } @NotNull - public Map getAccessors() { - return accessors == null ? Collections.emptyMap() : accessors; + public Collection> getAccessors() { + return accessors == null ? Collections.>emptySet() : accessors.values(); } @NotNull - public D accessibleDescriptor(D descriptor) { + public D accessibleDescriptor( + @NotNull D descriptor, + @Nullable JetSuperExpression superCallExpression + ) { DeclarationDescriptor enclosing = descriptor.getContainingDeclaration(); if (!hasThisDescriptor() || enclosing == getThisDescriptor() || enclosing == getClassOrPackageParentContext().getContextDescriptor()) { return descriptor; } - return accessibleDescriptorIfNeeded(descriptor); + return accessibleDescriptorIfNeeded(descriptor, superCallExpression); } public void recordSyntheticAccessorIfNeeded(@NotNull CallableMemberDescriptor descriptor, @NotNull BindingContext bindingContext) { if (hasThisDescriptor() && (descriptor instanceof ConstructorDescriptor || Boolean.TRUE.equals(bindingContext.get(NEED_SYNTHETIC_ACCESSOR, descriptor)))) { - accessibleDescriptorIfNeeded(descriptor); + // Not a super call because neither constructors nor private members can be targets of super calls + accessibleDescriptorIfNeeded(descriptor, /* superCallExpression = */ null); } } @@ -352,7 +400,10 @@ public abstract class CodegenContext { @SuppressWarnings("unchecked") @NotNull - private D accessibleDescriptorIfNeeded(@NotNull D descriptor) { + private D accessibleDescriptorIfNeeded( + @NotNull D descriptor, + @Nullable JetSuperExpression superCallExpression + ) { CallableMemberDescriptor unwrappedDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor); int flag = getAccessFlags(unwrappedDescriptor); if ((flag & ACC_PRIVATE) == 0 && (flag & ACC_PROTECTED) == 0) { @@ -385,7 +436,7 @@ public abstract class CodegenContext { } } - return (D) descriptorContext.getAccessor(descriptor); + return (D) descriptorContext.getAccessor(descriptor, superCallExpression); } private void addChild(@NotNull CodegenContext child) { diff --git a/compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/1.kt b/compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/1.kt new file mode 100644 index 00000000000..3fabc5a4bdd --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/1.kt @@ -0,0 +1,49 @@ +import test.A +import kotlin.test.assertEquals + +open class B : A() { + fun box(): String { + val overriddenMethod: () -> String = { + method() + } + assertEquals("C.method", overriddenMethod()) + + val superMethod: () -> String = { + super.method() + } + assertEquals("A.method", superMethod()) + + val overriddenPropertyGetter: () -> String = { + property + } + assertEquals("C.property", overriddenPropertyGetter()) + + val superPropertyGetter: () -> String = { + super.property + } + assertEquals("A.property", superPropertyGetter()) + + val overriddenPropertySetter: () -> Unit = { + property = "" + } + overriddenPropertySetter() + + val superPropertySetter: () -> Unit = { + super.property = "" + } + superPropertySetter() + + assertEquals("C.property;A.property;", state) + + return "OK" + } +} + +class C : B() { + override fun method() = "C.method" + override var property: String + get() = "C.property" + set(value) { state += "C.property;" } +} + +fun box() = C().box() diff --git a/compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/2.kt b/compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/2.kt new file mode 100644 index 00000000000..59cb2e4deb1 --- /dev/null +++ b/compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/2.kt @@ -0,0 +1,13 @@ +package test + +abstract class A { + public var state = "" + + // These implementations should not be called, because they are overridden in C + + protected open fun method(): String = "A.method" + + protected open var property: String + get() = "A.property" + set(value) { state += "A.property;" } +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java index 0d712b66e03..8e55d8224af 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxMultiFileCodegenTestGenerated.java @@ -37,6 +37,12 @@ public class BlackBoxMultiFileCodegenTestGenerated extends AbstractBlackBoxCodeg doTestMultiFile(fileName); } + @TestMetadata("accessorForProtectedInvokeVirtual") + public void testAccessorForProtectedInvokeVirtual() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxMultiFile/accessorForProtectedInvokeVirtual/"); + doTestMultiFile(fileName); + } + public void testAllFilesPresentInBoxMultiFile() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxMultiFile"), Pattern.compile("^([^\\.]+)$"), false); }