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 }