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.codegen.binding.PsiCodegenPredictor;
037 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
038 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
039 import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
040 import org.jetbrains.jet.lang.psi.*;
041 import org.jetbrains.jet.lang.resolve.*;
042 import org.jetbrains.jet.lang.resolve.java.JvmAbi;
043 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
044 import org.jetbrains.jet.lang.resolve.name.FqName;
045
046 import java.util.Collection;
047 import java.util.Collections;
048 import java.util.List;
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
070 public CliLightClassGenerationSupport() {
071 }
072
073 @NotNull
074 public BindingTrace getTrace() {
075 if (trace == null) {
076 trace = new BindingTraceContext();
077 }
078 return trace;
079 }
080
081 public void newBindingTrace() {
082 assert ApplicationManager.getApplication().isUnitTestMode() : "Mutating project service's state shouldn't happen other than in tests";
083 trace = null;
084 }
085
086 @NotNull
087 @Override
088 public LightClassConstructionContext analyzeRelevantCode(@NotNull Collection<JetFile> files) {
089 return new LightClassConstructionContext(getTrace().getBindingContext(), null);
090 }
091
092 @NotNull
093 @Override
094 public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) {
095 ClassDescriptor classDescriptor = getTrace().get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqName);
096 if (classDescriptor != null) {
097 PsiElement element = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
098 if (element != null && PsiSearchScopeUtil.isInScope(searchScope, element)) {
099 return Collections.singletonList((JetClassOrObject) element);
100 }
101 }
102
103 if (JvmAbi.isClassObjectFqName(fqName)) {
104 Collection<JetClassOrObject> parentClasses = findClassOrObjectDeclarations(fqName.parent(), searchScope);
105 return ContainerUtil.mapNotNull(parentClasses,
106 new Function<JetClassOrObject, JetClassOrObject>() {
107 @Override
108 public JetClassOrObject fun(JetClassOrObject classOrObject) {
109 if (classOrObject instanceof JetClass) {
110 JetClass jetClass = (JetClass) classOrObject;
111 JetClassObject classObject = jetClass.getClassObject();
112 if (classObject != null) {
113 return classObject.getObjectDeclaration();
114 }
115 }
116 return null;
117 }
118 });
119 }
120
121 return Collections.emptyList();
122 }
123
124 @NotNull
125 @Override
126 public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
127 NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName);
128 if (namespaceDescriptor != null) {
129 Collection<JetFile> files = getTrace().get(BindingContext.NAMESPACE_TO_FILES, namespaceDescriptor);
130 if (files != null) {
131 return Collections2.filter(files, new Predicate<JetFile>() {
132 @Override
133 public boolean apply(JetFile input) {
134 return PsiSearchScopeUtil.isInScope(searchScope, input);
135 }
136 });
137 }
138 }
139 return Collections.emptyList();
140 }
141
142 @NotNull
143 @Override
144 public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
145 @NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
146 ) {
147 Collection<JetFile> files = findFilesForPackage(packageFqName, searchScope);
148 List<JetClassOrObject> result = new SmartList<JetClassOrObject>();
149 for (JetFile file : files) {
150 for (JetDeclaration declaration : file.getDeclarations()) {
151 if (declaration instanceof JetClassOrObject) {
152 result.add((JetClassOrObject) declaration);
153 }
154 }
155 }
156 return result;
157 }
158
159 @Override
160 public boolean packageExists(
161 @NotNull FqName fqName, @NotNull GlobalSearchScope scope
162 ) {
163 return getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName) != null;
164 }
165
166 @NotNull
167 @Override
168 public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
169 NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqn);
170 if (namespaceDescriptor == null) return Collections.emptyList();
171
172 Collection<DeclarationDescriptor> allDescriptors = namespaceDescriptor.getMemberScope().getAllDescriptors();
173 return ContainerUtil.mapNotNull(allDescriptors, new Function<DeclarationDescriptor, FqName>() {
174 @Override
175 public FqName fun(DeclarationDescriptor input) {
176 if (input instanceof NamespaceDescriptor) {
177 return DescriptorUtils.getFQName(input).toSafe();
178 }
179 return null;
180 }
181 });
182 }
183
184 @Nullable
185 @Override
186 public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
187 return KotlinLightClassForExplicitDeclaration.create(classOrObject.getManager(), classOrObject);
188 }
189 }