From 012ffa299343e9d9fe23133dd3c14a4e8c027c30 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 17 Dec 2019 17:43:42 +0100 Subject: [PATCH] Support new scheme of compilation of OptionalExpectation annotations Instead of generating these annotation classes as package-private on JVM, serialize their metadata to the .kotlin_module file, and load it when compiling dependent multiplatform modules. The problem with generating them as package-private was that kotlin-stdlib for JVM would end up declaring symbols from other platforms, which would include some annotations from package kotlin.native. But using that package is discouraged by some tools because it has a Java keyword in its name. In particular, jlink refused to work with such artifact altogether (KT-21266). #KT-38652 Fixed --- .../metadata/jvm/DebugJvmModuleProtoBuf.java | 594 +++++++++++++++++- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 4 - .../kotlin/codegen/ClassFileFactory.java | 19 +- .../kotlin/codegen/PackageCodegenImpl.java | 8 +- .../kotlin/codegen/PackagePartRegistry.kt | 2 + .../kotlin/codegen/classFileUtils.kt | 76 ++- .../compiler/TopDownAnalyzerFacadeForJVM.kt | 11 +- .../kotlin/frontend/java/di/injection.kt | 2 + .../IncrementalPackagePartProvider.kt | 12 +- ...tionalAnnotationPackageFragmentProvider.kt | 118 ++++ .../lower/ExpectDeclarationsRemoveLowering.kt | 13 +- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 5 +- .../backend/jvm/codegen/irCodegenUtils.kt | 3 - .../jvm/lower/ProcessOptionalAnnotations.kt | 30 + .../ir/util/ExpectDeclarationRemover.kt | 11 +- .../jetbrains/kotlin/ir/backend/js/klib.kt | 9 +- .../multiplatform/optionalExpectation.txt | 7 - .../optionalAnnotation.kt | 10 +- .../multiplatform/optionalExpectation.kt | 12 - .../flags/WriteFlagsTestGenerated.java | 18 - .../codegen/ir/IrWriteFlagsTestGenerated.java | 18 - .../load/kotlin/JvmPackagePartProviderBase.kt | 16 + .../kotlin/load/kotlin/PackagePartProvider.kt | 5 + core/metadata.jvm/src/jvm_module.proto | 6 + .../metadata/jvm/JvmModuleProtoBuf.java | 362 +++++++++++ .../jvm/deserialization/BinaryModuleData.kt | 11 +- .../jvm/deserialization/ModuleMapping.kt | 26 +- .../caches/resolve/IDEPackagePartProvider.kt | 7 + .../build/IncrementalJvmJpsTestGenerated.java | 18 + .../custom/complementaryFiles/build.log | 1 - .../_dependencies.txt | 7 + .../modifyOptionalAnnotationUsage/build.log | 24 + .../c_declaration.kt | 3 + .../modifyOptionalAnnotationUsage/c_usage.kt | 2 + .../c_usage.kt.new.1 | 2 + .../metadata/jvm/KotlinModuleMetadata.kt | 49 +- .../org/jetbrains/kotlin/kotlinp/Kotlinp.kt | 4 +- .../org/jetbrains/kotlin/kotlinp/printers.kt | 17 +- .../kotlinp/test/KotlinpTestGenerated.java | 5 + .../kotlin/kotlinp/test/KotlinpTestUtils.kt | 3 +- .../kotlinp/testData/OptionalAnnotation.kt | 20 + .../kotlinp/testData/OptionalAnnotation.txt | 44 ++ 42 files changed, 1469 insertions(+), 145 deletions(-) create mode 100644 compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/multiplatform/OptionalAnnotationPackageFragmentProvider.kt create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ProcessOptionalAnnotations.kt delete mode 100644 compiler/testData/writeFlags/multiplatform/optionalExpectation.kt create mode 100644 jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/_dependencies.txt create mode 100644 jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/build.log create mode 100644 jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_declaration.kt create mode 100644 jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt create mode 100644 jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt.new.1 create mode 100644 libraries/tools/kotlinp/testData/OptionalAnnotation.kt create mode 100644 libraries/tools/kotlinp/testData/OptionalAnnotation.txt diff --git a/build-common/test/org/jetbrains/kotlin/metadata/jvm/DebugJvmModuleProtoBuf.java b/build-common/test/org/jetbrains/kotlin/metadata/jvm/DebugJvmModuleProtoBuf.java index 25f5cfc3965..29ab02863d0 100644 --- a/build-common/test/org/jetbrains/kotlin/metadata/jvm/DebugJvmModuleProtoBuf.java +++ b/build-common/test/org/jetbrains/kotlin/metadata/jvm/DebugJvmModuleProtoBuf.java @@ -208,6 +208,65 @@ public final class DebugJvmModuleProtoBuf { */ org.jetbrains.kotlin.metadata.DebugProtoBuf.AnnotationOrBuilder getAnnotationOrBuilder( int index); + + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + java.util.List + getOptionalAnnotationClassList(); + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + org.jetbrains.kotlin.metadata.DebugProtoBuf.Class getOptionalAnnotationClass(int index); + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + int getOptionalAnnotationClassCount(); + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + java.util.List + getOptionalAnnotationClassOrBuilderList(); + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + org.jetbrains.kotlin.metadata.DebugProtoBuf.ClassOrBuilder getOptionalAnnotationClassOrBuilder( + int index); } /** * Protobuf type {@code org.jetbrains.kotlin.metadata.jvm.Module} @@ -320,6 +379,14 @@ public final class DebugJvmModuleProtoBuf { annotation_.add(input.readMessage(org.jetbrains.kotlin.metadata.DebugProtoBuf.Annotation.PARSER, extensionRegistry)); break; } + case 130: { + if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000040; + } + optionalAnnotationClass_.add(input.readMessage(org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.PARSER, extensionRegistry)); + break; + } } } } catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) { @@ -340,6 +407,9 @@ public final class DebugJvmModuleProtoBuf { if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { annotation_ = java.util.Collections.unmodifiableList(annotation_); } + if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = java.util.Collections.unmodifiableList(optionalAnnotationClass_); + } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); } @@ -628,6 +698,76 @@ public final class DebugJvmModuleProtoBuf { return annotation_.get(index); } + public static final int OPTIONAL_ANNOTATION_CLASS_FIELD_NUMBER = 16; + private java.util.List optionalAnnotationClass_; + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public java.util.List getOptionalAnnotationClassList() { + return optionalAnnotationClass_; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public java.util.List + getOptionalAnnotationClassOrBuilderList() { + return optionalAnnotationClass_; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public int getOptionalAnnotationClassCount() { + return optionalAnnotationClass_.size(); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.Class getOptionalAnnotationClass(int index) { + return optionalAnnotationClass_.get(index); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.ClassOrBuilder getOptionalAnnotationClassOrBuilder( + int index) { + return optionalAnnotationClass_.get(index); + } + private void initFields() { packageParts_ = java.util.Collections.emptyList(); metadataParts_ = java.util.Collections.emptyList(); @@ -635,6 +775,7 @@ public final class DebugJvmModuleProtoBuf { stringTable_ = org.jetbrains.kotlin.metadata.DebugProtoBuf.StringTable.getDefaultInstance(); qualifiedNameTable_ = org.jetbrains.kotlin.metadata.DebugProtoBuf.QualifiedNameTable.getDefaultInstance(); annotation_ = java.util.Collections.emptyList(); + optionalAnnotationClass_ = java.util.Collections.emptyList(); } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -666,6 +807,12 @@ public final class DebugJvmModuleProtoBuf { return false; } } + for (int i = 0; i < getOptionalAnnotationClassCount(); i++) { + if (!getOptionalAnnotationClass(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } memoizedIsInitialized = 1; return true; } @@ -691,6 +838,9 @@ public final class DebugJvmModuleProtoBuf { for (int i = 0; i < annotation_.size(); i++) { output.writeMessage(6, annotation_.get(i)); } + for (int i = 0; i < optionalAnnotationClass_.size(); i++) { + output.writeMessage(16, optionalAnnotationClass_.get(i)); + } getUnknownFields().writeTo(output); } @@ -729,6 +879,10 @@ public final class DebugJvmModuleProtoBuf { size += org.jetbrains.kotlin.protobuf.CodedOutputStream .computeMessageSize(6, annotation_.get(i)); } + for (int i = 0; i < optionalAnnotationClass_.size(); i++) { + size += org.jetbrains.kotlin.protobuf.CodedOutputStream + .computeMessageSize(16, optionalAnnotationClass_.get(i)); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -843,6 +997,7 @@ public final class DebugJvmModuleProtoBuf { getStringTableFieldBuilder(); getQualifiedNameTableFieldBuilder(); getAnnotationFieldBuilder(); + getOptionalAnnotationClassFieldBuilder(); } } private static Builder create() { @@ -883,6 +1038,12 @@ public final class DebugJvmModuleProtoBuf { } else { annotationBuilder_.clear(); } + if (optionalAnnotationClassBuilder_ == null) { + optionalAnnotationClass_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + } else { + optionalAnnotationClassBuilder_.clear(); + } return this; } @@ -959,6 +1120,15 @@ public final class DebugJvmModuleProtoBuf { } else { result.annotation_ = annotationBuilder_.build(); } + if (optionalAnnotationClassBuilder_ == null) { + if (((bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = java.util.Collections.unmodifiableList(optionalAnnotationClass_); + bitField0_ = (bitField0_ & ~0x00000040); + } + result.optionalAnnotationClass_ = optionalAnnotationClass_; + } else { + result.optionalAnnotationClass_ = optionalAnnotationClassBuilder_.build(); + } result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -1069,6 +1239,32 @@ public final class DebugJvmModuleProtoBuf { } } } + if (optionalAnnotationClassBuilder_ == null) { + if (!other.optionalAnnotationClass_.isEmpty()) { + if (optionalAnnotationClass_.isEmpty()) { + optionalAnnotationClass_ = other.optionalAnnotationClass_; + bitField0_ = (bitField0_ & ~0x00000040); + } else { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.addAll(other.optionalAnnotationClass_); + } + onChanged(); + } + } else { + if (!other.optionalAnnotationClass_.isEmpty()) { + if (optionalAnnotationClassBuilder_.isEmpty()) { + optionalAnnotationClassBuilder_.dispose(); + optionalAnnotationClassBuilder_ = null; + optionalAnnotationClass_ = other.optionalAnnotationClass_; + bitField0_ = (bitField0_ & ~0x00000040); + optionalAnnotationClassBuilder_ = + org.jetbrains.kotlin.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getOptionalAnnotationClassFieldBuilder() : null; + } else { + optionalAnnotationClassBuilder_.addAllMessages(other.optionalAnnotationClass_); + } + } + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -1098,6 +1294,12 @@ public final class DebugJvmModuleProtoBuf { return false; } } + for (int i = 0; i < getOptionalAnnotationClassCount(); i++) { + if (!getOptionalAnnotationClass(i).isInitialized()) { + + return false; + } + } return true; } @@ -2426,6 +2628,372 @@ public final class DebugJvmModuleProtoBuf { return annotationBuilder_; } + private java.util.List optionalAnnotationClass_ = + java.util.Collections.emptyList(); + private void ensureOptionalAnnotationClassIsMutable() { + if (!((bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = new java.util.ArrayList(optionalAnnotationClass_); + bitField0_ |= 0x00000040; + } + } + + private org.jetbrains.kotlin.protobuf.RepeatedFieldBuilder< + org.jetbrains.kotlin.metadata.DebugProtoBuf.Class, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder, org.jetbrains.kotlin.metadata.DebugProtoBuf.ClassOrBuilder> optionalAnnotationClassBuilder_; + + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public java.util.List getOptionalAnnotationClassList() { + if (optionalAnnotationClassBuilder_ == null) { + return java.util.Collections.unmodifiableList(optionalAnnotationClass_); + } else { + return optionalAnnotationClassBuilder_.getMessageList(); + } + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public int getOptionalAnnotationClassCount() { + if (optionalAnnotationClassBuilder_ == null) { + return optionalAnnotationClass_.size(); + } else { + return optionalAnnotationClassBuilder_.getCount(); + } + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.Class getOptionalAnnotationClass(int index) { + if (optionalAnnotationClassBuilder_ == null) { + return optionalAnnotationClass_.get(index); + } else { + return optionalAnnotationClassBuilder_.getMessage(index); + } + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder setOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class value) { + if (optionalAnnotationClassBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.set(index, value); + onChanged(); + } else { + optionalAnnotationClassBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder setOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder builderForValue) { + if (optionalAnnotationClassBuilder_ == null) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.set(index, builderForValue.build()); + onChanged(); + } else { + optionalAnnotationClassBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass(org.jetbrains.kotlin.metadata.DebugProtoBuf.Class value) { + if (optionalAnnotationClassBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(value); + onChanged(); + } else { + optionalAnnotationClassBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class value) { + if (optionalAnnotationClassBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(index, value); + onChanged(); + } else { + optionalAnnotationClassBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass( + org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder builderForValue) { + if (optionalAnnotationClassBuilder_ == null) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(builderForValue.build()); + onChanged(); + } else { + optionalAnnotationClassBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder builderForValue) { + if (optionalAnnotationClassBuilder_ == null) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(index, builderForValue.build()); + onChanged(); + } else { + optionalAnnotationClassBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addAllOptionalAnnotationClass( + java.lang.Iterable values) { + if (optionalAnnotationClassBuilder_ == null) { + ensureOptionalAnnotationClassIsMutable(); + org.jetbrains.kotlin.protobuf.AbstractMessageLite.Builder.addAll( + values, optionalAnnotationClass_); + onChanged(); + } else { + optionalAnnotationClassBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder clearOptionalAnnotationClass() { + if (optionalAnnotationClassBuilder_ == null) { + optionalAnnotationClass_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + } else { + optionalAnnotationClassBuilder_.clear(); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder removeOptionalAnnotationClass(int index) { + if (optionalAnnotationClassBuilder_ == null) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.remove(index); + onChanged(); + } else { + optionalAnnotationClassBuilder_.remove(index); + } + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder getOptionalAnnotationClassBuilder( + int index) { + return getOptionalAnnotationClassFieldBuilder().getBuilder(index); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.ClassOrBuilder getOptionalAnnotationClassOrBuilder( + int index) { + if (optionalAnnotationClassBuilder_ == null) { + return optionalAnnotationClass_.get(index); } else { + return optionalAnnotationClassBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public java.util.List + getOptionalAnnotationClassOrBuilderList() { + if (optionalAnnotationClassBuilder_ != null) { + return optionalAnnotationClassBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(optionalAnnotationClass_); + } + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder addOptionalAnnotationClassBuilder() { + return getOptionalAnnotationClassFieldBuilder().addBuilder( + org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.getDefaultInstance()); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder addOptionalAnnotationClassBuilder( + int index) { + return getOptionalAnnotationClassFieldBuilder().addBuilder( + index, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.getDefaultInstance()); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public java.util.List + getOptionalAnnotationClassBuilderList() { + return getOptionalAnnotationClassFieldBuilder().getBuilderList(); + } + private org.jetbrains.kotlin.protobuf.RepeatedFieldBuilder< + org.jetbrains.kotlin.metadata.DebugProtoBuf.Class, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder, org.jetbrains.kotlin.metadata.DebugProtoBuf.ClassOrBuilder> + getOptionalAnnotationClassFieldBuilder() { + if (optionalAnnotationClassBuilder_ == null) { + optionalAnnotationClassBuilder_ = new org.jetbrains.kotlin.protobuf.RepeatedFieldBuilder< + org.jetbrains.kotlin.metadata.DebugProtoBuf.Class, org.jetbrains.kotlin.metadata.DebugProtoBuf.Class.Builder, org.jetbrains.kotlin.metadata.DebugProtoBuf.ClassOrBuilder>( + optionalAnnotationClass_, + ((bitField0_ & 0x00000040) == 0x00000040), + getParentForChildren(), + isClean()); + optionalAnnotationClass_ = null; + } + return optionalAnnotationClassBuilder_; + } + // @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.metadata.jvm.Module) } @@ -4644,7 +5212,7 @@ public final class DebugJvmModuleProtoBuf { "\n,core/metadata.jvm/src/jvm_module.debug" + ".proto\022!org.jetbrains.kotlin.metadata.jv" + "m\032&core/metadata/src/metadata.debug.prot" + - "o\"\205\003\n\006Module\022F\n\rpackage_parts\030\001 \003(\0132/.or" + + "o\"\316\003\n\006Module\022F\n\rpackage_parts\030\001 \003(\0132/.or" + "g.jetbrains.kotlin.metadata.jvm.PackageP" + "arts\022G\n\016metadata_parts\030\002 \003(\0132/.org.jetbr" + "ains.kotlin.metadata.jvm.PackageParts\022\030\n" + @@ -4653,16 +5221,18 @@ public final class DebugJvmModuleProtoBuf { "ringTable\022O\n\024qualified_name_table\030\005 \001(\0132", "1.org.jetbrains.kotlin.metadata.Qualifie" + "dNameTable\022=\n\nannotation\030\006 \003(\0132).org.jet" + - "brains.kotlin.metadata.Annotation\"\276\002\n\014Pa" + - "ckageParts\022\027\n\017package_fq_name\030\001 \002(\t\022\030\n\020s" + - "hort_class_name\030\002 \003(\t\022*\n\036multifile_facad" + - "e_short_name_id\030\003 \003(\005B\002\020\001\022#\n\033multifile_f" + - "acade_short_name\030\004 \003(\t\022.\n&class_with_jvm" + - "_package_name_short_name\030\005 \003(\t\022F\n:class_" + - "with_jvm_package_name_multifile_facade_s" + - "hort_name_id\030\007 \003(\005B\002\020\001\0222\n&class_with_jvm", - "_package_name_package_id\030\006 \003(\005B\002\020\001B\030B\026De" + - "bugJvmModuleProtoBuf" + "brains.kotlin.metadata.Annotation\022G\n\031opt" + + "ional_annotation_class\030\020 \003(\0132$.org.jetbr" + + "ains.kotlin.metadata.Class\"\276\002\n\014PackagePa" + + "rts\022\027\n\017package_fq_name\030\001 \002(\t\022\030\n\020short_cl" + + "ass_name\030\002 \003(\t\022*\n\036multifile_facade_short" + + "_name_id\030\003 \003(\005B\002\020\001\022#\n\033multifile_facade_s" + + "hort_name\030\004 \003(\t\022.\n&class_with_jvm_packag" + + "e_name_short_name\030\005 \003(\t\022F\n:class_with_jv", + "m_package_name_multifile_facade_short_na" + + "me_id\030\007 \003(\005B\002\020\001\0222\n&class_with_jvm_packag" + + "e_name_package_id\030\006 \003(\005B\002\020\001B\030B\026DebugJvmM" + + "oduleProtoBuf" }; org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { @@ -4682,7 +5252,7 @@ public final class DebugJvmModuleProtoBuf { internal_static_org_jetbrains_kotlin_metadata_jvm_Module_fieldAccessorTable = new org.jetbrains.kotlin.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_org_jetbrains_kotlin_metadata_jvm_Module_descriptor, - new java.lang.String[] { "PackageParts", "MetadataParts", "JvmPackageName", "StringTable", "QualifiedNameTable", "Annotation", }); + new java.lang.String[] { "PackageParts", "MetadataParts", "JvmPackageName", "StringTable", "QualifiedNameTable", "Annotation", "OptionalAnnotationClass", }); internal_static_org_jetbrains_kotlin_metadata_jvm_PackageParts_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_org_jetbrains_kotlin_metadata_jvm_PackageParts_fieldAccessorTable = new diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 09efebcd8d1..31d971ff096 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -39,7 +39,6 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver; import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt; -import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker; import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.jvm.*; @@ -433,9 +432,6 @@ public class AsmUtil { if (descriptor instanceof SyntheticClassDescriptorForLambda) { return getVisibilityAccessFlagForAnonymous(descriptor); } - if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(descriptor)) { - return NO_FLAG_PACKAGE_PRIVATE; - } if (descriptor.getKind() == ClassKind.ENUM_ENTRY) { return NO_FLAG_PACKAGE_PRIVATE; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java index 9090acb75f2..87620c140b1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf; import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping; import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMappingKt; import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts; +import org.jetbrains.kotlin.metadata.serialization.StringTable; import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtFile; @@ -129,15 +130,18 @@ public class ClassFileFactory implements OutputFileCollection { JvmModuleProtoBuf.Module.Builder builder = JvmModuleProtoBuf.Module.newBuilder(); String outputFilePath = getMappingFileName(state.getModuleName()); - for (PackageParts part : ClassFileUtilsKt.addCompiledPartsAndSort(packagePartRegistry.getParts().values(), state)) { - part.addTo(builder); - } + StringTableImpl stringTable = new StringTableImpl(); + ClassFileUtilsKt.addDataFromCompiledModule(builder, packagePartRegistry, stringTable, state); List experimental = state.getLanguageVersionSettings().getFlag(AnalysisFlags.getExperimental()); if (!experimental.isEmpty()) { - writeExperimentalMarkers(state.getModule(), builder, experimental); + writeExperimentalMarkers(state.getModule(), builder, experimental, stringTable); } + Pair tables = stringTable.buildProto(); + builder.setStringTable(tables.getFirst()); + builder.setQualifiedNameTable(tables.getSecond()); + JvmModuleProtoBuf.Module moduleProto = builder.build(); generators.put(outputFilePath, new OutAndSourceFileList(CollectionsKt.toList(sourceFiles)) { @@ -160,9 +164,9 @@ public class ClassFileFactory implements OutputFileCollection { private static void writeExperimentalMarkers( @NotNull ModuleDescriptor module, @NotNull JvmModuleProtoBuf.Module.Builder builder, - @NotNull List experimental + @NotNull List experimental, + @NotNull StringTable stringTable ) { - StringTableImpl stringTable = new StringTableImpl(); for (String fqName : experimental) { ClassDescriptor descriptor = DescriptorUtilKt.resolveClassByFqName(module, new FqName(fqName), NoLookupLocation.FOR_ALREADY_TRACKED); @@ -175,9 +179,6 @@ public class ClassFileFactory implements OutputFileCollection { } } } - Pair tables = stringTable.buildProto(); - builder.setStringTable(tables.getFirst()); - builder.setQualifiedNameTable(tables.getSecond()); } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java index 4f63e424beb..e0d2b6466ef 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java @@ -90,8 +90,12 @@ public class PackageCodegenImpl implements PackageCodegen { for (KtDeclaration declaration : file.getDeclarations()) { if (declaration instanceof KtClassOrObject) { ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, declaration); - if (PsiUtilsKt.hasExpectModifier(declaration) && - (descriptor == null || !ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor))) { + if (PsiUtilsKt.hasExpectModifier(declaration)) { + if (descriptor != null && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor)) { + assert ExpectedActualDeclarationChecker.isOptionalAnnotationClass(descriptor) : + "Expect class should be generated only if it's an optional annotation: " + descriptor; + state.getFactory().getPackagePartRegistry().getOptionalAnnotations().add(descriptor); + } continue; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartRegistry.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartRegistry.kt index 1f35fd45266..29a526e91a6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartRegistry.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartRegistry.kt @@ -16,11 +16,13 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts import org.jetbrains.kotlin.name.FqName class PackagePartRegistry { val parts = mutableMapOf() + val optionalAnnotations = mutableListOf() fun addPart(packageFqName: FqName, partInternalName: String, facadeInternalName: String?) { parts.computeIfAbsent(packageFqName) { PackageParts(it.asString()) }.addPart(partInternalName, facadeInternalName) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/classFileUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/classFileUtils.kt index ead6ba6a55a..8d1b64ca375 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/classFileUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/classFileUtils.kt @@ -18,10 +18,19 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.backend.common.output.OutputFile import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.load.kotlin.loadModuleMapping +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion +import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.serialization.DescriptorSerializer +import org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase +import org.jetbrains.kotlin.serialization.StringTableImpl +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol fun ClassFileFactory.getClassFiles(): Iterable { return asList().filterClassFiles() @@ -31,27 +40,64 @@ fun List.filterClassFiles(): List { return filter { it.relativePath.endsWith(".class") } } -fun Iterable.addCompiledPartsAndSort(state: GenerationState): List = - addCompiledParts(state).sortedBy { it.packageFqName } - -private fun Iterable.addCompiledParts(state: GenerationState): List { - val incrementalCache = state.incrementalCacheForThisTarget ?: return this.toList() - val moduleMappingData = incrementalCache.getModuleMappingData() ?: return this.toList() - - val mapping = ModuleMapping.loadModuleMapping(moduleMappingData, "", state.deserializationConfiguration) { version -> - throw IllegalStateException("Version of the generated module cannot be incompatible: $version") +fun JvmModuleProtoBuf.Module.Builder.addDataFromCompiledModule( + registry: PackagePartRegistry, stringTable: StringTableImpl, state: GenerationState +) { + for (part in registry.parts.values.addCompiledPartsAndSort(state)) { + part.addTo(this) } - incrementalCache.getObsoletePackageParts().forEach { internalName -> + // Take all optional annotation classes from sources, as well as look up all previously compiled optional annotation classes + // by FQ name in the current module. The latter is needed because in incremental compilation scenario, the already compiled + // classes will not be available via sources. + val optionalAnnotationClassDescriptors = + registry.optionalAnnotations.toSet() + + state.loadCompiledModule()?.moduleData?.run { + optionalAnnotations.mapNotNull { proto -> + state.module.findClassAcrossModuleDependencies( + ClassId.fromString(nameResolver.getQualifiedClassName(proto.fqName)) + ) + } + }.orEmpty() + + val serializer = DescriptorSerializer.createTopLevel(JvmOptionalAnnotationSerializerExtension(stringTable)) + for (descriptor in optionalAnnotationClassDescriptors) { + addOptionalAnnotationClass(serializer.classProto(descriptor)) + } +} + +class JvmOptionalAnnotationSerializerExtension( + override val stringTable: StringTableImpl +) : KotlinSerializerExtensionBase(BuiltInSerializerProtocol) { + override val metadataVersion: BinaryVersion + get() = JvmMetadataVersion.INSTANCE + + override fun shouldUseTypeTable(): Boolean = true +} + +private fun Iterable.addCompiledPartsAndSort(state: GenerationState): List = + addCompiledParts(state).sortedBy { it.packageFqName } + +private fun Iterable.addCompiledParts(state: GenerationState): List { + val mapping = state.loadCompiledModule() ?: return this.toList() + + state.incrementalCacheForThisTarget?.getObsoletePackageParts()?.forEach { internalName -> val qualifier = JvmClassName.byInternalName(internalName).packageFqName.asString() mapping.findPackageParts(qualifier)?.removePart(internalName) } return (this + mapping.packageFqName2Parts.values) - .groupBy { it.packageFqName } - .map { (packageFqName, allOldPackageParts) -> - PackageParts(packageFqName).apply { - allOldPackageParts.forEach { packageParts -> this += packageParts } - } + .groupBy { it.packageFqName } + .map { (packageFqName, allOldPackageParts) -> + PackageParts(packageFqName).apply { + allOldPackageParts.forEach { packageParts -> this += packageParts } } + } +} + +private fun GenerationState.loadCompiledModule(): ModuleMapping? { + val moduleMappingData = incrementalCacheForThisTarget?.getModuleMappingData() ?: return null + return ModuleMapping.loadModuleMapping(moduleMappingData, "", deserializationConfiguration) { version -> + throw IllegalStateException("Version of the generated module cannot be incompatible: $version") + } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt index 6e04c6da1d1..70e1a890e26 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.konan.properties.propertyList import org.jetbrains.kotlin.konan.util.KlibMetadataFactories import org.jetbrains.kotlin.library.KLIB_PROPERTY_DEPENDS import org.jetbrains.kotlin.library.KotlinLibrary +import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer import org.jetbrains.kotlin.load.java.JavaClassesTracker import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver import org.jetbrains.kotlin.load.java.structure.JavaClass @@ -68,10 +69,10 @@ import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension +import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory -import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager import java.util.* @@ -195,7 +196,8 @@ object TopDownAnalyzerFacadeForJVM { CompositePackageFragmentProvider( listOf( moduleClassResolver.compiledCodeResolver.packageFragmentProvider, - dependenciesContainer.get() + dependenciesContainer.get(), + dependenciesContainer.get() ) ) ) @@ -252,7 +254,10 @@ object TopDownAnalyzerFacadeForJVM { ) module.initialize( CompositePackageFragmentProvider( - listOf(container.get().packageFragmentProvider) + additionalProviders + listOf( + container.get().packageFragmentProvider, + container.get() + ) + additionalProviders ) ) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt index 4a1956b8e01..bef3b2dcd29 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JvmDiagnosticComponents +import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory @@ -121,6 +122,7 @@ fun StorageComponentContainer.configureJavaSpecificComponents( useInstance((moduleContext.module.builtIns as JvmBuiltIns).settings) useImpl() } + useImpl() useInstance(javaClassTracker ?: JavaClassesTracker.Default) useInstance( diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackagePartProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackagePartProvider.kt index 6eb6ba6675f..4e439270dd0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackagePartProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackagePartProvider.kt @@ -16,18 +16,20 @@ package org.jetbrains.kotlin.load.kotlin.incremental +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartProviderBase import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache import org.jetbrains.kotlin.load.kotlin.loadModuleMapping import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.serialization.deserialization.ClassData import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration import org.jetbrains.kotlin.storage.StorageManager class IncrementalPackagePartProvider( - private val parent: PackagePartProvider, - incrementalCaches: List, - storageManager: StorageManager + private val parent: PackagePartProvider, + incrementalCaches: List, + storageManager: StorageManager ) : PackagePartProvider { lateinit var deserializationConfiguration: DeserializationConfiguration @@ -48,4 +50,8 @@ class IncrementalPackagePartProvider( override fun getAnnotationsOnBinaryModule(moduleName: String): List { return parent.getAnnotationsOnBinaryModule(moduleName) } + + override fun getAllOptionalAnnotationClasses(): List = + moduleMappings().flatMap((JvmPackagePartProviderBase)::getAllOptionalAnnotationClasses) + + parent.getAllOptionalAnnotationClasses() } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/multiplatform/OptionalAnnotationPackageFragmentProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/multiplatform/OptionalAnnotationPackageFragmentProvider.kt new file mode 100644 index 00000000000..788bfbdca58 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/multiplatform/OptionalAnnotationPackageFragmentProvider.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.jvm.multiplatform + +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration +import org.jetbrains.kotlin.resolve.sam.SamConversionResolver +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl +import org.jetbrains.kotlin.serialization.deserialization.* +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol +import org.jetbrains.kotlin.storage.NotNullLazyValue +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.storage.getValue +import org.jetbrains.kotlin.utils.Printer + +class OptionalAnnotationPackageFragmentProvider( + module: ModuleDescriptor, + storageManager: StorageManager, + notFoundClasses: NotFoundClasses, + languageVersionSettings: LanguageVersionSettings, + packagePartProvider: PackagePartProvider, +) : PackageFragmentProvider { + val packages: Map by storageManager.createLazyValue p@{ + // We call getAllOptionalAnnotationClasses under lazy value only because IncrementalPackagePartProvider requires + // deserializationConfiguration to be injected. + val optionalAnnotationClasses = packagePartProvider.getAllOptionalAnnotationClasses() + if (optionalAnnotationClasses.isEmpty()) return@p emptyMap() + + mutableMapOf().also { packages -> + // We use BuiltInSerializerProtocol when serializing optional annotation classes (see + // JvmOptionalAnnotationSerializerExtension). Use it in deserialization as well, to be able to load annotations on + // optional annotation classes and their members (in particular, the `@OptionalExpectation` annotation). + val serializerProtocol = BuiltInSerializerProtocol + + val classDataFinder = OptionalAnnotationClassDataFinder(optionalAnnotationClasses) + val components = storageManager.createLazyValue { + DeserializationComponents( + storageManager, module, CompilerDeserializationConfiguration(languageVersionSettings), + classDataFinder, + AnnotationAndConstantLoaderImpl(module, notFoundClasses, serializerProtocol), + this, + LocalClassifierTypeSettings.Default, + ErrorReporter.DO_NOTHING, + LookupTracker.DO_NOTHING, + FlexibleTypeDeserializer.ThrowException, + emptyList(), + notFoundClasses, + ContractDeserializer.DEFAULT, + extensionRegistryLite = serializerProtocol.extensionRegistry, + samConversionResolver = SamConversionResolver.Empty + ) + } + + for ((packageFqName, classes) in classDataFinder.classIdToData.entries.groupBy { it.key.packageFqName }) { + val classNames = classes.mapNotNull { (classId) -> + classId.shortClassName.takeUnless { classId.isNestedClass } + }.toSet() + // TODO: make this lazy value more granular, e.g. a memoized function ClassId -> ClassDescriptor + val classDescriptors = storageManager.createLazyValue { + classes.mapNotNull { (classId, classData) -> + components().classDeserializer.deserializeClass(classId, classData) + }.associateBy(ClassDescriptor::getName) + } + packages[packageFqName] = PackageFragmentForOptionalAnnotations(module, packageFqName, classNames, classDescriptors) + } + } + } + + override fun getPackageFragments(fqName: FqName): List = + packages[fqName]?.let(::listOf).orEmpty() + + override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection = + emptyList() +} + +private class OptionalAnnotationClassDataFinder(classes: List) : ClassDataFinder { + val classIdToData = classes.associateBy { (nameResolver, klass) -> nameResolver.getClassId(klass.fqName) } + + override fun findClassData(classId: ClassId): ClassData? = classIdToData[classId] +} + +private class PackageFragmentForOptionalAnnotations( + module: ModuleDescriptor, + fqName: FqName, + classNames: Set, + classDescriptors: NotNullLazyValue>, +) : PackageFragmentDescriptorImpl(module, fqName) { + private val scope = object : MemberScopeImpl() { + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = classDescriptors()[name] + + override fun getContributedDescriptors( + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean + ): Collection = + if (kindFilter.acceptsKinds(DescriptorKindFilter.CLASSIFIERS_MASK)) classDescriptors().values else emptyList() + + override fun getClassifierNames(): Set = classNames + + override fun printScopeStructure(p: Printer) { + p.print("PackageFragmentForOptionalAnnotations{${classNames.joinToString(transform = Name::asString)}}") + } + } + + override fun getMemberScope(): MemberScope = scope +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoveLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoveLowering.kt index d1b01cf546b..1fc65bc339c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoveLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoveLowering.kt @@ -7,19 +7,14 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.DeclarationTransformer -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.util.ExpectDeclarationRemover /** * This pass removes all declarations with `isExpect == true`. */ -class ExpectDeclarationsRemoveLowering(context: BackendContext, keepOptionalAnnotations: Boolean = false) : DeclarationTransformer { - - private val remover = ExpectDeclarationRemover( - symbolTable = context.ir.symbols.externalSymbolTable, - doRemove = true, - keepOptionalAnnotations = keepOptionalAnnotations - ) +class ExpectDeclarationsRemoveLowering(context: BackendContext) : DeclarationTransformer { + private val remover = ExpectDeclarationRemover(context.ir.symbols.externalSymbolTable, true) override fun transformFlat(declaration: IrDeclaration): List? { return remover.transformFlat(declaration) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 33e18c44a28..a720c3b1e0a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -75,8 +75,8 @@ private val arrayConstructorPhase = makeIrFilePhase( description = "Transform `Array(size) { index -> value }` into a loop" ) -private val expectDeclarationsRemovingPhase = makeIrModulePhase( - { context -> ExpectDeclarationsRemoveLowering(context, keepOptionalAnnotations = true) }, +private val expectDeclarationsRemovingPhase = makeIrModulePhase( + ::ExpectDeclarationsRemoveLowering, name = "ExpectDeclarationsRemoving", description = "Remove expect declaration from module fragment" ) @@ -375,6 +375,7 @@ val jvmPhases = namedIrModulePhase( name = "IrLowering", description = "IR lowering", lower = validateIrBeforeLowering then + processOptionalAnnotationsPhase then expectDeclarationsRemovingPhase then fileClassPhase then performByIrFile(lower = jvmFilePhases) then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index 4bff18116d6..0181460ce46 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt @@ -372,9 +372,6 @@ internal fun getSignature( */ fun IrClass.getVisibilityAccessFlagForClass(): Int { /* Original had a check for SyntheticClassDescriptorForJava, never invoked in th IR backend. */ - if (isOptionalAnnotationClass()) { - return AsmUtil.NO_FLAG_PACKAGE_PRIVATE - } if (kind == ClassKind.ENUM_ENTRY) { return AsmUtil.NO_FLAG_PACKAGE_PRIVATE } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ProcessOptionalAnnotations.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ProcessOptionalAnnotations.kt new file mode 100644 index 00000000000..4a7f7edbe68 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ProcessOptionalAnnotations.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.codegen.isOptionalAnnotationClass +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.MetadataSource + +internal val processOptionalAnnotationsPhase = makeIrModulePhase( + ::ProcessOptionalAnnotations, + name = "ProcessOptionalAnnotations", + description = "Record metadata of @OptionalExpectation-annotated classes to backend-specific storage, later written to .kotlin_module" +) + +class ProcessOptionalAnnotations(private val context: JvmBackendContext) : FileLoweringPass { + override fun lower(irFile: IrFile) { + for (declaration in irFile.declarations) { + if (declaration !is IrClass || !declaration.isOptionalAnnotationClass()) continue + val metadataSource = (declaration.metadata as? MetadataSource.Class)?.descriptor ?: continue + context.state.factory.packagePartRegistry.optionalAnnotations += metadataSource + } + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ExpectDeclarationRemover.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ExpectDeclarationRemover.kt index 35ff4666e1d..944e1c3299b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ExpectDeclarationRemover.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ExpectDeclarationRemover.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.ir.util -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* @@ -23,11 +22,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver // `doRemove` means should expect-declaration be removed from IR -class ExpectDeclarationRemover( - val symbolTable: ReferenceSymbolTable, - private val doRemove: Boolean, - private val keepOptionalAnnotations: Boolean -) : IrElementVisitorVoid { +class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, private val doRemove: Boolean) : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } @@ -58,10 +53,8 @@ class ExpectDeclarationRemover( } private fun shouldRemoveTopLevelDeclaration(declaration: IrDeclaration): Boolean { - // TODO: rewrite findCompatibleActualForExpected using IR structures instead of descriptors val descriptor = declaration.descriptor - return doRemove && descriptor is MemberDescriptor && descriptor.isExpect && - !(keepOptionalAnnotations && descriptor is ClassDescriptor && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor)) + return doRemove && descriptor is MemberDescriptor && descriptor.isExpect } private fun tryCopyDefaultArguments(declaration: IrValueParameter) { diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index e1beb373bfe..daedfb6659a 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -27,7 +27,10 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupTracker -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.* +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.KlibMetadataIncrementalSerializer import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns @@ -51,8 +54,8 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext -import org.jetbrains.kotlin.psi2ir.generators.createGeneratorContext import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions +import org.jetbrains.kotlin.psi2ir.generators.createGeneratorContext import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.storage.LockBasedStorageManager @@ -162,7 +165,7 @@ fun generateKLib( val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!! if (!configuration.expectActualLinker) { - moduleFragment.acceptVoid(ExpectDeclarationRemover(psi2IrContext.symbolTable, doRemove = false, keepOptionalAnnotations = false)) + moduleFragment.acceptVoid(ExpectDeclarationRemover(psi2IrContext.symbolTable, false)) } serializeModuleIntoKlib( diff --git a/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt index 1f4b3fb78f7..89dba1c107e 100644 --- a/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt +++ b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt @@ -1,10 +1,3 @@ -@java.lang.annotation.Retention -@kotlin.Metadata -@kotlin.OptionalExpectation -annotation class Anno { - public abstract method s(): java.lang.String -} - @kotlin.Metadata public interface Foo$Nested { inner class Foo$Nested diff --git a/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt b/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt index 9c8bebf4988..0af7b987b29 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt @@ -31,8 +31,10 @@ fun box(): String { val annotations = Test::class.java.declaredMethods.single().annotations.toList() if (annotations.toString() != "[@a.A(x=42)]") return "Fail 1: $annotations" - // Can't use B::class.java because "Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry" - if (Modifier.isPublic(Class.forName("a.B").modifiers)) return "Fail 2: optional annotation class should not be public in the bytecode" - - return "OK" + try { + Class.forName("a.B") + return "Fail 2: there should be no class file for a.B" + } catch (e: ClassNotFoundException) { + return "OK" + } } diff --git a/compiler/testData/writeFlags/multiplatform/optionalExpectation.kt b/compiler/testData/writeFlags/multiplatform/optionalExpectation.kt deleted file mode 100644 index a9dd4446a1f..00000000000 --- a/compiler/testData/writeFlags/multiplatform/optionalExpectation.kt +++ /dev/null @@ -1,12 +0,0 @@ -// !LANGUAGE: +MultiPlatformProjects -// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform -// WITH_RUNTIME - -@file:Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") // TODO: support common sources in the test infrastructure - -@OptionalExpectation -expect annotation class Anno(val s: String) - -// TESTED_OBJECT_KIND: class -// TESTED_OBJECTS: Anno -// FLAGS: ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java index b425aa44df7..bfeaa809453 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java @@ -895,24 +895,6 @@ public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest { } } - @TestMetadata("compiler/testData/writeFlags/multiplatform") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class Multiplatform extends AbstractWriteFlagsTest { - private void runTest(String testDataFilePath) throws Exception { - KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); - } - - public void testAllFilesPresentInMultiplatform() throws Exception { - KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/writeFlags/multiplatform"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); - } - - @TestMetadata("optionalExpectation.kt") - public void testOptionalExpectation() throws Exception { - runTest("compiler/testData/writeFlags/multiplatform/optionalExpectation.kt"); - } - } - @TestMetadata("compiler/testData/writeFlags/property") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java index 0ac9221c8bf..acd0fcf9953 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java @@ -895,24 +895,6 @@ public class IrWriteFlagsTestGenerated extends AbstractIrWriteFlagsTest { } } - @TestMetadata("compiler/testData/writeFlags/multiplatform") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class Multiplatform extends AbstractIrWriteFlagsTest { - private void runTest(String testDataFilePath) throws Exception { - KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); - } - - public void testAllFilesPresentInMultiplatform() throws Exception { - KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/writeFlags/multiplatform"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); - } - - @TestMetadata("optionalExpectation.kt") - public void testOptionalExpectation() throws Exception { - runTest("compiler/testData/writeFlags/multiplatform/optionalExpectation.kt"); - } - } - @TestMetadata("compiler/testData/writeFlags/property") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartProviderBase.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartProviderBase.kt index 76ea64ef933..ca97263b9fd 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartProviderBase.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/JvmPackagePartProviderBase.kt @@ -5,9 +5,11 @@ package org.jetbrains.kotlin.load.kotlin +import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.serialization.deserialization.ClassData import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider abstract class JvmPackagePartProviderBase : PackagePartProvider, MetadataPartProvider { @@ -51,4 +53,18 @@ abstract class JvmPackagePartProviderBase : PackagePartProvider, Me if (name == moduleName) mapping.moduleData.annotations.map(ClassId::fromString) else null }.flatten() } + + override fun getAllOptionalAnnotationClasses(): List = + loadedModules.flatMap { module -> + getAllOptionalAnnotationClasses(module.mapping) + } + + companion object { + fun getAllOptionalAnnotationClasses(module: ModuleMapping): List { + val data = module.moduleData + return data.optionalAnnotations.map { proto -> + ClassData(data.nameResolver, proto, module.version, SourceElement.NO_SOURCE) + } + } + } } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/PackagePartProvider.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/PackagePartProvider.kt index c81b19d6542..a6e2647a782 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/PackagePartProvider.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/PackagePartProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.load.kotlin import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.serialization.deserialization.ClassData interface PackagePartProvider { /** @@ -19,9 +20,13 @@ interface PackagePartProvider { fun getAnnotationsOnBinaryModule(moduleName: String): List + fun getAllOptionalAnnotationClasses(): List + object Empty : PackagePartProvider { override fun findPackageParts(packageFqName: String): List = emptyList() override fun getAnnotationsOnBinaryModule(moduleName: String): List = emptyList() + + override fun getAllOptionalAnnotationClasses(): List = emptyList() } } diff --git a/core/metadata.jvm/src/jvm_module.proto b/core/metadata.jvm/src/jvm_module.proto index aae14760aa1..b8443b02a60 100644 --- a/core/metadata.jvm/src/jvm_module.proto +++ b/core/metadata.jvm/src/jvm_module.proto @@ -38,6 +38,12 @@ message Module { // Annotations on the whole module repeated Annotation annotation = 6; + + // @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling + // a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM. + // This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves + // it to the corresponding class with the resolution capabilities of common modules. + repeated Class optional_annotation_class = 16; } message PackageParts { diff --git a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/JvmModuleProtoBuf.java b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/JvmModuleProtoBuf.java index 29ac15f44d4..270651827bb 100644 --- a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/JvmModuleProtoBuf.java +++ b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/JvmModuleProtoBuf.java @@ -146,6 +146,41 @@ public final class JvmModuleProtoBuf { * */ int getAnnotationCount(); + + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + java.util.List + getOptionalAnnotationClassList(); + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + org.jetbrains.kotlin.metadata.ProtoBuf.Class getOptionalAnnotationClass(int index); + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + int getOptionalAnnotationClassCount(); } /** * Protobuf type {@code org.jetbrains.kotlin.metadata.jvm.Module} @@ -256,6 +291,14 @@ public final class JvmModuleProtoBuf { annotation_.add(input.readMessage(org.jetbrains.kotlin.metadata.ProtoBuf.Annotation.PARSER, extensionRegistry)); break; } + case 130: { + if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000040; + } + optionalAnnotationClass_.add(input.readMessage(org.jetbrains.kotlin.metadata.ProtoBuf.Class.PARSER, extensionRegistry)); + break; + } } } } catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) { @@ -276,6 +319,9 @@ public final class JvmModuleProtoBuf { if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { annotation_ = java.util.Collections.unmodifiableList(annotation_); } + if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = java.util.Collections.unmodifiableList(optionalAnnotationClass_); + } try { unknownFieldsCodedOutput.flush(); } catch (java.io.IOException e) { @@ -546,6 +592,76 @@ public final class JvmModuleProtoBuf { return annotation_.get(index); } + public static final int OPTIONAL_ANNOTATION_CLASS_FIELD_NUMBER = 16; + private java.util.List optionalAnnotationClass_; + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public java.util.List getOptionalAnnotationClassList() { + return optionalAnnotationClass_; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public java.util.List + getOptionalAnnotationClassOrBuilderList() { + return optionalAnnotationClass_; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public int getOptionalAnnotationClassCount() { + return optionalAnnotationClass_.size(); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public org.jetbrains.kotlin.metadata.ProtoBuf.Class getOptionalAnnotationClass(int index) { + return optionalAnnotationClass_.get(index); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+     * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+     * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+     * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+     * it to the corresponding class with the resolution capabilities of common modules.
+     * 
+ */ + public org.jetbrains.kotlin.metadata.ProtoBuf.ClassOrBuilder getOptionalAnnotationClassOrBuilder( + int index) { + return optionalAnnotationClass_.get(index); + } + private void initFields() { packageParts_ = java.util.Collections.emptyList(); metadataParts_ = java.util.Collections.emptyList(); @@ -553,6 +669,7 @@ public final class JvmModuleProtoBuf { stringTable_ = org.jetbrains.kotlin.metadata.ProtoBuf.StringTable.getDefaultInstance(); qualifiedNameTable_ = org.jetbrains.kotlin.metadata.ProtoBuf.QualifiedNameTable.getDefaultInstance(); annotation_ = java.util.Collections.emptyList(); + optionalAnnotationClass_ = java.util.Collections.emptyList(); } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -584,6 +701,12 @@ public final class JvmModuleProtoBuf { return false; } } + for (int i = 0; i < getOptionalAnnotationClassCount(); i++) { + if (!getOptionalAnnotationClass(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } memoizedIsInitialized = 1; return true; } @@ -609,6 +732,9 @@ public final class JvmModuleProtoBuf { for (int i = 0; i < annotation_.size(); i++) { output.writeMessage(6, annotation_.get(i)); } + for (int i = 0; i < optionalAnnotationClass_.size(); i++) { + output.writeMessage(16, optionalAnnotationClass_.get(i)); + } output.writeRawBytes(unknownFields); } @@ -647,6 +773,10 @@ public final class JvmModuleProtoBuf { size += org.jetbrains.kotlin.protobuf.CodedOutputStream .computeMessageSize(6, annotation_.get(i)); } + for (int i = 0; i < optionalAnnotationClass_.size(); i++) { + size += org.jetbrains.kotlin.protobuf.CodedOutputStream + .computeMessageSize(16, optionalAnnotationClass_.get(i)); + } size += unknownFields.size(); memoizedSerializedSize = size; return size; @@ -753,6 +883,8 @@ public final class JvmModuleProtoBuf { bitField0_ = (bitField0_ & ~0x00000010); annotation_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000020); + optionalAnnotationClass_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); return this; } @@ -804,6 +936,11 @@ public final class JvmModuleProtoBuf { bitField0_ = (bitField0_ & ~0x00000020); } result.annotation_ = annotation_; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = java.util.Collections.unmodifiableList(optionalAnnotationClass_); + bitField0_ = (bitField0_ & ~0x00000040); + } + result.optionalAnnotationClass_ = optionalAnnotationClass_; result.bitField0_ = to_bitField0_; return result; } @@ -855,6 +992,16 @@ public final class JvmModuleProtoBuf { annotation_.addAll(other.annotation_); } + } + if (!other.optionalAnnotationClass_.isEmpty()) { + if (optionalAnnotationClass_.isEmpty()) { + optionalAnnotationClass_ = other.optionalAnnotationClass_; + bitField0_ = (bitField0_ & ~0x00000040); + } else { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.addAll(other.optionalAnnotationClass_); + } + } setUnknownFields( getUnknownFields().concat(other.unknownFields)); @@ -886,6 +1033,12 @@ public final class JvmModuleProtoBuf { return false; } } + for (int i = 0; i < getOptionalAnnotationClassCount(); i++) { + if (!getOptionalAnnotationClass(i).isInitialized()) { + + return false; + } + } return true; } @@ -1685,6 +1838,215 @@ public final class JvmModuleProtoBuf { return this; } + private java.util.List optionalAnnotationClass_ = + java.util.Collections.emptyList(); + private void ensureOptionalAnnotationClassIsMutable() { + if (!((bitField0_ & 0x00000040) == 0x00000040)) { + optionalAnnotationClass_ = new java.util.ArrayList(optionalAnnotationClass_); + bitField0_ |= 0x00000040; + } + } + + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public java.util.List getOptionalAnnotationClassList() { + return java.util.Collections.unmodifiableList(optionalAnnotationClass_); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public int getOptionalAnnotationClassCount() { + return optionalAnnotationClass_.size(); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public org.jetbrains.kotlin.metadata.ProtoBuf.Class getOptionalAnnotationClass(int index) { + return optionalAnnotationClass_.get(index); + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder setOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.ProtoBuf.Class value) { + if (value == null) { + throw new NullPointerException(); + } + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.set(index, value); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder setOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.ProtoBuf.Class.Builder builderForValue) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.set(index, builderForValue.build()); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass(org.jetbrains.kotlin.metadata.ProtoBuf.Class value) { + if (value == null) { + throw new NullPointerException(); + } + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(value); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.ProtoBuf.Class value) { + if (value == null) { + throw new NullPointerException(); + } + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(index, value); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass( + org.jetbrains.kotlin.metadata.ProtoBuf.Class.Builder builderForValue) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(builderForValue.build()); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addOptionalAnnotationClass( + int index, org.jetbrains.kotlin.metadata.ProtoBuf.Class.Builder builderForValue) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.add(index, builderForValue.build()); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder addAllOptionalAnnotationClass( + java.lang.Iterable values) { + ensureOptionalAnnotationClassIsMutable(); + org.jetbrains.kotlin.protobuf.AbstractMessageLite.Builder.addAll( + values, optionalAnnotationClass_); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder clearOptionalAnnotationClass() { + optionalAnnotationClass_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + + return this; + } + /** + * repeated .org.jetbrains.kotlin.metadata.Class optional_annotation_class = 16; + * + *
+       * @OptionalExpectation-annotated annotation classes in this module. This list is only used in the compiler frontend when compiling
+       * a second-tier multiplatform module against a multiplatform module which uses optional annotations, not actualized on the JVM.
+       * This is not needed in the IDE because optional annotations can only be used in common modules, where the IDE plugin resolves
+       * it to the corresponding class with the resolution capabilities of common modules.
+       * 
+ */ + public Builder removeOptionalAnnotationClass(int index) { + ensureOptionalAnnotationClassIsMutable(); + optionalAnnotationClass_.remove(index); + + return this; + } + // @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.metadata.jvm.Module) } diff --git a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/BinaryModuleData.kt b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/BinaryModuleData.kt index a6be035d470..89b5d57cf08 100644 --- a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/BinaryModuleData.kt +++ b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/BinaryModuleData.kt @@ -5,7 +5,16 @@ package org.jetbrains.kotlin.metadata.jvm.deserialization +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolver + /** * @param annotations list of module annotations, in the format: "org/foo/bar/Baz.Inner" (see [ClassId.fromString]) + * @param optionalAnnotations list of @OptionalExpectation-annotated annotation classes in this module. + * @param nameResolver string table to resolve names referenced in classes in [optionalAnnotations]. */ -class BinaryModuleData(val annotations: List) +class BinaryModuleData( + val annotations: List, + val optionalAnnotations: List, + val nameResolver: NameResolver, +) diff --git a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt index 69b12a2f79d..f1301776c08 100644 --- a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt +++ b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt @@ -5,13 +5,17 @@ package org.jetbrains.kotlin.metadata.jvm.deserialization +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.builtins.BuiltInsProtoBuf import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl import org.jetbrains.kotlin.metadata.deserialization.isKotlin1Dot4OrLater import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf +import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite import java.io.* class ModuleMapping private constructor( + val version: JvmMetadataVersion, val packageFqName2Parts: Map, val moduleData: BinaryModuleData, private val debugName: String @@ -26,10 +30,10 @@ class ModuleMapping private constructor( const val MAPPING_FILE_EXT: String = "kotlin_module" @JvmField - val EMPTY: ModuleMapping = ModuleMapping(emptyMap(), BinaryModuleData(emptyList()), "EMPTY") + val EMPTY: ModuleMapping = ModuleMapping(JvmMetadataVersion.INSTANCE, emptyMap(), emptyBinaryData(), "EMPTY") @JvmField - val CORRUPTED: ModuleMapping = ModuleMapping(emptyMap(), BinaryModuleData(emptyList()), "CORRUPTED") + val CORRUPTED: ModuleMapping = ModuleMapping(JvmMetadataVersion.INSTANCE, emptyMap(), emptyBinaryData(), "CORRUPTED") const val STRICT_METADATA_VERSION_SEMANTICS_FLAG = 1 shl 0 @@ -69,7 +73,9 @@ class ModuleMapping private constructor( return EMPTY } - val moduleProto = JvmModuleProtoBuf.Module.parseFrom(stream) ?: return EMPTY + // "Builtin" extension registry is needed in order to deserialize annotations on optional annotation classes and their members. + val extensions = ExtensionRegistryLite.newInstance().apply(BuiltInsProtoBuf::registerAllExtensions) + val moduleProto = JvmModuleProtoBuf.Module.parseFrom(stream, extensions) ?: return EMPTY val result = linkedMapOf() for (proto in moduleProto.packagePartsList) { @@ -114,7 +120,12 @@ class ModuleMapping private constructor( val nameResolver = NameResolverImpl(moduleProto.stringTable, moduleProto.qualifiedNameTable) val annotations = moduleProto.annotationList.map { proto -> nameResolver.getQualifiedClassName(proto.id) } - return ModuleMapping(result, BinaryModuleData(annotations), debugName) + return ModuleMapping( + version, + result, + BinaryModuleData(annotations, moduleProto.optionalAnnotationClassList, nameResolver), + debugName + ) } private fun loadMultiFileFacadeInternalName( @@ -127,6 +138,13 @@ class ModuleMapping private constructor( val facadeShortName = multifileFacadeId?.let(multifileFacadeShortNames::getOrNull) return facadeShortName?.let { internalNameOf(packageFqName, it) } } + + private fun emptyBinaryData(): BinaryModuleData = + BinaryModuleData( + emptyList(), + emptyList(), + NameResolverImpl(ProtoBuf.StringTable.getDefaultInstance(), ProtoBuf.QualifiedNameTable.getDefaultInstance()) + ) } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDEPackagePartProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDEPackagePartProvider.kt index eb3787d713d..24866a3d816 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDEPackagePartProvider.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDEPackagePartProvider.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.serialization.deserialization.ClassData import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider, MetadataPartProvider { @@ -38,4 +39,10 @@ class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider // Note that in case of several modules with the same name, we return all annotations on all of them, which is probably incorrect override fun getAnnotationsOnBinaryModule(moduleName: String): List = FileBasedIndex.getInstance().getValues(KotlinJvmModuleAnnotationsIndex.KEY, moduleName, scope).flatten() + + // Optional annotations are not needed in IDE because they can only be used in common module sources, and they are loaded via the + // standard common module resolution there. (In the CLI compiler the situation is different because we compile common+platform + // sources together, _without_ common dependencies.) + override fun getAllOptionalAnnotationClasses(): List = + emptyList() } diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java index fa0ed3f2b4d..8f6dc896138 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalJvmJpsTestGenerated.java @@ -598,6 +598,11 @@ public class IncrementalJvmJpsTestGenerated extends AbstractIncrementalJvmJpsTes runTest("jps-plugin/testData/incremental/multiModule/multiplatform/custom/complementaryFiles/"); } + @TestMetadata("modifyOptionalAnnotationUsage") + public void testModifyOptionalAnnotationUsage() throws Exception { + runTest("jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/"); + } + @TestMetadata("notSameCompiler") public void testNotSameCompiler() throws Exception { runTest("jps-plugin/testData/incremental/multiModule/multiplatform/custom/notSameCompiler/"); @@ -655,6 +660,19 @@ public class IncrementalJvmJpsTestGenerated extends AbstractIncrementalJvmJpsTes } } + @TestMetadata("jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ModifyOptionalAnnotationUsage extends AbstractIncrementalJvmJpsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInModifyOptionalAnnotationUsage() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage"), Pattern.compile("^([^\\.]+)$"), null, true); + } + } + @TestMetadata("jps-plugin/testData/incremental/multiModule/multiplatform/custom/notSameCompiler") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/complementaryFiles/build.log b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/complementaryFiles/build.log index d50092a5710..3a90f366b38 100644 --- a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/complementaryFiles/build.log +++ b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/complementaryFiles/build.log @@ -8,7 +8,6 @@ Cleaning output files: End of files Cleaning output files: out/production/pJvm/FKt.class - out/production/pJvm/SharedImmutable.class End of files Compiling files: c/src/f.kt diff --git a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/_dependencies.txt b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/_dependencies.txt new file mode 100644 index 00000000000..94161f8d01d --- /dev/null +++ b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/_dependencies.txt @@ -0,0 +1,7 @@ +c [sourceSetHolder] + +pJvm [compilationAndSourceSetHolder, jvm] +pJvm -> c [include] + +pJs [compilationAndSourceSetHolder, js] +pJs -> c [include] diff --git a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/build.log b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/build.log new file mode 100644 index 00000000000..85eb646b8c3 --- /dev/null +++ b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/build.log @@ -0,0 +1,24 @@ +================ Step #1 ================= + +Building c +Building pJs +Cleaning output files: + out/production/pJs/pJs.js + out/production/pJs/pJs.meta.js + out/production/pJs/pJs/root-package.kjsm +End of files +Compiling files: + c/src/usage.kt +End of files +Exit code: OK +------------------------------------------ +Building pJvm +Cleaning output files: + out/production/pJvm/META-INF/pJvm.kotlin_module + out/production/pJvm/Usage.class +End of files +Compiling files: + c/src/usage.kt +End of files +Exit code: OK +------------------------------------------ \ No newline at end of file diff --git a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_declaration.kt b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_declaration.kt new file mode 100644 index 00000000000..84c59ee8284 --- /dev/null +++ b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_declaration.kt @@ -0,0 +1,3 @@ +@OptIn(ExperimentalMultiplatform::class) +@OptionalExpectation +expect annotation class Optional(val value: String) diff --git a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt new file mode 100644 index 00000000000..a76180ac767 --- /dev/null +++ b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt @@ -0,0 +1,2 @@ +@Optional("1") +class Usage diff --git a/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt.new.1 b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt.new.1 new file mode 100644 index 00000000000..834e6ff534d --- /dev/null +++ b/jps-plugin/testData/incremental/multiModule/multiplatform/custom/modifyOptionalAnnotationUsage/c_usage.kt.new.1 @@ -0,0 +1,2 @@ +@Optional("2") +class Usage diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt index 7b6d73bab8d..e7c33c0bfa8 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt @@ -3,10 +3,15 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +@file:Suppress("MemberVisibilityCanBePrivate") + package kotlinx.metadata.jvm import kotlinx.metadata.InconsistentKotlinMetadataException import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmClass +import kotlinx.metadata.KmClassVisitor +import kotlinx.metadata.impl.accept import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping @@ -62,6 +67,17 @@ class KotlinModuleMetadata(@Suppress("CanBeParameter", "MemberVisibilityCanBePri */ } + override fun visitOptionalAnnotationClass(): KmClassVisitor? { + /* + return object : ClassWriter(TODO() /* use StringTableImpl here */) { + override fun visitEnd() { + b.addOptionalAnnotationClass(t) + } + } + */ + return null + } + /** * Returns the metadata of the module file that was written with this writer. * @@ -80,13 +96,19 @@ class KotlinModuleMetadata(@Suppress("CanBeParameter", "MemberVisibilityCanBePri fun accept(v: KmModuleVisitor) { for ((fqName, parts) in data.packageFqName2Parts) { val (fileFacades, multiFileClassParts) = parts.parts.partition { parts.getMultifileFacadeName(it) == null } - v.visitPackageParts(fqName, fileFacades, multiFileClassParts.associate { it to parts.getMultifileFacadeName(it)!! }) + v.visitPackageParts(fqName, fileFacades, multiFileClassParts.associateWith { parts.getMultifileFacadeName(it)!! }) } for (annotation in data.moduleData.annotations) { v.visitAnnotation(KmAnnotation(annotation, emptyMap())) } + for (classProto in data.moduleData.optionalAnnotations) { + v.visitOptionalAnnotationClass()?.let { + classProto.accept(it, data.moduleData.nameResolver) + } + } + v.visitEnd() } @@ -147,6 +169,17 @@ abstract class KmModuleVisitor(private val delegate: KmModuleVisitor? = null) { delegate?.visitAnnotation(annotation) } + /** + * Visits an `@OptionalExpectation`-annotated annotation class declared in this module. + * Such classes are not materialized to bytecode on JVM, but the Kotlin compiler stores their metadata in the module file on JVM, + * and loads it during compilation of dependent modules, in order to avoid reporting "unresolved reference" errors on usages. + * + * Multiplatform projects are an experimental feature of Kotlin, and their behavior and/or binary format + * may change in a subsequent release. + */ + open fun visitOptionalAnnotationClass(): KmClassVisitor? = + delegate?.visitOptionalAnnotationClass() + /** * Visits the end of the module. */ @@ -171,6 +204,16 @@ class KmModule : KmModuleVisitor() { */ val annotations: MutableList = ArrayList(0) + /** + * `@OptionalExpectation`-annotated annotation classes declared in this module. + * Such classes are not materialized to bytecode on JVM, but the Kotlin compiler stores their metadata in the module file on JVM, + * and loads it during compilation of dependent modules, in order to avoid reporting "unresolved reference" errors on usages. + * + * Multiplatform projects are an experimental feature of Kotlin, and their behavior and/or binary format + * may change in a subsequent release. + */ + val optionalAnnotationClasses: MutableList = ArrayList(0) + override fun visitPackageParts(fqName: String, fileFacades: List, multiFileClassParts: Map) { packageParts[fqName] = KmPackageParts(fileFacades.toMutableList(), multiFileClassParts.toMutableMap()) } @@ -179,6 +222,9 @@ class KmModule : KmModuleVisitor() { annotations.add(annotation) } + override fun visitOptionalAnnotationClass(): KmClass = + KmClass().also(optionalAnnotationClasses::add) + /** * Populates the given visitor with data in this module. * @@ -189,6 +235,7 @@ class KmModule : KmModuleVisitor() { visitor.visitPackageParts(fqName, parts.fileFacades, parts.multiFileClassParts) } annotations.forEach(visitor::visitAnnotation) + optionalAnnotationClasses.forEach { visitor.visitOptionalAnnotationClass()?.let(it::accept) } } } diff --git a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/Kotlinp.kt b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/Kotlinp.kt index 2f7ba4d333e..4a863f116ec 100644 --- a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/Kotlinp.kt +++ b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/Kotlinp.kt @@ -10,7 +10,7 @@ import kotlinx.metadata.jvm.KotlinClassMetadata import kotlinx.metadata.jvm.KotlinModuleMetadata import java.io.File -class Kotlinp(val settings: KotlinpSettings) { +class Kotlinp(private val settings: KotlinpSettings) { internal fun renderClassFile(classFile: KotlinClassMetadata?): String = when (classFile) { is KotlinClassMetadata.Class -> ClassPrinter(settings).print(classFile) @@ -35,7 +35,7 @@ class Kotlinp(val settings: KotlinpSettings) { } internal fun renderModuleFile(metadata: KotlinModuleMetadata?): String = - if (metadata != null) ModuleFilePrinter().print(metadata) + if (metadata != null) ModuleFilePrinter(settings).print(metadata) else buildString { appendln("unsupported file") } internal fun readModuleFile(file: File): KotlinModuleMetadata? = diff --git a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt index 9714475d648..bdbf6098f31 100644 --- a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt +++ b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt @@ -689,7 +689,7 @@ interface AbstractPrinter { class ClassPrinter(private val settings: KotlinpSettings) : KmClassVisitor(), AbstractPrinter { private val sb = StringBuilder() - private val result = StringBuilder() + internal val result = StringBuilder() private var flags: Flags? = null private var name: ClassName? = null @@ -880,7 +880,9 @@ class MultiFileClassFacadePrinter : AbstractPrinter() + private val sb = StringBuilder().apply { appendln("module {") } @@ -901,7 +903,18 @@ class ModuleFilePrinter : KmModuleVisitor() { // TODO } + override fun visitOptionalAnnotationClass(): KmClassVisitor = + ClassPrinter(settings).also(optionalAnnotations::add) + override fun visitEnd() { + if (optionalAnnotations.isNotEmpty()) { + sb.appendln() + sb.appendln(" // Optional annotations") + sb.appendln() + for (element in optionalAnnotations) { + sb.appendln(" " + element.result.toString().replace("\n", "\n ").trimEnd()) + } + } sb.appendln("}") } diff --git a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java index b02ac639298..bb66ad857d6 100644 --- a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java +++ b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java @@ -63,6 +63,11 @@ public class KotlinpTestGenerated extends AbstractKotlinpTest { runTest("libraries/tools/kotlinp/testData/NestedClasses.kt"); } + @TestMetadata("OptionalAnnotation.kt") + public void testOptionalAnnotation() throws Exception { + runTest("libraries/tools/kotlinp/testData/OptionalAnnotation.kt"); + } + @TestMetadata("PlatformType.kt") public void testPlatformType() throws Exception { runTest("libraries/tools/kotlinp/testData/PlatformType.kt"); diff --git a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestUtils.kt b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestUtils.kt index 02a65d43d4d..dd4fb7bb9ce 100644 --- a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestUtils.kt +++ b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestUtils.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.jvm.compiler.AbstractLoadJavaTest import org.jetbrains.kotlin.kotlinp.Kotlinp import org.jetbrains.kotlin.kotlinp.KotlinpSettings import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.TestJdkKind import java.io.File @@ -63,7 +64,7 @@ fun compileAndPrintAllFiles(file: File, disposable: Disposable, tmpdir: File, co KotlinTestUtils.assertEqualsToFile(File(file.path.replace(".kt", ".txt")), main.toString()) } - if (readWriteAndCompare) { + if (readWriteAndCompare && InTextDirectivesUtils.findStringWithPrefixes(file.readText(), "// NO_READ_WRITE_COMPARE") == null) { assertEquals("Metadata is different after transformation with visitors.", main.toString(), afterVisitors.toString()) assertEquals("Metadata is different after transformation with nodes.", main.toString(), afterNodes.toString()) } diff --git a/libraries/tools/kotlinp/testData/OptionalAnnotation.kt b/libraries/tools/kotlinp/testData/OptionalAnnotation.kt new file mode 100644 index 00000000000..2381c2ef110 --- /dev/null +++ b/libraries/tools/kotlinp/testData/OptionalAnnotation.kt @@ -0,0 +1,20 @@ +// !LANGUAGE: +MultiPlatformProjects +// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform +// NO_READ_WRITE_COMPARE + +package test + +@OptionalExpectation +expect annotation class A(val x: Int) + +@OptionalExpectation +expect annotation class B(val a: Array) + +@OptionalExpectation +expect annotation class C() + +@Suppress("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE") +@A(42) +@B(["OK", ""]) +@C +fun ok() {} diff --git a/libraries/tools/kotlinp/testData/OptionalAnnotation.txt b/libraries/tools/kotlinp/testData/OptionalAnnotation.txt new file mode 100644 index 00000000000..fa3ba05afa0 --- /dev/null +++ b/libraries/tools/kotlinp/testData/OptionalAnnotation.txt @@ -0,0 +1,44 @@ +// test/OptionalAnnotationKt.class +// ------------------------------------------ +package { + + // signature: ok()V + public final fun ok(): kotlin/Unit +} +// META-INF/test-module.kotlin_module +// ------------------------------------------ +module { + package test { + test/OptionalAnnotationKt + } + + // Optional annotations + + public final expect annotation class test/A : kotlin/Annotation { + + // signature: (I)V + public /* primary */ constructor(x: kotlin/Int) + + public final expect val x: kotlin/Int + public final get + + // module name: main + } + public final expect annotation class test/B : kotlin/Annotation { + + // signature: (Lkotlin/Array;)V + public /* primary */ constructor(a: kotlin/Array) + + public final expect val a: kotlin/Array + public final get + + // module name: main + } + public final expect annotation class test/C : kotlin/Annotation { + + // signature: ()V + public /* primary */ constructor() + + // module name: main + } +}