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
017package org.jetbrains.jet.codegen;
018
019import com.google.common.collect.Lists;
020import com.intellij.openapi.vfs.VirtualFile;
021import com.intellij.psi.PsiFile;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.asm4.Type;
024import org.jetbrains.jet.codegen.state.GenerationState;
025import org.jetbrains.jet.codegen.state.GenerationStateAware;
026import org.jetbrains.jet.codegen.state.JetTypeMapperMode;
027import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
028import org.jetbrains.jet.lang.psi.JetFile;
029import org.jetbrains.jet.lang.resolve.name.FqName;
030
031import javax.inject.Inject;
032import java.io.File;
033import java.util.*;
034
035import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
036
037public 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}