001 /*
002 * Copyright 2010-2013 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.application.ApplicationManager;
022 import com.intellij.openapi.components.ServiceManager;
023 import com.intellij.openapi.project.Project;
024 import com.intellij.psi.PsiClass;
025 import com.intellij.psi.PsiElement;
026 import com.intellij.psi.search.GlobalSearchScope;
027 import com.intellij.psi.search.PsiSearchScopeUtil;
028 import com.intellij.util.Function;
029 import com.intellij.util.SmartList;
030 import com.intellij.util.containers.ContainerUtil;
031 import org.jetbrains.annotations.NotNull;
032 import org.jetbrains.annotations.Nullable;
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.NamespaceDescriptor;
039 import org.jetbrains.jet.lang.psi.*;
040 import org.jetbrains.jet.lang.resolve.*;
041 import org.jetbrains.jet.lang.resolve.java.JvmAbi;
042 import org.jetbrains.jet.lang.resolve.name.FqName;
043 import org.jetbrains.jet.util.slicedmap.WritableSlice;
044
045 import java.util.Collection;
046 import java.util.Collections;
047 import java.util.List;
048
049 /**
050 * This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
051 *
052 * Consider the following example:
053 *
054 * KClass.kt refers to JClass.java and vice versa
055 *
056 * To analyze KClass.kt we need to load descriptors from JClass.java, and to do that we need a JetLightClass instance for KClass,
057 * which can only be constructed when the structure of KClass is known.
058 *
059 * To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses
060 */
061 public class CliLightClassGenerationSupport extends LightClassGenerationSupport {
062
063 public static CliLightClassGenerationSupport getInstanceForCli(@NotNull Project project) {
064 return ServiceManager.getService(project, CliLightClassGenerationSupport.class);
065 }
066
067 private BindingTrace trace;
068
069 public CliLightClassGenerationSupport() {
070 }
071
072 @NotNull
073 public BindingTrace getTrace() {
074 if (trace == null) {
075 trace = new BindingTraceContextWithoutScopeRecording();
076 }
077 return trace;
078 }
079
080 public void newBindingTrace() {
081 assert ApplicationManager.getApplication().isUnitTestMode() : "Mutating project service's state shouldn't happen other than in tests";
082 trace = null;
083 }
084
085 @NotNull
086 @Override
087 public LightClassConstructionContext analyzeRelevantCode(@NotNull Collection<JetFile> files) {
088 return new LightClassConstructionContext(getTrace().getBindingContext(), null);
089 }
090
091 @NotNull
092 @Override
093 public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) {
094 ClassDescriptor classDescriptor = getTrace().get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqName);
095 if (classDescriptor != null) {
096 PsiElement element = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
097 if (element != null && PsiSearchScopeUtil.isInScope(searchScope, element)) {
098 return Collections.singletonList((JetClassOrObject) element);
099 }
100 }
101
102 if (JvmAbi.isClassObjectFqName(fqName)) {
103 Collection<JetClassOrObject> parentClasses = findClassOrObjectDeclarations(fqName.parent(), searchScope);
104 return ContainerUtil.mapNotNull(parentClasses,
105 new Function<JetClassOrObject, JetClassOrObject>() {
106 @Override
107 public JetClassOrObject fun(JetClassOrObject classOrObject) {
108 if (classOrObject instanceof JetClass) {
109 JetClass jetClass = (JetClass) classOrObject;
110 JetClassObject classObject = jetClass.getClassObject();
111 if (classObject != null) {
112 return classObject.getObjectDeclaration();
113 }
114 }
115 return null;
116 }
117 });
118 }
119
120 return Collections.emptyList();
121 }
122
123 @NotNull
124 @Override
125 public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
126 NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName);
127 if (namespaceDescriptor != null) {
128 Collection<JetFile> files = getTrace().get(BindingContext.NAMESPACE_TO_FILES, namespaceDescriptor);
129 if (files != null) {
130 return Collections2.filter(files, new Predicate<JetFile>() {
131 @Override
132 public boolean apply(JetFile input) {
133 return PsiSearchScopeUtil.isInScope(searchScope, input);
134 }
135 });
136 }
137 }
138 return Collections.emptyList();
139 }
140
141 @NotNull
142 @Override
143 public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
144 @NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
145 ) {
146 Collection<JetFile> files = findFilesForPackage(packageFqName, searchScope);
147 List<JetClassOrObject> result = new SmartList<JetClassOrObject>();
148 for (JetFile file : files) {
149 for (JetDeclaration declaration : file.getDeclarations()) {
150 if (declaration instanceof JetClassOrObject) {
151 result.add((JetClassOrObject) declaration);
152 }
153 }
154 }
155 return result;
156 }
157
158 @Override
159 public boolean packageExists(
160 @NotNull FqName fqName, @NotNull GlobalSearchScope scope
161 ) {
162 return getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName) != null;
163 }
164
165 @NotNull
166 @Override
167 public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
168 NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqn);
169 if (namespaceDescriptor == null) return Collections.emptyList();
170
171 Collection<DeclarationDescriptor> allDescriptors = namespaceDescriptor.getMemberScope().getAllDescriptors();
172 return ContainerUtil.mapNotNull(allDescriptors, new Function<DeclarationDescriptor, FqName>() {
173 @Override
174 public FqName fun(DeclarationDescriptor input) {
175 if (input instanceof NamespaceDescriptor) {
176 return DescriptorUtils.getFQName(input).toSafe();
177 }
178 return null;
179 }
180 });
181 }
182
183 @Nullable
184 @Override
185 public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
186 return KotlinLightClassForExplicitDeclaration.create(classOrObject.getManager(), classOrObject);
187 }
188
189 public static class BindingTraceContextWithoutScopeRecording extends BindingTraceContext {
190 @Override
191 public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
192 if (slice == BindingContext.RESOLUTION_SCOPE || slice == BindingContext.TYPE_RESOLUTION_SCOPE) {
193 // In the compiler there's no need to keep scopes
194 return;
195 }
196 super.record(slice, key, value);
197 }
198
199 @Override
200 public String toString() {
201 return "Filtering trace for the CLI compiler: does not save scopes";
202 }
203 }
204 }