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.openapi.vfs.StandardFileSystems;
020 import com.intellij.openapi.vfs.VfsUtilCore;
021 import com.intellij.openapi.vfs.VirtualFile;
022 import com.intellij.psi.PsiElement;
023 import kotlin.KotlinPackage;
024 import kotlin.jvm.functions.Function1;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
028 import org.jetbrains.kotlin.codegen.context.CodegenContext;
029 import org.jetbrains.kotlin.codegen.context.MethodContext;
030 import org.jetbrains.kotlin.codegen.context.PackageContext;
031 import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
032 import org.jetbrains.kotlin.descriptors.*;
033 import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
034 import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
035 import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
036 import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider;
037 import org.jetbrains.kotlin.psi.JetFile;
038 import org.jetbrains.kotlin.psi.JetFunction;
039 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilPackage;
040 import org.jetbrains.kotlin.resolve.BindingContext;
041 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
042 import org.jetbrains.kotlin.resolve.DescriptorUtils;
043 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
044 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
045 import org.jetbrains.kotlin.types.JetType;
046
047 import java.io.File;
048
049 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
050 import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
051
052 public class JvmCodegenUtil {
053
054 private JvmCodegenUtil() {
055 }
056
057 public static boolean isInterface(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 isInterface(JetType type) {
066 return isInterface(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 PackageContext
082 && isSamePackageInSameModule(context.getParentContext().getContextDescriptor(), containingDeclaration)))
083 && context.getContextKind() != OwnerKind.TRAIT_IMPL);
084 }
085
086 private static boolean isSamePackageInSameModule(
087 @NotNull DeclarationDescriptor callerOwner,
088 @NotNull DeclarationDescriptor calleeOwner
089 ) {
090 if (callerOwner instanceof PackageFragmentDescriptor && calleeOwner instanceof PackageFragmentDescriptor) {
091 PackageFragmentDescriptor callerFragment = (PackageFragmentDescriptor) callerOwner;
092 PackageFragmentDescriptor calleeFragment = (PackageFragmentDescriptor) calleeOwner;
093
094 // backing field should be used directly within same module of same package
095 if (callerFragment == calleeFragment) {
096 return true;
097 }
098 return callerFragment.getFqName().equals(calleeFragment.getFqName())
099 && calleeFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment;
100 }
101 return false;
102 }
103
104 public static boolean isCallInsideSameModuleAsDeclared(
105 @NotNull CallableMemberDescriptor declarationDescriptor,
106 @NotNull CodegenContext context,
107 @Nullable File outDirectory
108 ) {
109 if (context == CodegenContext.STATIC) {
110 return true;
111 }
112 DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
113
114 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
115 if (directMember instanceof DeserializedCallableMemberDescriptor) {
116 return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory);
117 }
118 else {
119 return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
120 }
121 }
122
123 private static boolean isContainedByCompiledPartOfOurModule(
124 @NotNull DeserializedCallableMemberDescriptor descriptor,
125 @Nullable File outDirectory
126 ) {
127 DeclarationDescriptor packageFragment = descriptor.getContainingDeclaration();
128 if (packageFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) {
129 return true;
130 }
131
132 if (outDirectory == null) {
133 return false;
134 }
135
136 if (!(packageFragment instanceof LazyJavaPackageFragment)) {
137 return false;
138 }
139
140 KotlinJvmBinaryClass binaryClass = ((LazyJavaPackageFragment) packageFragment).getMemberScope().getKotlinBinaryClass();
141 if (binaryClass instanceof VirtualFileKotlinClass) {
142 VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile();
143 if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) {
144 File ioFile = VfsUtilCore.virtualToIoFile(file);
145 return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator);
146 }
147 }
148
149 return false;
150 }
151
152 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
153 return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
154 new Function1<DeclarationDescriptor, Boolean>() {
155 @Override
156 public Boolean invoke(DeclarationDescriptor descriptor) {
157 return descriptor instanceof CallableMemberDescriptor &&
158 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
159 }
160 }
161 );
162 }
163
164 public static boolean couldUseDirectAccessToProperty(
165 @NotNull PropertyDescriptor property,
166 boolean forGetter,
167 boolean isDelegated,
168 @NotNull MethodContext context
169 ) {
170 if (JetTypeMapper.isAccessor(property)) return false;
171
172 // Inline functions can't use direct access because a field may not be visible at the call site
173 if (context.isInlineFunction() &&
174 (!Visibilities.isPrivate(property.getVisibility()) || DescriptorUtils.isTopLevelDeclaration(property))) {
175 return false;
176 }
177
178 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
179 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
180
181 // Delegated and extension properties have no backing fields
182 if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
183
184 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
185 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
186
187 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
188
189 // If there's no accessor declared we can use direct access
190 if (accessor == null) return true;
191
192 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
193 if (accessor.hasBody()) return false;
194
195 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
196 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
197 }
198
199 private static boolean isDebuggerContext(@NotNull MethodContext context) {
200 JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
201 return file != null && CodeFragmentUtilPackage.getSuppressDiagnosticsInDebugMode(file);
202 }
203
204 @Nullable
205 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
206 @NotNull ConstructorDescriptor descriptor,
207 @Nullable CalculatedClosure closure
208 ) {
209 //for compilation against sources
210 if (closure != null) {
211 return closure.getCaptureThis();
212 }
213
214 //for compilation against binaries
215 //TODO: It's best to use this code also for compilation against sources
216 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
217 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
218 if (dispatchReceiver != null) {
219 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
220 if (!expectedThisClass.getKind().isSingleton()) {
221 return expectedThisClass;
222 }
223 }
224
225 return null;
226 }
227
228 @NotNull
229 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
230 return descriptor instanceof PropertyAccessorDescriptor
231 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
232 : descriptor;
233 }
234
235 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
236 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
237 return InlineUtil.canBeInlineArgument(declaration) &&
238 InlineUtil.isInlinedArgument((JetFunction) declaration, bindingContext, false);
239 }
240 }