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