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