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 }