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