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    }