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 }