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