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