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.google.common.collect.Lists;
020    import com.intellij.openapi.vfs.VirtualFile;
021    import com.intellij.psi.PsiFile;
022    import com.intellij.util.Function;
023    import com.intellij.util.containers.ContainerUtil;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.annotations.TestOnly;
027    import org.jetbrains.kotlin.codegen.state.GenerationState;
028    import org.jetbrains.kotlin.psi.JetFile;
029    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
030    import org.jetbrains.kotlin.name.FqName;
031    import org.jetbrains.kotlin.backend.common.output.OutputFile;
032    import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
033    import org.jetbrains.org.objectweb.asm.Type;
034    
035    import java.io.File;
036    import java.util.*;
037    
038    public class ClassFileFactory implements OutputFileCollection {
039        private final GenerationState state;
040        private final ClassBuilderFactory builderFactory;
041        private final Map<FqName, PackageCodegen> package2codegen = new HashMap<FqName, PackageCodegen>();
042        private final Map<String, ClassBuilderAndSourceFileList> generators = new LinkedHashMap<String, ClassBuilderAndSourceFileList>();
043    
044        private boolean isDone = false;
045    
046        public ClassFileFactory(@NotNull GenerationState state, @NotNull ClassBuilderFactory builderFactory) {
047            this.state = state;
048            this.builderFactory = builderFactory;
049        }
050    
051        @NotNull
052        public ClassBuilder newVisitor(
053                @NotNull JvmDeclarationOrigin origin,
054                @NotNull Type asmType,
055                @NotNull PsiFile sourceFile) {
056            return newVisitor(origin, asmType, Collections.singletonList(sourceFile));
057        }
058    
059        @NotNull
060        public ClassBuilder newVisitor(
061                @NotNull JvmDeclarationOrigin origin,
062                @NotNull Type asmType,
063                @NotNull Collection<? extends PsiFile> sourceFiles) {
064            String outputFilePath = asmType.getInternalName() + ".class";
065            List<File> ioSourceFiles = toIoFilesIgnoringNonPhysical(sourceFiles);
066            state.getProgress().reportOutput(ioSourceFiles, new File(outputFilePath));
067            ClassBuilder answer = builderFactory.newClassBuilder(origin);
068            generators.put(outputFilePath, new ClassBuilderAndSourceFileList(answer, ioSourceFiles));
069            return answer;
070        }
071    
072        void done() {
073            if (!isDone) {
074                isDone = true;
075                for (PackageCodegen codegen : package2codegen.values()) {
076                    codegen.done();
077                }
078            }
079        }
080    
081        @NotNull
082        @Override
083        public List<OutputFile> asList() {
084            done();
085            return ContainerUtil.map(generators.keySet(), new Function<String, OutputFile>() {
086                @Override
087                public OutputFile fun(String relativeClassFilePath) {
088                    return new OutputClassFile(relativeClassFilePath);
089                }
090            });
091        }
092    
093        @Override
094        @Nullable
095        public OutputFile get(@NotNull String relativePath) {
096            return generators.containsKey(relativePath) ? new OutputClassFile(relativePath) : null;
097        }
098    
099        @NotNull
100        @TestOnly
101        public String createText() {
102            StringBuilder answer = new StringBuilder();
103    
104            for (OutputFile file : asList()) {
105                answer.append("@").append(file.getRelativePath()).append('\n');
106                answer.append(file.asText());
107            }
108    
109            return answer.toString();
110        }
111    
112        @NotNull
113        public PackageCodegen forPackage(@NotNull FqName fqName, @NotNull Collection<JetFile> files) {
114            assert !isDone : "Already done!";
115            PackageCodegen codegen = package2codegen.get(fqName);
116            if (codegen == null) {
117                codegen = new PackageCodegen(state, files, fqName);
118                package2codegen.put(fqName, codegen);
119            }
120    
121            return codegen;
122        }
123    
124        @NotNull
125        private static List<File> toIoFilesIgnoringNonPhysical(@NotNull Collection<? extends PsiFile> psiFiles) {
126            List<File> result = Lists.newArrayList();
127            for (PsiFile psiFile : psiFiles) {
128                VirtualFile virtualFile = psiFile.getVirtualFile();
129                // We ignore non-physical files here, because this code is needed to tell the make what inputs affect which outputs
130                // a non-physical file cannot be processed by make
131                if (virtualFile != null) {
132                    result.add(new File(virtualFile.getPath()));
133                }
134            }
135            return result;
136        }
137    
138        private class OutputClassFile implements OutputFile {
139            private final String relativeClassFilePath;
140    
141            public OutputClassFile(String relativeClassFilePath) {
142                this.relativeClassFilePath = relativeClassFilePath;
143            }
144    
145            @NotNull
146            @Override
147            public String getRelativePath() {
148                return relativeClassFilePath;
149            }
150    
151            @NotNull
152            @Override
153            public List<File> getSourceFiles() {
154                ClassBuilderAndSourceFileList pair = generators.get(relativeClassFilePath);
155                if (pair == null) {
156                    throw new IllegalStateException("No record for binary file " + relativeClassFilePath);
157                }
158    
159                return pair.sourceFiles;
160            }
161    
162            @NotNull
163            @Override
164            public byte[] asByteArray() {
165                return builderFactory.asBytes(generators.get(relativeClassFilePath).classBuilder);
166            }
167    
168            @NotNull
169            @Override
170            public String asText() {
171                return builderFactory.asText(generators.get(relativeClassFilePath).classBuilder);
172            }
173    
174            @NotNull
175            @Override
176            public String toString() {
177                return getRelativePath() + " (compiled from " + getSourceFiles() + ")";
178            }
179        }
180    
181        private static final class ClassBuilderAndSourceFileList {
182            private final ClassBuilder classBuilder;
183            private final List<File> sourceFiles;
184    
185            private ClassBuilderAndSourceFileList(ClassBuilder classBuilder, List<File> sourceFiles) {
186                this.classBuilder = classBuilder;
187                this.sourceFiles = sourceFiles;
188            }
189        }
190    
191        public void removeInlinedClasses(Set<String> classNamesToRemove) {
192            for (String classInternalName : classNamesToRemove) {
193                generators.remove(classInternalName + ".class");
194            }
195        }
196    }