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.Lists; 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.PathUtil; 026 import org.jetbrains.annotations.NotNull; 027 import org.jetbrains.asm4.AnnotationVisitor; 028 import org.jetbrains.asm4.MethodVisitor; 029 import org.jetbrains.asm4.Type; 030 import org.jetbrains.jet.codegen.context.CodegenContext; 031 import org.jetbrains.jet.codegen.context.FieldOwnerContext; 032 import org.jetbrains.jet.codegen.state.GenerationState; 033 import org.jetbrains.jet.lang.descriptors.*; 034 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; 035 import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; 036 import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; 037 import org.jetbrains.jet.lang.psi.*; 038 import org.jetbrains.jet.lang.resolve.BindingContext; 039 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 040 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 041 import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; 042 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 043 import org.jetbrains.jet.lang.resolve.name.FqName; 044 import org.jetbrains.jet.lang.resolve.name.Name; 045 046 import java.util.Collection; 047 import java.util.Collections; 048 import java.util.List; 049 050 import static org.jetbrains.asm4.Opcodes.*; 051 052 public class NamespaceCodegen extends MemberCodegen { 053 @NotNull 054 private final ClassBuilderOnDemand v; 055 @NotNull private final FqName name; 056 private final Collection<JetFile> files; 057 058 public NamespaceCodegen( 059 @NotNull ClassBuilderOnDemand v, 060 @NotNull final FqName fqName, 061 GenerationState state, 062 Collection<JetFile> namespaceFiles 063 ) { 064 super(state, null); 065 checkAllFilesHaveSameNamespace(namespaceFiles); 066 067 this.v = v; 068 name = fqName; 069 this.files = namespaceFiles; 070 071 final PsiFile sourceFile = namespaceFiles.size() == 1 ? namespaceFiles.iterator().next().getContainingFile() : null; 072 073 v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() { 074 @Override 075 public void doSomething(@NotNull ClassBuilder v) { 076 v.defineClass(sourceFile, V1_6, 077 ACC_PUBLIC | ACC_FINAL, 078 getJVMClassNameForKotlinNs(fqName).getInternalName(), 079 null, 080 //"jet/lang/Namespace", 081 "java/lang/Object", 082 new String[0] 083 ); 084 //We don't generate any source information for namespace with multiple files 085 if (sourceFile != null) { 086 v.visitSource(sourceFile.getName(), null); 087 } 088 } 089 }); 090 } 091 092 public void generate(CompilationErrorHandler errorHandler) { 093 if (shouldGenerateNSClass(files)) { 094 AnnotationVisitor packageClassAnnotation = v.getClassBuilder().newAnnotation(JvmStdlibNames.JET_PACKAGE_CLASS.getDescriptor(), true); 095 packageClassAnnotation.visit(JvmStdlibNames.ABI_VERSION_NAME, JvmAbi.VERSION); 096 packageClassAnnotation.visitEnd(); 097 } 098 099 for (JetFile file : files) { 100 VirtualFile vFile = file.getVirtualFile(); 101 try { 102 generate(file); 103 } 104 catch (ProcessCanceledException e) { 105 throw e; 106 } 107 catch (Throwable e) { 108 if (errorHandler != null) errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); 109 DiagnosticUtils.throwIfRunningOnServer(e); 110 if (ApplicationManager.getApplication().isInternal()) { 111 //noinspection CallToPrintStackTrace 112 e.printStackTrace(); 113 } 114 } 115 } 116 117 assert v.isActivated() == shouldGenerateNSClass(files) : "Different algorithms for generating namespace class and for heuristics"; 118 } 119 120 private void generate(JetFile file) { 121 NamespaceDescriptor descriptor = state.getBindingContext().get(BindingContext.FILE_TO_NAMESPACE, file); 122 assert descriptor != null : "No namespace found for file " + file + " declared package: " + file.getPackageName(); 123 int countOfDeclarationsInSrcClass = 0; 124 for (JetDeclaration declaration : file.getDeclarations()) { 125 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 126 countOfDeclarationsInSrcClass++; 127 } 128 else if (declaration instanceof JetClassOrObject) { 129 if (state.isGenerateDeclaredClasses()) { 130 generateClassOrObject(descriptor, (JetClassOrObject) declaration); 131 } 132 } 133 else if (declaration instanceof JetScript) { 134 state.getScriptCodegen().generate((JetScript) declaration); 135 } 136 } 137 138 if (countOfDeclarationsInSrcClass > 0) { 139 String namespaceInternalName = JvmClassName.byFqNameWithoutInnerClasses( 140 PackageClassUtils.getPackageClassFqName(name)).getInternalName(); 141 String className = getMultiFileNamespaceInternalName(namespaceInternalName, file); 142 ClassBuilder builder = state.getFactory().forNamespacepart(className, file); 143 144 builder.defineClass(file, V1_6, 145 ACC_PUBLIC | ACC_FINAL, 146 className, 147 null, 148 //"jet/lang/Namespace", 149 "java/lang/Object", 150 new String[0] 151 ); 152 builder.visitSource(file.getName(), null); 153 154 FieldOwnerContext nameSpaceContext = 155 CodegenContext.STATIC.intoNamespace(descriptor); 156 157 FieldOwnerContext nameSpacePart = 158 CodegenContext.STATIC.intoNamespacePart(className, descriptor); 159 160 for (JetDeclaration declaration : file.getDeclarations()) { 161 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) { 162 genFunctionOrProperty(nameSpaceContext, (JetTypeParameterListOwner) declaration, builder); 163 genFunctionOrProperty(nameSpacePart, (JetTypeParameterListOwner) declaration, v.getClassBuilder()); 164 } 165 } 166 167 generateStaticInitializers(descriptor, builder, file, nameSpaceContext); 168 169 builder.done(); 170 } 171 } 172 173 public void generateClassOrObject(@NotNull NamespaceDescriptor descriptor, @NotNull JetClassOrObject classOrObject) { 174 CodegenContext context = CodegenContext.STATIC.intoNamespace(descriptor); 175 genClassOrObject(context, classOrObject); 176 } 177 178 /** 179 * @param namespaceFiles all files should have same package name 180 * @return 181 */ 182 public static boolean shouldGenerateNSClass(Collection<JetFile> namespaceFiles) { 183 checkAllFilesHaveSameNamespace(namespaceFiles); 184 185 for (JetFile file : namespaceFiles) { 186 for (JetDeclaration declaration : file.getDeclarations()) { 187 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 188 return true; 189 } 190 } 191 } 192 193 return false; 194 } 195 196 private static void checkAllFilesHaveSameNamespace(Collection<JetFile> namespaceFiles) { 197 FqName commonFqName = null; 198 for (JetFile file : namespaceFiles) { 199 FqName fqName = JetPsiUtil.getFQName(file); 200 if (commonFqName != null) { 201 if (!commonFqName.equals(fqName)) { 202 throw new IllegalArgumentException("All files should have same package name"); 203 } 204 } 205 else { 206 commonFqName = JetPsiUtil.getFQName(file); 207 } 208 } 209 } 210 211 private void generateStaticInitializers( 212 NamespaceDescriptor descriptor, 213 @NotNull ClassBuilder builder, 214 @NotNull JetFile file, 215 @NotNull FieldOwnerContext context 216 ) { 217 List<JetProperty> properties = collectPropertiesToInitialize(file); 218 if (properties.isEmpty()) return; 219 220 MethodVisitor mv = builder.newMethod(file, ACC_STATIC, "<clinit>", "()V", null, null); 221 if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { 222 mv.visitCode(); 223 224 FrameMap frameMap = new FrameMap(); 225 226 SimpleFunctionDescriptorImpl clInit = 227 new SimpleFunctionDescriptorImpl(descriptor, Collections.<AnnotationDescriptor>emptyList(), 228 Name.special("<clinit>"), 229 CallableMemberDescriptor.Kind.SYNTHESIZED); 230 clInit.initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(), 231 Collections.<ValueParameterDescriptor>emptyList(), null, null, Visibilities.PRIVATE, false); 232 233 ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, context.intoFunction(clInit), state); 234 235 for (JetDeclaration declaration : properties) { 236 ImplementationBodyCodegen. 237 initializeProperty(codegen, state.getBindingContext(), (JetProperty) declaration); 238 } 239 240 mv.visitInsn(RETURN); 241 FunctionCodegen.endVisit(mv, "static initializer for namespace", file); 242 mv.visitEnd(); 243 } 244 } 245 246 @NotNull 247 private List<JetProperty> collectPropertiesToInitialize(@NotNull JetFile file) { 248 List<JetProperty> result = Lists.newArrayList(); 249 for (JetDeclaration declaration : file.getDeclarations()) { 250 if (declaration instanceof JetProperty && 251 ImplementationBodyCodegen.shouldInitializeProperty((JetProperty) declaration, typeMapper)) { 252 result.add((JetProperty) declaration); 253 } 254 } 255 return result; 256 } 257 258 public void done() { 259 v.done(); 260 } 261 262 @NotNull 263 public static JvmClassName getJVMClassNameForKotlinNs(@NotNull FqName fqName) { 264 String packageClassName = PackageClassUtils.getPackageClassName(fqName); 265 if (fqName.isRoot()) { 266 return JvmClassName.byInternalName(packageClassName); 267 } 268 269 return JvmClassName.byFqNameWithoutInnerClasses(fqName.child(Name.identifier(packageClassName))); 270 } 271 272 @NotNull 273 private static String getMultiFileNamespaceInternalName(@NotNull String namespaceInternalName, @NotNull PsiFile file) { 274 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); 275 276 // path hashCode to prevent same name / different path collision 277 return namespaceInternalName + "$src$" + replaceSpecialSymbols(fileName) + "$" + Integer.toHexString( 278 CodegenUtil.getPathHashCode(file)); 279 } 280 281 private static String replaceSpecialSymbols(@NotNull String str) { 282 return str.replace('.', '_'); 283 } 284 285 @NotNull 286 public static String getNamespacePartInternalName(@NotNull JetFile file) { 287 FqName fqName = JetPsiUtil.getFQName(file); 288 JvmClassName namespaceJvmClassName = NamespaceCodegen.getJVMClassNameForKotlinNs(fqName); 289 String namespaceInternalName = namespaceJvmClassName.getInternalName(); 290 return NamespaceCodegen.getMultiFileNamespaceInternalName(namespaceInternalName, file); 291 } 292 }