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.cli.jvm.compiler;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.collect.Collections2;
021    import com.intellij.openapi.application.ApplicationManager;
022    import com.intellij.openapi.components.ServiceManager;
023    import com.intellij.openapi.project.Project;
024    import com.intellij.psi.PsiClass;
025    import com.intellij.psi.PsiElement;
026    import com.intellij.psi.search.GlobalSearchScope;
027    import com.intellij.psi.search.PsiSearchScopeUtil;
028    import com.intellij.util.Function;
029    import com.intellij.util.SmartList;
030    import com.intellij.util.containers.ContainerUtil;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.annotations.Nullable;
033    import org.jetbrains.jet.asJava.KotlinLightClassForExplicitDeclaration;
034    import org.jetbrains.jet.asJava.LightClassConstructionContext;
035    import org.jetbrains.jet.asJava.LightClassGenerationSupport;
036    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
037    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
038    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
039    import org.jetbrains.jet.lang.psi.*;
040    import org.jetbrains.jet.lang.resolve.*;
041    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
042    import org.jetbrains.jet.lang.resolve.name.FqName;
043    import org.jetbrains.jet.util.slicedmap.WritableSlice;
044    
045    import java.util.Collection;
046    import java.util.Collections;
047    import java.util.List;
048    
049    /**
050     * This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
051     *
052     * Consider the following example:
053     *
054     * KClass.kt refers to JClass.java and vice versa
055     *
056     * To analyze KClass.kt we need to load descriptors from JClass.java, and to do that we need a JetLightClass instance for KClass,
057     * which can only be constructed when the structure of KClass is known.
058     *
059     * To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses
060     */
061    public class CliLightClassGenerationSupport extends LightClassGenerationSupport {
062    
063        public static CliLightClassGenerationSupport getInstanceForCli(@NotNull Project project) {
064            return ServiceManager.getService(project, CliLightClassGenerationSupport.class);
065        }
066    
067        private BindingTrace trace;
068    
069        public CliLightClassGenerationSupport() {
070        }
071    
072        @NotNull
073        public BindingTrace getTrace() {
074            if (trace == null) {
075                trace = new BindingTraceContextWithoutScopeRecording();
076            }
077            return trace;
078        }
079    
080        public void newBindingTrace() {
081            assert ApplicationManager.getApplication().isUnitTestMode() : "Mutating project service's state shouldn't happen other than in tests";
082            trace = null;
083        }
084    
085        @NotNull
086        @Override
087        public LightClassConstructionContext analyzeRelevantCode(@NotNull Collection<JetFile> files) {
088            return new LightClassConstructionContext(getTrace().getBindingContext(), null);
089        }
090    
091        @NotNull
092        @Override
093        public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) {
094            ClassDescriptor classDescriptor = getTrace().get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqName);
095            if (classDescriptor != null) {
096                PsiElement element = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
097                if (element != null && PsiSearchScopeUtil.isInScope(searchScope, element)) {
098                    return Collections.singletonList((JetClassOrObject) element);
099                }
100            }
101    
102            if (JvmAbi.isClassObjectFqName(fqName)) {
103                Collection<JetClassOrObject> parentClasses = findClassOrObjectDeclarations(fqName.parent(), searchScope);
104                return ContainerUtil.mapNotNull(parentClasses,
105                                                new Function<JetClassOrObject, JetClassOrObject>() {
106                                                    @Override
107                                                    public JetClassOrObject fun(JetClassOrObject classOrObject) {
108                                                        if (classOrObject instanceof JetClass) {
109                                                            JetClass jetClass = (JetClass) classOrObject;
110                                                            JetClassObject classObject = jetClass.getClassObject();
111                                                            if (classObject != null) {
112                                                                return classObject.getObjectDeclaration();
113                                                            }
114                                                        }
115                                                        return null;
116                                                    }
117                                                });
118            }
119    
120            return Collections.emptyList();
121        }
122    
123        @NotNull
124        @Override
125        public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
126            NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName);
127            if (namespaceDescriptor != null) {
128                Collection<JetFile> files = getTrace().get(BindingContext.NAMESPACE_TO_FILES, namespaceDescriptor);
129                if (files != null) {
130                    return Collections2.filter(files, new Predicate<JetFile>() {
131                        @Override
132                        public boolean apply(JetFile input) {
133                            return PsiSearchScopeUtil.isInScope(searchScope, input);
134                        }
135                    });
136                }
137            }
138            return Collections.emptyList();
139        }
140    
141        @NotNull
142        @Override
143        public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
144                @NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
145        ) {
146            Collection<JetFile> files = findFilesForPackage(packageFqName, searchScope);
147            List<JetClassOrObject> result = new SmartList<JetClassOrObject>();
148            for (JetFile file : files) {
149                for (JetDeclaration declaration : file.getDeclarations()) {
150                    if (declaration instanceof JetClassOrObject) {
151                        result.add((JetClassOrObject) declaration);
152                    }
153                }
154            }
155            return result;
156        }
157    
158        @Override
159        public boolean packageExists(
160                @NotNull FqName fqName, @NotNull GlobalSearchScope scope
161        ) {
162            return getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName) != null;
163        }
164    
165        @NotNull
166        @Override
167        public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
168            NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqn);
169            if (namespaceDescriptor == null) return Collections.emptyList();
170    
171            Collection<DeclarationDescriptor> allDescriptors = namespaceDescriptor.getMemberScope().getAllDescriptors();
172            return ContainerUtil.mapNotNull(allDescriptors, new Function<DeclarationDescriptor, FqName>() {
173                @Override
174                public FqName fun(DeclarationDescriptor input) {
175                    if (input instanceof NamespaceDescriptor) {
176                        return DescriptorUtils.getFQName(input).toSafe();
177                    }
178                    return null;
179                }
180            });
181        }
182    
183        @Nullable
184        @Override
185        public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
186            return  KotlinLightClassForExplicitDeclaration.create(classOrObject.getManager(), classOrObject);
187        }
188    
189        public static class BindingTraceContextWithoutScopeRecording extends BindingTraceContext {
190            @Override
191            public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
192                if (slice == BindingContext.RESOLUTION_SCOPE || slice == BindingContext.TYPE_RESOLUTION_SCOPE) {
193                    // In the compiler there's no need to keep scopes
194                    return;
195                }
196                super.record(slice, key, value);
197            }
198    
199            @Override
200            public String toString() {
201                return "Filtering trace for the CLI compiler: does not save scopes";
202            }
203        }
204    }