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.KotlinTypeMapper;
032 import org.jetbrains.kotlin.descriptors.*;
033 import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
034 import org.jetbrains.kotlin.load.kotlin.ModuleMapping;
035 import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
036 import org.jetbrains.kotlin.psi.KtFile;
037 import org.jetbrains.kotlin.psi.KtFunction;
038 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
039 import org.jetbrains.kotlin.resolve.BindingContext;
040 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
041 import org.jetbrains.kotlin.resolve.DescriptorUtils;
042 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
043 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
044 import org.jetbrains.kotlin.types.KotlinType;
045
046 import java.io.File;
047
048 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
049 import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
050 import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
051
052 public class JvmCodegenUtil {
053
054 private JvmCodegenUtil() {
055 }
056
057 public static boolean isJvmInterface(DeclarationDescriptor descriptor) {
058 if (descriptor instanceof ClassDescriptor) {
059 ClassKind kind = ((ClassDescriptor) descriptor).getKind();
060 return kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS;
061 }
062 return false;
063 }
064
065 public static boolean isJvmInterface(KotlinType type) {
066 return isJvmInterface(type.getConstructor().getDeclarationDescriptor());
067 }
068
069 public static boolean isConst(@NotNull CalculatedClosure closure) {
070 return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
071 }
072
073 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
074 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
075 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
076
077 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
078
079 return !isFakeOverride && !isDelegate &&
080 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
081 ((context.getParentContext() instanceof FacadePartWithSourceFile)
082 && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
083 && context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
084 }
085
086 private static boolean isWithinSameFile(
087 @Nullable KtFile callerFile,
088 @NotNull CallableMemberDescriptor descriptor
089 ) {
090 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
091 if (containingDeclaration instanceof PackageFragmentDescriptor) {
092 PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
093 PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
094 return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
095
096 }
097 return false;
098 }
099
100 public static boolean isCallInsideSameModuleAsDeclared(
101 @NotNull CallableMemberDescriptor declarationDescriptor,
102 @NotNull CodegenContext context,
103 @Nullable File outDirectory
104 ) {
105 if (context instanceof RootContext) {
106 return true;
107 }
108 DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
109
110 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
111 if (directMember instanceof DeserializedCallableMemberDescriptor) {
112 return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
113 }
114 else {
115 return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
116 }
117 }
118
119 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
120 return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()),
121 new Function1<DeclarationDescriptor, Boolean>() {
122 @Override
123 public Boolean invoke(DeclarationDescriptor descriptor) {
124 return descriptor instanceof CallableMemberDescriptor &&
125 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
126 }
127 }
128 );
129 }
130
131 public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) {
132 return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor);
133 }
134
135 public static boolean couldUseDirectAccessToProperty(
136 @NotNull PropertyDescriptor property,
137 boolean forGetter,
138 boolean isDelegated,
139 @NotNull MethodContext contextBeforeInline
140 ) {
141 if (KotlinTypeMapper.isAccessor(property)) return false;
142
143 CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
144 // Inline functions can't use direct access because a field may not be visible at the call site
145 if (context.isInlineMethodContext()) {
146 return false;
147 }
148
149 if (!isCallInsideSameClassAsDeclared(property, context)) {
150 if (!isDebuggerContext(context)) {
151 // Unless we are evaluating expression in debugger context, only properties of the same class can be directly accessed
152 return false;
153 }
154 else {
155 // In debugger we want to access through accessors if they are generated
156
157 // Non default accessors must always be generated
158 for (PropertyAccessorDescriptor accessorDescriptor : property.getAccessors()) {
159 if (!accessorDescriptor.isDefault()) {
160 if (forGetter == accessorDescriptor instanceof PropertyGetterDescriptor) {
161 return false;
162 }
163 }
164 }
165
166 // If property overrides something, accessors must be generated too
167 if (!property.getOverriddenDescriptors().isEmpty()) return false;
168 }
169 }
170
171 // Delegated and extension properties have no backing fields
172 if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
173
174 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
175 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
176
177 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
178
179 // If there's no accessor declared we can use direct access
180 if (accessor == null) return true;
181
182 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
183 if (DescriptorPsiUtilsKt.hasBody(accessor)) return false;
184
185 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
186 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
187 }
188
189 private static boolean isDebuggerContext(@NotNull CodegenContext context) {
190 KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
191 return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file);
192 }
193
194 @Nullable
195 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
196 @NotNull ConstructorDescriptor descriptor,
197 @Nullable CalculatedClosure closure
198 ) {
199 //for compilation against sources
200 if (closure != null) {
201 return closure.getCaptureThis();
202 }
203
204 //for compilation against binaries
205 //TODO: It's best to use this code also for compilation against sources
206 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
207 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
208 if (dispatchReceiver != null) {
209 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
210 if (!expectedThisClass.getKind().isSingleton()) {
211 return expectedThisClass;
212 }
213 }
214
215 return null;
216 }
217
218 @NotNull
219 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
220 return descriptor instanceof PropertyAccessorDescriptor
221 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
222 : descriptor;
223 }
224
225 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
226 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
227 return InlineUtil.canBeInlineArgument(declaration) &&
228 InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
229 }
230
231 @NotNull
232 public static String getModuleName(ModuleDescriptor module) {
233 return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
234 }
235
236 @NotNull
237 public static String getMappingFileName(@NotNull String moduleName) {
238 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
239 }
240
241 public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) {
242 if (!(descriptor instanceof JavaPropertyDescriptor)) return false;
243 return descriptor.isConst();
244 }
245 }