001 /* 002 * Copyright 2010-2015 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.kotlin.codegen; 018 019 import com.intellij.openapi.application.ApplicationManager; 020 import com.intellij.openapi.progress.ProcessCanceledException; 021 import com.intellij.openapi.vfs.VirtualFile; 022 import com.intellij.util.SmartList; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.annotations.Nullable; 025 import org.jetbrains.kotlin.codegen.context.PackageContext; 026 import org.jetbrains.kotlin.codegen.state.GenerationState; 027 import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; 028 import org.jetbrains.kotlin.diagnostics.DiagnosticUtils; 029 import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo; 030 import org.jetbrains.kotlin.name.FqName; 031 import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; 032 import org.jetbrains.kotlin.psi.*; 033 import org.jetbrains.kotlin.resolve.BindingContext; 034 import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; 035 import org.jetbrains.org.objectweb.asm.Type; 036 037 import java.util.Collection; 038 039 public class PackageCodegen { 040 private final GenerationState state; 041 private final Collection<KtFile> files; 042 private final PackageFragmentDescriptor packageFragment; 043 private final PackagePartRegistry packagePartRegistry; 044 045 public PackageCodegen( 046 @NotNull GenerationState state, 047 @NotNull Collection<KtFile> files, 048 @NotNull FqName packageFqName, 049 @NotNull PackagePartRegistry registry 050 ) { 051 this.state = state; 052 this.files = files; 053 this.packageFragment = getOnlyPackageFragment(packageFqName); 054 packagePartRegistry = registry; 055 } 056 057 public void generate(@NotNull CompilationErrorHandler errorHandler) { 058 for (KtFile file : files) { 059 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); 060 try { 061 generateFile(file); 062 state.afterIndependentPart(); 063 } 064 catch (ProcessCanceledException e) { 065 throw e; 066 } 067 catch (Throwable e) { 068 VirtualFile vFile = file.getVirtualFile(); 069 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); 070 DiagnosticUtils.throwIfRunningOnServer(e); 071 if (ApplicationManager.getApplication().isInternal()) { 072 //noinspection CallToPrintStackTrace 073 e.printStackTrace(); 074 } 075 } 076 } 077 } 078 079 private void generateFile(@NotNull KtFile file) { 080 JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file); 081 082 if (fileClassInfo.getWithJvmMultifileClass()) { 083 return; 084 } 085 086 Type fileClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFileClassFqName()); 087 PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, fileClassType, file); 088 089 boolean generatePackagePart = false; 090 091 for (KtDeclaration declaration : file.getDeclarations()) { 092 if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) { 093 generatePackagePart = true; 094 } 095 else if (declaration instanceof KtClassOrObject) { 096 KtClassOrObject classOrObject = (KtClassOrObject) declaration; 097 if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) { 098 generateClassOrObject(classOrObject, packagePartContext); 099 } 100 } 101 else if (declaration instanceof KtScript) { 102 KtScript script = (KtScript) declaration; 103 104 if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) { 105 ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate(); 106 } 107 } 108 } 109 110 if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return; 111 112 String name = fileClassType.getInternalName(); 113 packagePartRegistry.addPart(name.substring(name.lastIndexOf('/') + 1)); 114 115 ClassBuilder builder = state.getFactory().newVisitor(JvmDeclarationOriginKt.PackagePart(file, packageFragment), fileClassType, file); 116 117 new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate(); 118 } 119 120 @Nullable 121 private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedPackageFqName) { 122 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>(); 123 for (KtFile file : files) { 124 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); 125 assert fragment != null : "package fragment is null for " + file + "\n" + file.getText(); 126 127 assert expectedPackageFqName.equals(fragment.getFqName()) : 128 "expected package fq name: " + expectedPackageFqName + ", actual: " + fragment.getFqName(); 129 130 if (!fragments.contains(fragment)) { 131 fragments.add(fragment); 132 } 133 } 134 if (fragments.size() > 1) { 135 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments); 136 } 137 138 if (fragments.isEmpty()) { 139 return null; 140 } 141 return fragments.get(0); 142 } 143 144 public void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext) { 145 MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null); 146 } 147 148 public Collection<KtFile> getFiles() { 149 return files; 150 } 151 152 public PackageFragmentDescriptor getPackageFragment() { 153 return packageFragment; 154 } 155 }