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