/* * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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.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.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor; import org.jetbrains.kotlin.load.kotlin.*; import org.jetbrains.kotlin.psi.Call; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFunction; import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; 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.org.objectweb.asm.Opcodes; import java.io.File; import static org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS; import static org.jetbrains.kotlin.descriptors.ClassKind.INTERFACE; import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT; import static org.jetbrains.kotlin.descriptors.Modality.FINAL; import static org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_CALL; import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation; public class JvmCodegenUtil { private JvmCodegenUtil() { } public static boolean isAnnotationOrJvmInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) { return isAnnotationOrJvmInterfaceWithoutDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults()); } private static boolean isAnnotationOrJvmInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) { if (!isJvmInterface(descriptor)) { return false; } if (ANNOTATION_CLASS == ((ClassDescriptor) descriptor).getKind()) return true; if (descriptor instanceof DeserializedClassDescriptor) { SourceElement source = ((DeserializedClassDescriptor) descriptor).getSource(); if (source instanceof KotlinJvmBinarySourceElement) { 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 !isJvm8TargetWithDefaults; } 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) && !isAnnotationOrJvmInterfaceWithoutDefaults(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) { if (!isJvmInterface(descriptor.getContainingDeclaration())) { return false; } if (descriptor instanceof JavaCallableMemberDescriptor) { return descriptor.getModality() == Modality.ABSTRACT; } return !isJvm8InterfaceWithDefaultsMember(descriptor, state); } public static boolean isJvmInterface(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.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty() && !closure.isSuspend(); } private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) { boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE; boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION; DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal(); 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 hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) { return CollectionsKt.any( DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()), descriptor -> descriptor instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT ); } public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) { return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor); } 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 (!isCallInsideSameClassAsDeclared(property, context)) { if (!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; // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) 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(property.getVisibility()) || accessor.getModality() == FINAL; } private static boolean isDebuggerContext(@NotNull CodegenContext context) { KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor()); return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file); } @Nullable public static ClassDescriptor getDispatchReceiverParameterForConstructorCall( @NotNull ConstructorDescriptor descriptor, @Nullable CalculatedClosure closure ) { //for compilation against sources if (closure != null) { return closure.getCaptureThis(); } //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) { return StringsKt.removeSurrounding(module.getName().asString(), "<", ">"); } @NotNull public static String getMappingFileName(@NotNull String moduleName) { return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT; } public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) { if (!(descriptor instanceof JavaPropertyDescriptor)) return false; return 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(); } }