Generate accessor for private companion object

This commit is contained in:
Dmitry Petrov
2018-06-21 17:49:21 +03:00
parent ba3411e7d7
commit d35a92a81d
23 changed files with 583 additions and 7 deletions
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.name.Name
class AccessorForCompanionObjectInstanceFieldDescriptor(
val companionObjectDescriptor: ClassDescriptor,
name: Name
) :
SimpleFunctionDescriptorImpl(
companionObjectDescriptor.containingDeclaration,
null, Annotations.EMPTY,
name,
CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE
) {
init {
initialize(
null, null, emptyList(), emptyList(),
companionObjectDescriptor.defaultType,
Modality.FINAL,
Visibilities.LOCAL
)
}
override fun createSubstitutedCopy(
newOwner: DeclarationDescriptor,
original: FunctionDescriptor?,
kind: CallableMemberDescriptor.Kind,
newName: Name?,
annotations: Annotations,
source: SourceElement
): FunctionDescriptorImpl {
throw UnsupportedOperationException("Accessor for companion object $companionObjectDescriptor should not be substituted")
}
}
@@ -400,6 +400,10 @@ public class AsmUtil {
return NO_FLAG_PACKAGE_PRIVATE;
}
if (memberDescriptor instanceof AccessorForCompanionObjectInstanceFieldDescriptor) {
return NO_FLAG_PACKAGE_PRIVATE;
}
// the following code is only for PRIVATE visibility of member
if (memberDescriptor instanceof ConstructorDescriptor) {
if (isEnumEntry(containingDeclaration)) {
@@ -1756,6 +1756,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (shouldGenerateSingletonAsThisOrOuterFromContext(classDescriptor)) {
return generateThisOrOuterFromContext(classDescriptor, false, false);
}
if (isCompanionObject(classDescriptor) && !couldUseDirectAccessToCompanionObject(classDescriptor, context)) {
return generateAccessorCallForCompanionObject(classDescriptor);
}
if (isObject(classDescriptor)) {
return StackValue.singleton(classDescriptor, typeMapper);
}
@@ -2612,9 +2615,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
else if (isPossiblyUninitializedSingleton(receiverDescriptor) && isInsideSingleton(receiverDescriptor)) {
return generateThisOrOuterFromContext(receiverDescriptor, false, false);
}
else {
else if (couldUseDirectAccessToCompanionObject(receiverDescriptor, context)) {
return StackValue.singleton(receiverDescriptor, typeMapper);
}
else {
return generateAccessorCallForCompanionObject(receiverDescriptor);
}
}
else if (receiverDescriptor instanceof ScriptDescriptor) {
return generateScriptReceiver((ScriptDescriptor) receiverDescriptor);
@@ -2642,6 +2648,41 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
@NotNull
private StackValue generateAccessorCallForCompanionObject(@NotNull ClassDescriptor companionObjectDescriptor) {
DeclarationDescriptor hostClassDescriptor = companionObjectDescriptor.getContainingDeclaration();
assert hostClassDescriptor instanceof ClassDescriptor :
"Containing declaration of the companion object " + companionObjectDescriptor +
": expected a class, actual: " + hostClassDescriptor;
CodegenContext hostClassContext = context;
while (hostClassContext.getContextDescriptor() != hostClassDescriptor) {
hostClassContext = hostClassContext.getParentContext();
assert hostClassContext != null :
"Host class context for " + hostClassDescriptor + " not found in context hierarchy for " + context;
}
hostClassContext.markCompanionObjectDescriptorWithAccessorRequired(companionObjectDescriptor);
Type hostClassType = typeMapper.mapClass((ClassifierDescriptor) hostClassDescriptor);
Type companionObjectType = typeMapper.mapClass(companionObjectDescriptor);
// TODO given that we actually have corresponding AccessorForCompanionObjectInstanceFieldDescriptor,
// it might be a better idea to use general method call generation
return StackValue.operation(
companionObjectType,
companionObjectDescriptor.getDefaultType(),
v -> {
v.invokestatic(
hostClassType.getInternalName(),
getCompanionObjectAccessorName(companionObjectDescriptor),
Type.getMethodDescriptor(companionObjectType),
/* itf */ false
);
return null;
});
}
@NotNull
private StackValue generateExtensionReceiver(@NotNull CallableDescriptor descriptor) {
ReceiverParameterDescriptor parameter = descriptor.getExtensionReceiverParameter();
@@ -22,9 +22,6 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass;
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement;
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping;
import org.jetbrains.kotlin.psi.Call;
@@ -153,6 +150,29 @@ public class JvmCodegenUtil {
return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor);
}
public static boolean couldUseDirectAccessToCompanionObject(
@NotNull ClassDescriptor companionObjectDescriptor,
@NotNull MethodContext contextBeforeInline
) {
if (!Visibilities.isPrivate(companionObjectDescriptor.getVisibility())) {
// Non-private companion object can be directly accessed anywhere it's allowed by the front-end.
return true;
}
CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
if (context.isInlineMethodContext()) {
// Inline method can be called from a nested class.
return false;
}
// Private companion object is directly accessible only from the corresponding class
return context.getContextDescriptor().getContainingDeclaration() == companionObjectDescriptor.getContainingDeclaration();
}
public static String getCompanionObjectAccessorName(@NotNull ClassDescriptor companionObjectDescriptor) {
return "access$" + companionObjectDescriptor.getName();
}
public static boolean couldUseDirectAccessToProperty(
@NotNull PropertyDescriptor property,
boolean forGetter,
@@ -708,6 +708,30 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
generateSyntheticAccessor(accessor);
}
}
AccessorForCompanionObjectInstanceFieldDescriptor accessorForCompanionObjectInstanceFieldDescriptor =
context.getAccessorForCompanionObjectDescriptorIfRequired();
if (accessorForCompanionObjectInstanceFieldDescriptor != null) {
generateSyntheticAccessorForCompanionObject(accessorForCompanionObjectInstanceFieldDescriptor);
}
}
private void generateSyntheticAccessorForCompanionObject(@NotNull AccessorForCompanionObjectInstanceFieldDescriptor accessor) {
ClassDescriptor companionObjectDescriptor = accessor.getCompanionObjectDescriptor();
DeclarationDescriptor hostClassDescriptor = companionObjectDescriptor.getContainingDeclaration();
assert hostClassDescriptor instanceof ClassDescriptor : "Class descriptor expected: " + hostClassDescriptor;
functionCodegen.generateMethod(
Synthetic(null, companionObjectDescriptor),
accessor,
new FunctionGenerationStrategy.CodegenBased(state) {
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
Type companionObjectType = typeMapper.mapClass(companionObjectDescriptor);
StackValue.singleton(companionObjectDescriptor, typeMapper).put(companionObjectType, codegen.v);
codegen.v.areturn(companionObjectType);
}
}
);
}
private void generateSyntheticAccessor(@NotNull AccessorForCallableDescriptor<?> accessorForCallableDescriptor) {
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.JavaVisibilities;
import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
@@ -44,12 +45,12 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
private Map<DeclarationDescriptor, CodegenContext> childContexts;
private Map<AccessorKey, AccessorForCallableDescriptor<?>> accessors;
private Map<AccessorKey, AccessorForPropertyDescriptorFactory> propertyAccessorFactories;
private AccessorForCompanionObjectInstanceFieldDescriptor accessorForCompanionObjectInstanceFieldDescriptor = null;
private static class AccessorKey {
public final DeclarationDescriptor descriptor;
public final ClassDescriptor superCallLabelTarget;
public final AccessorKind accessorKind;
public AccessorKey(
@NotNull DeclarationDescriptor descriptor,
@Nullable ClassDescriptor superCallLabelTarget,
@@ -742,4 +743,30 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
public LocalLookup getEnclosingLocalLookup() {
return enclosingLocalLookup;
}
@NotNull
public AccessorForCompanionObjectInstanceFieldDescriptor markCompanionObjectDescriptorWithAccessorRequired(@NotNull ClassDescriptor companionObjectDescriptor) {
assert DescriptorUtils.isCompanionObject(companionObjectDescriptor) : "Companion object expected: " + companionObjectDescriptor;
assert accessorForCompanionObjectInstanceFieldDescriptor == null
|| accessorForCompanionObjectInstanceFieldDescriptor.getCompanionObjectDescriptor() == companionObjectDescriptor
: "Unexpected companion object descriptor with accessor required: " + companionObjectDescriptor +
"; should be " + accessorForCompanionObjectInstanceFieldDescriptor.getCompanionObjectDescriptor();
if (accessorForCompanionObjectInstanceFieldDescriptor == null) {
accessorForCompanionObjectInstanceFieldDescriptor =
new AccessorForCompanionObjectInstanceFieldDescriptor(
companionObjectDescriptor,
Name.identifier(JvmCodegenUtil.getCompanionObjectAccessorName(companionObjectDescriptor))
);
}
return accessorForCompanionObjectInstanceFieldDescriptor;
}
@Nullable
public AccessorForCompanionObjectInstanceFieldDescriptor getAccessorForCompanionObjectDescriptorIfRequired() {
return accessorForCompanionObjectInstanceFieldDescriptor;
}
}
@@ -948,7 +948,8 @@ public class KotlinTypeMapper {
}
public static boolean isAccessor(@Nullable CallableMemberDescriptor descriptor) {
return descriptor instanceof AccessorForCallableDescriptor<?>;
return descriptor instanceof AccessorForCallableDescriptor<?> ||
descriptor instanceof AccessorForCompanionObjectInstanceFieldDescriptor;
}
public static boolean isStaticAccessor(@Nullable CallableMemberDescriptor descriptor) {