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.intellij.openapi.application.ApplicationManager; 020 import com.intellij.openapi.progress.ProcessCanceledException; 021 import com.intellij.openapi.util.io.FileUtil; 022 import com.intellij.openapi.vfs.VirtualFile; 023 import com.intellij.psi.PsiFile; 024 import com.intellij.util.PathUtil; 025 import org.jetbrains.annotations.NotNull; 026 import org.jetbrains.annotations.Nullable; 027 import org.jetbrains.asm4.AnnotationVisitor; 028 import org.jetbrains.asm4.Type; 029 import org.jetbrains.jet.codegen.context.CodegenContext; 030 import org.jetbrains.jet.codegen.context.FieldOwnerContext; 031 import org.jetbrains.jet.codegen.state.GenerationState; 032 import org.jetbrains.jet.descriptors.serialization.BitEncoding; 033 import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer; 034 import org.jetbrains.jet.descriptors.serialization.PackageData; 035 import org.jetbrains.jet.descriptors.serialization.ProtoBuf; 036 import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; 037 import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; 038 import org.jetbrains.jet.lang.psi.*; 039 import org.jetbrains.jet.lang.resolve.BindingContext; 040 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 041 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames; 042 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 043 import org.jetbrains.jet.lang.resolve.name.FqName; 044 import org.jetbrains.jet.lang.resolve.name.Name; 045 046 import java.util.ArrayList; 047 import java.util.Collection; 048 import java.util.List; 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 @NotNull 058 private final ClassBuilderOnDemand v; 059 060 @NotNull 061 private final FqName name; 062 063 @NotNull 064 private final Collection<JetFile> files; 065 private final NamespaceDescriptor descriptor; 066 067 public NamespaceCodegen( 068 @NotNull ClassBuilderOnDemand v, 069 @NotNull final FqName fqName, 070 @NotNull GenerationState state, 071 @NotNull Collection<JetFile> namespaceFiles 072 ) { 073 super(state, null); 074 checkAllFilesHaveSameNamespace(namespaceFiles); 075 076 this.v = v; 077 name = fqName; 078 this.files = namespaceFiles; 079 080 descriptor = state.getBindingContext().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, name); 081 assert descriptor != null : "No namespace found for FQ name " + name; 082 083 final PsiFile sourceFile = namespaceFiles.size() == 1 ? namespaceFiles.iterator().next().getContainingFile() : null; 084 085 v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() { 086 @Override 087 public void doSomething(@NotNull ClassBuilder v) { 088 v.defineClass(sourceFile, V1_6, 089 ACC_PUBLIC | ACC_FINAL, 090 JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(fqName)).getInternalName(), 091 null, 092 //"jet/lang/Namespace", 093 "java/lang/Object", 094 new String[0] 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(descriptor).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 packageFragmentContext = CodegenContext.STATIC.intoNamespace(descriptor); 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, packageFragmentContext).generate(); 183 } 184 } 185 186 if (!generateSrcClass) return null; 187 188 Type packageFragmentType = getNamespacePartType(getPackageClassFqName(name), file.getVirtualFile()); 189 ClassBuilder builder = state.getFactory().forPackageFragment(packageFragmentType, file); 190 191 new NamespacePartCodegen(builder, file, packageFragmentType, packageFragmentContext, state).generate(); 192 193 FieldOwnerContext namespaceFacade = CodegenContext.STATIC.intoNamespaceFacade(packageFragmentType, descriptor); 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 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) { 205 CodegenContext context = CodegenContext.STATIC.intoNamespace(descriptor); 206 genClassOrObject(context, classOrObject); 207 } 208 209 /** 210 * @param namespaceFiles all files should have same package name 211 * @return 212 */ 213 public static boolean shouldGenerateNSClass(Collection<JetFile> namespaceFiles) { 214 checkAllFilesHaveSameNamespace(namespaceFiles); 215 216 for (JetFile file : namespaceFiles) { 217 for (JetDeclaration declaration : file.getDeclarations()) { 218 if (declaration instanceof JetProperty || 219 declaration instanceof JetNamedFunction || 220 declaration instanceof JetObjectDeclaration) { 221 return true; 222 } 223 } 224 } 225 226 return false; 227 } 228 229 private static void checkAllFilesHaveSameNamespace(Collection<JetFile> namespaceFiles) { 230 FqName commonFqName = null; 231 for (JetFile file : namespaceFiles) { 232 FqName fqName = JetPsiUtil.getFQName(file); 233 if (commonFqName != null) { 234 if (!commonFqName.equals(fqName)) { 235 throw new IllegalArgumentException("All files should have same package name"); 236 } 237 } 238 else { 239 commonFqName = JetPsiUtil.getFQName(file); 240 } 241 } 242 } 243 244 public void done() { 245 v.done(); 246 } 247 248 @NotNull 249 public static Type getNamespacePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) { 250 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); 251 252 // path hashCode to prevent same name / different path collision 253 String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString( 254 CodegenUtil.getPathHashCode(file)); 255 256 FqName srcFqName = facadeFqName.parent().child(Name.identifier(srcName)); 257 258 return asmTypeByFqNameWithoutInnerClasses(srcFqName); 259 } 260 261 @NotNull 262 private static String replaceSpecialSymbols(@NotNull String str) { 263 return str.replace('.', '_'); 264 } 265 266 @NotNull 267 public static String getNamespacePartInternalName(@NotNull JetFile file) { 268 FqName packageFqName = JetPsiUtil.getFQName(file); 269 return getNamespacePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName(); 270 } 271 }