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