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.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 NamespaceCodegen 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 NamespaceCodegen( 067 @NotNull ClassBuilderOnDemand v, 068 @NotNull final FqName fqName, 069 @NotNull GenerationState state, 070 @NotNull Collection<JetFile> namespaceFiles 071 ) { 072 super(state, null); 073 checkAllFilesHaveSameNamespace(namespaceFiles); 074 075 this.v = v; 076 name = fqName; 077 this.files = namespaceFiles; 078 079 packageFragments = Sets.newHashSet(); 080 for (JetFile file : namespaceFiles) { 081 packageFragments.add(getPackageFragment(file)); 082 } 083 084 final PsiFile sourceFile = namespaceFiles.size() == 1 ? namespaceFiles.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 namespace 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 = shouldGenerateNSClass(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 namespace 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 if (state.isGenerateDeclaredClasses()) { 178 generateClassOrObject((JetClassOrObject) declaration); 179 } 180 } 181 else if (declaration instanceof JetScript) { 182 ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate(); 183 } 184 } 185 186 if (!generateSrcClass) return null; 187 188 Type packagePartType = getNamespacePartType(getPackageClassFqName(name), file.getVirtualFile()); 189 ClassBuilder builder = state.getFactory().forPackageFragment(packagePartType, file); 190 191 new NamespacePartCodegen(builder, file, packagePartType, packagePartContext, state).generate(); 192 193 FieldOwnerContext namespaceFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, getPackageFragment(file)); 194 195 for (JetDeclaration declaration : file.getDeclarations()) { 196 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) { 197 genFunctionOrProperty(namespaceFacade, (JetTypeParameterListOwner) declaration, v.getClassBuilder()); 198 } 199 } 200 201 return builder; 202 } 203 204 @NotNull 205 private PackageFragmentDescriptor getPackageFragment(@NotNull JetFile file) { 206 PackageFragmentDescriptor packageFragment = bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); 207 assert packageFragment != null : "package fragment is null for " + file; 208 return packageFragment; 209 } 210 211 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) { 212 CodegenContext context = CodegenContext.STATIC.intoPackagePart(getPackageFragment((JetFile) classOrObject.getContainingFile())); 213 genClassOrObject(context, classOrObject); 214 } 215 216 /** 217 * @param namespaceFiles all files should have same package name 218 * @return 219 */ 220 public static boolean shouldGenerateNSClass(@NotNull Collection<JetFile> namespaceFiles) { 221 checkAllFilesHaveSameNamespace(namespaceFiles); 222 223 for (JetFile file : namespaceFiles) { 224 for (JetDeclaration declaration : file.getDeclarations()) { 225 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 226 return true; 227 } 228 } 229 } 230 231 return false; 232 } 233 234 private static void checkAllFilesHaveSameNamespace(Collection<JetFile> namespaceFiles) { 235 FqName commonFqName = null; 236 for (JetFile file : namespaceFiles) { 237 FqName fqName = JetPsiUtil.getFQName(file); 238 if (commonFqName != null) { 239 if (!commonFqName.equals(fqName)) { 240 throw new IllegalArgumentException("All files should have same package name"); 241 } 242 } 243 else { 244 commonFqName = JetPsiUtil.getFQName(file); 245 } 246 } 247 } 248 249 public void done() { 250 v.done(); 251 } 252 253 @NotNull 254 public static Type getNamespacePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) { 255 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); 256 257 // path hashCode to prevent same name / different path collision 258 String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString( 259 CodegenUtil.getPathHashCode(file)); 260 261 FqName srcFqName = facadeFqName.parent().child(Name.identifier(srcName)); 262 263 return asmTypeByFqNameWithoutInnerClasses(srcFqName); 264 } 265 266 @NotNull 267 private static String replaceSpecialSymbols(@NotNull String str) { 268 return str.replace('.', '_'); 269 } 270 271 @NotNull 272 public static String getNamespacePartInternalName(@NotNull JetFile file) { 273 FqName packageFqName = JetPsiUtil.getFQName(file); 274 return getNamespacePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName(); 275 } 276 }