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.context.RootContext;
032 import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
033 import org.jetbrains.kotlin.descriptors.*;
034 import org.jetbrains.kotlin.load.java.JvmAbi;
035 import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
036 import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
037 import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
038 import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
039 import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement;
040 import org.jetbrains.kotlin.load.kotlin.ModuleMapping;
041 import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
042 import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider;
043 import org.jetbrains.kotlin.psi.JetFile;
044 import org.jetbrains.kotlin.psi.JetFunction;
045 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilPackage;
046 import org.jetbrains.kotlin.resolve.BindingContext;
047 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
048 import org.jetbrains.kotlin.resolve.DescriptorUtils;
049 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
050 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
051 import org.jetbrains.kotlin.types.JetType;
052 import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
053
054 import java.io.File;
055
056 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
057 import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
058
059 public class JvmCodegenUtil {
060
061 private JvmCodegenUtil() {
062 }
063
064 public static boolean isInterface(DeclarationDescriptor descriptor) {
065 if (descriptor instanceof ClassDescriptor) {
066 ClassKind kind = ((ClassDescriptor) descriptor).getKind();
067 return kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS;
068 }
069 return false;
070 }
071
072 public static boolean isInterface(JetType type) {
073 return isInterface(type.getConstructor().getDeclarationDescriptor());
074 }
075
076 public static boolean isConst(@NotNull CalculatedClosure closure) {
077 return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
078 }
079
080 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
081 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
082 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
083
084 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
085
086 return !isFakeOverride && !isDelegate &&
087 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
088 (context.getParentContext() instanceof PackageContext
089 && isSamePackageInSameModule(context.getParentContext().getContextDescriptor(), containingDeclaration)))
090 && context.getContextKind() != OwnerKind.TRAIT_IMPL);
091 }
092
093 private static boolean isSamePackageInSameModule(
094 @NotNull DeclarationDescriptor callerOwner,
095 @NotNull DeclarationDescriptor calleeOwner
096 ) {
097 if (callerOwner instanceof PackageFragmentDescriptor && calleeOwner instanceof PackageFragmentDescriptor) {
098 PackageFragmentDescriptor callerFragment = (PackageFragmentDescriptor) callerOwner;
099 PackageFragmentDescriptor calleeFragment = (PackageFragmentDescriptor) calleeOwner;
100
101 // backing field should be used directly within same module of same package
102 if (callerFragment == calleeFragment) {
103 return true;
104 }
105 return callerFragment.getFqName().equals(calleeFragment.getFqName())
106 && calleeFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment;
107 }
108 return false;
109 }
110
111 public static boolean isCallInsideSameModuleAsDeclared(
112 @NotNull CallableMemberDescriptor declarationDescriptor,
113 @NotNull CodegenContext context,
114 @Nullable File outDirectory
115 ) {
116 if (context instanceof RootContext) {
117 return true;
118 }
119 DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
120
121 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
122 if (directMember instanceof DeserializedCallableMemberDescriptor) {
123 return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory);
124 }
125 else {
126 return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
127 }
128 }
129
130 private static boolean isContainedByCompiledPartOfOurModule(
131 @NotNull DeserializedCallableMemberDescriptor descriptor,
132 @Nullable File outDirectory
133 ) {
134 DeclarationDescriptor packageFragment = descriptor.getContainingDeclaration();
135 if (packageFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) {
136 return true;
137 }
138
139 if (outDirectory == null) {
140 return false;
141 }
142
143 if (!(packageFragment instanceof LazyJavaPackageFragment)) {
144 return false;
145 }
146
147 SourceElement source = ((LazyJavaPackageFragment) packageFragment).getSource();
148 if (source instanceof KotlinJvmBinarySourceElement) {
149 KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass();
150 if (binaryClass instanceof VirtualFileKotlinClass) {
151 VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile();
152 if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) {
153 File ioFile = VfsUtilCore.virtualToIoFile(file);
154 return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator);
155 }
156 }
157 }
158
159 return false;
160 }
161
162 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
163 return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
164 new Function1<DeclarationDescriptor, Boolean>() {
165 @Override
166 public Boolean invoke(DeclarationDescriptor descriptor) {
167 return descriptor instanceof CallableMemberDescriptor &&
168 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
169 }
170 }
171 );
172 }
173
174 public static boolean couldUseDirectAccessToProperty(
175 @NotNull PropertyDescriptor property,
176 boolean forGetter,
177 boolean isDelegated,
178 @NotNull MethodContext context
179 ) {
180 if (JetTypeMapper.isAccessor(property)) return false;
181
182 // Inline functions can't use direct access because a field may not be visible at the call site
183 if (context.isInlineFunction() &&
184 (!Visibilities.isPrivate(property.getVisibility()) || DescriptorUtils.isTopLevelDeclaration(property))) {
185 return false;
186 }
187
188 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
189 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
190
191 // Delegated and extension properties have no backing fields
192 if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
193
194 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
195 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
196
197 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
198
199 // If there's no accessor declared we can use direct access
200 if (accessor == null) return true;
201
202 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
203 if (accessor.hasBody()) return false;
204
205 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
206 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
207 }
208
209 private static boolean isDebuggerContext(@NotNull MethodContext context) {
210 JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
211 return file != null && CodeFragmentUtilPackage.getSuppressDiagnosticsInDebugMode(file);
212 }
213
214 @Nullable
215 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
216 @NotNull ConstructorDescriptor descriptor,
217 @Nullable CalculatedClosure closure
218 ) {
219 //for compilation against sources
220 if (closure != null) {
221 return closure.getCaptureThis();
222 }
223
224 //for compilation against binaries
225 //TODO: It's best to use this code also for compilation against sources
226 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
227 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
228 if (dispatchReceiver != null) {
229 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
230 if (!expectedThisClass.getKind().isSingleton()) {
231 return expectedThisClass;
232 }
233 }
234
235 return null;
236 }
237
238 @NotNull
239 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
240 return descriptor instanceof PropertyAccessorDescriptor
241 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
242 : descriptor;
243 }
244
245 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
246 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
247 return InlineUtil.canBeInlineArgument(declaration) &&
248 InlineUtil.isInlinedArgument((JetFunction) declaration, bindingContext, false);
249 }
250
251 public static boolean shouldUseJavaClassForClassLiteral(@NotNull ClassifierDescriptor descriptor) {
252 ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
253 return descriptor instanceof JavaClassDescriptor ||
254 module == module.getBuiltIns().getBuiltInsModule() ||
255 DescriptorUtils.isAnnotationClass(descriptor);
256 }
257
258 @NotNull
259 public static String getModuleName(ModuleDescriptor module) {
260 return KotlinPackage.removeSurrounding(module.getName().asString(), "<", ">");
261 }
262
263 @NotNull
264 public static String getMappingFileName(@NotNull String moduleName) {
265 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
266 }
267
268 public static void writeAbiVersion(@NotNull AnnotationVisitor av) {
269 av.visit(JvmAnnotationNames.VERSION_FIELD_NAME, JvmAbi.VERSION.toArray());
270
271 // TODO: drop after some time
272 av.visit(JvmAnnotationNames.OLD_ABI_VERSION_FIELD_NAME, JvmAbi.VERSION.getMinor());
273 }
274 }