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.asJava; 018 019 import com.google.common.collect.Lists; 020 import com.intellij.openapi.diagnostic.Logger; 021 import com.intellij.openapi.progress.ProcessCanceledException; 022 import com.intellij.openapi.project.Project; 023 import com.intellij.openapi.util.SystemInfo; 024 import com.intellij.openapi.vfs.VirtualFile; 025 import com.intellij.psi.ClassFileViewProvider; 026 import com.intellij.psi.PsiElement; 027 import com.intellij.psi.PsiFile; 028 import com.intellij.psi.PsiManager; 029 import com.intellij.psi.impl.PsiManagerImpl; 030 import com.intellij.psi.impl.compiled.ClsFileImpl; 031 import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; 032 import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; 033 import com.intellij.psi.search.GlobalSearchScope; 034 import com.intellij.psi.stubs.PsiClassHolderFileStub; 035 import com.intellij.psi.stubs.StubElement; 036 import com.intellij.psi.util.CachedValueProvider; 037 import com.intellij.psi.util.PsiModificationTracker; 038 import com.intellij.psi.util.PsiTreeUtil; 039 import com.intellij.testFramework.LightVirtualFile; 040 import com.intellij.util.containers.ContainerUtil; 041 import com.intellij.util.containers.Stack; 042 import org.jetbrains.annotations.NotNull; 043 import org.jetbrains.annotations.Nullable; 044 import org.jetbrains.jet.codegen.CompilationErrorHandler; 045 import org.jetbrains.jet.codegen.KotlinCodegenFacade; 046 import org.jetbrains.jet.codegen.PackageCodegen; 047 import org.jetbrains.jet.codegen.binding.CodegenBinding; 048 import org.jetbrains.jet.codegen.state.GenerationState; 049 import org.jetbrains.jet.codegen.state.Progress; 050 import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 051 import org.jetbrains.jet.lang.psi.JetClassOrObject; 052 import org.jetbrains.jet.lang.psi.JetFile; 053 import org.jetbrains.jet.lang.psi.JetPsiUtil; 054 import org.jetbrains.jet.lang.resolve.BindingContext; 055 import org.jetbrains.jet.lang.resolve.BindingTraceContext; 056 import org.jetbrains.jet.lang.resolve.Diagnostics; 057 import org.jetbrains.jet.lang.resolve.name.FqName; 058 059 import java.util.Collection; 060 import java.util.Collections; 061 import java.util.Map; 062 063 import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration; 064 065 public class KotlinJavaFileStubProvider<T extends WithFileStubAndExtraDiagnostics> implements CachedValueProvider<T> { 066 067 @NotNull 068 public static KotlinJavaFileStubProvider<KotlinPackageLightClassData> createForPackageClass( 069 @NotNull final Project project, 070 @NotNull final FqName packageFqName, 071 @NotNull final GlobalSearchScope searchScope 072 ) { 073 return new KotlinJavaFileStubProvider<KotlinPackageLightClassData>( 074 project, 075 false, 076 new StubGenerationStrategy<KotlinPackageLightClassData>() { 077 @NotNull 078 @Override 079 public LightClassConstructionContext getContext(@NotNull Collection<JetFile> files) { 080 return LightClassGenerationSupport.getInstance(project).getContextForPackage(files); 081 } 082 083 @NotNull 084 @Override 085 public Collection<JetFile> getFiles() { 086 // Don't memoize this, it can be called again after an out-of-code-block modification occurs, 087 // and the set of files changes 088 return LightClassGenerationSupport.getInstance(project).findFilesForPackage(packageFqName, searchScope); 089 } 090 091 @NotNull 092 @Override 093 public KotlinPackageLightClassData createLightClassData( 094 PsiJavaFileStub javaFileStub, 095 BindingContext bindingContext, 096 Diagnostics extraDiagnostics 097 ) { 098 return new KotlinPackageLightClassData(javaFileStub, extraDiagnostics); 099 } 100 101 @NotNull 102 @Override 103 public FqName getPackageFqName() { 104 return packageFqName; 105 } 106 107 @Override 108 public GenerationState.GenerateClassFilter getGenerateClassFilter() { 109 return new GenerationState.GenerateClassFilter() { 110 @Override 111 public boolean shouldProcess(JetClassOrObject classOrObject) { 112 // Top-level classes and such should not be generated for performance reasons. 113 // Local classes in top-level functions must still be generated 114 return JetPsiUtil.isLocal(classOrObject); 115 } 116 }; 117 } 118 119 @Override 120 public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) { 121 PackageCodegen codegen = state.getFactory().forPackage(packageFqName, files); 122 codegen.generate(CompilationErrorHandler.THROW_EXCEPTION); 123 state.getFactory().asList(); 124 } 125 126 @Override 127 public String toString() { 128 return StubGenerationStrategy.class.getName() + " for package class"; 129 } 130 } 131 ); 132 } 133 134 @NotNull 135 public static KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData> createForDeclaredClass(@NotNull final JetClassOrObject classOrObject) { 136 return new KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData>( 137 classOrObject.getProject(), 138 classOrObject.isLocal(), 139 new StubGenerationStrategy<OutermostKotlinClassLightClassData>() { 140 private JetFile getFile() { 141 return classOrObject.getContainingJetFile(); 142 } 143 144 @NotNull 145 @Override 146 public LightClassConstructionContext getContext(@NotNull Collection<JetFile> files) { 147 return LightClassGenerationSupport.getInstance(classOrObject.getProject()).getContextForClassOrObject(classOrObject); 148 } 149 150 @NotNull 151 @Override 152 public OutermostKotlinClassLightClassData createLightClassData( 153 PsiJavaFileStub javaFileStub, 154 BindingContext bindingContext, 155 Diagnostics extraDiagnostics 156 ) { 157 ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject); 158 if (classDescriptor == null) { 159 return new OutermostKotlinClassLightClassData( 160 javaFileStub, extraDiagnostics, 161 "", classOrObject, null, Collections.<JetClassOrObject, InnerKotlinClassLightClassData>emptyMap() 162 ); 163 } 164 165 String jvmInternalName = CodegenBinding.getJvmInternalName(bindingContext, classDescriptor); 166 Collection<ClassDescriptor> allInnerClasses = CodegenBinding.getAllInnerClasses(bindingContext, classDescriptor); 167 168 Map<JetClassOrObject, InnerKotlinClassLightClassData> innerClassesMap = ContainerUtil.newHashMap(); 169 for (ClassDescriptor innerClassDescriptor : allInnerClasses) { 170 JetClassOrObject innerClass = (JetClassOrObject) descriptorToDeclaration(innerClassDescriptor); 171 if (innerClass == null) continue; 172 173 InnerKotlinClassLightClassData innerLightClassData = new InnerKotlinClassLightClassData( 174 CodegenBinding.getJvmInternalName(bindingContext, innerClassDescriptor), 175 innerClass, 176 innerClassDescriptor 177 ); 178 179 innerClassesMap.put(innerClass, innerLightClassData); 180 } 181 182 return new OutermostKotlinClassLightClassData( 183 javaFileStub, 184 extraDiagnostics, 185 jvmInternalName, 186 classOrObject, 187 classDescriptor, 188 innerClassesMap 189 ); 190 } 191 192 @NotNull 193 @Override 194 public Collection<JetFile> getFiles() { 195 return Collections.singletonList(getFile()); 196 } 197 198 @NotNull 199 @Override 200 public FqName getPackageFqName() { 201 return getFile().getPackageFqName(); 202 } 203 204 @Override 205 public GenerationState.GenerateClassFilter getGenerateClassFilter() { 206 return new GenerationState.GenerateClassFilter() { 207 @Override 208 public boolean shouldProcess(JetClassOrObject generatedClassOrObject) { 209 // Trivial: generate and analyze class we are interested in. 210 if (generatedClassOrObject == classOrObject) return true; 211 212 // Process all parent classes as they are context for current class 213 // Process child classes because they probably affect members (heuristic) 214 if (PsiTreeUtil.isAncestor(generatedClassOrObject, classOrObject, true) || 215 PsiTreeUtil.isAncestor(classOrObject, generatedClassOrObject, true)) { 216 return true; 217 } 218 219 if (generatedClassOrObject.isLocal() && classOrObject.isLocal()) { 220 // Local classes should be process by CodegenAnnotatingVisitor to 221 // decide what class they should be placed in. 222 // 223 // Example: 224 // class A 225 // fun foo() { 226 // trait Z: A {} 227 // fun bar() { 228 // class <caret>O2: Z {} 229 // } 230 // } 231 232 // TODO: current method will process local classes in irrelevant declarations, it should be fixed. 233 PsiElement commonParent = PsiTreeUtil.findCommonParent(generatedClassOrObject, classOrObject); 234 return commonParent != null && !(commonParent instanceof PsiFile); 235 } 236 237 return false; 238 } 239 }; 240 } 241 242 @Override 243 public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) { 244 PackageCodegen packageCodegen = state.getFactory().forPackage(getPackageFqName(), files); 245 packageCodegen.generateClassOrObject(classOrObject); 246 state.getFactory().asList(); 247 } 248 249 @Override 250 public String toString() { 251 return StubGenerationStrategy.class.getName() + " for explicit class " + classOrObject.getName(); 252 } 253 } 254 ); 255 } 256 257 private static final Logger LOG = Logger.getInstance(KotlinJavaFileStubProvider.class); 258 259 private final Project project; 260 private final StubGenerationStrategy<T> stubGenerationStrategy; 261 private final boolean local; 262 263 private KotlinJavaFileStubProvider( 264 @NotNull Project project, 265 boolean local, 266 @NotNull StubGenerationStrategy<T> stubGenerationStrategy 267 ) { 268 this.project = project; 269 this.stubGenerationStrategy = stubGenerationStrategy; 270 this.local = local; 271 } 272 273 @Nullable 274 @Override 275 public Result<T> compute() { 276 FqName packageFqName = stubGenerationStrategy.getPackageFqName(); 277 Collection<JetFile> files = stubGenerationStrategy.getFiles(); 278 279 checkForBuiltIns(packageFqName, files); 280 281 LightClassConstructionContext context = stubGenerationStrategy.getContext(files); 282 283 PsiJavaFileStub javaFileStub = createJavaFileStub(packageFqName, getRepresentativeVirtualFile(files)); 284 BindingContext bindingContext; 285 BindingTraceContext forExtraDiagnostics = new BindingTraceContext(); 286 try { 287 Stack<StubElement> stubStack = new Stack<StubElement>(); 288 stubStack.push(javaFileStub); 289 290 GenerationState state = new GenerationState( 291 project, 292 new KotlinLightClassBuilderFactory(stubStack), 293 Progress.DEAF, 294 context.getModule(), 295 context.getBindingContext(), 296 Lists.newArrayList(files), 297 /*disable not-null assertions*/false, false, 298 /*generateClassFilter=*/stubGenerationStrategy.getGenerateClassFilter(), 299 /*disableInline=*/false, 300 /*disableOptimization=*/false, 301 null, 302 null, 303 forExtraDiagnostics, 304 null 305 ); 306 KotlinCodegenFacade.prepareForCompilation(state); 307 308 bindingContext = state.getBindingContext(); 309 310 stubGenerationStrategy.generate(state, files); 311 312 StubElement pop = stubStack.pop(); 313 if (pop != javaFileStub) { 314 LOG.error("Unbalanced stack operations: " + pop); 315 } 316 } 317 catch (ProcessCanceledException e) { 318 throw e; 319 } 320 catch (RuntimeException e) { 321 logErrorWithOSInfo(e, packageFqName, null); 322 throw e; 323 } 324 325 Diagnostics extraDiagnostics = forExtraDiagnostics.getBindingContext().getDiagnostics(); 326 return Result.create( 327 stubGenerationStrategy.createLightClassData(javaFileStub, bindingContext, extraDiagnostics), 328 local ? PsiModificationTracker.MODIFICATION_COUNT : PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT 329 ); 330 } 331 332 @NotNull 333 private PsiJavaFileStub createJavaFileStub(@NotNull final FqName packageFqName, @NotNull VirtualFile virtualFile) { 334 PsiManager manager = PsiManager.getInstance(project); 335 336 final PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(packageFqName.asString(), true); 337 javaFileStub.setPsiFactory(new ClsWrapperStubPsiFactory()); 338 339 ClsFileImpl fakeFile = 340 new ClsFileImpl((PsiManagerImpl) manager, new ClassFileViewProvider(manager, virtualFile)) { 341 @NotNull 342 @Override 343 public PsiClassHolderFileStub getStub() { 344 return javaFileStub; 345 } 346 347 @NotNull 348 @Override 349 public String getPackageName() { 350 return packageFqName.asString(); 351 } 352 }; 353 354 fakeFile.setPhysical(false); 355 javaFileStub.setPsi(fakeFile); 356 return javaFileStub; 357 } 358 359 @NotNull 360 private static VirtualFile getRepresentativeVirtualFile(@NotNull Collection<JetFile> files) { 361 JetFile firstFile = files.iterator().next(); 362 VirtualFile virtualFile = files.size() == 1 ? firstFile.getVirtualFile() : new LightVirtualFile(); 363 assert virtualFile != null : "No virtual file for " + firstFile; 364 return virtualFile; 365 } 366 367 private static void checkForBuiltIns(@NotNull FqName fqName, @NotNull Collection<JetFile> files) { 368 for (JetFile file : files) { 369 if (LightClassUtil.belongsToKotlinBuiltIns(file)) { 370 // We may not fail later due to some luck, but generating JetLightClasses for built-ins is a bad idea anyways 371 // If it fails later, there will be an exception logged 372 logErrorWithOSInfo(null, fqName, file.getVirtualFile()); 373 } 374 } 375 } 376 377 private static void logErrorWithOSInfo(@Nullable Throwable cause, @NotNull FqName fqName, @Nullable VirtualFile virtualFile) { 378 String path = virtualFile == null ? "<null>" : virtualFile.getPath(); 379 LOG.error( 380 "Could not generate LightClass for " + fqName + " declared in " + path + "\n" + 381 "built-ins dir URL is " + LightClassUtil.getBuiltInsDirUrl() + "\n" + 382 "System: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + " Java Runtime: " + SystemInfo.JAVA_RUNTIME_VERSION, 383 cause); 384 } 385 386 private interface StubGenerationStrategy<T extends WithFileStubAndExtraDiagnostics> { 387 @NotNull Collection<JetFile> getFiles(); 388 @NotNull FqName getPackageFqName(); 389 390 @NotNull LightClassConstructionContext getContext(@NotNull Collection<JetFile> files); 391 @NotNull T createLightClassData(PsiJavaFileStub javaFileStub, BindingContext bindingContext, Diagnostics extraDiagnostics); 392 393 GenerationState.GenerateClassFilter getGenerateClassFilter(); 394 void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files); 395 } 396 }