001 /*
002 * Copyright 2010-2015 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.kotlin.cli.jvm.compiler;
018
019 import com.google.common.base.Predicate;
020 import com.google.common.collect.Collections2;
021 import com.intellij.openapi.project.Project;
022 import com.intellij.psi.PsiClass;
023 import com.intellij.psi.PsiElement;
024 import com.intellij.psi.PsiManager;
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.kotlin.asJava.KotlinLightClassForExplicitDeclaration;
034 import org.jetbrains.kotlin.asJava.KotlinLightClassForPackage;
035 import org.jetbrains.kotlin.asJava.LightClassConstructionContext;
036 import org.jetbrains.kotlin.asJava.LightClassGenerationSupport;
037 import org.jetbrains.kotlin.descriptors.ClassDescriptor;
038 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
039 import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
040 import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
041 import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
042 import org.jetbrains.kotlin.name.FqName;
043 import org.jetbrains.kotlin.psi.*;
044 import org.jetbrains.kotlin.resolve.*;
045 import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer;
046 import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils;
047 import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
048 import org.jetbrains.kotlin.resolve.scopes.JetScope;
049 import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
050 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
051 import org.jetbrains.kotlin.utils.UtilsPackage;
052
053 import java.util.Collection;
054 import java.util.Collections;
055 import java.util.List;
056
057 /**
058 * This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
059 *
060 * Consider the following example:
061 *
062 * KClass.kt refers to JClass.java and vice versa
063 *
064 * To analyze KClass.kt we need to load descriptors from JClass.java, and to do that we need a JetLightClass instance for KClass,
065 * which can only be constructed when the structure of KClass is known.
066 *
067 * To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses
068 */
069 public class CliLightClassGenerationSupport extends LightClassGenerationSupport implements CodeAnalyzerInitializer {
070 private final PsiManager psiManager;
071 private BindingContext bindingContext = null;
072 private ModuleDescriptor module = null;
073
074 public CliLightClassGenerationSupport(@NotNull Project project) {
075 this.psiManager = PsiManager.getInstance(project);
076 }
077
078 @Override
079 public void initialize(@NotNull BindingTrace trace, @NotNull ModuleDescriptor module, @Nullable KotlinCodeAnalyzer analyzer) {
080 this.bindingContext = trace.getBindingContext();
081 this.module = module;
082
083 if (!(trace instanceof CliBindingTrace)) {
084 throw new IllegalArgumentException("Shared trace is expected to be subclass of " + CliBindingTrace.class.getSimpleName() + " class");
085 }
086
087 ((CliBindingTrace) trace).setKotlinCodeAnalyzer(analyzer);
088 }
089
090 @NotNull
091 private BindingContext getBindingContext() {
092 assert bindingContext != null : "Call initialize() first";
093 return bindingContext;
094 }
095
096 @NotNull
097 private ModuleDescriptor getModule() {
098 assert module != null : "Call initialize() first";
099 return module;
100 }
101
102 @NotNull
103 @Override
104 public LightClassConstructionContext getContextForPackage(@NotNull Collection<JetFile> files) {
105 return getContext();
106 }
107
108 @NotNull
109 @Override
110 public LightClassConstructionContext getContextForClassOrObject(@NotNull JetClassOrObject classOrObject) {
111 return getContext();
112 }
113
114 @NotNull
115 private LightClassConstructionContext getContext() {
116 return new LightClassConstructionContext(bindingContext, getModule());
117 }
118
119 @NotNull
120 @Override
121 public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
122 Collection<ClassDescriptor> classDescriptors = ResolveSessionUtils.getClassDescriptorsByFqName(getModule(), fqName);
123
124 return ContainerUtil.mapNotNull(classDescriptors, new Function<ClassDescriptor, JetClassOrObject>() {
125 @Override
126 public JetClassOrObject fun(ClassDescriptor descriptor) {
127 PsiElement element = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor);
128 if (element instanceof JetClassOrObject && PsiSearchScopeUtil.isInScope(searchScope, element)) {
129 return (JetClassOrObject) element;
130 }
131 return null;
132 }
133 });
134 }
135
136 @NotNull
137 @Override
138 public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
139 Collection<JetFile> files = getBindingContext().get(BindingContext.PACKAGE_TO_FILES, fqName);
140 if (files != null) {
141 return Collections2.filter(files, new Predicate<JetFile>() {
142 @Override
143 public boolean apply(JetFile input) {
144 return PsiSearchScopeUtil.isInScope(searchScope, input);
145 }
146 });
147 }
148 return Collections.emptyList();
149 }
150
151 @NotNull
152 @Override
153 public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
154 @NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
155 ) {
156 Collection<JetFile> files = findFilesForPackage(packageFqName, searchScope);
157 List<JetClassOrObject> result = new SmartList<JetClassOrObject>();
158 for (JetFile file : files) {
159 for (JetDeclaration declaration : file.getDeclarations()) {
160 if (declaration instanceof JetClassOrObject) {
161 result.add((JetClassOrObject) declaration);
162 }
163 }
164 }
165 return result;
166 }
167
168 @Override
169 public boolean packageExists(@NotNull FqName fqName, @NotNull GlobalSearchScope scope) {
170 return getModule().getPackage(fqName) != null;
171 }
172
173 @NotNull
174 @Override
175 public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
176 PackageViewDescriptor packageView = getModule().getPackage(fqn);
177 if (packageView == null) return Collections.emptyList();
178
179 Collection<DeclarationDescriptor> members = packageView.getMemberScope().getDescriptors(DescriptorKindFilter.PACKAGES, JetScope.ALL_NAME_FILTER);
180 return ContainerUtil.mapNotNull(members, new Function<DeclarationDescriptor, FqName>() {
181 @Override
182 public FqName fun(DeclarationDescriptor member) {
183 if (member instanceof PackageViewDescriptor) {
184 return ((PackageViewDescriptor) member).getFqName();
185 }
186 return null;
187 }
188 });
189 }
190
191 @Nullable
192 @Override
193 public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
194 return KotlinLightClassForExplicitDeclaration.create(psiManager, classOrObject);
195 }
196
197 @NotNull
198 @Override
199 public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
200 Collection<JetFile> filesInPackage = findFilesForPackage(packageFqName, scope);
201
202 List<JetFile> filesWithCallables = PackagePartClassUtils.getPackageFilesWithCallables(filesInPackage);
203 if (filesWithCallables.isEmpty()) return Collections.emptyList();
204
205 //noinspection RedundantTypeArguments
206 return UtilsPackage.<PsiClass>emptyOrSingletonList(KotlinLightClassForPackage.Factory.create(psiManager, packageFqName, scope, filesWithCallables));
207 }
208
209 @NotNull
210 @Override
211 public BindingTraceContext createTrace() {
212 return new NoScopeRecordCliBindingTrace();
213 }
214
215 public static class NoScopeRecordCliBindingTrace extends CliBindingTrace {
216 @Override
217 public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
218 if (slice == BindingContext.RESOLUTION_SCOPE || slice == BindingContext.TYPE_RESOLUTION_SCOPE) {
219 // In the compiler there's no need to keep scopes
220 return;
221 }
222 super.record(slice, key, value);
223 }
224
225 @Override
226 public String toString() {
227 return NoScopeRecordCliBindingTrace.class.getName();
228 }
229 }
230
231 public static class CliBindingTrace extends BindingTraceContext {
232 private KotlinCodeAnalyzer kotlinCodeAnalyzer;
233
234 @TestOnly
235 public CliBindingTrace() {
236 }
237
238 @Override
239 public String toString() {
240 return CliBindingTrace.class.getName();
241 }
242
243 public void setKotlinCodeAnalyzer(KotlinCodeAnalyzer kotlinCodeAnalyzer) {
244 this.kotlinCodeAnalyzer = kotlinCodeAnalyzer;
245 }
246
247 @Override
248 public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
249 V value = super.get(slice, key);
250
251 if (value == null) {
252 if (BindingContext.FUNCTION == slice || BindingContext.VARIABLE == slice) {
253 if (key instanceof JetDeclaration) {
254 JetDeclaration jetDeclaration = (JetDeclaration) key;
255 if (!JetPsiUtil.isLocal(jetDeclaration)) {
256 kotlinCodeAnalyzer.resolveToDescriptor(jetDeclaration);
257 }
258 }
259 }
260
261 return super.get(slice, key);
262 }
263
264 return value;
265 }
266 }
267 }