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 017package org.jetbrains.jet.cli.jvm.compiler; 018 019import com.google.common.base.Predicate; 020import com.google.common.collect.Collections2; 021import com.intellij.openapi.application.ApplicationManager; 022import com.intellij.openapi.components.ServiceManager; 023import com.intellij.openapi.project.Project; 024import com.intellij.psi.PsiClass; 025import com.intellij.psi.PsiElement; 026import com.intellij.psi.search.GlobalSearchScope; 027import com.intellij.psi.search.PsiSearchScopeUtil; 028import com.intellij.util.Function; 029import com.intellij.util.SmartList; 030import com.intellij.util.containers.ContainerUtil; 031import org.jetbrains.annotations.NotNull; 032import org.jetbrains.annotations.Nullable; 033import org.jetbrains.jet.asJava.KotlinLightClassForExplicitDeclaration; 034import org.jetbrains.jet.asJava.LightClassConstructionContext; 035import org.jetbrains.jet.asJava.LightClassGenerationSupport; 036import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor; 037import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 038import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 039import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; 040import org.jetbrains.jet.lang.psi.*; 041import org.jetbrains.jet.lang.resolve.*; 042import org.jetbrains.jet.lang.resolve.java.JvmAbi; 043import org.jetbrains.jet.lang.resolve.java.JvmClassName; 044import org.jetbrains.jet.lang.resolve.name.FqName; 045 046import java.util.Collection; 047import java.util.Collections; 048import 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 */ 062public 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}