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    }