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 org.jetbrains.annotations.NotNull; 023 import org.jetbrains.asm4.Type; 024 import org.jetbrains.jet.codegen.state.GenerationState; 025 import org.jetbrains.jet.codegen.state.GenerationStateAware; 026 import org.jetbrains.jet.codegen.state.JetTypeMapperMode; 027 import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 028 import org.jetbrains.jet.lang.psi.JetFile; 029 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 030 import org.jetbrains.jet.lang.resolve.name.FqName; 031 032 import javax.inject.Inject; 033 import java.io.File; 034 import java.util.*; 035 036 import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; 037 038 public final class ClassFileFactory extends GenerationStateAware { 039 @NotNull private ClassBuilderFactory builderFactory; 040 041 private final Map<FqName, NamespaceCodegen> ns2codegen = new HashMap<FqName, NamespaceCodegen>(); 042 private final Map<String, ClassBuilder> generators = new LinkedHashMap<String, ClassBuilder>(); 043 private boolean isDone = false; 044 045 public ClassFileFactory(@NotNull GenerationState state) { 046 super(state); 047 } 048 049 050 @Inject 051 public void setBuilderFactory(@NotNull ClassBuilderFactory builderFactory) { 052 this.builderFactory = builderFactory; 053 } 054 055 @NotNull 056 ClassBuilder newVisitor(@NotNull JvmClassName className, @NotNull PsiFile sourceFile) { 057 return newVisitor(className, Collections.singletonList(sourceFile)); 058 } 059 060 @NotNull 061 private ClassBuilder newVisitor(@NotNull JvmClassName className, @NotNull Collection<? extends PsiFile> sourceFiles) { 062 String outputFilePath = className.getInternalName() + ".class"; 063 state.getProgress().reportOutput(toIoFilesIgnoringNonPhysical(sourceFiles), new File(outputFilePath)); 064 ClassBuilder answer = builderFactory.newClassBuilder(); 065 generators.put(outputFilePath, answer); 066 return answer; 067 } 068 069 private void done() { 070 if (!isDone) { 071 isDone = true; 072 for (NamespaceCodegen codegen : ns2codegen.values()) { 073 codegen.done(); 074 } 075 } 076 } 077 078 public String asText(String file) { 079 done(); 080 return builderFactory.asText(generators.get(file)); 081 } 082 083 public byte[] asBytes(String file) { 084 done(); 085 return builderFactory.asBytes(generators.get(file)); 086 } 087 088 public List<String> files() { 089 done(); 090 return new ArrayList<String>(generators.keySet()); 091 } 092 093 public String createText() { 094 StringBuilder answer = new StringBuilder(); 095 096 List<String> files = files(); 097 for (String file : files) { 098 // if (!file.startsWith("kotlin/")) { 099 answer.append("@").append(file).append('\n'); 100 answer.append(asText(file)); 101 // } 102 } 103 104 return answer.toString(); 105 } 106 107 public NamespaceCodegen forNamespace(final FqName fqName, final Collection<JetFile> files) { 108 assert !isDone : "Already done!"; 109 NamespaceCodegen codegen = ns2codegen.get(fqName); 110 if (codegen == null) { 111 ClassBuilderOnDemand onDemand = new ClassBuilderOnDemand() { 112 @NotNull 113 @Override 114 protected ClassBuilder createClassBuilder() { 115 return newVisitor(NamespaceCodegen.getJVMClassNameForKotlinNs(fqName), files); 116 } 117 }; 118 codegen = new NamespaceCodegen(onDemand, fqName, state, files); 119 ns2codegen.put(fqName, codegen); 120 } 121 122 return codegen; 123 } 124 125 public ClassBuilder forClassImplementation(ClassDescriptor aClass, PsiFile sourceFile) { 126 Type type = state.getTypeMapper().mapType(aClass.getDefaultType(), JetTypeMapperMode.IMPL); 127 if (isPrimitive(type)) { 128 throw new IllegalStateException("Codegen for primitive type is not possible: " + aClass); 129 } 130 return newVisitor(JvmClassName.byType(type), sourceFile); 131 } 132 133 @NotNull 134 public ClassBuilder forNamespacePart(@NotNull JvmClassName name, @NotNull PsiFile sourceFile) { 135 return newVisitor(name, sourceFile); 136 } 137 138 @NotNull 139 public ClassBuilder forTraitImplementation(@NotNull ClassDescriptor aClass, @NotNull GenerationState state, @NotNull PsiFile file) { 140 Type type = state.getTypeMapper().mapType(aClass.getDefaultType(), JetTypeMapperMode.TRAIT_IMPL); 141 return newVisitor(JvmClassName.byType(type), file); 142 } 143 144 private static Collection<File> toIoFilesIgnoringNonPhysical(Collection<? extends PsiFile> psiFiles) { 145 List<File> result = Lists.newArrayList(); 146 for (PsiFile psiFile : psiFiles) { 147 VirtualFile virtualFile = psiFile.getVirtualFile(); 148 // We ignore non-physical files here, because this code is needed to tell the make what inputs affect which outputs 149 // a non-physical file cannot be processed by make 150 if (virtualFile != null) { 151 result.add(new File(virtualFile.getPath())); 152 } 153 } 154 return result; 155 } 156 157 }