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