001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.codegen;
018
019 import com.intellij.psi.PsiElement;
020 import com.intellij.psi.PsiFile;
021 import kotlin.collections.CollectionsKt;
022 import kotlin.jvm.functions.Function1;
023 import kotlin.text.StringsKt;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
027 import org.jetbrains.kotlin.codegen.context.CodegenContext;
028 import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile;
029 import org.jetbrains.kotlin.codegen.context.MethodContext;
030 import org.jetbrains.kotlin.codegen.context.RootContext;
031 import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
032 import org.jetbrains.kotlin.descriptors.*;
033 import org.jetbrains.kotlin.load.kotlin.ModuleMapping;
034 import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
035 import org.jetbrains.kotlin.psi.KtFile;
036 import org.jetbrains.kotlin.psi.KtFunction;
037 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
038 import org.jetbrains.kotlin.resolve.BindingContext;
039 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
040 import org.jetbrains.kotlin.resolve.DescriptorUtils;
041 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
042 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
043 import org.jetbrains.kotlin.types.KotlinType;
044
045 import java.io.File;
046
047 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
048 import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
049 import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
050
051 public class JvmCodegenUtil {
052
053 private JvmCodegenUtil() {
054 }
055
056 public static boolean isJvmInterface(DeclarationDescriptor descriptor) {
057 if (descriptor instanceof ClassDescriptor) {
058 ClassKind kind = ((ClassDescriptor) descriptor).getKind();
059 return kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS;
060 }
061 return false;
062 }
063
064 public static boolean isJvmInterface(KotlinType type) {
065 return isJvmInterface(type.getConstructor().getDeclarationDescriptor());
066 }
067
068 public static boolean isConst(@NotNull CalculatedClosure closure) {
069 return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
070 }
071
072 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
073 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
074 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
075
076 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
077
078 return !isFakeOverride && !isDelegate &&
079 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
080 ((context.getParentContext() instanceof FacadePartWithSourceFile)
081 && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
082 && context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
083 }
084
085 private static boolean isWithinSameFile(
086 @Nullable KtFile callerFile,
087 @NotNull CallableMemberDescriptor descriptor
088 ) {
089 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
090 if (containingDeclaration instanceof PackageFragmentDescriptor) {
091 PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
092 PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
093 return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
094
095 }
096 return false;
097 }
098
099 public static boolean isCallInsideSameModuleAsDeclared(
100 @NotNull CallableMemberDescriptor declarationDescriptor,
101 @NotNull CodegenContext context,
102 @Nullable File outDirectory
103 ) {
104 if (context instanceof RootContext) {
105 return true;
106 }
107 DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
108
109 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
110 if (directMember instanceof DeserializedCallableMemberDescriptor) {
111 return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
112 }
113 else {
114 return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
115 }
116 }
117
118 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
119 return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()),
120 new Function1<DeclarationDescriptor, Boolean>() {
121 @Override
122 public Boolean invoke(DeclarationDescriptor descriptor) {
123 return descriptor instanceof CallableMemberDescriptor &&
124 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
125 }
126 }
127 );
128 }
129
130 public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) {
131 return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor);
132 }
133
134 public static boolean couldUseDirectAccessToProperty(
135 @NotNull PropertyDescriptor property,
136 boolean forGetter,
137 boolean isDelegated,
138 @NotNull MethodContext contextBeforeInline
139 ) {
140 if (JetTypeMapper.isAccessor(property)) return false;
141
142 CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
143 // Inline functions can't use direct access because a field may not be visible at the call site
144 if (context.isInlineMethodContext()) {
145 return false;
146 }
147
148 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
149 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
150
151 // Delegated and extension properties have no backing fields
152 if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
153
154 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
155 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
156
157 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
158
159 // If there's no accessor declared we can use direct access
160 if (accessor == null) return true;
161
162 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
163 if (accessor.hasBody()) return false;
164
165 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
166 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
167 }
168
169 private static boolean isDebuggerContext(@NotNull CodegenContext context) {
170 KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
171 return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file);
172 }
173
174 @Nullable
175 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
176 @NotNull ConstructorDescriptor descriptor,
177 @Nullable CalculatedClosure closure
178 ) {
179 //for compilation against sources
180 if (closure != null) {
181 return closure.getCaptureThis();
182 }
183
184 //for compilation against binaries
185 //TODO: It's best to use this code also for compilation against sources
186 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
187 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
188 if (dispatchReceiver != null) {
189 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
190 if (!expectedThisClass.getKind().isSingleton()) {
191 return expectedThisClass;
192 }
193 }
194
195 return null;
196 }
197
198 @NotNull
199 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
200 return descriptor instanceof PropertyAccessorDescriptor
201 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
202 : descriptor;
203 }
204
205 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
206 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
207 return InlineUtil.canBeInlineArgument(declaration) &&
208 InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
209 }
210
211 @NotNull
212 public static String getModuleName(ModuleDescriptor module) {
213 return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
214 }
215
216 @NotNull
217 public static String getMappingFileName(@NotNull String moduleName) {
218 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
219 }
220 }