001 /* 002 * Copyright 2010-2015 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.kotlin.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.vfs.VirtualFile; 024 import com.intellij.util.ArrayUtil; 025 import com.intellij.util.SmartList; 026 import com.intellij.util.containers.ContainerUtil; 027 import kotlin.jvm.functions.Function0; 028 import org.jetbrains.annotations.Mutable; 029 import org.jetbrains.annotations.NotNull; 030 import org.jetbrains.annotations.Nullable; 031 import org.jetbrains.kotlin.codegen.context.CodegenContext; 032 import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; 033 import org.jetbrains.kotlin.codegen.context.MethodContext; 034 import org.jetbrains.kotlin.codegen.context.PackageContext; 035 import org.jetbrains.kotlin.codegen.state.GenerationState; 036 import org.jetbrains.kotlin.config.IncrementalCompilation; 037 import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; 038 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 039 import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; 040 import org.jetbrains.kotlin.diagnostics.DiagnosticUtils; 041 import org.jetbrains.kotlin.fileClasses.FileClassesPackage; 042 import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo; 043 import org.jetbrains.kotlin.load.java.JvmAbi; 044 import org.jetbrains.kotlin.load.java.JvmAnnotationNames; 045 import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; 046 import org.jetbrains.kotlin.load.kotlin.PackageParts; 047 import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider; 048 import org.jetbrains.kotlin.name.FqName; 049 import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; 050 import org.jetbrains.kotlin.psi.*; 051 import org.jetbrains.kotlin.resolve.BindingContext; 052 import org.jetbrains.kotlin.resolve.MemberComparator; 053 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; 054 import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter; 055 import org.jetbrains.kotlin.resolve.scopes.JetScope; 056 import org.jetbrains.kotlin.serialization.DescriptorSerializer; 057 import org.jetbrains.kotlin.serialization.ProtoBuf; 058 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; 059 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor; 060 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor; 061 import org.jetbrains.kotlin.serialization.jvm.BitEncoding; 062 import org.jetbrains.org.objectweb.asm.AnnotationVisitor; 063 import org.jetbrains.org.objectweb.asm.MethodVisitor; 064 import org.jetbrains.org.objectweb.asm.Type; 065 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 066 import org.jetbrains.org.objectweb.asm.commons.Method; 067 068 import java.util.*; 069 070 import static org.jetbrains.kotlin.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses; 071 import static org.jetbrains.kotlin.codegen.AsmUtil.method; 072 import static org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassFqName; 073 import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PACKAGE_TYPE; 074 import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.getType; 075 import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.*; 076 import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN; 077 import static org.jetbrains.org.objectweb.asm.Opcodes.*; 078 079 public class PackageCodegen { 080 private final ClassBuilderOnDemand v; 081 private final GenerationState state; 082 private final Collection<JetFile> files; 083 private final Type packageClassType; 084 private final PackageFragmentDescriptor packageFragment; 085 private final PackageFragmentDescriptor compiledPackageFragment; 086 private final List<DeserializedCallableMemberDescriptor> previouslyCompiledCallables; 087 088 private final PackageParts packageParts; 089 090 public PackageCodegen( 091 @NotNull GenerationState state, 092 @NotNull Collection<JetFile> files, 093 @NotNull FqName fqName 094 ) { 095 this.state = state; 096 this.files = files; 097 this.packageFragment = getOnlyPackageFragment(fqName); 098 this.packageClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName)); 099 this.compiledPackageFragment = getCompiledPackageFragment(fqName); 100 this.previouslyCompiledCallables = filterDeserializedCallables(compiledPackageFragment); 101 102 assert packageFragment != null || compiledPackageFragment != null : fqName.asString() + " " + files; 103 104 this.v = new ClassBuilderOnDemand(new Function0<ClassBuilder>() { 105 @Override 106 public ClassBuilder invoke() { 107 Collection<JetFile> files = PackageCodegen.this.files; 108 JetFile sourceFile = getRepresentativePackageFile(files); 109 110 ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor( 111 PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment), 112 packageClassType, PackagePartClassUtils.getFilesWithCallables(files) 113 ); 114 v.defineClass(sourceFile, V1_6, 115 ACC_PUBLIC | ACC_FINAL | ACC_DEPRECATED, 116 packageClassType.getInternalName(), 117 null, 118 "java/lang/Object", 119 ArrayUtil.EMPTY_STRING_ARRAY 120 ); 121 //We don't generate any source information for package with multiple files 122 if (sourceFile != null) { 123 v.visitSource(sourceFile.getName(), null); 124 } 125 return v; 126 } 127 }); 128 packageParts = new PackageParts(fqName.asString()); 129 } 130 131 // Returns null if file has callables in several files 132 @Nullable 133 private JetFile getRepresentativePackageFile(@NotNull Collection<JetFile> packageFiles) { 134 if (!previouslyCompiledCallables.isEmpty()) { 135 return null; 136 } 137 138 List<JetFile> packageFilesWithCallables = PackagePartClassUtils.getFilesWithCallables(packageFiles); 139 return packageFilesWithCallables.size() == 1 ? packageFilesWithCallables.get(0) : null; 140 } 141 142 @Nullable 143 private PackageFragmentDescriptor getCompiledPackageFragment(@NotNull FqName fqName) { 144 if (!IncrementalCompilation.isEnabled()) { 145 return null; 146 } 147 148 // TODO rewrite it to something more robust when module system is implemented 149 for (PackageFragmentDescriptor fragment : state.getModule().getPackage(fqName).getFragments()) { 150 if (fragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment && 151 ((IncrementalPackageFragmentProvider.IncrementalPackageFragment) fragment).getTarget().equals(state.getTargetId())) { 152 return fragment; 153 } 154 } 155 return null; 156 } 157 158 @NotNull 159 private static List<DeserializedCallableMemberDescriptor> filterDeserializedCallables(@Nullable PackageFragmentDescriptor packageFragment) { 160 if (packageFragment == null) { 161 return Collections.emptyList(); 162 } 163 List<DeserializedCallableMemberDescriptor> callables = Lists.newArrayList(); 164 for (DeclarationDescriptor member : packageFragment.getMemberScope().getDescriptors(DescriptorKindFilter.CALLABLES, JetScope.ALL_NAME_FILTER)) { 165 if (member instanceof DeserializedCallableMemberDescriptor) { 166 callables.add((DeserializedCallableMemberDescriptor) member); 167 } 168 } 169 return callables; 170 } 171 172 private void generateDelegationsToPreviouslyCompiled(@NotNull @Mutable Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { 173 for (final DeserializedCallableMemberDescriptor member : previouslyCompiledCallables) { 174 generateCallableMemberTasks.put(member, new Runnable() { 175 @Override 176 public void run() { 177 FieldOwnerContext context = state.getRootContext().intoPackageFacade( 178 AsmUtil.asmTypeByFqNameWithoutInnerClasses(PackagePartClassUtils.getPackagePartFqName(member)), 179 compiledPackageFragment 180 ); 181 182 MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(context); 183 184 if (member instanceof DeserializedSimpleFunctionDescriptor) { 185 DeserializedSimpleFunctionDescriptor function = (DeserializedSimpleFunctionDescriptor) member; 186 memberCodegen.functionCodegen.generateMethod(OtherOrigin(function), function, 187 new FunctionGenerationStrategy() { 188 @Override 189 public void generateBody( 190 @NotNull MethodVisitor mv, 191 @NotNull FrameMap frameMap, 192 @NotNull JvmMethodSignature signature, 193 @NotNull MethodContext context, 194 @NotNull MemberCodegen<?> parentCodegen 195 ) { 196 throw new IllegalStateException("shouldn't be called"); 197 } 198 } 199 ); 200 201 memberCodegen.functionCodegen.generateDefaultIfNeeded( 202 context.intoFunction(function), function, OwnerKind.PACKAGE, DefaultParameterValueLoader.DEFAULT, null 203 ); 204 205 memberCodegen.functionCodegen.generateOverloadsWithDefaultValues(null, function, function); 206 } 207 else if (member instanceof DeserializedPropertyDescriptor) { 208 memberCodegen.propertyCodegen.generateInPackageFacade((DeserializedPropertyDescriptor) member); 209 } 210 else { 211 throw new IllegalStateException("Unexpected member: " + member); 212 } 213 } 214 }); 215 } 216 } 217 218 public void generate(@NotNull CompilationErrorHandler errorHandler) { 219 List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1); 220 221 Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks = new HashMap<CallableMemberDescriptor, Runnable>(); 222 223 for (JetFile file : files) { 224 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); 225 try { 226 ClassBuilder builder = generateFile(file, generateCallableMemberTasks); 227 if (builder != null) { 228 bindings.add(builder.getSerializationBindings()); 229 } 230 } 231 catch (ProcessCanceledException e) { 232 throw e; 233 } 234 catch (Throwable e) { 235 VirtualFile vFile = file.getVirtualFile(); 236 errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl()); 237 DiagnosticUtils.throwIfRunningOnServer(e); 238 if (ApplicationManager.getApplication().isInternal()) { 239 //noinspection CallToPrintStackTrace 240 e.printStackTrace(); 241 } 242 } 243 } 244 245 generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks); 246 247 if (!generateCallableMemberTasks.isEmpty()) { 248 generatePackageFacadeClass(generateCallableMemberTasks, bindings); 249 } 250 } 251 252 private void generatePackageFacadeClass( 253 @NotNull Map<CallableMemberDescriptor, Runnable> tasks, 254 @NotNull List<JvmSerializationBindings> bindings 255 ) { 256 generateKotlinPackageReflectionField(); 257 MemberCodegen.generateModuleNameField(state, v); 258 259 for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(tasks.keySet())) { 260 tasks.get(member).run(); 261 } 262 263 bindings.add(v.getSerializationBindings()); 264 writeDeprecatedAnnotation(); 265 writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings)); 266 } 267 268 private void generateKotlinPackageReflectionField() { 269 MethodVisitor mv = v.newMethod(NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null); 270 Method method = method("createKotlinPackage", K_PACKAGE_TYPE, getType(Class.class), getType(String.class)); 271 InstructionAdapter iv = new InstructionAdapter(mv); 272 MemberCodegen.generateReflectionObjectField(state, packageClassType, v, method, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME, iv); 273 iv.areturn(Type.VOID_TYPE); 274 FunctionCodegen.endVisit(mv, "package facade static initializer", null); 275 } 276 277 private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) { 278 if (state.getClassBuilderMode() != ClassBuilderMode.FULL) { 279 return; 280 } 281 282 // SCRIPT: Do not write KotlinPackage annotation for scripts (if any is??) 283 for (JetFile file : files) { 284 if (file.isScript()) return; 285 } 286 287 DescriptorSerializer serializer = DescriptorSerializer.createTopLevel(new JvmSerializerExtension(bindings, state.getTypeMapper())); 288 Collection<PackageFragmentDescriptor> packageFragments = Lists.newArrayList(); 289 ContainerUtil.addIfNotNull(packageFragments, packageFragment); 290 ContainerUtil.addIfNotNull(packageFragments, compiledPackageFragment); 291 ProtoBuf.Package packageProto = serializer.packageProtoWithoutDescriptors().build(); 292 293 AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true); 294 JvmCodegenUtil.writeAbiVersion(av); 295 AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME); 296 for (String string : BitEncoding.encodeBytes(serializer.serialize(packageProto))) { 297 array.visit(null, string); 298 } 299 array.visitEnd(); 300 av.visitEnd(); 301 } 302 303 private void writeDeprecatedAnnotation() { 304 AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.JAVA_LANG_DEPRECATED), true); 305 av.visitEnd(); 306 } 307 308 @Nullable 309 private ClassBuilder generateFile(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { 310 JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file); 311 312 if (fileClassInfo.getIsMultifileClass()) { 313 Type fileFacadeType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFacadeClassFqName()); 314 addDelegateToFileClassMemberTasks(file, generateCallableMemberTasks, fileFacadeType); 315 return null; 316 } 317 318 Type fileClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFileClassFqName()); 319 PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, fileClassType); 320 321 boolean generatePackagePart = false; 322 323 for (JetDeclaration declaration : file.getDeclarations()) { 324 if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { 325 generatePackagePart = true; 326 } 327 else if (declaration instanceof JetClassOrObject) { 328 JetClassOrObject classOrObject = (JetClassOrObject) declaration; 329 if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) { 330 generateClassOrObject(classOrObject); 331 } 332 } 333 else if (declaration instanceof JetScript) { 334 JetScript script = (JetScript) declaration; 335 336 // SCRIPT: generate script code, should be separate execution branch 337 if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) { 338 ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate(); 339 } 340 } 341 } 342 343 if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return null; 344 345 String name = fileClassType.getInternalName(); 346 packageParts.getParts().add(name.substring(name.lastIndexOf('/') + 1)); 347 348 ClassBuilder builder = state.getFactory().newVisitor(PackagePart(file, packageFragment), fileClassType, file); 349 350 new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate(); 351 352 addDelegateToFileClassMemberTasks(file, generateCallableMemberTasks, fileClassType); 353 354 return builder; 355 } 356 357 private void addDelegateToFileClassMemberTasks( 358 @NotNull JetFile file, 359 @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks, 360 @NotNull Type fileClassType 361 ) { 362 FieldOwnerContext packageFacade = state.getRootContext().intoPackageFacade(fileClassType, packageFragment); 363 final MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(packageFacade); 364 365 for (final JetDeclaration declaration : file.getDeclarations()) { 366 if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) { 367 DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration); 368 assert descriptor instanceof CallableMemberDescriptor : 369 "Expected callable member, was " + descriptor + " for " + declaration.getText(); 370 generateCallableMemberTasks.put( 371 (CallableMemberDescriptor) descriptor, 372 new Runnable() { 373 @Override 374 public void run() { 375 memberCodegen.genFunctionOrProperty(declaration); 376 } 377 } 378 ); 379 } 380 } 381 } 382 383 private MemberCodegen<?> createCodegenForPartOfPackageFacade(@NotNull FieldOwnerContext packageFacade) { 384 return new MemberCodegen<JetFile>(state, null, packageFacade, null, v) { 385 @Override 386 protected void generateDeclaration() { 387 throw new UnsupportedOperationException(); 388 } 389 390 @Override 391 protected void generateBody() { 392 throw new UnsupportedOperationException(); 393 } 394 395 @Override 396 protected void generateKotlinAnnotation() { 397 throw new UnsupportedOperationException(); 398 } 399 }; 400 } 401 402 @Nullable 403 private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedFqName) { 404 SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>(); 405 for (JetFile file : files) { 406 PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); 407 assert fragment != null : "package fragment is null for " + file + "\n" + file.getText(); 408 409 assert expectedFqName.equals(fragment.getFqName()) : 410 "expected package fq name: " + expectedFqName + ", actual: " + fragment.getFqName(); 411 412 if (!fragments.contains(fragment)) { 413 fragments.add(fragment); 414 } 415 } 416 if (fragments.size() > 1) { 417 throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments); 418 } 419 420 if (fragments.isEmpty()) { 421 return null; 422 } 423 return fragments.get(0); 424 } 425 426 public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) { 427 JetFile file = classOrObject.getContainingJetFile(); 428 Type packagePartType = FileClassesPackage.getFileClassType(state.getFileClassesProvider(), file); 429 CodegenContext context = state.getRootContext().intoPackagePart(packageFragment, packagePartType); 430 MemberCodegen.genClassOrObject(context, classOrObject, state, null); 431 } 432 433 public void done() { 434 v.done(); 435 } 436 437 public PackageParts getPackageParts() { 438 return packageParts; 439 } 440 441 public Collection<JetFile> getFiles() { 442 return files; 443 } 444 }