Deserialization/class reading: pass chosen JvmMetadataVersion whenever possible
This commit is contained in:
+2
-1
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -122,7 +123,7 @@ class AnonymousObjectTransformer(
|
||||
|
||||
override fun visitEnd() {}
|
||||
}, ClassReader.SKIP_FRAMES)
|
||||
val header = metadataReader.createHeader()
|
||||
val header = metadataReader.createHeader(inliningContext.state.languageVersionSettings.languageVersion.toMetadataVersion())
|
||||
assert(isSameModule || (header != null && isPublicAbi(header))) {
|
||||
"Trying to inline an anonymous object which is not part of the public ABI: ${oldObjectType.className}"
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.OnDemandSuppressCache
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmCompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
@@ -184,7 +185,7 @@ class GenerationState private constructor(
|
||||
val packagesWithObsoleteParts: Set<FqName>
|
||||
val obsoleteMultifileClasses: List<FqName>
|
||||
val deserializationConfiguration: DeserializationConfiguration =
|
||||
CompilerDeserializationConfiguration(languageVersionSettings)
|
||||
JvmCompilerDeserializationConfiguration(languageVersionSettings)
|
||||
|
||||
val deprecationProvider = DeprecationResolver(
|
||||
LockBasedStorageManager.NO_LOCKS, languageVersionSettings, JavaDeprecationSettings
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.load.kotlin.loadModuleMapping
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
|
||||
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmCompilerDeserializationConfiguration
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.EOFException
|
||||
import java.io.PrintStream
|
||||
@@ -38,7 +39,7 @@ class JvmPackagePartProvider(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
private val scope: GlobalSearchScope
|
||||
) : JvmPackagePartProviderBase<VirtualFile>() {
|
||||
override val deserializationConfiguration = CompilerDeserializationConfiguration(languageVersionSettings)
|
||||
override val deserializationConfiguration = JvmCompilerDeserializationConfiguration(languageVersionSettings)
|
||||
|
||||
override val loadedModules: MutableList<ModuleMappingInfo<VirtualFile>> = SmartList()
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.CommonPlatforms
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmCompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
@@ -232,7 +232,7 @@ private class KlibMetadataDependencyContainer(
|
||||
packageFragmentNames = packageFragmentNames,
|
||||
storageManager = LockBasedStorageManager("KlibMetadataPackageFragmentProvider"),
|
||||
moduleDescriptor = libraryModuleDescriptor,
|
||||
configuration = CompilerDeserializationConfiguration(languageVersionSettings),
|
||||
configuration = JvmCompilerDeserializationConfiguration(languageVersionSettings),
|
||||
compositePackageFragmentAddend = null,
|
||||
lookupTracker = LookupTracker.DO_NOTHING
|
||||
).also {
|
||||
|
||||
+3
-1
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.java.createConstantOrError
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredPropertySymbols
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.resolve.constants.ClassLiteralValue
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
|
||||
internal class AnnotationsLoader(private val session: FirSession, private val kotlinClassFinder: KotlinClassFinder) {
|
||||
private fun loadAnnotation(
|
||||
@@ -190,7 +192,7 @@ internal class AnnotationsLoader(private val session: FirSession, private val ko
|
||||
classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME
|
||||
) return false
|
||||
|
||||
val klass = kotlinClassFinder.findKotlinClass(classId)
|
||||
val klass = kotlinClassFinder.findKotlinClass(classId, session.languageVersionSettings.languageVersion.toMetadataVersion())
|
||||
return klass != null && SpecialJvmAnnotations.isAnnotatedWithContainerMetaAnnotation(klass)
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.deserialization.AbstractAnnotationDeserializer
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.kotlin.*
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
@@ -24,6 +25,7 @@ import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
|
||||
class JvmBinaryAnnotationDeserializer(
|
||||
val session: FirSession,
|
||||
@@ -37,8 +39,9 @@ class JvmBinaryAnnotationDeserializer(
|
||||
|
||||
private val annotationInfoForDefaultImpls by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
val defaultImplsClassId = kotlinBinaryClass.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME))
|
||||
val (defaultImplsClass, defaultImplsByteContent) = kotlinClassFinder.findKotlinClassOrContent(defaultImplsClassId) as? KotlinClassFinder.Result.KotlinClass
|
||||
?: return@lazy null
|
||||
val (defaultImplsClass, defaultImplsByteContent) = kotlinClassFinder.findKotlinClassOrContent(
|
||||
defaultImplsClassId, session.languageVersionSettings.languageVersion.toMetadataVersion()
|
||||
) as? KotlinClassFinder.Result.KotlinClass ?: return@lazy null
|
||||
session.loadMemberAnnotations(defaultImplsClass, defaultImplsByteContent, kotlinClassFinder)
|
||||
}
|
||||
|
||||
|
||||
+17
-6
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.deserialization.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.java.FirJavaFacade
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.load.kotlin.*
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
@@ -60,12 +62,16 @@ class JvmClassFileBasedSymbolProvider(
|
||||
if (partName in kotlinBuiltins) return@mapNotNull null
|
||||
val classId = ClassId.topLevel(JvmClassName.byInternalName(partName).fqNameForTopLevelClassMaybeWithDollars)
|
||||
if (!javaFacade.hasTopLevelClassOf(classId)) return@mapNotNull null
|
||||
val (kotlinJvmBinaryClass, byteContent) =
|
||||
kotlinClassFinder.findKotlinClassOrContent(classId) as? KotlinClassFinder.Result.KotlinClass ?: return@mapNotNull null
|
||||
val jvmMetadataVersion = session.languageVersionSettings.languageVersion.toMetadataVersion()
|
||||
val (kotlinJvmBinaryClass, byteContent) = kotlinClassFinder.findKotlinClassOrContent(
|
||||
classId, jvmMetadataVersion
|
||||
) as? KotlinClassFinder.Result.KotlinClass ?: return@mapNotNull null
|
||||
|
||||
val facadeName = kotlinJvmBinaryClass.classHeader.multifileClassName?.takeIf { it.isNotEmpty() }
|
||||
val facadeFqName = facadeName?.let { JvmClassName.byInternalName(it).fqNameForTopLevelClassMaybeWithDollars }
|
||||
val facadeBinaryClass = facadeFqName?.let { kotlinClassFinder.findKotlinClass(ClassId.topLevel(it)) }
|
||||
val facadeBinaryClass = facadeFqName?.let {
|
||||
kotlinClassFinder.findKotlinClass(ClassId.topLevel(it), jvmMetadataVersion)
|
||||
}
|
||||
|
||||
val moduleData = moduleDataProvider.getModuleData(kotlinJvmBinaryClass.containingLibrary.toPath()) ?: return@mapNotNull null
|
||||
|
||||
@@ -94,8 +100,11 @@ class JvmClassFileBasedSymbolProvider(
|
||||
private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>?
|
||||
get() {
|
||||
// TODO: skipMetadataVersionCheck
|
||||
if (classHeader.metadataVersion.isCompatible()) return null
|
||||
return IncompatibleVersionErrorData(classHeader.metadataVersion, JvmMetadataVersion.INSTANCE, location, classId)
|
||||
val expectedMetadataVersion = session.languageVersionSettings.languageVersion.toMetadataVersion()
|
||||
if (classHeader.metadataVersion.isCompatible(expectedMetadataVersion)) return null
|
||||
return IncompatibleVersionErrorData(
|
||||
classHeader.metadataVersion, maxOf(expectedMetadataVersion, JvmMetadataVersion.INSTANCE.next()), location, classId
|
||||
)
|
||||
}
|
||||
|
||||
private val KotlinJvmBinaryClass.isPreReleaseInvisible: Boolean
|
||||
@@ -105,7 +114,9 @@ class JvmClassFileBasedSymbolProvider(
|
||||
// Kotlin classes are annotated Java classes, so this check also looks for them.
|
||||
if (!javaFacade.hasTopLevelClassOf(classId)) return null
|
||||
|
||||
val result = kotlinClassFinder.findKotlinClassOrContent(classId)
|
||||
val result = kotlinClassFinder.findKotlinClassOrContent(
|
||||
classId, session.languageVersionSettings.languageVersion.toMetadataVersion()
|
||||
)
|
||||
if (result !is KotlinClassFinder.Result.KotlinClass) {
|
||||
if (parentContext != null || (classId.isNestedClass && getClass(classId.outermostClassId)?.fir !is FirJavaClass)) {
|
||||
// Nested class of Kotlin class should have been a Kotlin class.
|
||||
|
||||
+3
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap;
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor;
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
@@ -76,6 +77,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
@Nullable
|
||||
public static <T> T create(
|
||||
@NotNull byte[] fileContents,
|
||||
@NotNull JvmMetadataVersion jvmMetadataVersionFromLanguageVersion,
|
||||
@NotNull Function4<ClassId, Integer, KotlinClassHeader, InnerClassesInfo, T> factory
|
||||
) {
|
||||
ReadKotlinClassHeaderAnnotationVisitor readHeaderVisitor = new ReadKotlinClassHeaderAnnotationVisitor();
|
||||
@@ -108,7 +110,7 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
String className = classNameRef.get();
|
||||
if (className == null) return null;
|
||||
|
||||
KotlinClassHeader header = readHeaderVisitor.createHeader();
|
||||
KotlinClassHeader header = readHeaderVisitor.createHeader(jvmMetadataVersionFromLanguageVersion);
|
||||
if (header == null) return null;
|
||||
|
||||
ClassId id = resolveNameByInternalName(className, innerClasses);
|
||||
|
||||
+14
-3
@@ -12,7 +12,7 @@ import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
@@ -59,8 +59,19 @@ class KotlinBinaryClassCache : Disposable {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Deprecated(
|
||||
"Please pass jvmMetadataVersion explicitly",
|
||||
ReplaceWith(
|
||||
"getKotlinBinaryClassOrClassFileContent(file, JvmMetadataVersion.INSTANCE, fileContent = fileContent)",
|
||||
"org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion"
|
||||
)
|
||||
)
|
||||
fun getKotlinBinaryClassOrClassFileContent(
|
||||
file: VirtualFile, fileContent: ByteArray? = null
|
||||
file: VirtualFile, fileContent: ByteArray?
|
||||
) = getKotlinBinaryClassOrClassFileContent(file, jvmMetadataVersion = JvmMetadataVersion.INSTANCE, fileContent = fileContent)
|
||||
|
||||
fun getKotlinBinaryClassOrClassFileContent(
|
||||
file: VirtualFile, jvmMetadataVersion: JvmMetadataVersion, fileContent: ByteArray? = null
|
||||
): KotlinClassFinder.Result? {
|
||||
if (file.extension != JavaClassFileType.INSTANCE.defaultExtension &&
|
||||
file.fileType !== JavaClassFileType.INSTANCE
|
||||
@@ -76,7 +87,7 @@ class KotlinBinaryClassCache : Disposable {
|
||||
}
|
||||
|
||||
val aClass = ApplicationManager.getApplication().runReadAction(Computable {
|
||||
VirtualFileKotlinClass.create(file, fileContent)
|
||||
VirtualFileKotlinClass.create(file, jvmMetadataVersion, fileContent)
|
||||
})
|
||||
|
||||
return requestCache.cache(file, aClass)
|
||||
|
||||
+3
-2
@@ -11,6 +11,7 @@ import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.util.PerformanceCounter
|
||||
import java.io.FileNotFoundException
|
||||
@@ -46,14 +47,14 @@ class VirtualFileKotlinClass private constructor(
|
||||
private val LOG = Logger.getInstance(VirtualFileKotlinClass::class.java)
|
||||
private val perfCounter = PerformanceCounter.create("Binary class from Kotlin file")
|
||||
|
||||
internal fun create(file: VirtualFile, fileContent: ByteArray?): KotlinClassFinder.Result? {
|
||||
internal fun create(file: VirtualFile, jvmMetadataVersion: JvmMetadataVersion, fileContent: ByteArray?): KotlinClassFinder.Result? {
|
||||
return perfCounter.time {
|
||||
assert(file.extension == JavaClassFileType.INSTANCE.defaultExtension || file.fileType == JavaClassFileType.INSTANCE) { "Trying to read binary data from a non-class file $file" }
|
||||
|
||||
try {
|
||||
val byteContent = fileContent ?: file.contentsToByteArray(false)
|
||||
if (byteContent.isNotEmpty()) {
|
||||
val kotlinJvmBinaryClass = create(byteContent) { name, classVersion, header, innerClasses ->
|
||||
val kotlinJvmBinaryClass = create(byteContent, jvmMetadataVersion) { name, classVersion, header, innerClasses ->
|
||||
VirtualFileKotlinClass(file, name, classVersion, header, innerClasses)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
|
||||
@@ -32,12 +32,12 @@ abstract class VirtualFileFinder : KotlinClassFinder {
|
||||
|
||||
abstract fun findSourceOrBinaryVirtualFile(classId: ClassId): VirtualFile?
|
||||
|
||||
override fun findKotlinClassOrContent(classId: ClassId): KotlinClassFinder.Result? {
|
||||
override fun findKotlinClassOrContent(classId: ClassId, jvmMetadataVersion: JvmMetadataVersion): KotlinClassFinder.Result? {
|
||||
val file = findVirtualFileWithHeader(classId) ?: return null
|
||||
return KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file)
|
||||
return KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, jvmMetadataVersion)
|
||||
}
|
||||
|
||||
override fun findKotlinClassOrContent(javaClass: JavaClass): KotlinClassFinder.Result? {
|
||||
override fun findKotlinClassOrContent(javaClass: JavaClass, jvmMetadataVersion: JvmMetadataVersion): KotlinClassFinder.Result? {
|
||||
var file = (javaClass as? VirtualFileBoundJavaClass)?.virtualFile ?: return null
|
||||
|
||||
if (javaClass.outerClass != null) {
|
||||
@@ -46,7 +46,7 @@ abstract class VirtualFileFinder : KotlinClassFinder {
|
||||
file = file.parent!!.findChild(classFileName(javaClass) + ".class").sure { "Virtual file not found for $javaClass" }
|
||||
}
|
||||
|
||||
return KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file)
|
||||
return KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, jvmMetadataVersion)
|
||||
}
|
||||
|
||||
private fun classFileName(jClass: JavaClass): String {
|
||||
|
||||
+5
-1
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.serialization.deserialization.DeserializationCompone
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.jvmMetadataVersionOrDefault
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
|
||||
class IncrementalPackageFragmentProvider(
|
||||
@@ -94,7 +95,10 @@ class IncrementalPackageFragmentProvider(
|
||||
|
||||
val partName = JvmClassName.byInternalName(internalName)
|
||||
val jvmBinaryClass =
|
||||
kotlinClassFinder.findKotlinClass(ClassId.topLevel(partName.fqNameForTopLevelClassMaybeWithDollars))
|
||||
kotlinClassFinder.findKotlinClass(
|
||||
ClassId.topLevel(partName.fqNameForTopLevelClassMaybeWithDollars),
|
||||
deserializationComponents.configuration.jvmMetadataVersionOrDefault()
|
||||
)
|
||||
|
||||
val metadataVersion =
|
||||
jvmBinaryClass?.classHeader?.metadataVersion
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
|
||||
class JvmCompilerDeserializationConfiguration(
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) : CompilerDeserializationConfiguration(languageVersionSettings) {
|
||||
override val binaryVersion: JvmMetadataVersion
|
||||
get() = languageVersionSettings.languageVersion.toMetadataVersion()
|
||||
}
|
||||
+2
-2
@@ -14,7 +14,7 @@ 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.jvm.JvmCompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.resolve.sam.SamConversionResolver
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
@@ -49,7 +49,7 @@ class OptionalAnnotationPackageFragmentProvider(
|
||||
val classDataFinder = OptionalAnnotationClassDataFinder(optionalAnnotationClasses)
|
||||
val components = storageManager.createLazyValue {
|
||||
DeserializationComponents(
|
||||
storageManager, module, CompilerDeserializationConfiguration(languageVersionSettings),
|
||||
storageManager, module, JvmCompilerDeserializationConfiguration(languageVersionSettings),
|
||||
classDataFinder,
|
||||
AnnotationAndConstantLoaderImpl(module, notFoundClasses, serializerProtocol),
|
||||
this,
|
||||
|
||||
+13
-11
@@ -5,24 +5,26 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
|
||||
class CompilerDeserializationConfiguration(languageVersionSettings: LanguageVersionSettings) : DeserializationConfiguration {
|
||||
override val skipMetadataVersionCheck = languageVersionSettings.getFlag(AnalysisFlags.skipMetadataVersionCheck)
|
||||
open class CompilerDeserializationConfiguration(
|
||||
protected val languageVersionSettings: LanguageVersionSettings
|
||||
) : DeserializationConfiguration {
|
||||
|
||||
override val skipPrereleaseCheck = languageVersionSettings.getFlag(AnalysisFlags.skipPrereleaseCheck)
|
||||
final override val skipMetadataVersionCheck = languageVersionSettings.getFlag(AnalysisFlags.skipMetadataVersionCheck)
|
||||
|
||||
override val reportErrorsOnPreReleaseDependencies =
|
||||
final override val skipPrereleaseCheck = languageVersionSettings.getFlag(AnalysisFlags.skipPrereleaseCheck)
|
||||
|
||||
final override val reportErrorsOnPreReleaseDependencies =
|
||||
!skipPrereleaseCheck && !languageVersionSettings.isPreRelease()
|
||||
|
||||
override val allowUnstableDependencies = languageVersionSettings.getFlag(AnalysisFlags.allowUnstableDependencies)
|
||||
final override val allowUnstableDependencies = languageVersionSettings.getFlag(AnalysisFlags.allowUnstableDependencies)
|
||||
|
||||
override val typeAliasesAllowed = languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)
|
||||
final override val typeAliasesAllowed = languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)
|
||||
|
||||
override val isJvmPackageNameSupported = languageVersionSettings.supportsFeature(LanguageFeature.JvmPackageName)
|
||||
final override val isJvmPackageNameSupported = languageVersionSettings.supportsFeature(LanguageFeature.JvmPackageName)
|
||||
|
||||
override val readDeserializedContracts: Boolean = languageVersionSettings.supportsFeature(LanguageFeature.ReadDeserializedContracts)
|
||||
final override val readDeserializedContracts: Boolean =
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ReadDeserializedContracts)
|
||||
}
|
||||
|
||||
+10
-3
@@ -32,8 +32,8 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.compilerRunner.MessageCollectorToOutputItemsCollectorAdapter
|
||||
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||
import org.jetbrains.kotlin.compilerRunner.SimpleOutputItem
|
||||
import org.jetbrains.kotlin.compilerRunner.toGeneratedFile
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
@@ -41,9 +41,11 @@ import org.jetbrains.kotlin.incremental.parsing.classesFqNames
|
||||
import org.jetbrains.kotlin.incremental.util.BufferingMessageCollector
|
||||
import org.jetbrains.kotlin.incremental.util.ExceptionLocation
|
||||
import org.jetbrains.kotlin.incremental.util.reportException
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.util.removeSuffixIfPresent
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
import java.io.File
|
||||
|
||||
abstract class IncrementalCompilerRunner<
|
||||
@@ -411,6 +413,10 @@ abstract class IncrementalCompilerRunner<
|
||||
|
||||
var exitCode = ExitCode.OK
|
||||
|
||||
// TODO: ideally we should read arguments not here but at earlier stages
|
||||
val jvmMetadataVersionFromLanguageVersion =
|
||||
LanguageVersion.fromVersionString(args.languageVersion)?.toMetadataVersion() ?: JvmMetadataVersion.INSTANCE
|
||||
|
||||
while (dirtySources.any() || runWithNoDirtyKotlinSources(caches)) {
|
||||
val complementaryFiles = caches.platformCache.getComplementaryFilesRecursive(dirtySources)
|
||||
dirtySources.addAll(complementaryFiles)
|
||||
@@ -447,8 +453,9 @@ abstract class IncrementalCompilerRunner<
|
||||
val text = allDirtySources.joinToString(separator = System.getProperty("line.separator")) { it.normalize().absolutePath }
|
||||
dirtySourcesSinceLastTimeFile.writeText(text)
|
||||
|
||||
|
||||
val generatedFiles = outputItemsCollector.outputs.map(SimpleOutputItem::toGeneratedFile)
|
||||
val generatedFiles = outputItemsCollector.outputs.map {
|
||||
it.toGeneratedFile(jvmMetadataVersionFromLanguageVersion)
|
||||
}
|
||||
if (compilationMode is CompilationMode.Incremental) {
|
||||
// todo: feels dirty, can this be refactored?
|
||||
val dirtySourcesSet = dirtySources.toHashSet()
|
||||
|
||||
+4
-2
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled
|
||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.NoChanges
|
||||
@@ -85,8 +86,9 @@ fun makeIncrementally(
|
||||
val buildReporter = BuildReporter(icReporter = reporter, buildMetricsReporter = DoNothingBuildMetricsReporter)
|
||||
|
||||
withIC(args) {
|
||||
val useK2 = args.useK2 || LanguageVersion.fromVersionString(args.languageVersion)?.usesK2 == true
|
||||
val compiler =
|
||||
if (args.useK2 && args.useFirIC && args.useFirLT /* TODO: move LT check into runner */ )
|
||||
if (useK2 && args.useFirIC && args.useFirLT /* TODO: move LT check into runner */)
|
||||
IncrementalFirJvmCompilerRunner(
|
||||
cachesDir,
|
||||
buildReporter,
|
||||
@@ -101,7 +103,7 @@ fun makeIncrementally(
|
||||
cachesDir,
|
||||
buildReporter,
|
||||
// Use precise setting in case of non-Gradle build
|
||||
usePreciseJavaTracking = !args.useK2, // TODO: add fir-based java classes tracker when available and set this to true
|
||||
usePreciseJavaTracking = !useK2, // TODO: add fir-based java classes tracker when available and set this to true
|
||||
buildHistoryFile = buildHistoryFile,
|
||||
outputDirs = null,
|
||||
modulesApiHistory = EmptyModulesApiHistory,
|
||||
|
||||
+1
-1
@@ -107,5 +107,5 @@ private class KotlinClassHeaderClassVisitor : ClassVisitor(Opcodes.API_VERSION)
|
||||
)
|
||||
}
|
||||
|
||||
fun getKotlinClassHeader(): KotlinClassHeader? = kotlinClassHeaderAnnotationVisitor.createHeader()
|
||||
fun getKotlinClassHeader(): KotlinClassHeader? = kotlinClassHeaderAnnotationVisitor.createHeaderWithDefaultMetadataVersion()
|
||||
}
|
||||
|
||||
+1
@@ -13,6 +13,7 @@ abstract class AbstractIncrementalFirICLightTreeJvmCompilerRunnerTest : Abstract
|
||||
override fun createCompilerArguments(destinationDir: File, testDir: File): K2JVMCompilerArguments =
|
||||
super.createCompilerArguments(destinationDir, testDir).apply {
|
||||
useK2 = true
|
||||
languageVersion = "2.0"
|
||||
useIR = true
|
||||
useFirIC = true
|
||||
useFirLT = true
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ abstract class AbstractIncrementalFirJvmCompilerRunnerTest : AbstractIncremental
|
||||
override fun createCompilerArguments(destinationDir: File, testDir: File): K2JVMCompilerArguments =
|
||||
super.createCompilerArguments(destinationDir, testDir).apply {
|
||||
useK2 = true
|
||||
languageVersion = "2.0"
|
||||
useIR = true
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -13,6 +13,7 @@ abstract class AbstractIncrementalFirLightTreeJvmCompilerRunnerTest : AbstractIn
|
||||
override fun createCompilerArguments(destinationDir: File, testDir: File): K2JVMCompilerArguments =
|
||||
super.createCompilerArguments(destinationDir, testDir).apply {
|
||||
useK2 = true
|
||||
languageVersion = "2.0"
|
||||
useIR = true
|
||||
useFirIC = false
|
||||
useFirLT = true
|
||||
|
||||
+4
-1
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
@@ -213,7 +214,9 @@ object InlineTestUtil {
|
||||
}
|
||||
|
||||
private fun loadBinaryClass(file: OutputFile): KotlinJvmBinaryClass =
|
||||
FileBasedKotlinClass.create<FileBasedKotlinClass>(file.asByteArray()) { className, classVersion, classHeader, innerClasses ->
|
||||
FileBasedKotlinClass.create<FileBasedKotlinClass>(
|
||||
file.asByteArray(), JvmMetadataVersion.INSTANCE
|
||||
) { className, classVersion, classHeader, innerClasses ->
|
||||
object : FileBasedKotlinClass(className, classVersion, classHeader, innerClasses) {
|
||||
override val location: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package lib
|
||||
|
||||
class Box(val value: String)
|
||||
|
||||
inline fun <T> get(block: () -> T): T = block()
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
warning: compiler flag -Xuse-k2 is deprecated, please use -language-version 2.0 instead
|
||||
OK
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import lib.*
|
||||
|
||||
fun main() {
|
||||
get { Box("OK").value }
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.load.kotlin.loadModuleMapping
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
|
||||
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmCompilerDeserializationConfiguration
|
||||
import org.jetbrains.kotlin.test.CompilerTestUtil
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
@@ -51,7 +51,7 @@ class JvmModuleProtoBufTest : KtUsefulTestCase() {
|
||||
|
||||
val mapping = ModuleMapping.loadModuleMapping(
|
||||
File(tmpdir, "META-INF/$moduleName.${ModuleMapping.MAPPING_FILE_EXT}").readBytes(), "test",
|
||||
CompilerDeserializationConfiguration(LanguageVersionSettingsImpl(loadWith, ApiVersion.createByLanguageVersion(loadWith))),
|
||||
JvmCompilerDeserializationConfiguration(LanguageVersionSettingsImpl(loadWith, ApiVersion.createByLanguageVersion(loadWith))),
|
||||
::error
|
||||
)
|
||||
val result = buildString {
|
||||
|
||||
@@ -143,7 +143,7 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase {
|
||||
int[] version = (int[]) CodegenTestUtil.getAnnotationAttribute(annotation, METADATA_VERSION_FIELD_NAME);
|
||||
assertNotNull(version);
|
||||
assertTrue("Annotation " + annotationFqName + " is written with an unsupported format",
|
||||
new JvmMetadataVersion(version).isCompatible());
|
||||
new JvmMetadataVersion(version).isCompatibleWithCurrentCompilerVersion());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+10
-2
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparatorAdaptor.validateAndCompareDescriptorWithFile
|
||||
import org.jetbrains.kotlin.utils.toMetadataVersion
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import java.io.ByteArrayInputStream
|
||||
@@ -388,7 +389,7 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
||||
}
|
||||
|
||||
fun testStrictMetadataVersionSemanticsOldVersion() {
|
||||
val nextMetadataVersion = JvmMetadataVersion(JvmMetadataVersion.INSTANCE.major, JvmMetadataVersion.INSTANCE.minor + 1, 0)
|
||||
val nextMetadataVersion = JvmMetadataVersion.INSTANCE.next()
|
||||
val library = compileLibrary(
|
||||
"library", additionalOptions = listOf("-Xgenerate-strict-metadata-version", "-Xmetadata-version=$nextMetadataVersion")
|
||||
)
|
||||
@@ -409,7 +410,7 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
||||
val expectedMajor = if (languageVersion.usesK2) 2 else 1
|
||||
val expectedMinor = if (languageVersion < LanguageVersion.KOTLIN_1_4) 1 else languageVersion.minor
|
||||
|
||||
val topLevelClass = LocalFileKotlinClass.create(File(tmpdir.absolutePath, "Foo.class"))!!
|
||||
val topLevelClass = LocalFileKotlinClass.create(File(tmpdir.absolutePath, "Foo.class"), languageVersion.toMetadataVersion())!!
|
||||
val classVersion = topLevelClass.classHeader.metadataVersion
|
||||
assertEquals("Actual version: $classVersion", expectedMajor, classVersion.major)
|
||||
assertEquals("Actual version: $classVersion", expectedMinor, classVersion.minor)
|
||||
@@ -641,6 +642,11 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
||||
}
|
||||
}
|
||||
|
||||
fun testFirAgainstFirUsingFlag() {
|
||||
val library = compileLibrary("library", additionalOptions = listOf("-language-version", "2.0"))
|
||||
compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-Xuse-k2"))
|
||||
}
|
||||
|
||||
fun testFirAgainstFir() {
|
||||
val library = compileLibrary("library", additionalOptions = listOf("-language-version", "2.0"))
|
||||
compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-language-version", "2.0"))
|
||||
@@ -691,6 +697,8 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
||||
}
|
||||
|
||||
fun testOldJvmAgainstFirWithStableAbi() {
|
||||
// TODO: looks like now it's not possible to compile library with version 2.0 to be able to compile against it without additional flags
|
||||
// Should we delete this test?
|
||||
val library = compileLibrary("library", additionalOptions = listOf("-language-version", "2.0", "-Xabi-stability=stable"))
|
||||
compileKotlin("source.kt", tmpdir, listOf(library))
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
@@ -49,7 +50,7 @@ class KotlinClassFinderTest : KotlinTestWithEnvironmentManagement() {
|
||||
fun testNestedClass() {
|
||||
val tmpdir = KotlinTestUtils.tmpDirForTest(this)
|
||||
KotlinTestUtils.compileKotlinWithJava(
|
||||
listOf(), listOf(File("compiler/testData/kotlinClassFinder/nestedClass.kt")), tmpdir, testRootDisposable, null
|
||||
listOf(), listOf(File("compiler/testData/kotlinClassFinder/nestedClass.kt")), tmpdir, testRootDisposable, null
|
||||
)
|
||||
|
||||
val environment = createEnvironment(tmpdir)
|
||||
@@ -60,7 +61,9 @@ class KotlinClassFinderTest : KotlinTestWithEnvironmentManagement() {
|
||||
assertNotNull(psiClass, "Psi class not found for $className")
|
||||
assertTrue(psiClass !is KtLightClass, "Kotlin light classes are not not expected")
|
||||
|
||||
val binaryClass = VirtualFileFinder.SERVICE.getInstance(project).findKotlinClass(JavaClassImpl(psiClass))
|
||||
val binaryClass = VirtualFileFinder.SERVICE.getInstance(project).findKotlinClass(
|
||||
JavaClassImpl(psiClass), JvmMetadataVersion.INSTANCE
|
||||
)
|
||||
assertNotNull(binaryClass, "No binary class for $className")
|
||||
|
||||
assertEquals("test/A.B.C", binaryClass.classId.toString())
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.javac.components.JavacBasedClassFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
@@ -66,7 +67,7 @@ class KotlinJavacBasedClassFinderTest : KotlinTestWithEnvironmentManagement() {
|
||||
val found = classFinder.findClass(classId)
|
||||
assertNotNull(found, "Class not found for $className")
|
||||
|
||||
val binaryClass = VirtualFileFinder.SERVICE.getInstance(project).findKotlinClass(found)
|
||||
val binaryClass = VirtualFileFinder.SERVICE.getInstance(project).findKotlinClass(found, JvmMetadataVersion.INSTANCE)
|
||||
assertNotNull(binaryClass, "No binary class for $className")
|
||||
|
||||
assertEquals("test/A.B.C", binaryClass.classId.toString())
|
||||
|
||||
Reference in New Issue
Block a user