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