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    }