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 }