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