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 com.intellij.util.containers.Stack;
023    import kotlin.Function1;
024    import kotlin.KotlinPackage;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
028    import org.jetbrains.jet.codegen.context.CodegenContext;
029    import org.jetbrains.jet.codegen.context.MethodContext;
030    import org.jetbrains.jet.codegen.context.PackageContext;
031    import org.jetbrains.jet.codegen.state.JetTypeMapper;
032    import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor;
033    import org.jetbrains.jet.lang.descriptors.*;
034    import org.jetbrains.jet.lang.psi.JetFile;
035    import org.jetbrains.jet.lang.psi.codeFragmentUtil.CodeFragmentUtilPackage;
036    import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils;
037    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
038    import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
039    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPackageFragmentDescriptor;
040    import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyPackageFragmentScopeForJavaPackage;
041    import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass;
042    import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileKotlinClass;
043    import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalPackageFragmentProvider;
044    import org.jetbrains.jet.lang.resolve.name.Name;
045    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
046    import org.jetbrains.jet.lang.types.JetType;
047    import org.jetbrains.jet.lang.types.TypeUtils;
048    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
049    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
050    
051    import java.io.File;
052    import java.util.Arrays;
053    import java.util.Collection;
054    import java.util.List;
055    
056    import static org.jetbrains.jet.lang.descriptors.Modality.ABSTRACT;
057    import static org.jetbrains.jet.lang.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.TRAIT || 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        public static <T> T peekFromStack(Stack<T> stack) {
081            return stack.empty() ? null : stack.peek();
082        }
083    
084        @Nullable
085        public static FunctionDescriptor getDeclaredFunctionByRawSignature(
086                @NotNull ClassDescriptor owner,
087                @NotNull Name name,
088                @NotNull ClassifierDescriptor returnedClassifier,
089                @NotNull ClassifierDescriptor... valueParameterClassifiers
090        ) {
091            Collection<FunctionDescriptor> functions = owner.getDefaultType().getMemberScope().getFunctions(name);
092            for (FunctionDescriptor function : functions) {
093                if (!CallResolverUtil.isOrOverridesSynthesized(function)
094                    && function.getTypeParameters().isEmpty()
095                    && valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClassifiers))
096                    && rawTypeMatches(function.getReturnType(), returnedClassifier)) {
097                    return function;
098                }
099            }
100            return null;
101        }
102    
103        private static boolean valueParameterClassesMatch(
104                @NotNull List<ValueParameterDescriptor> parameters,
105                @NotNull List<ClassifierDescriptor> classifiers) {
106            if (parameters.size() != classifiers.size()) return false;
107            for (int i = 0; i < parameters.size(); i++) {
108                ValueParameterDescriptor parameterDescriptor = parameters.get(i);
109                ClassifierDescriptor classDescriptor = classifiers.get(i);
110                if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) {
111                    return false;
112                }
113            }
114            return true;
115        }
116    
117        private static boolean rawTypeMatches(JetType type, ClassifierDescriptor classifier) {
118            return type.getConstructor().getDeclarationDescriptor().getOriginal() == classifier.getOriginal();
119        }
120    
121        private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
122            boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
123            boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
124    
125            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
126    
127            return !isFakeOverride && !isDelegate &&
128                   (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
129                     (context.getParentContext() instanceof PackageContext
130                      && isSamePackageInSameModule(context.getParentContext().getContextDescriptor(), containingDeclaration)))
131                    && context.getContextKind() != OwnerKind.TRAIT_IMPL);
132        }
133    
134        private static boolean isSamePackageInSameModule(
135                @NotNull DeclarationDescriptor callerOwner,
136                @NotNull DeclarationDescriptor calleeOwner
137        ) {
138            if (callerOwner instanceof PackageFragmentDescriptor && calleeOwner instanceof PackageFragmentDescriptor) {
139                PackageFragmentDescriptor callerFragment = (PackageFragmentDescriptor) callerOwner;
140                PackageFragmentDescriptor calleeFragment = (PackageFragmentDescriptor) calleeOwner;
141    
142                // backing field should be used directly within same module of same package
143                if (callerFragment == calleeFragment) {
144                    return true;
145                }
146                return callerFragment.getFqName().equals(calleeFragment.getFqName())
147                       && calleeFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment;
148            }
149            return false;
150        }
151    
152        public static boolean isCallInsideSameModuleAsDeclared(
153                @NotNull CallableMemberDescriptor declarationDescriptor,
154                @NotNull CodegenContext context,
155                @Nullable File outDirectory
156        ) {
157            if (context == CodegenContext.STATIC) {
158                return true;
159            }
160            DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
161    
162            CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
163            if (directMember instanceof DeserializedCallableMemberDescriptor) {
164                return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory);
165            }
166            else {
167                return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
168            }
169        }
170    
171        private static boolean isContainedByCompiledPartOfOurModule(
172                @NotNull DeserializedCallableMemberDescriptor descriptor,
173                @Nullable File outDirectory
174        ) {
175            if (descriptor.getContainingDeclaration() instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) {
176                return true;
177            }
178    
179            if (outDirectory == null) {
180                return false;
181            }
182    
183            if (!(descriptor.getContainingDeclaration() instanceof JavaPackageFragmentDescriptor)) {
184                return false;
185            }
186            JavaPackageFragmentDescriptor packageFragment = (JavaPackageFragmentDescriptor) descriptor.getContainingDeclaration();
187            JetScope packageScope = packageFragment.getMemberScope();
188            if (!(packageScope instanceof LazyPackageFragmentScopeForJavaPackage)) {
189                return false;
190            }
191            KotlinJvmBinaryClass binaryClass = ((LazyPackageFragmentScopeForJavaPackage) packageScope).getKotlinBinaryClass();
192    
193            if (binaryClass instanceof VirtualFileKotlinClass) {
194                VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile();
195                if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) {
196                    File ioFile = VfsUtilCore.virtualToIoFile(file);
197                    return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator);
198                }
199            }
200            return false;
201        }
202    
203        public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
204            return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
205                                     new Function1<DeclarationDescriptor, Boolean>() {
206                                         @Override
207                                         public Boolean invoke(DeclarationDescriptor descriptor) {
208                                             return descriptor instanceof CallableMemberDescriptor &&
209                                                    ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
210                                         }
211                                     }
212            );
213        }
214    
215        public static boolean couldUseDirectAccessToProperty(
216                @NotNull PropertyDescriptor property,
217                boolean forGetter,
218                boolean isDelegated,
219                @NotNull MethodContext context
220        ) {
221            if (JetTypeMapper.isAccessor(property)) return false;
222    
223            // Inline functions can't use direct access because a field may not be visible at the call site
224            if (context.isInlineFunction()) return false;
225    
226            // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
227            if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
228    
229            // Delegated and extension properties have no backing fields
230            if (isDelegated || property.getReceiverParameter() != null) return false;
231    
232            // Class object properties cannot be accessed directly because their backing fields are stored in the containing class
233            if (DescriptorUtils.isClassObject(property.getContainingDeclaration())) return false;
234    
235            PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
236    
237            // If there's no accessor declared we can use direct access
238            if (accessor == null) return true;
239    
240            // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
241            if (accessor.hasBody()) return false;
242    
243            // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
244            return property.getVisibility() == Visibilities.PRIVATE || accessor.getModality() == FINAL;
245        }
246    
247        private static boolean isDebuggerContext(@NotNull MethodContext context) {
248            JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
249            return file != null && CodeFragmentUtilPackage.getSkipVisibilityCheck(file);
250        }
251    
252        @NotNull
253        public static ImplementationBodyCodegen getParentBodyCodegen(@Nullable MemberCodegen<?> classBodyCodegen) {
254            assert classBodyCodegen != null && classBodyCodegen.getParentCodegen() instanceof ImplementationBodyCodegen
255                    : "Class object should have appropriate parent BodyCodegen";
256    
257            return (ImplementationBodyCodegen) classBodyCodegen.getParentCodegen();
258        }
259    
260        @Nullable
261        public static ClassDescriptor getExpectedThisObjectForConstructorCall(
262                @NotNull ConstructorDescriptor descriptor,
263                @Nullable CalculatedClosure closure
264        ) {
265            //for compilation against sources
266            if (closure != null) {
267                return closure.getCaptureThis();
268            }
269    
270            //for compilation against binaries
271            //TODO: It's best to use this code also for compilation against sources
272            // but sometimes structures that have expectedThisObject (bug?) mapped to static classes
273            ReceiverParameterDescriptor expectedThisObject = descriptor.getExpectedThisObject();
274            if (expectedThisObject != null) {
275                ClassDescriptor expectedThisClass = (ClassDescriptor) expectedThisObject.getContainingDeclaration();
276                if (!expectedThisClass.getKind().isSingleton()) {
277                    return expectedThisClass;
278                }
279            }
280    
281            return null;
282        }
283    
284        public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
285            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
286            JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
287            return "valueOf".equals(functionDescriptor.getName().asString())
288                   && methodTypeParameters.size() == 1
289                   && JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
290        }
291    
292        public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
293            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
294            return "values".equals(functionDescriptor.getName().asString())
295                   && methodTypeParameters.isEmpty();
296        }
297    
298        @NotNull
299        public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
300            return descriptor instanceof PropertyAccessorDescriptor
301                   ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
302                   : descriptor;
303        }
304    }