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