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