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.load.kotlin.PackageParts;
031 import org.jetbrains.kotlin.name.FqName;
032 import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
033 import org.jetbrains.kotlin.psi.*;
034 import org.jetbrains.kotlin.resolve.BindingContext;
035 import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
036 import org.jetbrains.org.objectweb.asm.Type;
037
038 import java.util.Collection;
039
040 public class PackageCodegen {
041 private final GenerationState state;
042 private final Collection<KtFile> files;
043 private final PackageFragmentDescriptor packageFragment;
044 private final PackageParts packageParts;
045
046 public PackageCodegen(
047 @NotNull GenerationState state,
048 @NotNull Collection<KtFile> files,
049 @NotNull FqName packageFqName
050 ) {
051 this.state = state;
052 this.files = files;
053 this.packageFragment = getOnlyPackageFragment(packageFqName);
054 packageParts = new PackageParts(packageFqName.asString());
055 }
056
057 public void generate(@NotNull CompilationErrorHandler errorHandler) {
058 for (KtFile file : files) {
059 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
060 try {
061 generateFile(file);
062 }
063 catch (ProcessCanceledException e) {
064 throw e;
065 }
066 catch (Throwable e) {
067 VirtualFile vFile = file.getVirtualFile();
068 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
069 DiagnosticUtils.throwIfRunningOnServer(e);
070 if (ApplicationManager.getApplication().isInternal()) {
071 //noinspection CallToPrintStackTrace
072 e.printStackTrace();
073 }
074 }
075 }
076 }
077
078 @Nullable
079 private ClassBuilder generateFile(@NotNull KtFile file) {
080 JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
081
082 if (fileClassInfo.getWithJvmMultifileClass()) {
083 return null;
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 null;
111
112 String name = fileClassType.getInternalName();
113 packageParts.getParts().add(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 return builder;
120 }
121
122 @Nullable
123 private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedPackageFqName) {
124 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
125 for (KtFile file : files) {
126 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
127 assert fragment != null : "package fragment is null for " + file + "\n" + file.getText();
128
129 assert expectedPackageFqName.equals(fragment.getFqName()) :
130 "expected package fq name: " + expectedPackageFqName + ", actual: " + fragment.getFqName();
131
132 if (!fragments.contains(fragment)) {
133 fragments.add(fragment);
134 }
135 }
136 if (fragments.size() > 1) {
137 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
138 }
139
140 if (fragments.isEmpty()) {
141 return null;
142 }
143 return fragments.get(0);
144 }
145
146 public void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext) {
147 MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null);
148 }
149
150 public PackageParts getPackageParts() {
151 return packageParts;
152 }
153
154 public Collection<KtFile> getFiles() {
155 return files;
156 }
157
158 public PackageFragmentDescriptor getPackageFragment() {
159 return packageFragment;
160 }
161 }