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