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.Lists; 020 import com.google.common.collect.Ordering; 021 import com.intellij.openapi.application.ApplicationManager; 022 import com.intellij.openapi.progress.ProcessCanceledException; 023 import com.intellij.openapi.util.io.FileUtil; 024 import com.intellij.openapi.vfs.VirtualFile; 025 import com.intellij.psi.PsiFile; 026 import com.intellij.util.ArrayUtil; 027 import com.intellij.util.PathUtil; 028 import com.intellij.util.SmartList; 029 import org.jetbrains.annotations.NotNull; 030 import org.jetbrains.annotations.Nullable; 031 import org.jetbrains.jet.codegen.context.CodegenContext; 032 import org.jetbrains.jet.codegen.context.FieldOwnerContext; 033 import org.jetbrains.jet.codegen.context.MethodContext; 034 import org.jetbrains.jet.codegen.context.PackageContext; 035 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; 036 import org.jetbrains.jet.codegen.state.GenerationState; 037 import org.jetbrains.jet.config.IncrementalCompilation; 038 import org.jetbrains.jet.descriptors.serialization.BitEncoding; 039 import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer; 040 import org.jetbrains.jet.descriptors.serialization.PackageData; 041 import org.jetbrains.jet.descriptors.serialization.ProtoBuf; 042 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor; 043 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor; 044 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor; 045 import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; 046 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 047 import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor; 048 import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; 049 import org.jetbrains.jet.lang.psi.*; 050 import org.jetbrains.jet.lang.resolve.BindingContext; 051 import org.jetbrains.jet.lang.resolve.MemberComparator; 052 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 053 import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames; 054 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 055 import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyJavaPackageFragmentScope; 056 import org.jetbrains.jet.lang.resolve.kotlin.BaseDescriptorDeserializer; 057 import org.jetbrains.jet.lang.resolve.name.FqName; 058 import org.jetbrains.jet.lang.resolve.name.Name; 059 import org.jetbrains.org.objectweb.asm.AnnotationVisitor; 060 import org.jetbrains.org.objectweb.asm.MethodVisitor; 061 import org.jetbrains.org.objectweb.asm.Type; 062 063 import java.util.*; 064 065 import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses; 066 import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses; 067 import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver; 068 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName; 069 import static org.jetbrains.org.objectweb.asm.Opcodes.*; 070 071 public class PackageCodegen { 072 private final GenerationState state; 073 private final ClassBuilderOnDemand v; 074 private final Collection<JetFile> files; 075 private final PackageFragmentDescriptor packageFragment; 076 private final PackageFragmentDescriptor compiledPackageFragment; 077 078 public PackageCodegen( 079 @NotNull ClassBuilderOnDemand v, 080 @NotNull final FqName fqName, 081 @NotNull GenerationState state, 082 @NotNull Collection<JetFile> packageFiles 083 ) { 084 this.state = state; 085 this.v = v; 086 this.files = packageFiles; 087 this.packageFragment = getOnlyPackageFragment(); 088 this.compiledPackageFragment = getCompiledPackageFragment(); 089 090 final PsiFile sourceFile = packageFiles.size() == 1 && getPreviouslyCompiledCallables().isEmpty() 091 ? packageFiles.iterator().next().getContainingFile() : null; 092 093 v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() { 094 @Override 095 public void doSomething(@NotNull ClassBuilder v) { 096 v.defineClass(sourceFile, V1_6, 097 ACC_PUBLIC | ACC_FINAL, 098 JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(fqName)).getInternalName(), 099 null, 100 "java/lang/Object", 101 ArrayUtil.EMPTY_STRING_ARRAY 102 ); 103 //We don't generate any source information for package with multiple files 104 if (sourceFile != null) { 105 v.visitSource(sourceFile.getName(), null); 106 } 107 } 108 }); 109 } 110 111 @Nullable 112 private PackageFragmentDescriptor getCompiledPackageFragment() { 113 if (!IncrementalCompilation.ENABLED) { 114 return null; 115 } 116 117 // TODO rewrite it to something more robust when module system is implemented 118 for (PackageFragmentDescriptor anotherFragment : packageFragment.getContainingDeclaration().getPackageFragmentProvider() 119 .getPackageFragments(packageFragment.getFqName())) { 120 if (anotherFragment.getMemberScope() instanceof LazyJavaPackageFragmentScope) { 121 return anotherFragment; 122 } 123 } 124 return null; 125 } 126 127 @NotNull 128 private List<DeserializedCallableMemberDescriptor> getPreviouslyCompiledCallables() { 129 List<DeserializedCallableMemberDescriptor> callables = Lists.newArrayList(); 130 if (compiledPackageFragment != null) { 131 for (DeclarationDescriptor member : compiledPackageFragment.getMemberScope().getAllDescriptors()) { 132 if (member instanceof DeserializedCallableMemberDescriptor) { 133 callables.add((DeserializedCallableMemberDescriptor) member); 134 } 135 } 136 } 137 return callables; 138 } 139 140 private void generateDelegationsToPreviouslyCompiled(Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { 141 for (final DeserializedCallableMemberDescriptor member : getPreviouslyCompiledCallables()) { 142 generateCallableMemberTasks.put(member, new Runnable() { 143 @Override 144 public void run() { 145 FieldOwnerContext context = CodegenContext.STATIC.intoPackageFacade( 146 Type.getObjectType(getPackagePartInternalName(member)), 147 compiledPackageFragment); 148 149 FunctionCodegen functionCodegen = new FunctionCodegen( 150 context, 151 v.getClassBuilder(), 152 state, 153 getMemberCodegen(context) 154 ); 155 156 if (member instanceof DeserializedSimpleFunctionDescriptor) { 157 DeserializedSimpleFunctionDescriptor function = (DeserializedSimpleFunctionDescriptor) member; 158 JvmMethodSignature signature = state.getTypeMapper().mapSignature(function, OwnerKind.PACKAGE); 159 functionCodegen.generateMethod(null, signature, function, 160 new FunctionGenerationStrategy() { 161 @Override 162 public void generateBody( 163 @NotNull MethodVisitor mv, 164 @NotNull JvmMethodSignature signature, 165 @NotNull MethodContext context, 166 @Nullable MemberCodegen<?> parentCodegen 167 ) { 168 throw new IllegalStateException("shouldn't be called"); 169 } 170 } 171 ); 172 } 173 else if (member instanceof DeserializedPropertyDescriptor) { 174 PropertyCodegen propertyCodegen = new PropertyCodegen( 175 context, v.getClassBuilder(), functionCodegen, getMemberCodegen(context)); 176 propertyCodegen.generateInPackageFacade((DeserializedPropertyDescriptor) member); 177 } 178 else { 179 throw new IllegalStateException("Unexpected member: " + member); 180 } 181 } 182 }); 183 } 184 } 185 186 public void generate(@NotNull CompilationErrorHandler errorHandler) { 187 List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1); 188 boolean shouldGeneratePackageClass = shouldGeneratePackageClass(files); 189 if (shouldGeneratePackageClass) { 190 bindings.add(v.getClassBuilder().getSerializationBindings()); 191 } 192 193 Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks = new HashMap<CallableMemberDescriptor, Runnable>(); 194 195 for (JetFile file : files) { 196 try { 197 ClassBuilder builder = generate(file, generateCallableMemberTasks); 198 if (builder != null) { 199 bindings.add(builder.getSerializationBindings()); 200 } 201 } 202 catch (ProcessCanceledException e) { 203 throw e; 204 } 205 catch (Throwable e) { 206 VirtualFile vFile = file.getVirtualFile(); 207 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); 208 DiagnosticUtils.throwIfRunningOnServer(e); 209 if (ApplicationManager.getApplication().isInternal()) { 210 //noinspection CallToPrintStackTrace 211 e.printStackTrace(); 212 } 213 } 214 } 215 216 if (shouldGeneratePackageClass) { 217 // Shouldn't generate delegations to previously compiled if we compile only "classes" part of a package. 218 generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks); 219 } 220 221 for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(generateCallableMemberTasks.keySet())) { 222 generateCallableMemberTasks.get(member).run(); 223 } 224 225 if (shouldGeneratePackageClass) { 226 writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings)); 227 } 228 229 assert v.isActivated() == shouldGeneratePackageClass : 230 "Different algorithms for generating package class and for heuristics for: " + packageFragment; 231 } 232 233 private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) { 234 if (state.getClassBuilderMode() != ClassBuilderMode.FULL) { 235 return; 236 } 237 238 // SCRIPT: Do not write annotations for scripts (if any is??) 239 for (JetFile file : files) { 240 if (file.isScript()) return; 241 } 242 243 DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(bindings)); 244 Collection<PackageFragmentDescriptor> packageFragments = compiledPackageFragment == null 245 ? Collections.singleton(packageFragment) 246 : Arrays.asList(packageFragment, compiledPackageFragment); 247 ProtoBuf.Package packageProto = serializer.packageProto(packageFragments).build(); 248 249 if (packageProto.getMemberCount() == 0) return; 250 251 PackageData data = new PackageData(createNameResolver(serializer.getNameTable()), packageProto); 252 253 AnnotationVisitor av = 254 v.getClassBuilder().newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true); 255 av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION); 256 AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME); 257 for (String string : BitEncoding.encodeBytes(data.toBytes())) { 258 array.visit(null, string); 259 } 260 array.visitEnd(); 261 av.visitEnd(); 262 } 263 264 @Nullable 265 private ClassBuilder generate(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { 266 boolean generateSrcClass = false; 267 Type packagePartType = getPackagePartType(getPackageClassFqName(packageFragment.getFqName()), file.getVirtualFile()); 268 PackageContext packagePartContext = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType); 269 270 for (JetDeclaration declaration : file.getDeclarations()) { 271 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 272 generateSrcClass = true; 273 } 274 else if (declaration instanceof JetClassOrObject) { 275 JetClassOrObject classOrObject = (JetClassOrObject) declaration; 276 if (state.getGenerateDeclaredClassFilter().shouldProcess(classOrObject)) { 277 generateClassOrObject(classOrObject); 278 } 279 } 280 else if (declaration instanceof JetScript) { 281 // SCRIPT: generate script code, should be separate execution branch 282 ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate(); 283 } 284 } 285 286 if (!generateSrcClass) return null; 287 288 ClassBuilder builder = state.getFactory().forPackagePart(packagePartType, file); 289 290 new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate(); 291 292 FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, packageFragment); 293 294 final MemberCodegen<?> memberCodegen = getMemberCodegen(packageFacade); 295 296 for (final JetDeclaration declaration : file.getDeclarations()) { 297 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) { 298 DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration); 299 assert descriptor instanceof CallableMemberDescriptor : 300 "Expected callable member, was " + descriptor + " for " + declaration.getText(); 301 generateCallableMemberTasks.put( 302 (CallableMemberDescriptor) descriptor, 303 new Runnable() { 304 @Override 305 public void run() { 306 memberCodegen.genFunctionOrProperty((JetTypeParameterListOwner) declaration, v.getClassBuilder()); 307 } 308 } 309 ); 310 } 311 } 312 313 return builder; 314 } 315 316 //TODO: FIX: Default method generated at facade without delegation 317 private MemberCodegen<?> getMemberCodegen(@NotNull FieldOwnerContext packageFacade) { 318 return new MemberCodegen<JetFile>(state, null, packageFacade, null, null) { 319 @Override 320 protected void generateDeclaration() { 321 throw new UnsupportedOperationException(); 322 } 323 324 @Override 325 protected void generateBody() { 326 throw new UnsupportedOperationException(); 327 } 328 329 @Override 330 protected void generateKotlinAnnotation() { 331 throw new UnsupportedOperationException(); 332 } 333 }; 334 } 335 336 @NotNull 337 private PackageFragmentDescriptor getOnlyPackageFragment() { 338 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>(); 339 for (JetFile file : files) { 340 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); 341 assert fragment != null : "package fragment is null for " + file; 342 343 if (!fragments.contains(fragment)) { 344 fragments.add(fragment); 345 } 346 } 347 if (fragments.size() != 1) { 348 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments); 349 } 350 return fragments.get(0); 351 } 352 353 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) { 354 JetFile file = classOrObject.getContainingJetFile(); 355 Type packagePartType = getPackagePartType(getPackageClassFqName(packageFragment.getFqName()), file.getVirtualFile()); 356 CodegenContext context = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType); 357 MemberCodegen.genClassOrObject(context, classOrObject, state, null); 358 } 359 360 /** 361 * @param packageFiles all files should have same package name 362 * @return 363 */ 364 public static boolean shouldGeneratePackageClass(@NotNull Collection<JetFile> packageFiles) { 365 for (JetFile file : packageFiles) { 366 for (JetDeclaration declaration : file.getDeclarations()) { 367 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 368 return true; 369 } 370 } 371 } 372 373 return false; 374 } 375 376 public void done() { 377 v.done(); 378 } 379 380 @NotNull 381 public static Type getPackagePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) { 382 String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); 383 384 // path hashCode to prevent same name / different path collision 385 String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString( 386 JvmCodegenUtil.getPathHashCode(file)); 387 388 return asmTypeByFqNameWithoutInnerClasses(facadeFqName.parent().child(Name.identifier(srcName))); 389 } 390 391 @NotNull 392 private static String replaceSpecialSymbols(@NotNull String str) { 393 return str.replace('.', '_'); 394 } 395 396 @NotNull 397 public static String getPackagePartInternalName(@NotNull JetFile file) { 398 FqName packageFqName = file.getPackageFqName(); 399 return getPackagePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName(); 400 } 401 402 @NotNull 403 public static String getPackagePartInternalName(@NotNull DeserializedCallableMemberDescriptor callable) { 404 FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName(); 405 FqName packagePartFqName = packageFqName.child(BaseDescriptorDeserializer.getPackagePartClassName(callable)); 406 return JvmClassName.byFqNameWithoutInnerClasses(packagePartFqName).getInternalName(); 407 } 408 }