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(), null);
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 }