
org.jetbrains.kotlin.codegen.JvmCodegenUtil Maven / Gradle / Ivy
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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 com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import kotlin.collections.CollectionsKt;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile;
import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.context.RootContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.ApiVersion;
import org.jetbrains.kotlin.config.JvmAnalysisFlags;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
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.ModuleVisibilityUtilsKt;
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.kotlin.resolve.source.PsiSourceElement;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.util.OperatorNameConventions;
import java.io.File;
import static org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt.SUSPEND_FUNCTION_CREATE_METHOD_NAME;
import static org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS;
import static org.jetbrains.kotlin.descriptors.ClassKind.INTERFACE;
import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
import static org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_CALL;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
import static org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt.hasJvmDefaultAnnotation;
import static org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt.hasJvmFieldAnnotation;
public class JvmCodegenUtil {
private JvmCodegenUtil() {
}
public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor) {
if (!isJvmInterface(descriptor.getContainingDeclaration())) {
return false;
}
if (descriptor instanceof JavaCallableMemberDescriptor) {
return descriptor.getModality() == Modality.ABSTRACT;
}
return !hasJvmDefaultAnnotation(descriptor);
}
public static boolean isJvmInterface(@Nullable DeclarationDescriptor descriptor) {
if (descriptor instanceof ClassDescriptor) {
ClassKind kind = ((ClassDescriptor) descriptor).getKind();
return kind == INTERFACE || kind == ANNOTATION_CLASS;
}
return false;
}
public static boolean isJvmInterface(KotlinType type) {
return isJvmInterface(type.getConstructor().getDeclarationDescriptor());
}
public static boolean isConst(@NotNull CalculatedClosure closure) {
return closure.getCapturedOuterClassDescriptor() == null &&
closure.getCapturedReceiverFromOuterContext() == null &&
closure.getCaptureVariables().isEmpty() &&
!closure.isSuspend();
}
private static boolean isCallInsideSameClassAsFieldRepresentingProperty(
@NotNull PropertyDescriptor descriptor,
@NotNull CodegenContext context
) {
boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
if (JvmAbi.isPropertyWithBackingFieldInOuterClass(descriptor)) {
// For property with backed field, check if the access is done in the same class containing the backed field and
// not the class that declared the field.
containingDeclaration = containingDeclaration.getContainingDeclaration();
}
return !isFakeOverride && !isDelegate &&
(((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
((context.getParentContext() instanceof FacadePartWithSourceFile)
&& isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
&& context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
}
private static boolean isWithinSameFile(
@Nullable KtFile callerFile,
@NotNull CallableMemberDescriptor descriptor
) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
if (containingDeclaration instanceof PackageFragmentDescriptor) {
PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
}
return false;
}
public static boolean isCallInsideSameModuleAsDeclared(
@NotNull CallableMemberDescriptor declarationDescriptor,
@NotNull CodegenContext context,
@Nullable File outDirectory
) {
if (context instanceof RootContext) {
return true;
}
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
if (directMember instanceof DeserializedCallableMemberDescriptor) {
return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
}
else {
return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
}
}
public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) {
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;
}
if (isDebuggerContext(contextBeforeInline)) {
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,
boolean isDelegated,
@NotNull MethodContext contextBeforeInline,
boolean shouldInlineConstVals
) {
if (shouldInlineConstVals && property.isConst()) return true;
if (KotlinTypeMapper.isAccessor(property)) return false;
CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
// Inline functions can't use direct access because a field may not be visible at the call site
if (context.isInlineMethodContext()) {
return false;
}
if (!isCallInsideSameClassAsFieldRepresentingProperty(property, context)) {
DeclarationDescriptor propertyOwner = property.getContainingDeclaration();
boolean isAnnotationValue;
if (propertyOwner instanceof ClassDescriptor) {
isAnnotationValue = ((ClassDescriptor) propertyOwner).getKind() == ANNOTATION_CLASS;
} else {
isAnnotationValue = false;
}
if (isAnnotationValue || !isDebuggerContext(context)) {
// Unless we are evaluating expression in debugger context, only properties of the same class can be directly accessed
return false;
}
else {
// In debugger we want to access through accessors if they are generated
// Non default accessors must always be generated
for (PropertyAccessorDescriptor accessorDescriptor : property.getAccessors()) {
if (!accessorDescriptor.isDefault()) {
if (forGetter == accessorDescriptor instanceof PropertyGetterDescriptor) {
return false;
}
}
}
// If property overrides something, accessors must be generated too
if (!property.getOverriddenDescriptors().isEmpty()) return false;
}
}
// Delegated and extension properties have no backing fields
if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
// If there's no accessor declared we can use direct access
if (accessor == null) return true;
// If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
if (DescriptorPsiUtilsKt.hasBody(accessor)) return false;
// If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
return Visibilities.isPrivate(accessor.getVisibility()) || accessor.getModality() == FINAL;
}
public static boolean isDebuggerContext(@NotNull CodegenContext context) {
PsiFile file = null;
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
if (contextDescriptor instanceof DeclarationDescriptorWithSource) {
SourceElement sourceElement = ((DeclarationDescriptorWithSource) contextDescriptor).getSource();
if (sourceElement instanceof PsiSourceElement) {
PsiElement psi = ((PsiSourceElement) sourceElement).getPsi();
if (psi != null) {
file = psi.getContainingFile();
}
}
}
return file instanceof KtCodeFragment;
}
@Nullable
public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
@NotNull ConstructorDescriptor descriptor,
@Nullable CalculatedClosure closure
) {
//for compilation against sources
if (closure != null) {
return closure.getCapturedOuterClassDescriptor();
}
//for compilation against binaries
//TODO: It's best to use this code also for compilation against sources
// but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
if (dispatchReceiver != null) {
ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
if (!expectedThisClass.getKind().isSingleton()) {
return expectedThisClass;
}
}
return null;
}
@NotNull
public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
return DescriptorUtils.getDirectMember(descriptor);
}
public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
return InlineUtil.canBeInlineArgument(declaration) &&
InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
}
@NotNull
public static String getModuleName(ModuleDescriptor module) {
Name name = module.getStableName();
if (name == null) {
// Defensive fallback to possibly unstable name, to not fail with exception
return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
} else {
return StringsKt.removeSurrounding(name.asString(), "<", ">");
}
}
@NotNull
public static String getMappingFileName(@NotNull String moduleName) {
return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
}
public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) {
return descriptor instanceof JavaPropertyDescriptor && descriptor.isConst();
}
@Nullable
public static KotlinType getPropertyDelegateType(
@NotNull VariableDescriptorWithAccessors descriptor,
@NotNull BindingContext bindingContext
) {
VariableAccessorDescriptor getter = descriptor.getGetter();
if (getter != null) {
Call call = bindingContext.get(DELEGATED_PROPERTY_CALL, getter);
if (call != null) {
assert call.getExplicitReceiver() != null : "No explicit receiver for call:" + call;
return ((ReceiverValue) call.getExplicitReceiver()).getType();
}
}
return null;
}
public static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) {
return descriptor instanceof LocalVariableDescriptor && ((LocalVariableDescriptor) descriptor).isDelegated();
}
@Nullable
public static ReceiverValue getBoundCallableReferenceReceiver(@NotNull ResolvedCall> resolvedCall) {
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
if (descriptor.getExtensionReceiverParameter() == null && descriptor.getDispatchReceiverParameter() == null) return null;
ReceiverValue dispatchReceiver = resolvedCall.getDispatchReceiver();
ReceiverValue extensionReceiver = resolvedCall.getExtensionReceiver();
assert dispatchReceiver == null || extensionReceiver == null : "Cannot generate reference with both receivers: " + descriptor;
ReceiverValue receiver = dispatchReceiver != null ? dispatchReceiver : extensionReceiver;
if (receiver instanceof TransientReceiver) return null;
return receiver;
}
public static boolean isCompanionObjectInInterfaceNotIntrinsic(@NotNull DeclarationDescriptor companionObject) {
return isCompanionObject(companionObject) &&
isJvmInterface(companionObject.getContainingDeclaration()) &&
!JvmAbi.isMappedIntrinsicCompanionObject((ClassDescriptor) companionObject);
}
public static boolean isNonIntrinsicPrivateCompanionObjectInInterface(@NotNull DeclarationDescriptorWithVisibility companionObject) {
return isCompanionObjectInInterfaceNotIntrinsic(companionObject) &&
Visibilities.isPrivate(companionObject.getVisibility());
}
public static boolean isDeclarationOfBigArityFunctionInvoke(@Nullable DeclarationDescriptor descriptor) {
return descriptor instanceof FunctionInvokeDescriptor && ((FunctionInvokeDescriptor) descriptor).hasBigArity();
}
public static boolean isDeclarationOfBigArityCreateCoroutineMethod(@Nullable DeclarationDescriptor descriptor) {
return descriptor instanceof SimpleFunctionDescriptor && descriptor.getName().asString().equals(SUSPEND_FUNCTION_CREATE_METHOD_NAME) &&
((SimpleFunctionDescriptor) descriptor).getValueParameters().size() >= FunctionInvokeDescriptor.BIG_ARITY - 1 &&
descriptor.getContainingDeclaration() instanceof AnonymousFunctionDescriptor && ((AnonymousFunctionDescriptor) descriptor.getContainingDeclaration()).isSuspend();
}
public static boolean isOverrideOfBigArityFunctionInvoke(@Nullable DeclarationDescriptor descriptor) {
return descriptor instanceof FunctionDescriptor &&
descriptor.getName().equals(OperatorNameConventions.INVOKE) &&
CollectionsKt.any(
DescriptorUtils.getAllOverriddenDeclarations((FunctionDescriptor) descriptor),
JvmCodegenUtil::isDeclarationOfBigArityFunctionInvoke
);
}
@Nullable
public static ClassDescriptor getSuperClass(
@NotNull KtSuperTypeListEntry specifier,
@NotNull GenerationState state,
@NotNull BindingContext bindingContext
) {
ClassDescriptor superClass = CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext);
assert superClass != null || state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES
: "ClassDescriptor should not be null:" + specifier.getText();
return superClass;
}
public static boolean isPolymorphicSignature(@NotNull FunctionDescriptor descriptor) {
return descriptor.getAnnotations().hasAnnotation(new FqName("java.lang.invoke.MethodHandle.PolymorphicSignature"));
}
@NotNull
public static String sanitizeNameIfNeeded(@NotNull String name, @NotNull LanguageVersionSettings languageVersionSettings) {
if (languageVersionSettings.getFlag(JvmAnalysisFlags.getSanitizeParentheses())) {
return name.replace("(", "$_").replace(")", "$_");
}
return name;
}
// Before metadata version 1.1.16 we did not generate equals-impl0 methods correctly.
// The method is still present on all inline classes, but the implementation always throws
// a NullPointerException.
public static boolean typeHasSpecializedInlineClassEquality(@NotNull KotlinType type, @NotNull GenerationState state) {
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
if (!(descriptor instanceof DeserializedClassDescriptor))
return true;
DeserializedClassDescriptor classDescriptor = (DeserializedClassDescriptor) descriptor;
// The Result class is the only inline class in the standard library without special rules for equality.
// We only call Result.equals-impl0 if we are compiling for Kotlin 1.4 or later. Otherwise, the code
// might well be running against an older version of the standard library.
if (DescriptorUtils.getFqNameSafe(classDescriptor).equals(DescriptorUtils.RESULT_FQ_NAME)) {
return state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_4) >= 0;
} else {
return ((DeserializedClassDescriptor) descriptor).getMetadataVersion().isAtLeast(1, 1, 16);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy