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.codegen.state.JetTypeMapperMode; 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 034 import javax.inject.Inject; 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 final class ClassFileFactory extends GenerationStateAware { 043 @NotNull private ClassBuilderFactory builderFactory; 044 045 private final Map<FqName, NamespaceCodegen> ns2codegen = new HashMap<FqName, NamespaceCodegen>(); 046 private final Map<String, ClassBuilderAndSourceFileList> generators = new LinkedHashMap<String, ClassBuilderAndSourceFileList>(); 047 private boolean isDone = false; 048 049 public ClassFileFactory(@NotNull GenerationState state) { 050 super(state); 051 } 052 053 054 @Inject 055 public void setBuilderFactory(@NotNull ClassBuilderFactory builderFactory) { 056 this.builderFactory = builderFactory; 057 } 058 059 @NotNull 060 ClassBuilder newVisitor(@NotNull Type asmType, @NotNull PsiFile sourceFile) { 061 return newVisitor(asmType, Collections.singletonList(sourceFile)); 062 } 063 064 @NotNull 065 private ClassBuilder newVisitor(@NotNull Type asmType, @NotNull Collection<? extends PsiFile> sourceFiles) { 066 String outputFilePath = asmType.getInternalName() + ".class"; 067 state.getProgress().reportOutput(toIoFilesIgnoringNonPhysical(sourceFiles), new File(outputFilePath)); 068 ClassBuilder answer = builderFactory.newClassBuilder(); 069 generators.put(outputFilePath, new ClassBuilderAndSourceFileList(answer, sourceFiles)); 070 return answer; 071 } 072 073 private void done() { 074 if (!isDone) { 075 isDone = true; 076 for (NamespaceCodegen codegen : ns2codegen.values()) { 077 codegen.done(); 078 } 079 } 080 } 081 082 public String asText(String file) { 083 done(); 084 return builderFactory.asText(generators.get(file).classBuilder); 085 } 086 087 public byte[] asBytes(String file) { 088 done(); 089 return builderFactory.asBytes(generators.get(file).classBuilder); 090 } 091 092 public List<String> files() { 093 done(); 094 return new ArrayList<String>(generators.keySet()); 095 } 096 097 public List<File> getSourceFiles(String relativeClassFilePath) { 098 ClassBuilderAndSourceFileList pair = generators.get(relativeClassFilePath); 099 if (pair == null) { 100 throw new IllegalStateException("No record for binary file " + relativeClassFilePath); 101 } 102 103 return ContainerUtil.mapNotNull( 104 pair.sourceFiles, 105 new Function<PsiFile, File>() { 106 @Override 107 public File fun(PsiFile file) { 108 VirtualFile virtualFile = file.getVirtualFile(); 109 if (virtualFile == null) return null; 110 111 return VfsUtilCore.virtualToIoFile(virtualFile); 112 } 113 } 114 ); 115 } 116 117 public String createText() { 118 StringBuilder answer = new StringBuilder(); 119 120 List<String> files = files(); 121 for (String file : files) { 122 // if (!file.startsWith("kotlin/")) { 123 answer.append("@").append(file).append('\n'); 124 answer.append(asText(file)); 125 // } 126 } 127 128 return answer.toString(); 129 } 130 131 public NamespaceCodegen forNamespace(final FqName fqName, final Collection<JetFile> files) { 132 assert !isDone : "Already done!"; 133 NamespaceCodegen codegen = ns2codegen.get(fqName); 134 if (codegen == null) { 135 ClassBuilderOnDemand onDemand = new ClassBuilderOnDemand() { 136 @NotNull 137 @Override 138 protected ClassBuilder createClassBuilder() { 139 return newVisitor(asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName)), files); 140 } 141 }; 142 codegen = new NamespaceCodegen(onDemand, fqName, state, files); 143 ns2codegen.put(fqName, codegen); 144 } 145 146 return codegen; 147 } 148 149 public ClassBuilder forClassImplementation(ClassDescriptor aClass, PsiFile sourceFile) { 150 Type type = state.getTypeMapper().mapType(aClass.getDefaultType(), JetTypeMapperMode.IMPL); 151 if (isPrimitive(type)) { 152 throw new IllegalStateException("Codegen for primitive type is not possible: " + aClass); 153 } 154 return newVisitor(type, sourceFile); 155 } 156 157 @NotNull 158 public ClassBuilder forPackageFragment(@NotNull Type asmType, @NotNull PsiFile sourceFile) { 159 return newVisitor(asmType, sourceFile); 160 } 161 162 @NotNull 163 public ClassBuilder forTraitImplementation(@NotNull ClassDescriptor aClass, @NotNull GenerationState state, @NotNull PsiFile file) { 164 Type type = state.getTypeMapper().mapType(aClass.getDefaultType(), JetTypeMapperMode.TRAIT_IMPL); 165 return newVisitor(type, file); 166 } 167 168 private static Collection<File> toIoFilesIgnoringNonPhysical(Collection<? extends PsiFile> psiFiles) { 169 List<File> result = Lists.newArrayList(); 170 for (PsiFile psiFile : psiFiles) { 171 VirtualFile virtualFile = psiFile.getVirtualFile(); 172 // We ignore non-physical files here, because this code is needed to tell the make what inputs affect which outputs 173 // a non-physical file cannot be processed by make 174 if (virtualFile != null) { 175 result.add(new File(virtualFile.getPath())); 176 } 177 } 178 return result; 179 } 180 181 private static class ClassBuilderAndSourceFileList { 182 private final ClassBuilder classBuilder; 183 private final Collection<? extends PsiFile> sourceFiles; 184 185 private ClassBuilderAndSourceFileList(ClassBuilder classBuilder, Collection<? extends PsiFile> sourceFiles) { 186 this.classBuilder = classBuilder; 187 this.sourceFiles = sourceFiles; 188 } 189 } 190 191 }