001 /* 002 * Copyright 2010-2014 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.Sets; 020 import com.intellij.openapi.application.ApplicationManager; 021 import com.intellij.openapi.progress.ProcessCanceledException; 022 import com.intellij.openapi.util.io.FileUtil; 023 import com.intellij.openapi.vfs.VirtualFile; 024 import com.intellij.psi.PsiFile; 025 import com.intellij.util.ArrayUtil; 026 import com.intellij.util.PathUtil; 027 import org.jetbrains.annotations.NotNull; 028 import org.jetbrains.annotations.Nullable; 029 import org.jetbrains.asm4.AnnotationVisitor; 030 import org.jetbrains.asm4.Type; 031 import org.jetbrains.jet.codegen.context.CodegenContext; 032 import org.jetbrains.jet.codegen.context.FieldOwnerContext; 033 import org.jetbrains.jet.codegen.context.PackageContext; 034 import org.jetbrains.jet.codegen.state.GenerationState; 035 import org.jetbrains.jet.codegen.state.GenerationStateAware; 036 import org.jetbrains.jet.descriptors.serialization.BitEncoding; 037 import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer; 038 import org.jetbrains.jet.descriptors.serialization.PackageData; 039 import org.jetbrains.jet.descriptors.serialization.ProtoBuf; 040 import org.jetbrains.jet.lang.descriptors.*; 041 import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; 042 import org.jetbrains.jet.lang.psi.*; 043 import org.jetbrains.jet.lang.resolve.BindingContext; 044 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 045 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames; 046 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 047 import org.jetbrains.jet.lang.resolve.name.FqName; 048 import org.jetbrains.jet.lang.resolve.name.Name; 049 050 import java.util.*; 051 052 import static org.jetbrains.asm4.Opcodes.*; 053 import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses; 054 import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses; 055 import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver; 056 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName; 057 058 public class PackageCodegen extends GenerationStateAware { 059 @NotNull 060 private final ClassBuilderOnDemand v; 061 062 @NotNull 063 private final FqName name; 064 065 @NotNull 066 private final Collection<JetFile> files; 067 private final Set<PackageFragmentDescriptor> packageFragments; 068 069 public PackageCodegen( 070 @NotNull ClassBuilderOnDemand v, 071 @NotNull final FqName fqName, 072 @NotNull GenerationState state, 073 @NotNull Collection<JetFile> packageFiles 074 ) { 075 super(state); 076 checkAllFilesHaveSamePackage(packageFiles); 077 078 this.v = v; 079 name = fqName; 080 this.files = packageFiles; 081 082 packageFragments = Sets.newHashSet(); 083 for (JetFile file : packageFiles) { 084 packageFragments.add(getPackageFragment(file)); 085 } 086 087 final PsiFile sourceFile = packageFiles.size() == 1 ? packageFiles.iterator().next().getContainingFile() : null; 088 089 v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() { 090 @Override 091 public void doSomething(@NotNull ClassBuilder v) { 092 v.defineClass(sourceFile, V1_6, 093 ACC_PUBLIC | ACC_FINAL, 094 JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(fqName)).getInternalName(), 095 null, 096 "java/lang/Object", 097 ArrayUtil.EMPTY_STRING_ARRAY 098 ); 099 //We don't generate any source information for package with multiple files 100 if (sourceFile != null) { 101 v.visitSource(sourceFile.getName(), null); 102 } 103 } 104 }); 105 } 106 107 public void generate(@NotNull CompilationErrorHandler errorHandler) { 108 List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1); 109 boolean shouldGeneratePackageClass = shouldGeneratePackageClass(files); 110 if (shouldGeneratePackageClass) { 111 bindings.add(v.getClassBuilder().getSerializationBindings()); 112 } 113 114 for (JetFile file : files) { 115 try { 116 ClassBuilder builder = generate(file); 117 if (builder != null) { 118 bindings.add(builder.getSerializationBindings()); 119 } 120 } 121 catch (ProcessCanceledException e) { 122 throw e; 123 } 124 catch (Throwable e) { 125 VirtualFile vFile = file.getVirtualFile(); 126 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); 127 DiagnosticUtils.throwIfRunningOnServer(e); 128 if (ApplicationManager.getApplication().isInternal()) { 129 //noinspection CallToPrintStackTrace 130 e.printStackTrace(); 131 } 132 } 133 } 134 135 if (shouldGeneratePackageClass) { 136 writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings)); 137 } 138 139 assert v.isActivated() == shouldGeneratePackageClass : 140 "Different algorithms for generating package class and for heuristics for: " + name.asString(); 141 } 142 143 private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) { 144 if (state.getClassBuilderMode() != ClassBuilderMode.FULL) { 145 return; 146 } 147 148 for (JetFile file : files) { 149 if (file.isScript()) return; 150 } 151 152 DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(bindings)); 153 ProtoBuf.Package packageProto = serializer.packageProto(packageFragments).build(); 154 155 if (packageProto.getMemberCount() == 0) return; 156 157 PackageData data = new PackageData(createNameResolver(serializer.getNameTable()), packageProto); 158 159 AnnotationVisitor av = 160 v.getClassBuilder().newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true); 161 av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION); 162 AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME); 163 for (String string : BitEncoding.encodeBytes(data.toBytes())) { 164 array.visit(null, string); 165 } 166 array.visitEnd(); 167 av.visitEnd(); 168 } 169 170 @Nullable 171 private ClassBuilder generate(@NotNull JetFile file) { 172 boolean generateSrcClass = false; 173 Type packagePartType = getPackagePartType(getPackageClassFqName(name), file.getVirtualFile()); 174 PackageContext packagePartContext = CodegenContext.STATIC.intoPackagePart(getPackageFragment(file), packagePartType); 175 176 for (JetDeclaration declaration : file.getDeclarations()) { 177 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 178 generateSrcClass = true; 179 } 180 else if (declaration instanceof JetClassOrObject) { 181 JetClassOrObject classOrObject = (JetClassOrObject) declaration; 182 if (state.getGenerateDeclaredClassFilter().shouldProcess(classOrObject)) { 183 generateClassOrObject(classOrObject); 184 } 185 } 186 else if (declaration instanceof JetScript) { 187 ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate(); 188 } 189 } 190 191 if (!generateSrcClass) return null; 192 193 ClassBuilder builder = state.getFactory().forPackagePart(packagePartType, file); 194 195 new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate(); 196 197 FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, getPackageFragment(file)); 198 //TODO: FIX: Default method generated at facade without delegation 199 MemberCodegen memberCodegen = new MemberCodegen(state, null, packageFacade, null) { 200 @NotNull 201 @Override 202 public ClassBuilder getBuilder() { 203 return v.getClassBuilder(); 204 } 205 }; 206 for (JetDeclaration declaration : file.getDeclarations()) { 207 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) { 208 memberCodegen.genFunctionOrProperty(packageFacade, (JetTypeParameterListOwner) declaration, v.getClassBuilder()); 209 } 210 } 211 212 return builder; 213 } 214 215 @NotNull 216 private PackageFragmentDescriptor getPackageFragment(@NotNull JetFile file) { 217 PackageFragmentDescriptor packageFragment = bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); 218 assert packageFragment != null : "package fragment is null for " + file; 219 return packageFragment; 220 } 221 222 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) { 223 JetFile file = (JetFile) classOrObject.getContainingFile(); 224 Type packagePartType = getPackagePartType(getPackageClassFqName(name), file.getVirtualFile()); 225 CodegenContext context = CodegenContext.STATIC.intoPackagePart(getPackageFragment(file), packagePartType); 226 MemberCodegen.genClassOrObject(context, classOrObject, state, null); 227 } 228 229 /** 230 * @param packageFiles all files should have same package name 231 * @return 232 */ 233 public static boolean shouldGeneratePackageClass(@NotNull Collection<JetFile> packageFiles) { 234 checkAllFilesHaveSamePackage(packageFiles); 235 236 for (JetFile file : packageFiles) { 237 for (JetDeclaration declaration : file.getDeclarations()) { 238 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 239 return true; 240 } 241 } 242 } 243 244 return false; 245 } 246 247 private static void checkAllFilesHaveSamePackage(Collection<JetFile> packageFiles) { 248 FqName commonFqName = null; 249 for (JetFile file : packageFiles) { 250 FqName fqName = JetPsiUtil.getFQName(file); 251 if (commonFqName != null) { 252 if (!commonFqName.equals(fqName)) { 253 throw new IllegalArgumentException("All files should have same package name"); 254 } 255 } 256 else { 257 commonFqName = JetPsiUtil.getFQName(file); 258 } 259 } 260 } 261 262 public void done() { 263 v.done(); 264 } 265 266 @NotNull 267 public static Type getPackagePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) { 268 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); 269 270 // path hashCode to prevent same name / different path collision 271 String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString( 272 CodegenUtil.getPathHashCode(file)); 273 274 FqName srcFqName = facadeFqName.parent().child(Name.identifier(srcName)); 275 276 return asmTypeByFqNameWithoutInnerClasses(srcFqName); 277 } 278 279 @NotNull 280 private static String replaceSpecialSymbols(@NotNull String str) { 281 return str.replace('.', '_'); 282 } 283 284 @NotNull 285 public static String getPackagePartInternalName(@NotNull JetFile file) { 286 FqName packageFqName = JetPsiUtil.getFQName(file); 287 return getPackagePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName(); 288 } 289 }