From 1e9694f1db1c765646edc936c6a3f64eeddfbaac Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 9 Jul 2018 20:23:21 +0200 Subject: [PATCH] Fix version requirement serialization for nested classes Use the version requierement table of the outer DescriptorSerializer instance when serializing metadata for a class. Pass parent serializer to DescriptorSerializer.create to make sure the correct table is used. Serialize nested classes before the outer class in JS and common code, to make sure requirements are not lost. Also, split VersionRequirementTest to JVM and JS #KT-25120 In Progress --- .../incremental/JavaClassesTrackerImpl.kt | 2 +- .../codegen/ImplementationBodyCodegen.java | 24 ++++- .../kotlin/codegen/MemberCodegen.java | 23 +---- .../jetbrains/kotlin/codegen/ScriptCodegen.kt | 24 +++-- .../kotlin/cli/metadata/MetadataSerializer.kt | 30 +++--- .../serialization/DescriptorSerializer.kt | 24 +++-- .../experimental.kt | 4 +- .../output.txt | 6 ++ .../versionRequirement/nestedClassMembers.kt | 30 ++++++ ...t.kt => AbstractVersionRequirementTest.kt} | 94 +++++++------------ .../JvmVersionRequirementTest.kt | 77 +++++++++++++++ .../js/JsVersionRequirementTest.kt | 83 ++++++++++++++++ .../js/KotlinJavascriptSerializationUtil.kt | 25 ++--- 13 files changed, 312 insertions(+), 134 deletions(-) create mode 100644 compiler/testData/versionRequirement/nestedClassMembers.kt rename compiler/tests/org/jetbrains/kotlin/serialization/{VersionRequirementTest.kt => AbstractVersionRequirementTest.kt} (63%) create mode 100644 compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt create mode 100644 compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt diff --git a/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesTrackerImpl.kt b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesTrackerImpl.kt index d52b209566c..4e4294720fe 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesTrackerImpl.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesTrackerImpl.kt @@ -96,7 +96,7 @@ fun JavaClassDescriptor.convertToProto(): SerializedJavaClassWithSource { val extension = JavaClassesSerializerExtension() val classProto = try { - DescriptorSerializer.create(this, extension).classProto(this).build() + DescriptorSerializer.create(this, extension, null).classProto(this).build() } catch (e: Exception) { throw IllegalStateException( "Error during writing proto for descriptor: ${DescriptorRenderer.DEBUG_TEXT.render(this)}\n" + diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index ebf7cc3e7b3..321f7f41bd8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.binding.MutableClosure; import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; +import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter; import org.jetbrains.kotlin.codegen.state.GenerationState; @@ -30,8 +31,11 @@ import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; import org.jetbrains.kotlin.load.kotlin.TypeMappingMode; +import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader; +import org.jetbrains.kotlin.metadata.ProtoBuf; import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; @@ -52,6 +56,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.serialization.DescriptorSerializer; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.org.objectweb.asm.FieldVisitor; import org.jetbrains.org.objectweb.asm.Label; @@ -97,6 +102,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private final List> additionalTasks = new ArrayList<>(); + private final DescriptorSerializer serializer; + public ImplementationBodyCodegen( @NotNull KtPureClassOrObject aClass, @NotNull ClassContext context, @@ -108,7 +115,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { super(aClass, context, v, state, parentCodegen); this.classAsmType = getObjectType(typeMapper.classInternalName(descriptor)); this.isLocal = isLocal; - delegationFieldsInfo = getDelegationFieldsInfo(myClass.getSuperTypeListEntries()); + this.delegationFieldsInfo = getDelegationFieldsInfo(myClass.getSuperTypeListEntries()); + + JvmSerializerExtension extension = new JvmSerializerExtension(v.getSerializationBindings(), state); + this.serializer = DescriptorSerializer.create( + descriptor, extension, + parentCodegen instanceof ImplementationBodyCodegen + ? ((ImplementationBodyCodegen) parentCodegen).serializer + : DescriptorSerializer.createTopLevel(extension) + ); } @Override @@ -282,7 +297,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { @Override protected void generateKotlinMetadataAnnotation() { - generateKotlinClassMetadataAnnotation(descriptor, false); + ProtoBuf.Class classProto = serializer.classProto(descriptor).build(); + + WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.CLASS, 0, av -> { + writeAnnotationData(av, serializer, classProto); + return Unit.INSTANCE; + }); } private void writeEnclosingMethod() { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 92e8c387338..30220e7d486 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.codegen; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.psi.PsiElement; -import kotlin.Unit; import kotlin.jvm.functions.Function0; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,7 +18,6 @@ import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper; import org.jetbrains.kotlin.codegen.inline.NameGenerator; import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages; import org.jetbrains.kotlin.codegen.inline.SourceMapper; -import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.descriptors.*; @@ -29,9 +27,6 @@ import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl; import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; import org.jetbrains.kotlin.load.java.JavaVisibilities; import org.jetbrains.kotlin.load.java.JvmAbi; -import org.jetbrains.kotlin.load.java.JvmAnnotationNames; -import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader; -import org.jetbrains.kotlin.metadata.ProtoBuf; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.SpecialNames; import org.jetbrains.kotlin.psi.*; @@ -45,7 +40,6 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; -import org.jetbrains.kotlin.serialization.DescriptorSerializer; import org.jetbrains.kotlin.storage.LockBasedStorageManager; import org.jetbrains.kotlin.storage.NotNullLazyValue; import org.jetbrains.kotlin.types.ErrorUtils; @@ -62,7 +56,8 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import static org.jetbrains.kotlin.codegen.AsmUtil.*; +import static org.jetbrains.kotlin.codegen.AsmUtil.calculateInnerClassAccessFlags; +import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isNonDefaultInterfaceMember; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getInlineName; import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED; @@ -914,20 +909,6 @@ public abstract class MemberCodegen { - writeAnnotationData(av, serializer, classProto); - return Unit.INSTANCE; - }); - } - public void generateAssertField() { if (jvmAssertFieldGenerated) return; AssertCodegenUtilKt.generateAssertionsDisabledFieldInitialization(this); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt index 8eff84767d4..2021873743e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt @@ -6,25 +6,31 @@ package org.jetbrains.kotlin.codegen import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.codegen.AsmUtil.writeAnnotationData import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.context.ScriptContext +import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.resolve.jvm.diagnostics.* +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN +import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin +import org.jetbrains.kotlin.serialization.DescriptorSerializer +import org.jetbrains.org.objectweb.asm.Opcodes.* import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN -import org.jetbrains.org.objectweb.asm.Opcodes.* - class ScriptCodegen private constructor( private val scriptDeclaration: KtScript, state: GenerationState, @@ -64,7 +70,11 @@ class ScriptCodegen private constructor( override fun generateSyntheticPartsAfterBody() {} override fun generateKotlinMetadataAnnotation() { - generateKotlinClassMetadataAnnotation(scriptDescriptor, true) + val serializer = DescriptorSerializer.create(scriptDescriptor, JvmSerializerExtension(v.serializationBindings, state), null) + val classProto = serializer.classProto(scriptDescriptor).build() + writeKotlinMetadata(v, state, KotlinClassHeader.Kind.CLASS, JvmAnnotationNames.METADATA_SCRIPT_FLAG) { av -> + writeAnnotationData(av, serializer, classProto) + } } private fun genConstructor( diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt index 2f8e8d393e7..8c9b986e058 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt @@ -151,29 +151,29 @@ open class MetadataSerializer(private val dependOnOldBuiltIns: Boolean) { private val extension = createSerializerExtension() fun run() { - serializeClasses(classes) - serializeMembers(members) + val serializer = DescriptorSerializer.createTopLevel(extension) + serializeClasses(classes, serializer) + serializeMembers(members, serializer) serializeStringTable() serializeBuiltInsFile() } - private fun serializeClass(classDescriptor: ClassDescriptor) { - val classProto = DescriptorSerializer.create(classDescriptor, extension).classProto(classDescriptor).build() - proto.addClass_(classProto) - - serializeClasses(classDescriptor.unsubstitutedInnerClassesScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS)) - } - - private fun serializeClasses(classes: Collection) { + private fun serializeClasses(classes: Collection, parentSerializer: DescriptorSerializer) { for (descriptor in DescriptorSerializer.sort(classes)) { - if (descriptor is ClassDescriptor && descriptor.kind != ClassKind.ENUM_ENTRY) { - serializeClass(descriptor) - } + if (descriptor !is ClassDescriptor || descriptor.kind == ClassKind.ENUM_ENTRY) continue + + val serializer = DescriptorSerializer.create(descriptor, extension, parentSerializer) + serializeClasses( + descriptor.unsubstitutedInnerClassesScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS), + serializer + ) + + proto.addClass_(serializer.classProto(descriptor).build()) } } - private fun serializeMembers(members: Collection) { - proto.`package` = DescriptorSerializer.createTopLevel(extension).packagePartProto(packageFqName, members).build() + private fun serializeMembers(members: Collection, serializer: DescriptorSerializer) { + proto.`package` = serializer.packagePartProto(packageFqName, members).build() } private fun serializeStringTable() { diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt index b650cc3782a..4161a21c0d7 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTabl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils.* +import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry import org.jetbrains.kotlin.resolve.MemberComparator import org.jetbrains.kotlin.resolve.RequireKotlinNames import org.jetbrains.kotlin.resolve.calls.components.isActualParameterWithAnyExpectedDefault @@ -709,10 +709,14 @@ class DescriptorSerializer private constructor( } @JvmStatic - fun create(descriptor: ClassDescriptor, extension: SerializerExtension): DescriptorSerializer { + fun create( + descriptor: ClassDescriptor, + extension: SerializerExtension, + parentSerializer: DescriptorSerializer? + ): DescriptorSerializer { val container = descriptor.containingDeclaration - val parentSerializer = if (container is ClassDescriptor) - create(container, extension) + val parent = if (container is ClassDescriptor) + parentSerializer ?: create(container, extension, null) else createTopLevel(extension) @@ -720,12 +724,12 @@ class DescriptorSerializer private constructor( // serializing outer classes before nested classes. // Otherwise our interner can get wrong ids because we may serialize classes in any order. val serializer = DescriptorSerializer( - descriptor, - Interner(parentSerializer.typeParameters), - parentSerializer.extension, - MutableTypeTable(), - MutableVersionRequirementTable(), - serializeTypeTableToFunction = false + descriptor, + Interner(parent.typeParameters), + extension, + MutableTypeTable(), + if (container is ClassDescriptor) parent.versionRequirementTable else MutableVersionRequirementTable(), + serializeTypeTableToFunction = false ) for (typeParameter in descriptor.declaredTypeParameters) { serializer.typeParameters.intern(typeParameter) diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt index 43531f93874..0767434d743 100644 --- a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt @@ -3,9 +3,7 @@ suspend fun callRelease() { C().dummy() - // TODO: This should be error WithNested.Nested().dummy() - // TODO: This should be error WithInner().Inner().dummy() -} \ No newline at end of file +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/output.txt index 266e4a2ae79..b3403fe1706 100644 --- a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/output.txt +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/output.txt @@ -4,4 +4,10 @@ compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExp compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt:4:9: error: 'dummy(): String' is only available since Kotlin 1.3 and cannot be used in Kotlin 1.2 C().dummy() ^ +compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt:6:25: error: 'dummy(): String' is only available since Kotlin 1.3 and cannot be used in Kotlin 1.2 + WithNested.Nested().dummy() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/releaseCoroutineCallFromExperimental/experimental.kt:8:25: error: 'dummy(): String' is only available since Kotlin 1.3 and cannot be used in Kotlin 1.2 + WithInner().Inner().dummy() + ^ COMPILATION_ERROR diff --git a/compiler/testData/versionRequirement/nestedClassMembers.kt b/compiler/testData/versionRequirement/nestedClassMembers.kt new file mode 100644 index 00000000000..0190c78cfa4 --- /dev/null +++ b/compiler/testData/versionRequirement/nestedClassMembers.kt @@ -0,0 +1,30 @@ +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +package test + +import kotlin.internal.RequireKotlin + +class Outer { + inner class Inner { + @RequireKotlin("1.1") + inner class Deep @RequireKotlin("1.1") constructor() { + @RequireKotlin("1.1") + fun f() {} + + @RequireKotlin("1.1") + val x = "" + + suspend fun s() {} + } + } + + class Nested { + @RequireKotlin("1.1") + fun g() {} + } + + @RequireKotlin("1.1") + companion object +} + +@RequireKotlin("1.1") +fun topLevel() {} diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt similarity index 63% rename from compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt rename to compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt index ee1095d6423..7a66b3352e7 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/VersionRequirementTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/AbstractVersionRequirementTest.kt @@ -5,29 +5,22 @@ package org.jetbrains.kotlin.serialization -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil import org.jetbrains.kotlin.metadata.ProtoBuf -import org.jetbrains.kotlin.metadata.ProtoBuf.VersionRequirement.VersionKind.* import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor -import org.jetbrains.kotlin.test.ConfigurationKind -import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.TestCaseWithTmpdir -import org.jetbrains.kotlin.test.TestJdkKind import java.io.File -class VersionRequirementTest : TestCaseWithTmpdir() { +abstract class AbstractVersionRequirementTest : TestCaseWithTmpdir() { fun doTest( expectedVersionRequirement: VersionRequirement.Version, expectedLevel: DeprecationLevel, @@ -37,47 +30,23 @@ class VersionRequirementTest : TestCaseWithTmpdir() { customLanguageVersion: LanguageVersion = LanguageVersionSettingsImpl.DEFAULT.languageVersion, fqNames: List ) { - LoadDescriptorUtil.compileKotlinToDirAndGetModule( - listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), tmpdir, - KotlinCoreEnvironment.createForTests( - testRootDisposable, - KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir).apply { - put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8) - languageVersionSettings = LanguageVersionSettingsImpl( - customLanguageVersion, - ApiVersion.createByLanguageVersion(customLanguageVersion), - mapOf(AnalysisFlag.jvmDefaultMode to JvmDefaultMode.ENABLE), - emptyMap() - ) - }, - EnvironmentConfigFiles.JVM_CONFIG_FILES - ) - ) + compileFiles(listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), tmpdir, customLanguageVersion) + val module = loadModule(tmpdir) - val (_, module) = JvmResolveUtil.analyze( - KotlinCoreEnvironment.createForTests( - testRootDisposable, - KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir), - EnvironmentConfigFiles.JVM_CONFIG_FILES - ) - ) + for (fqName in fqNames) { + val descriptor = module.findUnambiguousDescriptorByFqName(fqName) - fun check(descriptor: DeclarationDescriptor) { val requirement = when (descriptor) { is DeserializedMemberDescriptor -> descriptor.versionRequirement is DeserializedClassDescriptor -> descriptor.versionRequirement else -> throw AssertionError("Unknown descriptor: $descriptor") } ?: throw AssertionError("No VersionRequirement for $descriptor") - assertEquals(expectedVersionRequirement, requirement.version) - assertEquals(expectedLevel, requirement.level) - assertEquals(expectedMessage, requirement.message) - assertEquals(expectedVersionKind, requirement.kind) - assertEquals(expectedErrorCode, requirement.errorCode) - } - - for (fqName in fqNames) { - check(module.findUnambiguousDescriptorByFqName(fqName)) + assertEquals("Incorrect version for $fqName", expectedVersionRequirement, requirement.version) + assertEquals("Incorrect level for $fqName", expectedLevel, requirement.level) + assertEquals("Incorrect message for $fqName", expectedMessage, requirement.message) + assertEquals("Incorrect versionKind for $fqName", expectedVersionKind, requirement.kind) + assertEquals("Incorrect errorCode for $fqName", expectedErrorCode, requirement.errorCode) } } @@ -102,9 +71,13 @@ class VersionRequirementTest : TestCaseWithTmpdir() { return descriptor } + protected abstract fun compileFiles(files: List, outputDirectory: File, languageVersion: LanguageVersion) + + protected abstract fun loadModule(directory: File): ModuleDescriptor + fun testSuspendFun() { doTest( - VersionRequirement.Version(1, 1), DeprecationLevel.ERROR, null, LANGUAGE_VERSION, null, + VersionRequirement.Version(1, 1), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, fqNames = listOf( "test.topLevel", "test.Foo.member", @@ -118,7 +91,7 @@ class VersionRequirementTest : TestCaseWithTmpdir() { ) doTest( - VersionRequirement.Version(1, 3), DeprecationLevel.ERROR, null, LANGUAGE_VERSION, null, + VersionRequirement.Version(1, 3), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, customLanguageVersion = LanguageVersion.KOTLIN_1_3, fqNames = listOf( "test.topLevel", @@ -135,7 +108,8 @@ class VersionRequirementTest : TestCaseWithTmpdir() { fun testLanguageVersionViaAnnotation() { doTest( - VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", LANGUAGE_VERSION, 42, + VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", + ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, 42, fqNames = listOf( "test.Klass", "test.Konstructor.", @@ -148,7 +122,7 @@ class VersionRequirementTest : TestCaseWithTmpdir() { fun testApiVersionViaAnnotation() { doTest( - VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", API_VERSION, 42, + VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", ProtoBuf.VersionRequirement.VersionKind.API_VERSION, 42, fqNames = listOf( "test.Klass", "test.Konstructor.", @@ -161,7 +135,8 @@ class VersionRequirementTest : TestCaseWithTmpdir() { fun testCompilerVersionViaAnnotation() { doTest( - VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", COMPILER_VERSION, 42, + VersionRequirement.Version(1, 1), DeprecationLevel.WARNING, "message", + ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, 42, fqNames = listOf( "test.Klass", "test.Konstructor.", @@ -174,25 +149,24 @@ class VersionRequirementTest : TestCaseWithTmpdir() { fun testPatchVersion() { doTest( - VersionRequirement.Version(1, 1, 50), DeprecationLevel.HIDDEN, null, LANGUAGE_VERSION, null, + VersionRequirement.Version(1, 1, 50), DeprecationLevel.HIDDEN, null, + ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, fqNames = listOf("test.Klass") ) } - fun testJvmDefault() { + fun testNestedClassMembers() { doTest( - VersionRequirement.Version(1, 2, 40), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, + VersionRequirement.Version(1, 1), DeprecationLevel.ERROR, null, ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION, null, fqNames = listOf( - "test.Base", - "test.Derived" + "test.Outer.Inner.Deep", + "test.Outer.Inner.Deep.", + "test.Outer.Inner.Deep.f", + "test.Outer.Inner.Deep.x", + "test.Outer.Inner.Deep.s", + "test.Outer.Nested.g", + "test.Outer.Companion" ) ) } - - fun testJvmFieldInInterfaceCompanion() { - doTest( - VersionRequirement.Version(1, 2, 70), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, - fqNames = listOf("test.Base.Companion.foo") - ) - } } diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt new file mode 100644 index 00000000000..d5f5ccea127 --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/serialization/JvmVersionRequirementTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.serialization + +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION +import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement +import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestJdkKind +import java.io.File + +class JvmVersionRequirementTest : AbstractVersionRequirementTest() { + override fun compileFiles(files: List, outputDirectory: File, languageVersion: LanguageVersion) { + LoadDescriptorUtil.compileKotlinToDirAndGetModule( + listOf(File("compiler/testData/versionRequirement/${getTestName(true)}.kt")), outputDirectory, + KotlinCoreEnvironment.createForTests( + testRootDisposable, + KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, outputDirectory).apply { + put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8) + languageVersionSettings = LanguageVersionSettingsImpl( + languageVersion, + ApiVersion.createByLanguageVersion(languageVersion), + mapOf(AnalysisFlag.jvmDefaultMode to JvmDefaultMode.ENABLE), + emptyMap() + ) + }, + EnvironmentConfigFiles.JVM_CONFIG_FILES + ) + ) + } + + override fun loadModule(directory: File): ModuleDescriptor = JvmResolveUtil.analyze( + KotlinCoreEnvironment.createForTests( + testRootDisposable, + KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir), + EnvironmentConfigFiles.JVM_CONFIG_FILES + ) + ).moduleDescriptor + + fun testJvmDefault() { + doTest( + VersionRequirement.Version(1, 2, 40), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, + fqNames = listOf( + "test.Base", + "test.Derived" + ) + ) + } + + fun testJvmFieldInInterfaceCompanion() { + doTest( + VersionRequirement.Version(1, 2, 70), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, + fqNames = listOf("test.Base.Companion.foo") + ) + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt new file mode 100644 index 00000000000..5ea5d98244b --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/serialization/js/JsVersionRequirementTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.serialization.js + +import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.context.ContextForNewModule +import org.jetbrains.kotlin.context.MutableModuleContext +import org.jetbrains.kotlin.context.ProjectContext +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS +import org.jetbrains.kotlin.js.config.JSConfigurationKeys +import org.jetbrains.kotlin.js.config.JsConfig +import org.jetbrains.kotlin.js.facade.K2JSTranslator +import org.jetbrains.kotlin.js.facade.MainCallParameters +import org.jetbrains.kotlin.js.facade.TranslationResult +import org.jetbrains.kotlin.js.resolve.JsPlatform +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.serialization.AbstractVersionRequirementTest +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestJdkKind +import java.io.File + +class JsVersionRequirementTest : AbstractVersionRequirementTest() { + override fun compileFiles(files: List, outputDirectory: File, languageVersion: LanguageVersion) { + val environment = createEnvironment(languageVersion) + val ktFiles = files.map { file -> KotlinTestUtils.createFile(file.name, file.readText(), environment.project) } + val trace = BindingTraceContext() + val analysisResult = TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace( + ktFiles, trace, createModule(environment), environment.configuration + ) + + // There are INVISIBLE_REFERENCE errors on RequireKotlin and K2JSTranslator refuses to translate the code otherwise + trace.clearDiagnostics() + + val result = K2JSTranslator(JsConfig(environment.project, environment.configuration)).translate( + object : JsConfig.Reporter() {}, ktFiles, MainCallParameters.noCall(), analysisResult + ) as TranslationResult.Success + result.getOutputFiles(File(outputDirectory, "lib.js"), null, null).writeAllTo(outputDirectory) + } + + override fun loadModule(directory: File): ModuleDescriptor { + val environment = createEnvironment(extraDependencies = listOf(File(directory, "lib.meta.js"))) + return TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace( + emptyList(), BindingTraceContext(), createModule(environment), environment.configuration + ).moduleDescriptor + } + + private fun createEnvironment( + languageVersion: LanguageVersion? = null, + extraDependencies: List = emptyList() + ): KotlinCoreEnvironment = + KotlinCoreEnvironment.createForTests( + testRootDisposable, + KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK).apply { + put(JSConfigurationKeys.LIBRARIES, extraDependencies.map(File::getPath) + JsConfig.JS_STDLIB) + put(JSConfigurationKeys.META_INFO, true) + + if (languageVersion != null) { + languageVersionSettings = + LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(languageVersion)) + } + }, + EnvironmentConfigFiles.JS_CONFIG_FILES + ) + + private fun createModule(environment: KotlinCoreEnvironment): MutableModuleContext { + val config = JsConfig(environment.project, environment.configuration) + return ContextForNewModule(ProjectContext(environment.project), Name.special(""), JsPlatform.builtIns, null).apply { + setDependencies(listOf(module) + config.moduleDescriptors + module.builtIns.builtInsModule) + } + } +} diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt index fddce0577af..9f7ae0da45a 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt @@ -192,29 +192,24 @@ object KotlinJavascriptSerializationUtil { val classDescriptors = scope.filterIsInstance().sortedBy { it.fqNameSafe.asString() } - fun serializeClasses(descriptors: Collection) { - fun serializeClass(classDescriptor: ClassDescriptor) { - if (skip(classDescriptor)) return - val classProto = - DescriptorSerializer.create(classDescriptor, extension).classProto(classDescriptor).build() - ?: error("Class not serialized: $classDescriptor") - builder.addClass_(classProto) - serializeClasses(classDescriptor.unsubstitutedInnerClassesScope.getContributedDescriptors()) - } - + fun serializeClasses(descriptors: Collection, parentSerializer: DescriptorSerializer) { for (descriptor in descriptors) { - if (descriptor is ClassDescriptor) { - serializeClass(descriptor) - } + if (descriptor !is ClassDescriptor || skip(descriptor)) continue + + val serializer = DescriptorSerializer.create(descriptor, extension, parentSerializer) + serializeClasses(descriptor.unsubstitutedInnerClassesScope.getContributedDescriptors(), serializer) + val classProto = serializer.classProto(descriptor).build() ?: error("Class not serialized: $descriptor") + builder.addClass_(classProto) } } - serializeClasses(classDescriptors) + val serializer = DescriptorSerializer.createTopLevel(extension) + serializeClasses(classDescriptors, serializer) val stringTable = extension.stringTable val members = scope.filterNot(skip) - builder.`package` = DescriptorSerializer.createTopLevel(extension).packagePartProto(fqName, members).build() + builder.`package` = serializer.packagePartProto(fqName, members).build() builder.setExtension( JsProtoBuf.packageFragmentFiles,