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