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