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