Rename source set "descriptor.loader.java" -> "descriptors.jvm"

The new name is more convenient and precise because this module is no
longer only about loading declarations from Java, it also contains
implementation of loading Kotlin declarations from .class files, as well
as type mapping abstractions, JVM ABI specifications, etc.
This commit is contained in:
Alexander Udalov
2017-11-07 13:43:44 +01:00
parent 51e00eb05e
commit 328e906b57
104 changed files with 86 additions and 86 deletions
@@ -0,0 +1,3 @@
org.jetbrains.kotlin.load.java.FieldOverridabilityCondition
org.jetbrains.kotlin.load.java.ErasedOverridabilityCondition
org.jetbrains.kotlin.load.java.JavaIncompatibilityRulesOverridabilityCondition
@@ -0,0 +1,123 @@
/*
* Copyright 2010-2015 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.jvm;
import "core/deserialization/src/ext_options.proto";
import "core/deserialization/src/descriptors.proto";
option java_outer_classname = "JvmProtoBuf";
option optimize_for = LITE_RUNTIME;
message StringTableTypes {
message Record {
// The number of times this record should be repeated; this is used to collapse identical subsequent records in the list
optional int32 range = 1 [default = 1];
// Index of the predefined constant. If this field is present, the associated string is ignored
optional int32 predefined_index = 2;
// A string which should be used. If this field is present, both the associated string and the predefined string index are ignored
optional string string = 6;
enum Operation {
NONE = 0;
// replaceAll('$', '.')
// java/util/Map$Entry -> java/util/Map.Entry;
INTERNAL_TO_CLASS_ID = 1;
// substring(1, length - 1) and then replaceAll('$', '.')
// Ljava/util/Map$Entry; -> java/util/Map.Entry
DESC_TO_CLASS_ID = 2;
}
// Perform a described operation on the string
optional Operation operation = 3 [default = NONE];
// If this field is present, the "substring" operation must be performed with the first element of this list as the start index,
// and the second element as the end index.
// If an operation is not NONE, it's applied _after_ this substring operation
repeated int32 substring_index = 4 [packed = true];
// If this field is present, the "replaceAll" operation must be performed with the first element of this list as the code point
// of the character to replace, and the second element as the code point of the replacement character
repeated int32 replace_char = 5 [packed = true];
}
repeated Record record = 1;
// Indices of strings which are names of local classes or anonymous objects
repeated int32 local_name = 5 [packed = true];
}
message JvmMethodSignature {
optional int32 name = 1 [(string_id_in_table) = true];
// JVM descriptor of the method, e.g. '(Ljava/util/List;)[Ljava/lang/Object;'
optional int32 desc = 2 [(string_id_in_table) = true];
}
message JvmFieldSignature {
optional int32 name = 1 [(string_id_in_table) = true];
// JVM descriptor of the field type, e.g. 'Ljava/lang/String;'
optional int32 desc = 2 [(string_id_in_table) = true];
}
message JvmPropertySignature {
optional JvmFieldSignature field = 1;
// Annotations on properties are written on a synthetic method with this signature
optional JvmMethodSignature synthetic_method = 2;
optional JvmMethodSignature getter = 3;
optional JvmMethodSignature setter = 4;
}
extend Constructor {
optional JvmMethodSignature constructor_signature = 100;
}
extend Function {
optional JvmMethodSignature method_signature = 100;
}
extend Property {
optional JvmPropertySignature property_signature = 100;
}
extend Type {
repeated Annotation type_annotation = 100;
optional bool is_raw = 101;
}
extend TypeParameter {
repeated Annotation type_parameter_annotation = 100;
}
extend Class {
// If absent, assumed to be JvmAbi.DEFAULT_MODULE_NAME
optional int32 class_module_name = 101 [(string_id_in_table) = true];
repeated Property class_local_variable = 102;
}
extend Package {
optional int32 package_module_name = 101 [(string_id_in_table) = true];
repeated Property package_local_variable = 102;
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2015 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.jvm;
option java_outer_classname = "JvmPackageTable";
option optimize_for = LITE_RUNTIME;
message PackageTable {
// Names of .class files for each package
repeated PackageParts package_parts = 1;
// Names of .kotlin_metadata files for each package
repeated PackageParts metadata_parts = 2;
// Values of @JvmPackageName annotation used in this module; can be referenced in PackageParts#class_with_jvm_package_name_package_id.
// The names here are dot-separated, e.g. "org.foo.bar"
repeated string jvm_package_name = 3;
}
message PackageParts {
required string package_fq_name = 1;
// Short names of files, without extension, present in this package. Only single file facades and multi-file _parts_ are listed here
// (multi-file facades are not present in this list, they are defined below). Only files whose JVM package name is equal to the
// Kotlin package name (i.e. it has not been changed with @JvmPackageName) are listed here.
repeated string short_class_name = 2;
// For each name in short_class_name, index of the name of the corresponding multi-file facade class in multifile_facade_short_name + 1,
// or 0 if the class is not a multi-file part. If there's no value in this list at some index, the value is assumed to be 0.
// (e.g. if there are no multi-file classes in the module, this list is not going to exist at all)
repeated int32 multifile_facade_short_name_id = 3 [packed = true];
// Short names of multi-file facades, used in multifile_facade_short_name_id to store the part -> facade mapping.
repeated string multifile_facade_short_name = 4;
// Short names of files (single file facades), whose JVM package differs from the Kotlin package because of @JvmPackageName.
// The JVM package name of each file is stored at the same index in class_with_jvm_package_name_package_id.
repeated string class_with_jvm_package_name_short_name = 5;
// For each name in class_with_jvm_package_name_short_name, the index (into PackageTable#jvm_package_name) of the JVM package name.
// This list should have at least one element, otherwise classes with JVM package names are going to be ignored completely.
//
// If there's no value in this list at some index other than 0, the value is assumed to be the same as the value of the last element
// of this list. The intended use case for this optimization is to have just a list of a single element in the most frequent case
// when a bunch of files from the same Kotlin package have the same JVM package name.
repeated int32 class_with_jvm_package_name_package_id = 6 [packed = true];
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2016 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.builtins
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.GivenFunctionsMemberScope
import org.jetbrains.kotlin.storage.StorageManager
class CloneableClassScope(
storageManager: StorageManager,
containingClass: ClassDescriptor
) : GivenFunctionsMemberScope(storageManager, containingClass) {
override fun computeDeclaredFunctions(): List<FunctionDescriptor> = listOf(
SimpleFunctionDescriptorImpl.create(containingClass, Annotations.EMPTY, CLONE_NAME, DECLARATION, SourceElement.NO_SOURCE).apply {
initialize(
null, containingClass.thisAsReceiverParameter, emptyList(), emptyList(), containingClass.builtIns.anyType,
Modality.OPEN, Visibilities.PROTECTED
)
}
)
companion object {
internal val CLONE_NAME = Name.identifier("clone")
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2016 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.builtins
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.deserialization.ClassDescriptorFactory
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
class JvmBuiltInClassDescriptorFactory(
storageManager: StorageManager,
private val moduleDescriptor: ModuleDescriptor,
private val computeContainingDeclaration: (ModuleDescriptor) -> DeclarationDescriptor = { module ->
module.getPackage(KOTLIN_FQ_NAME).fragments.filterIsInstance<BuiltInsPackageFragment>().first()
}
) : ClassDescriptorFactory {
private val cloneable by storageManager.createLazyValue {
ClassDescriptorImpl(
computeContainingDeclaration(moduleDescriptor),
CLONEABLE_NAME, Modality.ABSTRACT, ClassKind.INTERFACE, listOf(moduleDescriptor.builtIns.anyType),
SourceElement.NO_SOURCE, /* isExternal = */ false
).apply {
initialize(CloneableClassScope(storageManager, this), emptySet(), null)
}
}
override fun shouldCreateClass(packageFqName: FqName, name: Name): Boolean =
name == CLONEABLE_NAME && packageFqName == KOTLIN_FQ_NAME
override fun createClass(classId: ClassId): ClassDescriptor? =
when (classId) {
CLONEABLE_CLASS_ID -> cloneable
else -> null
}
override fun getAllContributedClassesIfPossible(packageFqName: FqName): Collection<ClassDescriptor> =
when (packageFqName) {
KOTLIN_FQ_NAME -> setOf(cloneable)
else -> emptySet()
}
companion object {
private val KOTLIN_FQ_NAME = KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME
private val CLONEABLE_NAME = KotlinBuiltIns.FQ_NAMES.cloneable.shortName()
val CLONEABLE_CLASS_ID = ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.cloneable.toSafe())
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2016 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.builtins
import org.jetbrains.kotlin.builtins.functions.BuiltInFictitiousFunctionClassFactory
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.descriptors.deserialization.AdditionalClassPartsProvider
import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentDeclarationFilter
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.serialization.deserialization.*
import org.jetbrains.kotlin.storage.StorageManager
class JvmBuiltInsPackageFragmentProvider(
storageManager: StorageManager,
finder: KotlinClassFinder,
moduleDescriptor: ModuleDescriptor,
notFoundClasses: NotFoundClasses,
additionalClassPartsProvider: AdditionalClassPartsProvider,
platformDependentDeclarationFilter: PlatformDependentDeclarationFilter
) : AbstractDeserializedPackageFragmentProvider(storageManager, finder, moduleDescriptor) {
init {
components = DeserializationComponents(
storageManager,
moduleDescriptor,
DeserializationConfiguration.Default, // TODO
DeserializedClassDataFinder(this),
AnnotationAndConstantLoaderImpl(moduleDescriptor, notFoundClasses, BuiltInSerializerProtocol),
this,
LocalClassifierTypeSettings.Default,
ErrorReporter.DO_NOTHING,
LookupTracker.DO_NOTHING,
FlexibleTypeDeserializer.ThrowException,
listOf(
BuiltInFictitiousFunctionClassFactory(storageManager, moduleDescriptor),
JvmBuiltInClassDescriptorFactory(storageManager, moduleDescriptor)
),
notFoundClasses,
ContractDeserializer.DEFAULT,
additionalClassPartsProvider, platformDependentDeclarationFilter
)
}
override fun findPackage(fqName: FqName): DeserializedPackageFragment? =
finder.findBuiltInsData(fqName)?.let { inputStream ->
BuiltInsPackageFragmentImpl(fqName, storageManager, moduleDescriptor, inputStream)
}
}
@@ -0,0 +1,185 @@
/*
* 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.load.java
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.java.lazy.NullabilityQualifierWithApplicability
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifierWithMigrationStatus
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgumentValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.ReportLevel
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname")
private val TYPE_QUALIFIER_FQNAME = FqName("javax.annotation.meta.TypeQualifier")
private val TYPE_QUALIFIER_DEFAULT_FQNAME = FqName("javax.annotation.meta.TypeQualifierDefault")
private val MIGRATION_ANNOTATION_FQNAME = FqName("kotlin.annotations.jvm.UnderMigration")
private val BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS = mapOf(
FqName("javax.annotation.ParametersAreNullableByDefault") to
NullabilityQualifierWithApplicability(
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE),
listOf(AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER)
),
FqName("javax.annotation.ParametersAreNonnullByDefault") to
NullabilityQualifierWithApplicability(
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL),
listOf(AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER)
)
)
class AnnotationTypeQualifierResolver(storageManager: StorageManager, private val jsr305State: Jsr305State) {
enum class QualifierApplicabilityType {
METHOD_RETURN_TYPE, VALUE_PARAMETER, FIELD, TYPE_USE
}
class TypeQualifierWithApplicability(
private val typeQualifier: AnnotationDescriptor,
private val applicability: Int
) {
operator fun component1() = typeQualifier
operator fun component2() = QualifierApplicabilityType.values().filter(this::isApplicableTo)
private fun isApplicableTo(elementType: QualifierApplicabilityType) =
isApplicableConsideringMask(QualifierApplicabilityType.TYPE_USE) || isApplicableConsideringMask(elementType)
private fun isApplicableConsideringMask(elementType: QualifierApplicabilityType) =
(applicability and (1 shl elementType.ordinal)) != 0
}
private val resolvedNicknames =
storageManager.createMemoizedFunctionWithNullableValues(this::computeTypeQualifierNickname)
private fun computeTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? {
if (!classDescriptor.annotations.hasAnnotation(TYPE_QUALIFIER_NICKNAME_FQNAME)) return null
return classDescriptor.annotations.firstNotNullResult(this::resolveTypeQualifierAnnotation)
}
private fun resolveTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? {
if (classDescriptor.kind != ClassKind.ANNOTATION_CLASS) return null
return resolvedNicknames(classDescriptor)
}
fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
if (jsr305State.disabled) {
return null
}
val annotationClass = annotationDescriptor.annotationClass ?: return null
if (annotationClass.isAnnotatedWithTypeQualifier) return annotationDescriptor
return resolveTypeQualifierNickname(annotationClass)
}
fun resolveQualifierBuiltInDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithApplicability? {
if (jsr305State.disabled) {
return null
}
return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotationDescriptor.fqName]?.let { (qualifier, applicability) ->
val state = resolveJsr305AnnotationState(annotationDescriptor).takeIf { it != ReportLevel.IGNORE } ?: return null
return NullabilityQualifierWithApplicability(qualifier.copy(isForWarningOnly = state.isWarning), applicability)
}
}
fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
if (jsr305State.disabled) {
return null
}
val typeQualifierDefaultAnnotatedClass =
annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) }
?: return null
val elementTypesMask =
annotationDescriptor.annotationClass!!
.annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME)!!
.allValueArguments
.flatMap { (parameter, argument) ->
if (parameter == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME)
argument.mapConstantToQualifierApplicabilityTypes()
else
emptyList()
}
.fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) }
val typeQualifier = typeQualifierDefaultAnnotatedClass.annotations.firstOrNull { resolveTypeQualifierAnnotation(it) != null }
?: return null
return TypeQualifierWithApplicability(typeQualifier, elementTypesMask)
}
fun resolveJsr305AnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel {
resolveJsr305CustomState(annotationDescriptor)?.let { return it }
return jsr305State.global
}
fun resolveJsr305CustomState(annotationDescriptor: AnnotationDescriptor): ReportLevel? {
jsr305State.user[annotationDescriptor.fqName?.asString()]?.let { return it }
return annotationDescriptor.annotationClass?.migrationAnnotationStatus()
}
private fun ClassDescriptor.migrationAnnotationStatus(): ReportLevel? {
val stateDescriptor = annotations.findAnnotation(MIGRATION_ANNOTATION_FQNAME)?.firstArgumentValue()?.safeAs<ClassDescriptor>()
?: return null
jsr305State.migration?.let { return jsr305State.migration }
return when (stateDescriptor.name.asString()) {
"STRICT" -> ReportLevel.STRICT
"WARN" -> ReportLevel.WARN
"IGNORE" -> ReportLevel.IGNORE
else -> null
}
}
private fun ConstantValue<*>.mapConstantToQualifierApplicabilityTypes(): List<QualifierApplicabilityType> =
when (this) {
is ArrayValue -> value.flatMap { it.mapConstantToQualifierApplicabilityTypes() }
is EnumValue -> listOfNotNull(
when (value.name.identifier) {
"METHOD" -> QualifierApplicabilityType.METHOD_RETURN_TYPE
"FIELD" -> QualifierApplicabilityType.FIELD
"PARAMETER" -> QualifierApplicabilityType.VALUE_PARAMETER
"TYPE_USE" -> QualifierApplicabilityType.TYPE_USE
else -> null
}
)
else -> emptyList()
}
val disabled: Boolean = jsr305State.disabled
}
val BUILT_IN_TYPE_QUALIFIER_FQ_NAMES = setOf(JAVAX_NONNULL_ANNOTATION, JAVAX_CHECKFORNULL_ANNOTATION)
private val ClassDescriptor.isAnnotatedWithTypeQualifier: Boolean
get() = fqNameSafe in BUILT_IN_TYPE_QUALIFIER_FQ_NAMES || annotations.hasAnnotation(TYPE_QUALIFIER_FQNAME)
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2016 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.load.java
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
class BuiltinOverridabilityCondition : ExternalOverridabilityCondition {
override fun isOverridable(
superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor,
subClassDescriptor: ClassDescriptor?
) = Result.UNKNOWN
override fun getContract() = ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY
}
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2016 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.load.java
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.lazy.types.RawSubstitution
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
import org.jetbrains.kotlin.resolve.OverridingUtil
class ErasedOverridabilityCondition : ExternalOverridabilityCondition {
override fun isOverridable(superDescriptor: CallableDescriptor, subDescriptor: CallableDescriptor, subClassDescriptor: ClassDescriptor?): Result {
if (subDescriptor !is JavaMethodDescriptor || subDescriptor.typeParameters.isNotEmpty()) return Result.UNKNOWN
val basicOverridability = OverridingUtil.getBasicOverridabilityProblem(superDescriptor, subDescriptor)?.result
if (basicOverridability != null) return Result.UNKNOWN
val signatureTypes = subDescriptor.valueParameters.asSequence().map { it.type } +
subDescriptor.returnType!! +
listOfNotNull(subDescriptor.extensionReceiverParameter?.type)
if (signatureTypes.any { it.arguments.isNotEmpty() && it.unwrap() !is RawTypeImpl }) return Result.UNKNOWN
var erasedSuper = superDescriptor.substitute(RawSubstitution.buildSubstitutor()) ?: return Result.UNKNOWN
if (erasedSuper is SimpleFunctionDescriptor && erasedSuper.typeParameters.isNotEmpty()) {
// Only simple functions are supported now for erased overrides
erasedSuper = erasedSuper.newCopyBuilder().setTypeParameters(emptyList()).build()!!
}
val overridabilityResult =
OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions(erasedSuper, subDescriptor, false).result
return when (overridabilityResult) {
OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE -> Result.OVERRIDABLE
else -> Result.UNKNOWN
}
}
override fun getContract() = ExternalOverridabilityCondition.Contract.SUCCESS_ONLY
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2015 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.load.java
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.name.FqName
object FakePureImplementationsProvider {
fun getPurelyImplementedInterface(classFqName: FqName): FqName? = pureImplementations[classFqName]
private val pureImplementations = hashMapOf<FqName, FqName>()
private infix fun FqName.implementedWith(implementations: List<FqName>) {
implementations.associateTo(pureImplementations) { it to this }
}
init {
FQ_NAMES.mutableList implementedWith fqNameListOf("java.util.ArrayList", "java.util.LinkedList")
FQ_NAMES.mutableSet implementedWith fqNameListOf("java.util.HashSet", "java.util.TreeSet", "java.util.LinkedHashSet")
FQ_NAMES.mutableMap implementedWith fqNameListOf("java.util.HashMap", "java.util.TreeMap", "java.util.LinkedHashMap",
"java.util.concurrent.ConcurrentHashMap", "java.util.concurrent.ConcurrentSkipListMap")
FqName("java.util.function.Function") implementedWith fqNameListOf("java.util.function.UnaryOperator")
FqName("java.util.function.BiFunction") implementedWith fqNameListOf("java.util.function.BinaryOperator")
}
private fun fqNameListOf(vararg names: String): List<FqName> = names.map(::FqName)
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2015 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.load.java
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
class FieldOverridabilityCondition : ExternalOverridabilityCondition {
override fun isOverridable(superDescriptor: CallableDescriptor, subDescriptor: CallableDescriptor, subClassDescriptor: ClassDescriptor?): Result {
if (subDescriptor !is PropertyDescriptor || superDescriptor !is PropertyDescriptor) return Result.UNKNOWN
if (subDescriptor.name != superDescriptor.name) return Result.UNKNOWN
if (subDescriptor.isJavaField && superDescriptor.isJavaField) return Result.OVERRIDABLE
if (subDescriptor.isJavaField || superDescriptor.isJavaField) return Result.INCOMPATIBLE
return Result.UNKNOWN
}
override fun getContract() = ExternalOverridabilityCondition.Contract.BOTH
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2015 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.load.java;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.load.java.structure.JavaClass;
import org.jetbrains.kotlin.load.java.structure.JavaPackage;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import java.util.Set;
public interface JavaClassFinder {
@Nullable
JavaClass findClass(@NotNull ClassId classId);
@Nullable
JavaPackage findPackage(@NotNull FqName fqName);
@ReadOnly
@Nullable
Set<String> knownClassNamesInPackage(@NotNull FqName packageFqName);
}
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2015 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.load.java
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithDifferentJvmName.sameAsRenamedInJvmBuiltin
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.kotlin.JvmType
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
import org.jetbrains.kotlin.load.kotlin.forceSingleValueParameterBoxing
import org.jetbrains.kotlin.load.kotlin.mapToJvmType
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.typeUtil.makeNullable
/**
* This class contains Java-related overridability conditions that may force incompatibility
*/
class JavaIncompatibilityRulesOverridabilityCondition : ExternalOverridabilityCondition {
override fun isOverridable(
superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor,
subClassDescriptor: ClassDescriptor?
): Result {
if (isIncompatibleInAccordanceWithBuiltInOverridabilityRules(superDescriptor, subDescriptor, subClassDescriptor)) {
return Result.INCOMPATIBLE
}
if (doesJavaOverrideHaveIncompatibleValueParameterKinds(superDescriptor, subDescriptor)) {
return Result.INCOMPATIBLE
}
return Result.UNKNOWN
}
// This overridability condition checks two things:
// 1. Method accidentally having the same signature as special builtin has does not supposed to be override for it in Java class
// 2. In such Java class (with special signature clash) special builtin is loaded as hidden function with special signature, and
// it should not override non-special method in further inheritance
// See java.nio.Buffer
private fun isIncompatibleInAccordanceWithBuiltInOverridabilityRules(
superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor,
subClassDescriptor: ClassDescriptor?
): Boolean {
if (superDescriptor !is CallableMemberDescriptor || subDescriptor !is FunctionDescriptor ||
KotlinBuiltIns.isBuiltIn(subDescriptor)) {
return false
}
if (!subDescriptor.name.sameAsBuiltinMethodWithErasedValueParameters && !subDescriptor.name.sameAsRenamedInJvmBuiltin) {
return false
}
val overriddenBuiltin = superDescriptor.getOverriddenSpecialBuiltin()
// Checking second condition: special hidden override is not supposed to be an override to non-special irrelevant Java declaration
val isOneOfDescriptorsHidden =
subDescriptor.isHiddenToOvercomeSignatureClash != (superDescriptor as? FunctionDescriptor)?.isHiddenToOvercomeSignatureClash
if (isOneOfDescriptorsHidden &&
(overriddenBuiltin == null || !subDescriptor.isHiddenToOvercomeSignatureClash)) {
return true
}
// If new containing class is not Java class or subDescriptor signature was artificially changed, use basic overridability rules
if (subClassDescriptor !is JavaClassDescriptor || subDescriptor.initialSignatureDescriptor != null) {
return false
}
// If current Java class has Kotlin super class with override of overriddenBuiltin, then common overridability rules can be applied
// because of final special bridge generated in Kotlin super class
if (overriddenBuiltin == null || subClassDescriptor.hasRealKotlinSuperClassWithOverrideOf(overriddenBuiltin)) return false
// class A extends HashMap<Object, Object> {
// void get(Object x) {}
// }
//
// The problem is that when checking overridabilty of `A.get` and `HashMap.get` we fall through to here, because
// we do not recreate a magic copy of it, because it has the same signature.
// But it obviously that if subDescriptor and superDescriptor has the same JVM descriptor, they're one-way overridable.
// Note that it doesn't work if special builtIn was renamed, because we do not consider renamed built-ins
// in `computeJvmDescriptor`.
// TODO: things get more and more complicated here, consider moving signature mapping from backend and using it here instead of all of this magic
if (overriddenBuiltin is FunctionDescriptor && superDescriptor is FunctionDescriptor &&
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(overriddenBuiltin) != null &&
subDescriptor.computeJvmDescriptor(withReturnType = false) == superDescriptor.original.computeJvmDescriptor(withReturnType = false)) {
return false
}
// Here we know that something in Java with common signature is going to override some special builtin that is supposed to be
// incompatible override
return true
}
override fun getContract() = ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY
companion object {
/**
* Checks if any pair of corresponding value parameters has different type kinds, e.g. one is primitive and another is not
*
* As it comes from it's name it only checks overrides in Java classes
*/
fun doesJavaOverrideHaveIncompatibleValueParameterKinds(
superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor
): Boolean {
if (subDescriptor !is JavaMethodDescriptor || superDescriptor !is FunctionDescriptor) return false
assert(subDescriptor.valueParameters.size == superDescriptor.valueParameters.size) {
"External overridability condition with CONFLICTS_ONLY should not be run with different value parameters size"
}
for ((subParameter, superParameter) in subDescriptor.original.valueParameters.zip(superDescriptor.original.valueParameters)) {
val isSubPrimitive = mapValueParameterType(subDescriptor, subParameter) is JvmType.Primitive
val isSuperPrimitive = mapValueParameterType(superDescriptor, superParameter) is JvmType.Primitive
if (isSubPrimitive != isSuperPrimitive) {
return true
}
}
return false
}
private fun mapValueParameterType(f: FunctionDescriptor, valueParameterDescriptor: ValueParameterDescriptor) =
if (forceSingleValueParameterBoxing(f) || isPrimitiveCompareTo(f))
valueParameterDescriptor.type.makeNullable().mapToJvmType()
else
valueParameterDescriptor.type.mapToJvmType()
// It's useful here to suppose that 'Int.compareTo(Int)' requires boxing of it's value parameter
// As it happens in java.lang.Integer analogue
// It only affects additional built-ins loading (see 'testLoadBuiltIns' tests)
private fun isPrimitiveCompareTo(f: FunctionDescriptor): Boolean {
if (f.valueParameters.size != 1) return false
val classDescriptor =
f.containingDeclaration as? ClassDescriptor ?: return false
val parameterClass =
f.valueParameters.single().type.constructor.declarationDescriptor as? ClassDescriptor
?: return false
return KotlinBuiltIns.isPrimitiveClass(classDescriptor) && classDescriptor.fqNameSafe == parameterClass.fqNameSafe
}
}
}
@@ -0,0 +1,142 @@
/*
* Copyright 2010-2015 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.load.java;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
public class JavaVisibilities {
private JavaVisibilities() {
}
@NotNull
public static final Visibility PACKAGE_VISIBILITY = new Visibility("package", false) {
@Override
public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
return areInSamePackage(what, from);
}
@Override
public boolean mustCheckInImports() {
return true;
}
@Override
protected Integer compareTo(@NotNull Visibility visibility) {
if (this == visibility) return 0;
if (Visibilities.isPrivate(visibility)) return 1;
return -1;
}
@NotNull
@Override
public String getDisplayName() {
return "public/*package*/";
}
@NotNull
@Override
public Visibility normalize() {
return Visibilities.PROTECTED;
}
@NotNull
@Override
public EffectiveVisibility effectiveVisibility(@NotNull DeclarationDescriptor classDescriptor, boolean checkPublishedApi) {
return EffectiveVisibility.PackagePrivate.INSTANCE;
}
};
@NotNull
public static final Visibility PROTECTED_STATIC_VISIBILITY = new Visibility("protected_static", true) {
@Override
public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
return isVisibleForProtectedAndPackage(receiver, what, from);
}
@Override
public boolean mustCheckInImports() {
return false;
}
@NotNull
@Override
public String getDisplayName() {
return "protected/*protected static*/";
}
@NotNull
@Override
public Visibility normalize() {
return Visibilities.PROTECTED;
}
};
@NotNull
public static final Visibility PROTECTED_AND_PACKAGE = new Visibility("protected_and_package", true) {
@Override
public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
return isVisibleForProtectedAndPackage(receiver, what, from);
}
@Override
public boolean mustCheckInImports() {
return false;
}
@Override
protected Integer compareTo(@NotNull Visibility visibility) {
if (this == visibility) return 0;
if (visibility == Visibilities.INTERNAL) return null;
if (Visibilities.isPrivate(visibility)) return 1;
return -1;
}
@NotNull
@Override
public String getDisplayName() {
return "protected/*protected and package*/";
}
@NotNull
@Override
public Visibility normalize() {
return Visibilities.PROTECTED;
}
};
private static boolean isVisibleForProtectedAndPackage(
@Nullable ReceiverValue receiver,
@NotNull DeclarationDescriptorWithVisibility what,
@NotNull DeclarationDescriptor from
) {
if (areInSamePackage(DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(what), from)) {
return true;
}
return Visibilities.PROTECTED.isVisible(receiver, what, from);
}
private static boolean areInSamePackage(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) {
PackageFragmentDescriptor whatPackage = DescriptorUtils.getParentOfType(first, PackageFragmentDescriptor.class, false);
PackageFragmentDescriptor fromPackage = DescriptorUtils.getParentOfType(second, PackageFragmentDescriptor.class, false);
return fromPackage != null && whatPackage != null && whatPackage.getFqName().equals(fromPackage.getFqName());
}
}
@@ -0,0 +1,127 @@
/*
* Copyright 2010-2015 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.load.java;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.CompanionObjectMapping;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.util.capitalizeDecapitalize.CapitalizeDecapitalizeKt;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isClassOrEnumClass;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface;
public final class JvmAbi {
public static final String DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls";
/**
* Warning: use DEFAULT_IMPLS_CLASS_NAME and TypeMappingConfiguration.innerClassNameFactory when possible.
* This is false for KAPT3 mode.
*/
public static final String DEFAULT_IMPLS_SUFFIX = "$" + DEFAULT_IMPLS_CLASS_NAME;
public static final String DEFAULT_IMPLS_DELEGATE_SUFFIX = "$defaultImpl";
public static final String DEFAULT_PARAMS_IMPL_SUFFIX = "$default";
private static final String GET_PREFIX = "get";
private static final String IS_PREFIX = "is";
private static final String SET_PREFIX = "set";
public static final String DELEGATED_PROPERTY_NAME_SUFFIX = "$delegate";
public static final String DELEGATED_PROPERTIES_ARRAY_NAME = "$$delegatedProperties";
public static final String DELEGATE_SUPER_FIELD_PREFIX = "$$delegate_";
private static final String ANNOTATIONS_SUFFIX = "$annotations";
private static final String ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
private static final String ANNOTATED_TYPEALIAS_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
public static final String INSTANCE_FIELD = "INSTANCE";
public static final String HIDDEN_INSTANCE_FIELD = "$$" + INSTANCE_FIELD;
public static final String DEFAULT_MODULE_NAME = "main";
public static final ClassId REFLECTION_FACTORY_IMPL = ClassId.topLevel(new FqName("kotlin.reflect.jvm.internal.ReflectionFactoryImpl"));
public static final String LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT = "$i$a$";
public static final String LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION = "$i$f$";
@NotNull
public static String getSyntheticMethodNameForAnnotatedProperty(@NotNull Name propertyName) {
return propertyName.asString() + ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX;
}
@NotNull
public static String getSyntheticMethodNameForAnnotatedTypeAlias(@NotNull Name typeAliasName) {
return typeAliasName.asString() + ANNOTATED_TYPEALIAS_METHOD_NAME_SUFFIX;
}
public static boolean isGetterName(@NotNull String name) {
return name.startsWith(GET_PREFIX) || name.startsWith(IS_PREFIX);
}
public static boolean isSetterName(@NotNull String name) {
return name.startsWith(SET_PREFIX);
}
@NotNull
public static String getterName(@NotNull String propertyName) {
return startsWithIsPrefix(propertyName)
? propertyName
: GET_PREFIX + CapitalizeDecapitalizeKt.capitalizeAsciiOnly(propertyName);
}
@NotNull
public static String setterName(@NotNull String propertyName) {
return SET_PREFIX +
(startsWithIsPrefix(propertyName)
? propertyName.substring(IS_PREFIX.length())
: CapitalizeDecapitalizeKt.capitalizeAsciiOnly(propertyName));
}
public static boolean startsWithIsPrefix(String name) {
if (!name.startsWith(IS_PREFIX)) return false;
if (name.length() == IS_PREFIX.length()) return false;
char c = name.charAt(IS_PREFIX.length());
return !('a' <= c && c <= 'z');
}
public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) {
return propertyDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
isCompanionObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration());
}
public static boolean isCompanionObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor companionObject) {
return isCompanionObject(companionObject) &&
isClassOrEnumClass(companionObject.getContainingDeclaration()) &&
!isMappedIntrinsicCompanionObject((ClassDescriptor) companionObject);
}
public static boolean isMappedIntrinsicCompanionObject(@NotNull ClassDescriptor companionObject) {
return CompanionObjectMapping.INSTANCE.isMappedIntrinsicCompanionObject(companionObject);
}
public static boolean isCompanionObjectInInterfaceNotIntrinsic(@NotNull DeclarationDescriptor companionObject) {
return isCompanionObject(companionObject) &&
isInterface(companionObject.getContainingDeclaration()) &&
!isMappedIntrinsicCompanionObject((ClassDescriptor) companionObject);
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2015 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.load.java;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
@SuppressWarnings("PointlessBitwiseExpression")
public final class JvmAnnotationNames {
public static final FqName METADATA_FQ_NAME = new FqName("kotlin.Metadata");
public static final String METADATA_DESC = "L" + JvmClassName.byFqNameWithoutInnerClasses(METADATA_FQ_NAME).getInternalName() + ";";
public static final String METADATA_VERSION_FIELD_NAME = "mv";
public static final String BYTECODE_VERSION_FIELD_NAME = "bv";
public static final String KIND_FIELD_NAME = "k";
public static final String METADATA_DATA_FIELD_NAME = "d1";
public static final String METADATA_STRINGS_FIELD_NAME = "d2";
public static final String METADATA_EXTRA_STRING_FIELD_NAME = "xs";
public static final String METADATA_PACKAGE_NAME_FIELD_NAME = "pn";
public static final String METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME = METADATA_EXTRA_STRING_FIELD_NAME;
public static final String METADATA_EXTRA_INT_FIELD_NAME = "xi";
public static final int METADATA_MULTIFILE_PARTS_INHERIT_FLAG = 1 << 0;
public static final int METADATA_PRE_RELEASE_FLAG = 1 << 1;
public static final int METADATA_SCRIPT_FLAG = 1 << 2;
public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value");
public static final FqName JETBRAINS_NOT_NULL_ANNOTATION = new FqName("org.jetbrains.annotations.NotNull");
public static final FqName JETBRAINS_NULLABLE_ANNOTATION = new FqName("org.jetbrains.annotations.Nullable");
public static final FqName JETBRAINS_MUTABLE_ANNOTATION = new FqName("org.jetbrains.annotations.Mutable");
public static final FqName JETBRAINS_READONLY_ANNOTATION = new FqName("org.jetbrains.annotations.ReadOnly");
public static final FqName PURELY_IMPLEMENTS_ANNOTATION = new FqName("kotlin.jvm.PurelyImplements");
// Just for internal use: there is no such real classes in bytecode
public static final FqName ENHANCED_NULLABILITY_ANNOTATION = new FqName("kotlin.jvm.internal.EnhancedNullability");
public static final FqName ENHANCED_MUTABILITY_ANNOTATION = new FqName("kotlin.jvm.internal.EnhancedMutability");
public static final FqName PARAMETER_NAME_FQ_NAME = new FqName("kotlin.annotations.jvm.internal.ParameterName");
public static final FqName DEFAULT_VALUE_FQ_NAME = new FqName("kotlin.annotations.jvm.internal.DefaultValue");
public static final FqName DEFAULT_NULL_FQ_NAME = new FqName("kotlin.annotations.jvm.internal.DefaultNull");
private JvmAnnotationNames() {
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2015 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.load.java
import org.jetbrains.kotlin.name.FqName
val NULLABLE_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION,
FqName("android.support.annotation.Nullable"),
FqName("com.android.annotations.Nullable"),
FqName("org.eclipse.jdt.annotation.Nullable"),
FqName("org.checkerframework.checker.nullness.qual.Nullable"),
FqName("javax.annotation.Nullable"),
FqName("javax.annotation.CheckForNull"),
FqName("edu.umd.cs.findbugs.annotations.CheckForNull"),
FqName("edu.umd.cs.findbugs.annotations.Nullable"),
FqName("edu.umd.cs.findbugs.annotations.PossiblyNull"),
FqName("io.reactivex.annotations.Nullable")
)
val JAVAX_NONNULL_ANNOTATION = FqName("javax.annotation.Nonnull")
val JAVAX_CHECKFORNULL_ANNOTATION = FqName("javax.annotation.CheckForNull")
val NOT_NULL_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION,
FqName("edu.umd.cs.findbugs.annotations.NonNull"),
FqName("android.support.annotation.NonNull"),
FqName("com.android.annotations.NonNull"),
FqName("org.eclipse.jdt.annotation.NonNull"),
FqName("org.checkerframework.checker.nullness.qual.NonNull"),
FqName("lombok.NonNull"),
FqName("io.reactivex.annotations.NonNull")
)
val READ_ONLY_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_READONLY_ANNOTATION
)
val MUTABLE_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_MUTABLE_ANNOTATION
)
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2015 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.load.java
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
/**
* The version of conventions used in bytecode of generated .class files, such as default method naming & signatures,
* internal member name mangling specifics, property getter/setter names, etc.
*/
class JvmBytecodeBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
override fun isCompatible() = this.isCompatibleTo(INSTANCE)
companion object {
@JvmField
val INSTANCE = JvmBytecodeBinaryVersion(1, 0, 2)
@JvmField
val INVALID_VERSION = JvmBytecodeBinaryVersion()
}
}
@@ -0,0 +1,147 @@
/*
* Copyright 2010-2015 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.load.java.components;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.structure.*;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy;
import org.jetbrains.kotlin.resolve.OverridingUtil;
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public final class DescriptorResolverUtils {
private DescriptorResolverUtils() {
}
@NotNull
public static <D extends CallableMemberDescriptor> Collection<D> resolveOverridesForNonStaticMembers(
@NotNull Name name, @NotNull Collection<D> membersFromSupertypes, @NotNull Collection<D> membersFromCurrent,
@NotNull ClassDescriptor classDescriptor, @NotNull ErrorReporter errorReporter
) {
return resolveOverrides(name, membersFromSupertypes, membersFromCurrent, classDescriptor, errorReporter, false);
}
@NotNull
public static <D extends CallableMemberDescriptor> Collection<D> resolveOverridesForStaticMembers(
@NotNull Name name, @NotNull Collection<D> membersFromSupertypes, @NotNull Collection<D> membersFromCurrent,
@NotNull ClassDescriptor classDescriptor, @NotNull ErrorReporter errorReporter
) {
return resolveOverrides(name, membersFromSupertypes, membersFromCurrent, classDescriptor, errorReporter, true);
}
@NotNull
private static <D extends CallableMemberDescriptor> Collection<D> resolveOverrides(
@NotNull Name name,
@NotNull Collection<D> membersFromSupertypes,
@NotNull Collection<D> membersFromCurrent,
@NotNull ClassDescriptor classDescriptor,
@NotNull final ErrorReporter errorReporter,
final boolean isStaticContext
) {
final Set<D> result = new LinkedHashSet<D>();
OverridingUtil.generateOverridesInFunctionGroup(
name, membersFromSupertypes, membersFromCurrent, classDescriptor,
new NonReportingOverrideStrategy() {
@Override
@SuppressWarnings("unchecked")
public void addFakeOverride(@NotNull CallableMemberDescriptor fakeOverride) {
OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, new Function1<CallableMemberDescriptor, Unit>() {
@Override
public Unit invoke(@NotNull CallableMemberDescriptor descriptor) {
errorReporter.reportCannotInferVisibility(descriptor);
return Unit.INSTANCE;
}
});
result.add((D) fakeOverride);
}
@Override
public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) {
// nop
}
@Override
public void setOverriddenDescriptors(
@NotNull CallableMemberDescriptor member, @NotNull Collection<? extends CallableMemberDescriptor> overridden
) {
// do not set overridden descriptors for declared static fields and methods from java
if (isStaticContext && member.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
return;
}
super.setOverriddenDescriptors(member, overridden);
}
}
);
return result;
}
@Nullable
public static ValueParameterDescriptor getAnnotationParameterByName(@NotNull Name name, @NotNull ClassDescriptor annotationClass) {
Collection<ClassConstructorDescriptor> constructors = annotationClass.getConstructors();
if (constructors.size() != 1) return null;
for (ValueParameterDescriptor parameter : constructors.iterator().next().getValueParameters()) {
if (parameter.getName().equals(name)) {
return parameter;
}
}
return null;
}
public static boolean isObjectMethodInInterface(@NotNull JavaMember member) {
return member.getContainingClass().isInterface() && member instanceof JavaMethod && isObjectMethod((JavaMethod) member);
}
public static boolean isObjectMethod(@NotNull JavaMethod method) {
String name = method.getName().asString();
if (name.equals("toString") || name.equals("hashCode")) {
return method.getValueParameters().isEmpty();
}
else if (name.equals("equals")) {
return isMethodWithOneParameterWithFqName(method, "java.lang.Object");
}
return false;
}
private static boolean isMethodWithOneParameterWithFqName(@NotNull JavaMethod method, @NotNull String fqName) {
List<JavaValueParameter> parameters = method.getValueParameters();
if (parameters.size() == 1) {
JavaType type = parameters.get(0).getType();
if (type instanceof JavaClassifierType) {
JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
if (classifier instanceof JavaClass) {
FqName classFqName = ((JavaClass) classifier).getFqName();
return classFqName != null && classFqName.asString().equals(fqName);
}
}
}
return false;
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2015 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.load.java.components;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation;
import org.jetbrains.kotlin.load.java.structure.JavaAnnotationOwner;
import org.jetbrains.kotlin.name.FqName;
public interface ExternalAnnotationResolver {
ExternalAnnotationResolver EMPTY = new ExternalAnnotationResolver() {
@Nullable
@Override
public JavaAnnotation findExternalAnnotation(@NotNull JavaAnnotationOwner owner, @NotNull FqName fqName) {
return null;
}
};
@Nullable
JavaAnnotation findExternalAnnotation(@NotNull JavaAnnotationOwner owner, @NotNull FqName fqName);
}
@@ -0,0 +1,190 @@
/*
* Copyright 2010-2015 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.load.java.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaAnnotationDescriptor
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.ConstantValueFactory
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.SimpleType
import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.Target
import java.util.*
object JavaAnnotationMapper {
private val JAVA_TARGET_FQ_NAME = FqName(Target::class.java.canonicalName)
private val JAVA_RETENTION_FQ_NAME = FqName(Retention::class.java.canonicalName)
private val JAVA_DEPRECATED_FQ_NAME = FqName(java.lang.Deprecated::class.java.canonicalName)
private val JAVA_DOCUMENTED_FQ_NAME = FqName(Documented::class.java.canonicalName)
// Java8-specific thing
private val JAVA_REPEATABLE_FQ_NAME = FqName("java.lang.annotation.Repeatable")
internal val DEPRECATED_ANNOTATION_MESSAGE = Name.identifier("message")
internal val TARGET_ANNOTATION_ALLOWED_TARGETS = Name.identifier("allowedTargets")
internal val RETENTION_ANNOTATION_VALUE = Name.identifier("value")
fun mapOrResolveJavaAnnotation(annotation: JavaAnnotation, c: LazyJavaResolverContext): AnnotationDescriptor? =
when (annotation.classId) {
ClassId.topLevel(JAVA_TARGET_FQ_NAME) -> JavaTargetAnnotationDescriptor(annotation, c)
ClassId.topLevel(JAVA_RETENTION_FQ_NAME) -> JavaRetentionAnnotationDescriptor(annotation, c)
ClassId.topLevel(JAVA_REPEATABLE_FQ_NAME) -> JavaAnnotationDescriptor(c, annotation, KotlinBuiltIns.FQ_NAMES.repeatable)
ClassId.topLevel(JAVA_DOCUMENTED_FQ_NAME) -> JavaAnnotationDescriptor(c, annotation, KotlinBuiltIns.FQ_NAMES.mustBeDocumented)
ClassId.topLevel(JAVA_DEPRECATED_FQ_NAME) -> null
else -> LazyJavaAnnotationDescriptor(c, annotation)
}
fun findMappedJavaAnnotation(
kotlinName: FqName,
annotationOwner: JavaAnnotationOwner,
c: LazyJavaResolverContext
): AnnotationDescriptor? {
if (kotlinName == KotlinBuiltIns.FQ_NAMES.deprecated) {
val javaAnnotation = annotationOwner.findAnnotation(JAVA_DEPRECATED_FQ_NAME)
if (javaAnnotation != null || annotationOwner.isDeprecatedInJavaDoc) {
return JavaDeprecatedAnnotationDescriptor(javaAnnotation, c)
}
}
return kotlinToJavaNameMap[kotlinName]?.let {
annotationOwner.findAnnotation(it)?.let {
mapOrResolveJavaAnnotation(it, c)
}
}
}
// kotlin.annotation.annotation is treated separately
private val kotlinToJavaNameMap: Map<FqName, FqName> =
mapOf(KotlinBuiltIns.FQ_NAMES.target to JAVA_TARGET_FQ_NAME,
KotlinBuiltIns.FQ_NAMES.retention to JAVA_RETENTION_FQ_NAME,
KotlinBuiltIns.FQ_NAMES.repeatable to JAVA_REPEATABLE_FQ_NAME,
KotlinBuiltIns.FQ_NAMES.mustBeDocumented to JAVA_DOCUMENTED_FQ_NAME)
val javaToKotlinNameMap: Map<FqName, FqName> =
mapOf(JAVA_TARGET_FQ_NAME to KotlinBuiltIns.FQ_NAMES.target,
JAVA_RETENTION_FQ_NAME to KotlinBuiltIns.FQ_NAMES.retention,
JAVA_DEPRECATED_FQ_NAME to KotlinBuiltIns.FQ_NAMES.deprecated,
JAVA_REPEATABLE_FQ_NAME to KotlinBuiltIns.FQ_NAMES.repeatable,
JAVA_DOCUMENTED_FQ_NAME to KotlinBuiltIns.FQ_NAMES.mustBeDocumented)
}
open class JavaAnnotationDescriptor(
c: LazyJavaResolverContext,
annotation: JavaAnnotation?,
override val fqName: FqName
): AnnotationDescriptor {
override val source: SourceElement = annotation?.let { c.components.sourceElementFactory.source(it) } ?: SourceElement.NO_SOURCE
override val type: SimpleType by c.storageManager.createLazyValue { c.module.builtIns.getBuiltInClassByFqName(fqName).defaultType }
protected val firstArgument: JavaAnnotationArgument? = annotation?.arguments?.firstOrNull()
override val allValueArguments: Map<Name, ConstantValue<*>> get() = emptyMap()
}
class JavaDeprecatedAnnotationDescriptor(
annotation: JavaAnnotation?,
c: LazyJavaResolverContext
): JavaAnnotationDescriptor(c, annotation, KotlinBuiltIns.FQ_NAMES.deprecated) {
override val allValueArguments: Map<Name, ConstantValue<*>> by c.storageManager.createLazyValue {
mapOf(JavaAnnotationMapper.DEPRECATED_ANNOTATION_MESSAGE to
ConstantValueFactory(c.module.builtIns).createStringValue("Deprecated in Java"))
}
}
class JavaTargetAnnotationDescriptor(
annotation: JavaAnnotation,
c: LazyJavaResolverContext
): JavaAnnotationDescriptor(c, annotation, KotlinBuiltIns.FQ_NAMES.target) {
override val allValueArguments by c.storageManager.createLazyValue {
val targetArgument = when (firstArgument) {
is JavaArrayAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(firstArgument.getElements(), c.module.builtIns)
is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(listOf(firstArgument), c.module.builtIns)
else -> null
}
targetArgument?.let { mapOf(JavaAnnotationMapper.TARGET_ANNOTATION_ALLOWED_TARGETS to it) }.orEmpty()
}
}
class JavaRetentionAnnotationDescriptor(
annotation: JavaAnnotation,
c: LazyJavaResolverContext
): JavaAnnotationDescriptor(c, annotation, KotlinBuiltIns.FQ_NAMES.retention) {
override val allValueArguments by c.storageManager.createLazyValue {
val retentionArgument = JavaAnnotationTargetMapper.mapJavaRetentionArgument(firstArgument, c.module.builtIns)
retentionArgument?.let { mapOf(JavaAnnotationMapper.RETENTION_ANNOTATION_VALUE to it) }.orEmpty()
}
}
object JavaAnnotationTargetMapper {
private val targetNameLists = mapOf("PACKAGE" to EnumSet.noneOf(KotlinTarget::class.java),
"TYPE" to EnumSet.of(KotlinTarget.CLASS, KotlinTarget.FILE),
"ANNOTATION_TYPE" to EnumSet.of(KotlinTarget.ANNOTATION_CLASS),
"TYPE_PARAMETER" to EnumSet.of(KotlinTarget.TYPE_PARAMETER),
"FIELD" to EnumSet.of(KotlinTarget.FIELD),
"LOCAL_VARIABLE" to EnumSet.of(KotlinTarget.LOCAL_VARIABLE),
"PARAMETER" to EnumSet.of(KotlinTarget.VALUE_PARAMETER),
"CONSTRUCTOR" to EnumSet.of(KotlinTarget.CONSTRUCTOR),
"METHOD" to EnumSet.of(KotlinTarget.FUNCTION,
KotlinTarget.PROPERTY_GETTER,
KotlinTarget.PROPERTY_SETTER),
"TYPE_USE" to EnumSet.of(KotlinTarget.TYPE)
)
fun mapJavaTargetArgumentByName(argumentName: String?): Set<KotlinTarget> = targetNameLists[argumentName] ?: emptySet()
internal fun mapJavaTargetArguments(arguments: List<JavaAnnotationArgument>, builtIns: KotlinBuiltIns): ConstantValue<*> {
// Map arguments: java.lang.annotation.Target -> kotlin.annotation.Target
val kotlinTargets = arguments.filterIsInstance<JavaEnumValueAnnotationArgument>()
.flatMap { mapJavaTargetArgumentByName(it.resolve()?.name?.asString()) }
.mapNotNull { builtIns.getAnnotationTargetEnumEntry(it) }
.map(::EnumValue)
val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName(
JavaAnnotationMapper.TARGET_ANNOTATION_ALLOWED_TARGETS,
builtIns.getBuiltInClassByFqName(KotlinBuiltIns.FQ_NAMES.target)
)
return ArrayValue(kotlinTargets, parameterDescriptor?.type ?: ErrorUtils.createErrorType("Error: AnnotationTarget[]"), builtIns)
}
private val retentionNameList = mapOf(
"RUNTIME" to KotlinRetention.RUNTIME,
"CLASS" to KotlinRetention.BINARY,
"SOURCE" to KotlinRetention.SOURCE
)
internal fun mapJavaRetentionArgument(element: JavaAnnotationArgument?, builtIns: KotlinBuiltIns): ConstantValue<*>? {
// Map argument: java.lang.annotation.Retention -> kotlin.annotation.annotation
return (element as? JavaEnumValueAnnotationArgument)?.let {
retentionNameList[it.resolve()?.name?.asString()]?.let {
builtIns.getAnnotationRetentionEnumEntry(it)?.let(::EnumValue)
}
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2016 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.load.java.components
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.resolve.constants.ConstantValue
interface JavaPropertyInitializerEvaluator {
fun getInitializerConstant(field: JavaField, descriptor: PropertyDescriptor): ConstantValue<*>?
object DoNothing : JavaPropertyInitializerEvaluator {
override fun getInitializerConstant(field: JavaField, descriptor: PropertyDescriptor) = null
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2015 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.load.java.components;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.kotlin.load.java.structure.JavaClass;
import org.jetbrains.kotlin.load.java.structure.JavaElement;
import org.jetbrains.kotlin.load.java.structure.JavaField;
import org.jetbrains.kotlin.load.java.structure.JavaMethod;
import org.jetbrains.kotlin.name.FqName;
public interface JavaResolverCache {
JavaResolverCache EMPTY = new JavaResolverCache() {
@Nullable
@Override
public ClassDescriptor getClassResolvedFromSource(@NotNull FqName fqName) {
return null;
}
@Override
public void recordMethod(@NotNull JavaMethod method, @NotNull SimpleFunctionDescriptor descriptor) {
}
@Override
public void recordConstructor(@NotNull JavaElement element, @NotNull ConstructorDescriptor descriptor) {
}
@Override
public void recordField(@NotNull JavaField field, @NotNull PropertyDescriptor descriptor) {
}
@Override
public void recordClass(@NotNull JavaClass javaClass, @NotNull ClassDescriptor descriptor) {
}
};
@Nullable
ClassDescriptor getClassResolvedFromSource(@NotNull FqName fqName);
void recordMethod(@NotNull JavaMethod method, @NotNull SimpleFunctionDescriptor descriptor);
void recordConstructor(@NotNull JavaElement element, @NotNull ConstructorDescriptor descriptor);
void recordField(@NotNull JavaField field, @NotNull PropertyDescriptor descriptor);
void recordClass(@NotNull JavaClass javaClass, @NotNull ClassDescriptor descriptor);
}
@@ -0,0 +1,28 @@
/*
* 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.load.java.components
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.types.SimpleType
interface SamConversionResolver {
object Empty : SamConversionResolver {
override fun resolveFunctionTypeIfSamInterface(classDescriptor: JavaClassDescriptor): SimpleType? = null
}
fun resolveFunctionTypeIfSamInterface(classDescriptor: JavaClassDescriptor): SimpleType?
}
@@ -0,0 +1,119 @@
/*
* Copyright 2010-2015 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.load.java.components;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.load.java.structure.JavaMethod;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.Collections;
import java.util.List;
public interface SignaturePropagator {
SignaturePropagator DO_NOTHING = new SignaturePropagator() {
@NotNull
@Override
public PropagatedSignature resolvePropagatedSignature(
@NotNull JavaMethod method,
@NotNull ClassDescriptor owner,
@NotNull KotlinType returnType,
@Nullable KotlinType receiverType,
@NotNull List<ValueParameterDescriptor> valueParameters,
@NotNull List<TypeParameterDescriptor> typeParameters
) {
return new PropagatedSignature(
returnType, receiverType, valueParameters, typeParameters, Collections.<String>emptyList(), false
);
}
@Override
public void reportSignatureErrors(@NotNull CallableMemberDescriptor descriptor, @NotNull List<String> signatureErrors) {
throw new UnsupportedOperationException("Should not be called");
}
};
class PropagatedSignature {
private final KotlinType returnType;
private final KotlinType receiverType;
private final List<ValueParameterDescriptor> valueParameters;
private final List<TypeParameterDescriptor> typeParameters;
private final List<String> signatureErrors;
private final boolean hasStableParameterNames;
public PropagatedSignature(
@NotNull KotlinType returnType,
@Nullable KotlinType receiverType,
@NotNull List<ValueParameterDescriptor> valueParameters,
@NotNull List<TypeParameterDescriptor> typeParameters,
@NotNull List<String> signatureErrors,
boolean hasStableParameterNames
) {
this.returnType = returnType;
this.receiverType = receiverType;
this.valueParameters = valueParameters;
this.typeParameters = typeParameters;
this.signatureErrors = signatureErrors;
this.hasStableParameterNames = hasStableParameterNames;
}
@NotNull
public KotlinType getReturnType() {
return returnType;
}
@Nullable
public KotlinType getReceiverType() {
return receiverType;
}
@NotNull
public List<ValueParameterDescriptor> getValueParameters() {
return valueParameters;
}
@NotNull
public List<TypeParameterDescriptor> getTypeParameters() {
return typeParameters;
}
public boolean hasStableParameterNames() {
return hasStableParameterNames;
}
@NotNull
public List<String> getErrors() {
return signatureErrors;
}
}
@NotNull
PropagatedSignature resolvePropagatedSignature(
@NotNull JavaMethod method,
@NotNull ClassDescriptor owner,
@NotNull KotlinType returnType,
@Nullable KotlinType receiverType,
@NotNull List<ValueParameterDescriptor> valueParameters,
@NotNull List<TypeParameterDescriptor> typeParameters
);
void reportSignatureErrors(@NotNull CallableMemberDescriptor descriptor, @NotNull List<String> signatureErrors);
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2015 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.load.java.components;
/**
* We convert Java types differently, depending on where they occur in the Java code
* This enum encodes the kinds of occurrences
*/
public enum TypeUsage {
SUPERTYPE,
COMMON
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2015 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.load.java.descriptors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.List;
public interface JavaCallableMemberDescriptor extends CallableMemberDescriptor {
@NotNull
JavaCallableMemberDescriptor enhance(
@Nullable KotlinType enhancedReceiverType,
@NotNull List<ValueParameterData> enhancedValueParametersData,
@NotNull KotlinType enhancedReturnType
);
}
@@ -0,0 +1,141 @@
/*
* 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.load.java.descriptors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.SourceElement;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.List;
public class JavaClassConstructorDescriptor extends ClassConstructorDescriptorImpl implements JavaCallableMemberDescriptor {
private Boolean hasStableParameterNames = null;
private Boolean hasSynthesizedParameterNames = null;
protected JavaClassConstructorDescriptor(
@NotNull ClassDescriptor containingDeclaration,
@Nullable JavaClassConstructorDescriptor original,
@NotNull Annotations annotations,
boolean isPrimary,
@NotNull Kind kind,
@NotNull SourceElement source
) {
super(containingDeclaration, original, annotations, isPrimary, kind, source);
}
@NotNull
public static JavaClassConstructorDescriptor createJavaConstructor(
@NotNull ClassDescriptor containingDeclaration,
@NotNull Annotations annotations,
boolean isPrimary,
@NotNull SourceElement source
) {
return new JavaClassConstructorDescriptor(containingDeclaration, null, annotations, isPrimary, Kind.DECLARATION, source);
}
@Override
public boolean hasStableParameterNames() {
assert hasStableParameterNames != null : "hasStableParameterNames was not set: " + this;
return hasStableParameterNames;
}
public void setHasStableParameterNames(boolean hasStableParameterNames) {
this.hasStableParameterNames = hasStableParameterNames;
}
@Override
public boolean hasSynthesizedParameterNames() {
assert hasSynthesizedParameterNames != null : "hasSynthesizedParameterNames was not set: " + this;
return hasSynthesizedParameterNames;
}
public void setHasSynthesizedParameterNames(boolean hasSynthesizedParameterNames) {
this.hasSynthesizedParameterNames = hasSynthesizedParameterNames;
}
@NotNull
@Override
protected JavaClassConstructorDescriptor createSubstitutedCopy(
@NotNull DeclarationDescriptor newOwner,
@Nullable FunctionDescriptor original,
@NotNull Kind kind,
@Nullable Name newName,
@NotNull Annotations annotations,
@NotNull SourceElement source
) {
if (kind != Kind.DECLARATION && kind != Kind.SYNTHESIZED) {
throw new IllegalStateException(
"Attempt at creating a constructor that is not a declaration: \n" +
"copy from: " + this + "\n" +
"newOwner: " + newOwner + "\n" +
"kind: " + kind
);
}
assert newName == null : "Attempt to rename constructor: " + this;
JavaClassConstructorDescriptor result =
createDescriptor((ClassDescriptor) newOwner, (JavaClassConstructorDescriptor) original, kind, source, annotations);
result.setHasStableParameterNames(hasStableParameterNames());
result.setHasSynthesizedParameterNames(hasSynthesizedParameterNames());
return result;
}
@NotNull
protected JavaClassConstructorDescriptor createDescriptor(
@NotNull ClassDescriptor newOwner,
@Nullable JavaClassConstructorDescriptor original,
@NotNull Kind kind,
@NotNull SourceElement sourceElement,
@NotNull Annotations annotations
) {
return new JavaClassConstructorDescriptor(
newOwner, original, annotations, isPrimary, kind,
sourceElement
);
}
@Override
@NotNull
public JavaClassConstructorDescriptor enhance(
@Nullable KotlinType enhancedReceiverType,
@NotNull List<ValueParameterData> enhancedValueParametersData,
@NotNull KotlinType enhancedReturnType
) {
JavaClassConstructorDescriptor enhanced = createSubstitutedCopy(
getContainingDeclaration(), /* original = */ null, getKind(), null, getAnnotations(), getSource());
// We do not use doSubstitute here as in JavaMethodDescriptor.enhance because type parameters of constructor belongs to class
enhanced.initialize(
enhancedReceiverType,
getDispatchReceiverParameter(),
getTypeParameters(),
UtilKt.copyValueParameters(enhancedValueParametersData, getValueParameters(), enhanced),
enhancedReturnType,
getModality(),
getVisibility()
);
return enhanced;
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2015 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.load.java.descriptors;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.types.SimpleType;
public interface JavaClassDescriptor extends ClassDescriptor {
// Use SingleAbstractMethodUtils.getFunctionTypeForSamInterface() where possible. This is only a fallback
@Nullable
SimpleType getDefaultFunctionTypeForSamInterface();
/**
* May return false even in case when the class is not SAM interface, but returns true only if it's definitely not a SAM.
* But it should work much faster than the exact check.
*/
boolean isDefinitelyNotSamInterface();
}
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2015 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.load.java.descriptors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.util.OperatorChecks;
import java.util.List;
import java.util.Map;
public class JavaMethodDescriptor extends SimpleFunctionDescriptorImpl implements JavaCallableMemberDescriptor {
// TODO: It's only used to retrieve annotations from the original value parameter and can be removed when
// org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl.initialize accepts extension parameter descriptor
// instead of type
public static final UserDataKey<ValueParameterDescriptor> ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER =
new UserDataKey<ValueParameterDescriptor>() {};
private enum ParameterNamesStatus {
NON_STABLE_DECLARED(false, false),
STABLE_DECLARED(true, false),
NON_STABLE_SYNTHESIZED(false, true),
STABLE_SYNTHESIZED(true, true), // TODO: this makes no sense
;
public final boolean isStable;
public final boolean isSynthesized;
ParameterNamesStatus(boolean isStable, boolean isSynthesized) {
this.isStable = isStable;
this.isSynthesized = isSynthesized;
}
@NotNull
public static ParameterNamesStatus get(boolean stable, boolean synthesized) {
return stable ? (synthesized ? STABLE_SYNTHESIZED : STABLE_DECLARED) :
(synthesized ? NON_STABLE_SYNTHESIZED : NON_STABLE_DECLARED);
}
}
private ParameterNamesStatus parameterNamesStatus = null;
protected JavaMethodDescriptor(
@NotNull DeclarationDescriptor containingDeclaration,
@Nullable SimpleFunctionDescriptor original,
@NotNull Annotations annotations,
@NotNull Name name,
@NotNull Kind kind,
@NotNull SourceElement source
) {
super(containingDeclaration, original, annotations, name, kind, source);
}
@NotNull
public static JavaMethodDescriptor createJavaMethod(
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Annotations annotations,
@NotNull Name name,
@NotNull SourceElement source
) {
return new JavaMethodDescriptor(containingDeclaration, null, annotations, name, Kind.DECLARATION, source);
}
@NotNull
@Override
public SimpleFunctionDescriptorImpl initialize(
@Nullable KotlinType receiverParameterType,
@Nullable ReceiverParameterDescriptor dispatchReceiverParameter,
@NotNull List<? extends TypeParameterDescriptor> typeParameters,
@NotNull List<ValueParameterDescriptor> unsubstitutedValueParameters,
@Nullable KotlinType unsubstitutedReturnType,
@Nullable Modality modality,
@NotNull Visibility visibility,
@Nullable Map<? extends UserDataKey<?>, ?> userData
) {
SimpleFunctionDescriptorImpl descriptor = super.initialize(
receiverParameterType, dispatchReceiverParameter, typeParameters, unsubstitutedValueParameters,
unsubstitutedReturnType, modality, visibility, userData);
setOperator(OperatorChecks.INSTANCE.check(descriptor).isSuccess());
return descriptor;
}
@Override
public boolean hasStableParameterNames() {
assert parameterNamesStatus != null : "Parameter names status was not set: " + this;
return parameterNamesStatus.isStable;
}
@Override
public boolean hasSynthesizedParameterNames() {
assert parameterNamesStatus != null : "Parameter names status was not set: " + this;
return parameterNamesStatus.isSynthesized;
}
public void setParameterNamesStatus(boolean hasStableParameterNames, boolean hasSynthesizedParameterNames) {
this.parameterNamesStatus = ParameterNamesStatus.get(hasStableParameterNames, hasSynthesizedParameterNames);
}
@NotNull
@Override
protected JavaMethodDescriptor createSubstitutedCopy(
@NotNull DeclarationDescriptor newOwner,
@Nullable FunctionDescriptor original,
@NotNull Kind kind,
@Nullable Name newName,
@NotNull Annotations annotations,
@NotNull SourceElement source
) {
JavaMethodDescriptor result = new JavaMethodDescriptor(
newOwner,
(SimpleFunctionDescriptor) original,
annotations,
newName != null ? newName : getName(),
kind,
source
);
result.setParameterNamesStatus(hasStableParameterNames(), hasSynthesizedParameterNames());
return result;
}
@Override
@NotNull
public JavaMethodDescriptor enhance(
@Nullable KotlinType enhancedReceiverType,
@NotNull List<ValueParameterData> enhancedValueParametersData,
@NotNull KotlinType enhancedReturnType
) {
List<ValueParameterDescriptor> enhancedValueParameters =
UtilKt.copyValueParameters(enhancedValueParametersData, getValueParameters(), this);
JavaMethodDescriptor enhancedMethod =
(JavaMethodDescriptor) newCopyBuilder()
.setValueParameters(enhancedValueParameters)
.setReturnType(enhancedReturnType)
.setExtensionReceiverType(enhancedReceiverType)
.setDropOriginalInContainingParts()
.setPreserveSourceElement()
.build();
assert enhancedMethod != null : "null after substitution while enhancing " + toString();
return enhancedMethod;
}
}
@@ -0,0 +1,156 @@
/*
* Copyright 2010-2015 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.load.java.descriptors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl;
import org.jetbrains.kotlin.load.java.typeEnhancement.TypeEnhancementKt;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.List;
public class JavaPropertyDescriptor extends PropertyDescriptorImpl implements JavaCallableMemberDescriptor {
private final boolean isStaticFinal;
private JavaPropertyDescriptor(
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Annotations annotations,
@NotNull Modality modality,
@NotNull Visibility visibility,
boolean isVar,
@NotNull Name name,
@NotNull SourceElement source,
@Nullable PropertyDescriptor original,
@NotNull Kind kind,
boolean isStaticFinal
) {
super(containingDeclaration, original, annotations, modality, visibility, isVar, name, kind, source,
false, false, false, false, false, false);
this.isStaticFinal = isStaticFinal;
}
@NotNull
public static JavaPropertyDescriptor create(
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Annotations annotations,
@NotNull Modality modality,
@NotNull Visibility visibility,
boolean isVar,
@NotNull Name name,
@NotNull SourceElement source,
boolean isStaticFinal
) {
return new JavaPropertyDescriptor(
containingDeclaration, annotations, modality, visibility, isVar, name, source, null, Kind.DECLARATION, isStaticFinal
);
}
@NotNull
@Override
protected PropertyDescriptorImpl createSubstitutedCopy(
@NotNull DeclarationDescriptor newOwner,
@NotNull Modality newModality,
@NotNull Visibility newVisibility,
@Nullable PropertyDescriptor original,
@NotNull Kind kind,
@NotNull Name newName
) {
return new JavaPropertyDescriptor(
newOwner, getAnnotations(), newModality, newVisibility, isVar(), newName, SourceElement.NO_SOURCE, original,
kind, isStaticFinal
);
}
@Override
public boolean hasSynthesizedParameterNames() {
return false;
}
@NotNull
@Override
public JavaCallableMemberDescriptor enhance(
@Nullable KotlinType enhancedReceiverType,
@NotNull List<ValueParameterData> enhancedValueParametersData,
@NotNull KotlinType enhancedReturnType
) {
JavaPropertyDescriptor enhanced = new JavaPropertyDescriptor(
getContainingDeclaration(),
getAnnotations(),
getModality(),
getVisibility(),
isVar(),
getName(),
getSource(),
getOriginal(),
getKind(),
isStaticFinal
);
PropertyGetterDescriptorImpl newGetter = null;
PropertyGetterDescriptorImpl getter = getGetter();
if (getter != null) {
newGetter = new PropertyGetterDescriptorImpl(
enhanced, getter.getAnnotations(), getter.getModality(), getter.getVisibility(),
getter.isDefault(), getter.isExternal(), getter.isInline(), getKind(), getter, getter.getSource()
);
newGetter.setInitialSignatureDescriptor(getter.getInitialSignatureDescriptor());
newGetter.initialize(enhancedReturnType);
}
PropertySetterDescriptorImpl newSetter = null;
PropertySetterDescriptor setter = getSetter();
if (setter != null) {
newSetter = new PropertySetterDescriptorImpl(
enhanced, setter.getAnnotations(), setter.getModality(), setter.getVisibility(),
setter.isDefault(), setter.isExternal(), setter.isInline(), getKind(), setter, setter.getSource()
);
newSetter.setInitialSignatureDescriptor(newSetter.getInitialSignatureDescriptor());
newSetter.initialize(setter.getValueParameters().get(0));
}
enhanced.initialize(newGetter, newSetter);
enhanced.setSetterProjectedOut(isSetterProjectedOut());
if (compileTimeInitializer != null) {
enhanced.setCompileTimeInitializer(compileTimeInitializer);
}
enhanced.setOverriddenDescriptors(getOverriddenDescriptors());
enhanced.setType(
enhancedReturnType,
getTypeParameters(), // TODO
getDispatchReceiverParameter(),
enhancedReceiverType
);
return enhanced;
}
@Override
public boolean isConst() {
KotlinType type = getType();
return isStaticFinal && ConstUtil.canBeUsedForConstVal(type) &&
(!TypeEnhancementKt.hasEnhancedNullability(type) || KotlinBuiltIns.isString(type));
}
}
@@ -0,0 +1,103 @@
/*
* Copyright 2010-2015 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.load.java.descriptors
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaStaticClassScope
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgumentValue
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class ValueParameterData(val type: KotlinType, val hasDefaultValue: Boolean)
fun copyValueParameters(
newValueParametersTypes: Collection<ValueParameterData>,
oldValueParameters: Collection<ValueParameterDescriptor>,
newOwner: CallableDescriptor
): List<ValueParameterDescriptor> {
assert(newValueParametersTypes.size == oldValueParameters.size) {
"Different value parameters sizes: Enhanced = ${newValueParametersTypes.size}, Old = ${oldValueParameters.size}"
}
return newValueParametersTypes.zip(oldValueParameters).map { (newParameter, oldParameter) ->
ValueParameterDescriptorImpl(
newOwner,
null,
oldParameter.index,
oldParameter.annotations,
oldParameter.name,
newParameter.type,
newParameter.hasDefaultValue,
oldParameter.isCrossinline,
oldParameter.isNoinline,
if (oldParameter.varargElementType != null) newOwner.module.builtIns.getArrayElementType(newParameter.type) else null,
oldParameter.source
)
}
}
fun ClassDescriptor.getParentJavaStaticClassScope(): LazyJavaStaticClassScope? {
val superClassDescriptor = getSuperClassNotAny() ?: return null
val staticScope = superClassDescriptor.staticScope
if (staticScope !is LazyJavaStaticClassScope) return superClassDescriptor.getParentJavaStaticClassScope()
return staticScope
}
fun DeserializedMemberDescriptor.getImplClassNameForDeserialized(): JvmClassName? =
(containerSource as? JvmPackagePartSource)?.className
fun DeserializedMemberDescriptor.isFromJvmPackagePart(): Boolean =
containerSource is JvmPackagePartSource
fun ValueParameterDescriptor.getParameterNameAnnotation(): AnnotationDescriptor? {
val annotation = annotations.findAnnotation(JvmAnnotationNames.PARAMETER_NAME_FQ_NAME) ?: return null
if (annotation.firstArgumentValue()?.safeAs<String>()?.isEmpty() != false) {
return null
}
return annotation
}
sealed class AnnotationDefaultValue
class StringDefaultValue(val value: String) : AnnotationDefaultValue()
object NullDefaultValue : AnnotationDefaultValue()
fun ValueParameterDescriptor.getDefaultValueFromAnnotation(): AnnotationDefaultValue? {
annotations.findAnnotation(JvmAnnotationNames.DEFAULT_VALUE_FQ_NAME)
?.firstArgumentValue()
?.safeAs<String>()
?.let { return StringDefaultValue(it) }
if (annotations.hasAnnotation(JvmAnnotationNames.DEFAULT_NULL_FQ_NAME)) {
return NullDefaultValue
}
return null
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2015 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.load.java.lazy
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
import org.jetbrains.kotlin.load.java.structure.JavaAnnotationOwner
import org.jetbrains.kotlin.name.FqName
class LazyJavaAnnotations(
private val c: LazyJavaResolverContext,
private val annotationOwner: JavaAnnotationOwner
) : Annotations {
private val annotationDescriptors = c.components.storageManager.createMemoizedFunctionWithNullableValues {
annotation: JavaAnnotation -> JavaAnnotationMapper.mapOrResolveJavaAnnotation(annotation, c)
}
override fun findAnnotation(fqName: FqName) =
annotationOwner.findAnnotation(fqName)?.let(annotationDescriptors)
?: JavaAnnotationMapper.findMappedJavaAnnotation(fqName, annotationOwner, c)
override fun findExternalAnnotation(fqName: FqName) =
c.components.externalAnnotationResolver.findExternalAnnotation(annotationOwner, fqName)?.let(annotationDescriptors)
override fun getUseSiteTargetedAnnotations() = emptyList<AnnotationWithTarget>()
override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) }
override fun iterator() =
(annotationOwner.annotations.asSequence().map(annotationDescriptors)
+ JavaAnnotationMapper.findMappedJavaAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated, annotationOwner, c)).filterNotNull().iterator()
override fun isEmpty() = !iterator().hasNext()
}
fun LazyJavaResolverContext.resolveAnnotations(annotationsOwner: JavaAnnotationOwner): Annotations
= LazyJavaAnnotations(this, annotationsOwner)
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2015 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.load.java.lazy
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.MemoizedFunctionToNullable
class LazyJavaPackageFragmentProvider(
components: JavaResolverComponents
) : PackageFragmentProvider {
private val c = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY, lazyOf(null))
private val packageFragments: MemoizedFunctionToNullable<FqName, LazyJavaPackageFragment> =
c.storageManager.createMemoizedFunctionWithNullableValues {
fqName ->
val jPackage = c.components.finder.findPackage(fqName)
if (jPackage != null) {
LazyJavaPackageFragment(c, jPackage)
}
else null
}
private fun getPackageFragment(fqName: FqName) = packageFragments(fqName)
override fun getPackageFragments(fqName: FqName) = listOfNotNull(getPackageFragment(fqName))
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) =
getPackageFragment(fqName)?.getSubPackageFqNames().orEmpty()
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2015 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.load.java.lazy
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import javax.inject.Inject
interface ModuleClassResolver {
fun resolveClass(javaClass: JavaClass): ClassDescriptor?
}
class SingleModuleClassResolver() : ModuleClassResolver {
override fun resolveClass(javaClass: JavaClass): ClassDescriptor? = resolver.resolveClass(javaClass)
// component dependency cycle
lateinit var resolver: JavaDescriptorResolver
@Inject set
}
class ModuleClassResolverImpl(private val descriptorResolverByJavaClass: (JavaClass) -> JavaDescriptorResolver): ModuleClassResolver {
override fun resolveClass(javaClass: JavaClass): ClassDescriptor? = descriptorResolverByJavaClass(javaClass).resolveClass(javaClass)
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2015 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.load.java.lazy
//interface PackageMappingProvider {
//
// fun findPackageMembers(packageName: String): List<String>
//
// companion object {
// val EMPTY = object : PackageMappingProvider {
// override fun findPackageMembers(packageName: String): List<String> {
// return emptyList()
// }
// }
// }
//}
@@ -0,0 +1,211 @@
/*
* Copyright 2010-2016 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.load.java.lazy
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.java.components.*
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
import org.jetbrains.kotlin.load.java.typeEnhancement.JavaTypeQualifiers
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifierWithMigrationStatus
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.ReportLevel
import java.util.*
class JavaResolverComponents(
val storageManager: StorageManager,
val finder: JavaClassFinder,
val kotlinClassFinder: KotlinClassFinder,
val deserializedDescriptorResolver: DeserializedDescriptorResolver,
val externalAnnotationResolver: ExternalAnnotationResolver,
val signaturePropagator: SignaturePropagator,
val errorReporter: ErrorReporter,
val javaResolverCache: JavaResolverCache,
val javaPropertyInitializerEvaluator: JavaPropertyInitializerEvaluator,
val samConversionResolver: SamConversionResolver,
val sourceElementFactory: JavaSourceElementFactory,
val moduleClassResolver: ModuleClassResolver,
val packageMapper: PackagePartProvider,
val supertypeLoopChecker: SupertypeLoopChecker,
val lookupTracker: LookupTracker,
val module: ModuleDescriptor,
val reflectionTypes: ReflectionTypes,
val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver,
val signatureEnhancement: SignatureEnhancement
) {
fun replace(
javaResolverCache: JavaResolverCache = this.javaResolverCache
) = JavaResolverComponents(
storageManager, finder, kotlinClassFinder, deserializedDescriptorResolver,
externalAnnotationResolver, signaturePropagator, errorReporter, javaResolverCache,
javaPropertyInitializerEvaluator, samConversionResolver, sourceElementFactory,
moduleClassResolver, packageMapper, supertypeLoopChecker, lookupTracker, module, reflectionTypes,
annotationTypeQualifierResolver, signatureEnhancement
)
}
private typealias QualifierByApplicabilityType = EnumMap<AnnotationTypeQualifierResolver.QualifierApplicabilityType, NullabilityQualifierWithMigrationStatus?>
class JavaTypeQualifiersByElementType(
internal val nullabilityQualifiers: QualifierByApplicabilityType
) {
operator fun get(
applicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType?
): JavaTypeQualifiers? {
val nullabilityQualifierWithMigrationStatus = nullabilityQualifiers[applicabilityType] ?: return null
return JavaTypeQualifiers(
nullabilityQualifierWithMigrationStatus.qualifier, null,
isNotNullTypeParameter = false,
isNullabilityQualifierForWarning = nullabilityQualifierWithMigrationStatus.isForWarningOnly
)
}
}
class LazyJavaResolverContext internal constructor(
val components: JavaResolverComponents,
val typeParameterResolver: TypeParameterResolver,
internal val delegateForDefaultTypeQualifiers: Lazy<JavaTypeQualifiersByElementType?>
) {
constructor(
components: JavaResolverComponents,
typeParameterResolver: TypeParameterResolver,
typeQualifiersComputation: () -> JavaTypeQualifiersByElementType?
) : this(components, typeParameterResolver, lazy(LazyThreadSafetyMode.NONE, typeQualifiersComputation))
val defaultTypeQualifiers: JavaTypeQualifiersByElementType? by delegateForDefaultTypeQualifiers
val typeResolver = JavaTypeResolver(this, typeParameterResolver)
val storageManager: StorageManager
get() = components.storageManager
val module: ModuleDescriptor get() = components.module
}
fun LazyJavaResolverContext.child(
typeParameterResolver: TypeParameterResolver
) = LazyJavaResolverContext(components, typeParameterResolver, delegateForDefaultTypeQualifiers)
fun LazyJavaResolverContext.computeNewDefaultTypeQualifiers(
additionalAnnotations: Annotations
): JavaTypeQualifiersByElementType? {
if (components.annotationTypeQualifierResolver.disabled) return defaultTypeQualifiers
val nullabilityQualifiersWithApplicability =
additionalAnnotations.mapNotNull(this::extractDefaultNullabilityQualifier)
if (nullabilityQualifiersWithApplicability.isEmpty()) return defaultTypeQualifiers
val nullabilityQualifiersByType =
defaultTypeQualifiers?.nullabilityQualifiers?.let(::QualifierByApplicabilityType)
?: QualifierByApplicabilityType(AnnotationTypeQualifierResolver.QualifierApplicabilityType::class.java)
var wasUpdate = false
for ((nullability, applicableTo) in nullabilityQualifiersWithApplicability) {
for (applicabilityType in applicableTo) {
nullabilityQualifiersByType[applicabilityType] = nullability
wasUpdate = true
}
}
return if (!wasUpdate) defaultTypeQualifiers else JavaTypeQualifiersByElementType(nullabilityQualifiersByType)
}
private fun LazyJavaResolverContext.extractDefaultNullabilityQualifier(
annotationDescriptor: AnnotationDescriptor
): NullabilityQualifierWithApplicability? {
val typeQualifierResolver = components.annotationTypeQualifierResolver
typeQualifierResolver.resolveQualifierBuiltInDefaultAnnotation(annotationDescriptor)?.let { return it }
val (typeQualifier, applicability) =
typeQualifierResolver.resolveTypeQualifierDefaultAnnotation(annotationDescriptor)
?: return null
val jsr305State = typeQualifierResolver.resolveJsr305CustomState(annotationDescriptor)
?: typeQualifierResolver.resolveJsr305AnnotationState(typeQualifier)
if (jsr305State.isIgnore) {
return null
}
val nullabilityQualifier =
components
.signatureEnhancement
.extractNullability(typeQualifier)
?.copy(isForWarningOnly = jsr305State.isWarning)
?: return null
return NullabilityQualifierWithApplicability(nullabilityQualifier, applicability)
}
data class NullabilityQualifierWithApplicability(
val nullabilityQualifier: NullabilityQualifierWithMigrationStatus,
val qualifierApplicabilityTypes: Collection<AnnotationTypeQualifierResolver.QualifierApplicabilityType>
)
fun LazyJavaResolverContext.replaceComponents(
components: JavaResolverComponents
) = LazyJavaResolverContext(components, typeParameterResolver, delegateForDefaultTypeQualifiers)
private fun LazyJavaResolverContext.child(
containingDeclaration: DeclarationDescriptor,
typeParameterOwner: JavaTypeParameterListOwner?,
typeParametersIndexOffset: Int = 0,
delegateForTypeQualifiers: Lazy<JavaTypeQualifiersByElementType?>
) = LazyJavaResolverContext(
components,
typeParameterOwner?.let { LazyJavaTypeParameterResolver(this, containingDeclaration, it, typeParametersIndexOffset) }
?: typeParameterResolver,
delegateForTypeQualifiers
)
fun LazyJavaResolverContext.childForMethod(
containingDeclaration: DeclarationDescriptor,
typeParameterOwner: JavaTypeParameterListOwner,
typeParametersIndexOffset: Int = 0
) = child(containingDeclaration, typeParameterOwner, typeParametersIndexOffset, delegateForDefaultTypeQualifiers)
fun LazyJavaResolverContext.childForClassOrPackage(
containingDeclaration: ClassOrPackageFragmentDescriptor,
typeParameterOwner: JavaTypeParameterListOwner? = null,
typeParametersIndexOffset: Int = 0
) = child(
containingDeclaration, typeParameterOwner, typeParametersIndexOffset,
lazy(LazyThreadSafetyMode.NONE) { computeNewDefaultTypeQualifiers(containingDeclaration.annotations) }
)
fun LazyJavaResolverContext.copyWithNewDefaultTypeQualifiers(
additionalAnnotations: Annotations
) = if (additionalAnnotations.isEmpty())
this
else
LazyJavaResolverContext(
components, typeParameterResolver,
lazy(LazyThreadSafetyMode.NONE) { computeNewDefaultTypeQualifiers(additionalAnnotations) }
)
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2015 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaMember
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.name.Name
interface DeclaredMemberIndex {
fun findMethodsByName(name: Name): Collection<JavaMethod>
fun getMethodNames(): Set<Name>
fun findFieldByName(name: Name): JavaField?
fun getFieldNames(): Set<Name>
object Empty : DeclaredMemberIndex {
override fun findMethodsByName(name: Name) = listOf<JavaMethod>()
override fun getMethodNames() = emptySet<Name>()
override fun findFieldByName(name: Name): JavaField? = null
override fun getFieldNames() = emptySet<Name>()
}
}
open class ClassDeclaredMemberIndex(
val jClass: JavaClass,
private val memberFilter: (JavaMember) -> Boolean
) : DeclaredMemberIndex {
private val methodFilter = {
m: JavaMethod ->
memberFilter(m) && !DescriptorResolverUtils.isObjectMethodInInterface(m)
}
private val methods = jClass.methods.asSequence().filter(methodFilter).groupBy { m -> m.name }
private val fields = jClass.fields.asSequence().filter(memberFilter).associateBy { m -> m.name }
override fun findMethodsByName(name: Name): Collection<JavaMethod> = methods[name] ?: listOf()
override fun getMethodNames(): Set<Name> = jClass.methods.asSequence().filter(methodFilter).mapTo(mutableSetOf(), JavaMethod::name)
override fun findFieldByName(name: Name): JavaField? = fields[name]
override fun getFieldNames(): Set<Name> = jClass.fields.asSequence().filter(memberFilter).mapTo(mutableSetOf(), JavaField::name)
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2015 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
// Currently getter is null iff it's loaded from Java field
val PropertyDescriptor.isJavaField: Boolean
get() = getter == null
@@ -0,0 +1,102 @@
/*
* Copyright 2010-2016 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.record
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.structure.JavaPackage
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.flatMapClassifierNamesOrNull
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.util.collectionUtils.getFirstClassifierDiscriminateHeaders
import org.jetbrains.kotlin.util.collectionUtils.getFromAllScopes
import org.jetbrains.kotlin.utils.Printer
class JvmPackageScope(
private val c: LazyJavaResolverContext,
jPackage: JavaPackage,
private val packageFragment: LazyJavaPackageFragment
): MemberScope {
internal val javaScope = LazyJavaPackageScope(c, jPackage, packageFragment)
private val kotlinScopes by c.storageManager.createLazyValue {
packageFragment.binaryClasses.values.mapNotNull { partClass ->
c.components.deserializedDescriptorResolver.createKotlinPackagePartScope(packageFragment, partClass)
}.toList()
}
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
recordLookup(name, location)
val javaClassifier = javaScope.getContributedClassifier(name, location)
if (javaClassifier != null) return javaClassifier
return getFirstClassifierDiscriminateHeaders(kotlinScopes) { it.getContributedClassifier(name, location) }
}
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> {
recordLookup(name, location)
return getFromAllScopes(javaScope, kotlinScopes) { it.getContributedVariables(name, location) }
}
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> {
recordLookup(name, location)
return getFromAllScopes(javaScope, kotlinScopes) { it.getContributedFunctions(name, location) }
}
override fun getContributedDescriptors(
kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> =
getFromAllScopes(javaScope, kotlinScopes) { it.getContributedDescriptors(kindFilter, nameFilter) }
override fun getFunctionNames() = kotlinScopes.flatMapTo(mutableSetOf()) { it.getFunctionNames() }.apply {
addAll(javaScope.getFunctionNames())
}
override fun getVariableNames() = kotlinScopes.flatMapTo(mutableSetOf()) { it.getVariableNames() }.apply {
addAll(javaScope.getVariableNames())
}
override fun getClassifierNames(): Set<Name>? = kotlinScopes.flatMapClassifierNamesOrNull()?.apply {
addAll(javaScope.getClassifierNames())
}
override fun printScopeStructure(p: Printer) {
p.println(this::class.java.simpleName, " {")
p.pushIndent()
p.println("containingDeclaration: $packageFragment")
javaScope.printScopeStructure(p)
for (kotlinScope in kotlinScopes) {
kotlinScope.printScopeStructure(p)
}
p.popIndent()
p.println("}")
}
override fun recordLookup(name: Name, location: LookupLocation) {
c.components.lookupTracker.record(location, packageFragment, name)
}
}
@@ -0,0 +1,143 @@
/*
* Copyright 2010-2016 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.ConstantValueFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.types.*
class LazyJavaAnnotationDescriptor(
private val c: LazyJavaResolverContext,
private val javaAnnotation: JavaAnnotation
) : AnnotationDescriptor {
override val fqName by c.storageManager.createNullableLazyValue {
javaAnnotation.classId?.asSingleFqName()
}
override val type by c.storageManager.createLazyValue {
val fqName = fqName ?: return@createLazyValue ErrorUtils.createErrorType("No fqName: $javaAnnotation")
val annotationClass = JavaToKotlinClassMap.mapJavaToKotlin(fqName, c.module.builtIns)
?: javaAnnotation.resolve()?.let { javaClass -> c.components.moduleClassResolver.resolveClass(javaClass) }
?: createTypeForMissingDependencies(fqName)
annotationClass.defaultType
}
override val source = c.components.sourceElementFactory.source(javaAnnotation)
private val factory = ConstantValueFactory(c.module.builtIns)
override val allValueArguments by c.storageManager.createLazyValue {
javaAnnotation.arguments.mapNotNull { arg ->
val name = arg.name ?: DEFAULT_ANNOTATION_MEMBER_NAME
resolveAnnotationArgument(arg)?.let { value -> name to value }
}.toMap()
}
private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): ConstantValue<*>? {
return when (argument) {
is JavaLiteralAnnotationArgument -> factory.createConstantValue(argument.value)
is JavaEnumValueAnnotationArgument -> resolveFromEnumValue(argument.resolve(), argument.entryName)
is JavaArrayAnnotationArgument -> resolveFromArray(argument.name ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements())
is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation())
is JavaClassObjectAnnotationArgument -> resolveFromJavaClassObjectType(argument.getReferencedType())
else -> null
}
}
private fun resolveFromAnnotation(javaAnnotation: JavaAnnotation): ConstantValue<*> {
return factory.createAnnotationValue(LazyJavaAnnotationDescriptor(c, javaAnnotation))
}
private fun resolveFromArray(argumentName: Name, elements: List<JavaAnnotationArgument>): ConstantValue<*>? {
if (type.isError) return null
val arrayType =
DescriptorResolverUtils.getAnnotationParameterByName(argumentName, annotationClass!!)?.type
// Try to load annotation arguments even if the annotation class is not found
?: c.components.module.builtIns.getArrayType(
Variance.INVARIANT,
ErrorUtils.createErrorType("Unknown array element type")
)
val values = elements.map {
argument -> resolveAnnotationArgument(argument) ?: factory.createNullValue()
}
return factory.createArrayValue(values, arrayType)
}
private fun resolveFromEnumValue(element: JavaField?, entryName: Name?): ConstantValue<*>? {
if (element == null || !element.isEnumEntry) {
if (entryName == null) return null
return factory.createEnumValue(ErrorUtils.createErrorClassWithExactName(entryName))
}
val containingJavaClass = element.containingClass
val enumClass = c.components.moduleClassResolver.resolveClass(containingJavaClass) ?: return null
val classifier = enumClass.unsubstitutedInnerClassesScope.getContributedClassifier(element.name, NoLookupLocation.FROM_JAVA_LOADER)
as? ClassDescriptor ?: return null
return factory.createEnumValue(classifier)
}
private fun resolveFromJavaClassObjectType(javaType: JavaType): ConstantValue<*>? {
// Class type is never nullable in 'Foo.class' in Java
val type = TypeUtils.makeNotNullable(c.typeResolver.transformJavaType(
javaType,
TypeUsage.COMMON.toAttributes())
)
val jlClass = c.module.resolveTopLevelClass(FqName("java.lang.Class"), NoLookupLocation.FOR_NON_TRACKED_SCOPE) ?: return null
val arguments = listOf(TypeProjectionImpl(type))
val javaClassObjectType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, jlClass, arguments)
return factory.createKClassValue(javaClassObjectType)
}
override fun toString(): String {
return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderAnnotation(this)
}
private fun createTypeForMissingDependencies(fqName: FqName) =
c.module.findNonGenericClassAcrossDependencies(
ClassId.topLevel(fqName),
c.components.deserializedDescriptorResolver.components.notFoundClasses
)
}
@@ -0,0 +1,271 @@
/*
* 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.FakePureImplementationsProvider
import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
import org.jetbrains.kotlin.load.java.lazy.replaceComponents
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
import org.jetbrains.kotlin.load.java.structure.JavaType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isValidJavaFqName
import org.jetbrains.kotlin.platform.createMappedTypeParametersSubstitution
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.resolve.scopes.InnerClassesScopeWrapper
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
class LazyJavaClassDescriptor(
outerContext: LazyJavaResolverContext,
containingDeclaration: DeclarationDescriptor,
private val jClass: JavaClass,
private val additionalSupertypeClassDescriptor: ClassDescriptor? = null
) : ClassDescriptorBase(outerContext.storageManager, containingDeclaration, jClass.name,
outerContext.components.sourceElementFactory.source(jClass),
/* isExternal = */ false), JavaClassDescriptor {
companion object {
@JvmStatic
private val PUBLIC_METHOD_NAMES_IN_OBJECT = setOf("equals", "hashCode", "getClass", "wait", "notify", "notifyAll", "toString")
}
private val c: LazyJavaResolverContext = outerContext.childForClassOrPackage(this, jClass)
init {
c.components.javaResolverCache.recordClass(jClass, this)
assert(jClass.lightClassOriginKind == null) {
"Creating LazyJavaClassDescriptor for light class $jClass"
}
}
private val kind = when {
jClass.isAnnotationType -> ClassKind.ANNOTATION_CLASS
jClass.isInterface -> ClassKind.INTERFACE
jClass.isEnum -> ClassKind.ENUM_CLASS
else -> ClassKind.CLASS
}
private val modality = if (jClass.isAnnotationType)
Modality.FINAL
else Modality.convertFromFlags(jClass.isAbstract || jClass.isInterface, !jClass.isFinal)
private val visibility = jClass.visibility
private val isInner = jClass.outerClass != null && !jClass.isStatic
override fun getKind() = kind
override fun getModality() = modality
// To workaround a problem with Scala compatibility (KT-9700),
// we consider private visibility of a Java top level class as package private
// Shortly: Scala plugin introduces special kind of "private in package" classes
// which can be inherited from the same package.
// Kotlin considers this "private in package" just as "private" and thinks they are invisible for inheritors,
// so their functions are invisible fake which is not true.
override fun getVisibility() =
if (visibility == Visibilities.PRIVATE && jClass.outerClass == null) JavaVisibilities.PACKAGE_VISIBILITY else visibility
override fun isInner() = isInner
override fun isData() = false
override fun isCompanionObject() = false
override fun isExpect() = false
override fun isActual() = false
private val typeConstructor = LazyJavaClassTypeConstructor()
override fun getTypeConstructor(): TypeConstructor = typeConstructor
private val unsubstitutedMemberScope = LazyJavaClassMemberScope(c, this, jClass)
override fun getUnsubstitutedMemberScope() = unsubstitutedMemberScope
private val innerClassesScope = InnerClassesScopeWrapper(getUnsubstitutedMemberScope())
override fun getUnsubstitutedInnerClassesScope(): MemberScope = innerClassesScope
private val staticScope = LazyJavaStaticClassScope(c, jClass, this)
override fun getStaticScope(): MemberScope = staticScope
override fun getUnsubstitutedPrimaryConstructor(): ClassConstructorDescriptor? = null
override fun getCompanionObjectDescriptor(): ClassDescriptor? = null
override fun getConstructors() = unsubstitutedMemberScope.constructors()
override val annotations = c.resolveAnnotations(jClass)
private val declaredParameters = c.storageManager.createLazyValue {
jClass.typeParameters.map {
p ->
c.typeParameterResolver.resolveTypeParameter(p)
?: throw AssertionError("Parameter $p surely belongs to class $jClass, so it must be resolved")
}
}
override fun getDeclaredTypeParameters() = declaredParameters()
override fun getDefaultFunctionTypeForSamInterface(): SimpleType? = c.components.samConversionResolver.resolveFunctionTypeIfSamInterface(this)
override fun isDefinitelyNotSamInterface(): Boolean {
if (kind != ClassKind.INTERFACE) return true
val candidates = jClass.methods.filter { it.isAbstract && it.typeParameters.isEmpty() }
// From the definition of function interfaces in the Java specification (pt. 9.8):
// "methods that are members of I that do not have the same signature as any public instance method of the class Object"
// It means that if an interface declares `int hashCode()` then the method won't be taken into account when
// checking if the interface is SAM.
// We make here a conservative check just filtering out methods by name.
// If we ignore a method with wrong signature (different from one in Object) it's not very bad,
// we'll just say that the interface MAY BE a SAM when it's not and then more detailed check will be applied.
if (candidates.count { it.name.identifier !in PUBLIC_METHOD_NAMES_IN_OBJECT } > 1) return true
// Check if any of the super-interfaces contain too many methods to be a SAM
return typeConstructor.supertypes.any {
it.constructor.declarationDescriptor.safeAs<LazyJavaClassDescriptor>()?.isDefinitelyNotSamInterface == true
}
}
override fun getSealedSubclasses(): Collection<ClassDescriptor> = emptyList()
override fun toString() = "Lazy Java class ${this.fqNameUnsafe}"
private inner class LazyJavaClassTypeConstructor : AbstractClassTypeConstructor(c.storageManager) {
private val parameters = c.storageManager.createLazyValue {
this@LazyJavaClassDescriptor.computeConstructorTypeParameters()
}
override fun getParameters(): List<TypeParameterDescriptor> = parameters()
override fun computeSupertypes(): Collection<KotlinType> {
val javaTypes = jClass.supertypes
val result = ArrayList<KotlinType>(javaTypes.size)
val incomplete = ArrayList<JavaType>(0)
val purelyImplementedSupertype: KotlinType? = getPurelyImplementedSupertype()
for (javaType in javaTypes) {
val kotlinType = c.typeResolver.transformJavaType(javaType, TypeUsage.SUPERTYPE.toAttributes())
if (kotlinType.constructor.declarationDescriptor is NotFoundClasses.MockClassDescriptor) {
incomplete.add(javaType)
}
if (kotlinType.constructor == purelyImplementedSupertype?.constructor) {
continue
}
if (!KotlinBuiltIns.isAnyOrNullableAny(kotlinType)) {
result.add(kotlinType)
}
}
// Add fake supertype kotlin.collection.Collection<E> to java.util.Collection<E> class if needed
// Only needed when calculating built-ins member scope
result.addIfNotNull(
additionalSupertypeClassDescriptor?.let {
createMappedTypeParametersSubstitution(it, this@LazyJavaClassDescriptor)
.buildSubstitutor().substitute(it.defaultType, Variance.INVARIANT)
})
result.addIfNotNull(purelyImplementedSupertype)
if (incomplete.isNotEmpty()) {
c.components.errorReporter.reportIncompleteHierarchy(declarationDescriptor, incomplete.map { javaType ->
(javaType as JavaClassifierType).presentableText
})
}
return if (result.isNotEmpty()) result.toList() else listOf(c.module.builtIns.anyType)
}
private fun getPurelyImplementedSupertype(): KotlinType? {
val annotatedPurelyImplementedFqName = getPurelyImplementsFqNameFromAnnotation()?.takeIf { fqName ->
!fqName.isRoot && fqName.startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)
}
val purelyImplementedFqName =
annotatedPurelyImplementedFqName
?: FakePureImplementationsProvider.getPurelyImplementedInterface(fqNameSafe)
?: return null
val classDescriptor = c.module.resolveTopLevelClass(purelyImplementedFqName, NoLookupLocation.FROM_JAVA_LOADER) ?: return null
val supertypeParameterCount = classDescriptor.typeConstructor.parameters.size
val typeParameters = getTypeConstructor().parameters
val typeParameterCount = typeParameters.size
val parametersAsTypeProjections = when {
typeParameterCount == supertypeParameterCount ->
typeParameters.map {
parameter ->
TypeProjectionImpl(Variance.INVARIANT, parameter.defaultType)
}
typeParameterCount == 1 && supertypeParameterCount > 1 && annotatedPurelyImplementedFqName == null ->
{
val parameter = TypeProjectionImpl(Variance.INVARIANT, typeParameters.single().defaultType)
(1..supertypeParameterCount).map { parameter } // TODO: List(supertypeParameterCount) { parameter }
}
else -> return null
}
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, classDescriptor, parametersAsTypeProjections)
}
private fun getPurelyImplementsFqNameFromAnnotation(): FqName? {
val annotation =
this@LazyJavaClassDescriptor.annotations.findAnnotation(JvmAnnotationNames.PURELY_IMPLEMENTS_ANNOTATION)
?: return null
val fqNameString = (annotation.allValueArguments.values.singleOrNull() as? StringValue)?.value ?: return null
if (!isValidJavaFqName(fqNameString)) return null
return FqName(fqNameString)
}
override val supertypeLoopChecker: SupertypeLoopChecker
get() = c.components.supertypeLoopChecker
override fun isDenotable(): Boolean = true
override fun getDeclarationDescriptor(): ClassDescriptor = this@LazyJavaClassDescriptor
override fun toString(): String = name.asString()
}
// Only needed when calculating built-ins member scope
internal fun copy(
javaResolverCache: JavaResolverCache, additionalSupertypeClassDescriptor: ClassDescriptor?
) = LazyJavaClassDescriptor(
c.replaceComponents(c.components.replace(javaResolverCache = javaResolverCache)),
containingDeclaration, jClass, additionalSupertypeClassDescriptor)
}
@@ -0,0 +1,680 @@
/*
* 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.incremental.record
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithDifferentJvmName.isRemoveAtByIndex
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithDifferentJvmName.sameAsRenamedInJvmBuiltin
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters
import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils.resolveOverridesForNonStaticMembers
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.descriptors.*
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.childForMethod
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaConstructor
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.storage.NotNullLazyValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.ifEmpty
import java.util.*
class LazyJavaClassMemberScope(
c: LazyJavaResolverContext,
override val ownerDescriptor: ClassDescriptor,
private val jClass: JavaClass
) : LazyJavaScope(c) {
override fun computeMemberIndex() = ClassDeclaredMemberIndex(jClass, { !it.isStatic })
override fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) =
ownerDescriptor.typeConstructor.supertypes.flatMapTo(HashSet()) {
it.memberScope.getFunctionNames()
}.apply {
addAll(declaredMemberIndex().getMethodNames())
addAll(computeClassNames(kindFilter, nameFilter))
}
internal val constructors = c.storageManager.createLazyValue {
val constructors = jClass.constructors
val result = ArrayList<JavaClassConstructorDescriptor>(constructors.size)
for (constructor in constructors) {
val descriptor = resolveConstructor(constructor)
result.add(descriptor)
}
c.components.signatureEnhancement.enhanceSignatures(
c,
result.ifEmpty { listOfNotNull(createDefaultConstructor()) }
).toList()
}
override fun JavaMethodDescriptor.isVisibleAsFunction(): Boolean {
if (jClass.isAnnotationType) return false
return isVisibleAsFunctionInCurrentClass(this)
}
private fun isVisibleAsFunctionInCurrentClass(function: SimpleFunctionDescriptor): Boolean {
if (getPropertyNamesCandidatesByAccessorName(function.name).any {
propertyName ->
getPropertiesFromSupertypes(propertyName).any {
property ->
doesClassOverridesProperty(property) {
accessorName ->
// This lambda should return property accessors available in this class by their name
// If 'accessorName' is current function we return only it just because we check exactly
// that current method is override of accessor
if (function.name == accessorName)
listOf(function)
else
searchMethodsByNameWithoutBuiltinMagic(accessorName) + searchMethodsInSupertypesWithoutBuiltinMagic(accessorName)
} && (property.isVar || !JvmAbi.isSetterName(function.name.asString()))
}
}) return false
return !function.doesOverrideRenamedBuiltins() && !function.shouldBeVisibleAsOverrideOfBuiltInWithErasedValueParameters()
}
/**
* Checks if function is a valid override of JDK analogue of built-in method with erased value parameters (e.g. Map.containsKey(k: K))
*
* Examples:
* - boolean containsKey(Object key) -> true
* - boolean containsKey(K key) -> false // Wrong JDK method override, while it's a valid Kotlin built-in override
*/
private fun SimpleFunctionDescriptor.shouldBeVisibleAsOverrideOfBuiltInWithErasedValueParameters(): Boolean {
if (!name.sameAsBuiltinMethodWithErasedValueParameters) return false
val candidatesToOverride =
getFunctionsFromSupertypes(name).mapNotNull {
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(it)
}
return candidatesToOverride.any {
candidate ->
hasSameJvmDescriptorButDoesNotOverride(candidate)
}
}
private fun searchMethodsByNameWithoutBuiltinMagic(name: Name): Collection<SimpleFunctionDescriptor> =
declaredMemberIndex().findMethodsByName(name).map { resolveMethodToFunctionDescriptor(it) }
private fun searchMethodsInSupertypesWithoutBuiltinMagic(name: Name): Collection<SimpleFunctionDescriptor> =
getFunctionsFromSupertypes(name).filterNot {
it.doesOverrideBuiltinWithDifferentJvmName()
|| BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(it) != null
}
private fun SimpleFunctionDescriptor.doesOverrideRenamedBuiltins(): Boolean {
return BuiltinMethodsWithDifferentJvmName.getBuiltinFunctionNamesByJvmName(name).any {
// e.g. 'removeAt' or 'toInt'
builtinName ->
val builtinSpecialFromSuperTypes =
getFunctionsFromSupertypes(builtinName).filter { it.doesOverrideBuiltinWithDifferentJvmName() }
if (builtinSpecialFromSuperTypes.isEmpty()) return@any false
val methodDescriptor = this.createRenamedCopy(builtinName)
builtinSpecialFromSuperTypes.any { doesOverrideRenamedDescriptor(it, methodDescriptor) }
}
}
private fun SimpleFunctionDescriptor.createRenamedCopy(builtinName: Name): SimpleFunctionDescriptor =
this.newCopyBuilder().apply {
setName(builtinName)
setSignatureChange()
setPreserveSourceElement()
}.build()!!
private fun doesOverrideRenamedDescriptor(
superDescriptor: SimpleFunctionDescriptor,
subDescriptor: FunctionDescriptor
): Boolean {
// if we check 'removeAt', get original sub-descriptor to distinct `remove(int)` and `remove(E)` in Java
val subDescriptorToCheck = if (superDescriptor.isRemoveAtByIndex) subDescriptor.original else subDescriptor
return subDescriptorToCheck.doesOverride(superDescriptor)
}
private fun CallableDescriptor.doesOverride(superDescriptor: CallableDescriptor): Boolean {
val commonOverridabilityResult =
OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions(superDescriptor, this, true).result
return commonOverridabilityResult == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE &&
!JavaIncompatibilityRulesOverridabilityCondition.doesJavaOverrideHaveIncompatibleValueParameterKinds(
superDescriptor, this)
}
private fun PropertyDescriptor.findGetterOverride(
functions: (Name) -> Collection<SimpleFunctionDescriptor>
): SimpleFunctionDescriptor? {
val overriddenBuiltinProperty = getter?.getOverriddenBuiltinWithDifferentJvmName()
val specialGetterName = overriddenBuiltinProperty?.getBuiltinSpecialPropertyGetterName()
if (specialGetterName != null
&& !this@LazyJavaClassMemberScope.ownerDescriptor.hasRealKotlinSuperClassWithOverrideOf(overriddenBuiltinProperty)
) {
return findGetterByName(specialGetterName, functions)
}
return findGetterByName(JvmAbi.getterName(name.asString()), functions)
}
private fun PropertyDescriptor.findGetterByName(
getterName: String,
functions: (Name) -> Collection<SimpleFunctionDescriptor>
): SimpleFunctionDescriptor? {
return functions(Name.identifier(getterName)).firstNotNullResult factory@{
descriptor ->
if (descriptor.valueParameters.size != 0) return@factory null
descriptor.takeIf { KotlinTypeChecker.DEFAULT.isSubtypeOf(descriptor.returnType ?: return@takeIf false, type) }
}
}
private fun PropertyDescriptor.findSetterOverride(
functions: (Name) -> Collection<SimpleFunctionDescriptor>
): SimpleFunctionDescriptor? {
return functions(Name.identifier(JvmAbi.setterName(name.asString()))).firstNotNullResult factory@{
descriptor ->
if (descriptor.valueParameters.size != 1) return@factory null
if (!KotlinBuiltIns.isUnit(descriptor.returnType ?: return@factory null)) return@factory null
descriptor.takeIf { KotlinTypeChecker.DEFAULT.equalTypes(descriptor.valueParameters.single().type, type) }
}
}
private fun doesClassOverridesProperty(
property: PropertyDescriptor,
functions: (Name) -> Collection<SimpleFunctionDescriptor>
): Boolean {
if (property.isJavaField) return false
val getter = property.findGetterOverride(functions)
val setter = property.findSetterOverride(functions)
if (getter == null) return false
if (!property.isVar) return true
return setter != null && setter.modality == getter.modality
}
override fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
val functionsFromSupertypes = getFunctionsFromSupertypes(name)
if (!name.sameAsRenamedInJvmBuiltin && !name.sameAsBuiltinMethodWithErasedValueParameters) {
// Simple fast path in case of name is not suspicious (i.e. name is not one of builtins that have different signature in Java)
addFunctionFromSupertypes(
result, name,
functionsFromSupertypes.filter { isVisibleAsFunctionInCurrentClass(it) },
isSpecialBuiltinName = false)
return
}
val specialBuiltinsFromSuperTypes = SmartSet.create<SimpleFunctionDescriptor>()
// Merge functions with same signatures
val mergedFunctionFromSuperTypes = resolveOverridesForNonStaticMembers(
name, functionsFromSupertypes, emptyList(), ownerDescriptor, ErrorReporter.DO_NOTHING)
// add declarations
addOverriddenBuiltinMethods(
name, result, mergedFunctionFromSuperTypes, result,
this::searchMethodsByNameWithoutBuiltinMagic)
// add from super types
addOverriddenBuiltinMethods(
name, result, mergedFunctionFromSuperTypes, specialBuiltinsFromSuperTypes,
this::searchMethodsInSupertypesWithoutBuiltinMagic)
val visibleFunctionsFromSupertypes =
functionsFromSupertypes.filter { isVisibleAsFunctionInCurrentClass(it) } + specialBuiltinsFromSuperTypes
addFunctionFromSupertypes(result, name, visibleFunctionsFromSupertypes, isSpecialBuiltinName = true)
}
private fun addFunctionFromSupertypes(
result: MutableCollection<SimpleFunctionDescriptor>,
name: Name,
functionsFromSupertypes: Collection<SimpleFunctionDescriptor>,
isSpecialBuiltinName: Boolean
) {
val additionalOverrides = resolveOverridesForNonStaticMembers(
name, functionsFromSupertypes, result, ownerDescriptor, c.components.errorReporter
)
if (!isSpecialBuiltinName) {
result.addAll(additionalOverrides)
}
else {
val allDescriptors = result + additionalOverrides
result.addAll(
additionalOverrides.map {
resolvedOverride ->
val overriddenBuiltin = resolvedOverride.getOverriddenSpecialBuiltin()
?: return@map resolvedOverride
resolvedOverride.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(overriddenBuiltin, allDescriptors)
})
}
}
private fun addOverriddenBuiltinMethods(
name: Name,
alreadyDeclaredFunctions: Collection<SimpleFunctionDescriptor>,
candidatesForOverride: Collection<SimpleFunctionDescriptor>,
result: MutableCollection<SimpleFunctionDescriptor>,
functions: (Name) -> Collection<SimpleFunctionDescriptor>
) {
for (descriptor in candidatesForOverride) {
val overriddenBuiltin = descriptor.getOverriddenBuiltinWithDifferentJvmName() ?: continue
val nameInJava = getJvmMethodNameIfSpecial(overriddenBuiltin)!!
for (method in functions(Name.identifier(nameInJava))) {
val renamedCopy = method.createRenamedCopy(name)
if (doesOverrideRenamedDescriptor(overriddenBuiltin, renamedCopy)) {
result.add(
renamedCopy.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(overriddenBuiltin, alreadyDeclaredFunctions))
break
}
}
}
for (descriptor in candidatesForOverride) {
val overriddenBuiltin =
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor)
?: continue
createOverrideForBuiltinFunctionWithErasedParameterIfNeeded(overriddenBuiltin, functions)?.let {
override ->
if (isVisibleAsFunctionInCurrentClass(override)) {
result.add(override.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(overriddenBuiltin, alreadyDeclaredFunctions))
}
}
}
}
// In case when Java has declaration with signature reflecting one of special builtin we load override of builtin as hidden function
// Unless we do it then signature clash happens.
// For example see java.nio.CharBuffer implementing CharSequence and defining irrelevant 'get' method having the same signature as in kotlin.CharSequence
// We load java.nio.CharBuffer as having both 'get' functions, but one that is override of kotlin.CharSequence is hidden,
// so when someone calls CharBuffer.get it results in invoking java method CharBuffer.get
// But we still have the way to call 'charAt' java method by upcasting CharBuffer to kotlin.CharSequence
private fun SimpleFunctionDescriptor.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(
specialBuiltin: CallableDescriptor,
alreadyDeclaredFunctions: Collection<SimpleFunctionDescriptor>
): SimpleFunctionDescriptor =
if (alreadyDeclaredFunctions.none { this != it && it.initialSignatureDescriptor == null && it.doesOverride(specialBuiltin) })
this
else
newCopyBuilder().setHiddenToOvercomeSignatureClash().build()!!
private fun createOverrideForBuiltinFunctionWithErasedParameterIfNeeded(
overridden: FunctionDescriptor,
functions: (Name) -> Collection<SimpleFunctionDescriptor>
): SimpleFunctionDescriptor? {
return functions(overridden.name).firstOrNull {
it.hasSameJvmDescriptorButDoesNotOverride(overridden)
}?.let {
override ->
override.newCopyBuilder().apply {
setValueParameters(copyValueParameters(
overridden.valueParameters.map { ValueParameterData(it.type, it.hasDefaultValue()) },
override.valueParameters, overridden))
setSignatureChange()
setPreserveSourceElement()
}.build()
}
}
private fun getFunctionsFromSupertypes(name: Name): Set<SimpleFunctionDescriptor> {
return ownerDescriptor.typeConstructor.supertypes.flatMapTo(LinkedHashSet()) {
it.memberScope.getContributedFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS)
}
}
override fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>) {
if (jClass.isAnnotationType) {
computeAnnotationProperties(name, result)
}
val propertiesFromSupertypes = getPropertiesFromSupertypes(name)
if (propertiesFromSupertypes.isEmpty()) return
val propertiesOverridesFromSuperTypes = SmartSet.create<PropertyDescriptor>()
addPropertyOverrideByMethod(propertiesFromSupertypes, result) { searchMethodsByNameWithoutBuiltinMagic(it) }
addPropertyOverrideByMethod(propertiesFromSupertypes, propertiesOverridesFromSuperTypes) {
searchMethodsInSupertypesWithoutBuiltinMagic(it)
}
result.addAll(resolveOverridesForNonStaticMembers(
name, propertiesFromSupertypes + propertiesOverridesFromSuperTypes, result, ownerDescriptor, c.components.errorReporter))
}
private fun addPropertyOverrideByMethod(
propertiesFromSupertypes: Set<PropertyDescriptor>,
result: MutableCollection<PropertyDescriptor>,
functions: (Name) -> Collection<SimpleFunctionDescriptor>
) {
for (property in propertiesFromSupertypes) {
val newProperty = createPropertyDescriptorByMethods(property, functions)
if (newProperty != null) {
result.add(newProperty)
break
}
}
}
private fun computeAnnotationProperties(name: Name, result: MutableCollection<PropertyDescriptor>) {
val method = declaredMemberIndex().findMethodsByName(name).singleOrNull() ?: return
result.add(createPropertyDescriptorWithDefaultGetter(method, modality = Modality.FINAL))
}
private fun createPropertyDescriptorWithDefaultGetter(
method: JavaMethod, givenType: KotlinType? = null, modality: Modality
): JavaPropertyDescriptor {
val annotations = c.resolveAnnotations(method)
val propertyDescriptor = JavaPropertyDescriptor.create(
ownerDescriptor, annotations, modality, method.visibility,
/* isVar = */ false, method.name, c.components.sourceElementFactory.source(method),
/* isStaticFinal = */ false
)
val getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.EMPTY)
propertyDescriptor.initialize(getter, null)
val returnType = givenType ?: computeMethodReturnType(method, c.childForMethod(propertyDescriptor, method))
propertyDescriptor.setType(returnType, listOf(), getDispatchReceiverParameter(), null as KotlinType?)
getter.initialize(returnType)
return propertyDescriptor
}
private fun createPropertyDescriptorByMethods(
overriddenProperty: PropertyDescriptor,
functions: (Name) -> Collection<SimpleFunctionDescriptor>
): JavaPropertyDescriptor? {
if (!doesClassOverridesProperty(overriddenProperty, functions)) return null
val getterMethod = overriddenProperty.findGetterOverride(functions)!!
val setterMethod =
if (overriddenProperty.isVar)
overriddenProperty.findSetterOverride(functions)!!
else
null
assert(setterMethod?.let { it.modality == getterMethod.modality } ?: true) {
"Different accessors modalities when creating overrides for $overriddenProperty in $ownerDescriptor" +
"for getter is ${getterMethod.modality}, but for setter is ${setterMethod?.modality}"
}
val propertyDescriptor = JavaPropertyDescriptor.create(
ownerDescriptor, Annotations.EMPTY, getterMethod.modality, getterMethod.visibility,
/* isVar = */ setterMethod != null, overriddenProperty.name, getterMethod.source,
/* isStaticFinal = */ false
)
propertyDescriptor.setType(getterMethod.returnType!!, listOf(), getDispatchReceiverParameter(), null as KotlinType?)
val getter = DescriptorFactory.createGetter(
propertyDescriptor, getterMethod.annotations, /* isDefault = */false,
/* isExternal = */ false, /* isInline = */ false, getterMethod.source
).apply {
initialSignatureDescriptor = getterMethod
initialize(propertyDescriptor.type)
}
val setter = setterMethod?.let { setterMethod ->
DescriptorFactory.createSetter(propertyDescriptor, setterMethod.annotations, /* isDefault = */false,
/* isExternal = */ false, /* isInline = */ false, setterMethod.visibility, setterMethod.source
).apply {
initialSignatureDescriptor = setterMethod
}
}
return propertyDescriptor.apply { initialize(getter, setter) }
}
private fun getPropertiesFromSupertypes(name: Name): Set<PropertyDescriptor> {
return ownerDescriptor.typeConstructor.supertypes.flatMap {
it.memberScope.getContributedVariables(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS).map { p -> p }
}.toSet()
}
override fun resolveMethodSignature(
method: JavaMethod, methodTypeParameters: List<TypeParameterDescriptor>, returnType: KotlinType,
valueParameters: List<ValueParameterDescriptor>
): LazyJavaScope.MethodSignatureData {
val propagated = c.components.signaturePropagator.resolvePropagatedSignature(
method, ownerDescriptor, returnType, null, valueParameters, methodTypeParameters
)
return LazyJavaScope.MethodSignatureData(
propagated.returnType, propagated.receiverType, propagated.valueParameters, propagated.typeParameters,
propagated.hasStableParameterNames(), propagated.errors
)
}
private fun SimpleFunctionDescriptor.hasSameJvmDescriptorButDoesNotOverride(
builtinWithErasedParameters: FunctionDescriptor
): Boolean {
return computeJvmDescriptor(withReturnType = false) ==
builtinWithErasedParameters.original.computeJvmDescriptor(withReturnType = false)
&& !doesOverride(builtinWithErasedParameters)
}
private fun resolveConstructor(constructor: JavaConstructor): JavaClassConstructorDescriptor {
val classDescriptor = ownerDescriptor
val constructorDescriptor = JavaClassConstructorDescriptor.createJavaConstructor(
classDescriptor, c.resolveAnnotations(constructor), /* isPrimary = */ false, c.components.sourceElementFactory.source(constructor)
)
val c = c.childForMethod(constructorDescriptor, constructor, typeParametersIndexOffset = classDescriptor.declaredTypeParameters.size)
val valueParameters = resolveValueParameters(c, constructorDescriptor, constructor.valueParameters)
val constructorTypeParameters =
classDescriptor.declaredTypeParameters +
constructor.typeParameters.map { p -> c.typeParameterResolver.resolveTypeParameter(p)!! }
constructorDescriptor.initialize(valueParameters.descriptors, constructor.visibility, constructorTypeParameters)
constructorDescriptor.setHasStableParameterNames(false)
constructorDescriptor.setHasSynthesizedParameterNames(valueParameters.hasSynthesizedNames)
constructorDescriptor.returnType = classDescriptor.defaultType
c.components.javaResolverCache.recordConstructor(constructor, constructorDescriptor)
return constructorDescriptor
}
private fun createDefaultConstructor(): ClassConstructorDescriptor? {
val isAnnotation: Boolean = jClass.isAnnotationType
if (jClass.isInterface && !isAnnotation)
return null
val classDescriptor = ownerDescriptor
val constructorDescriptor = JavaClassConstructorDescriptor.createJavaConstructor(
classDescriptor, Annotations.EMPTY, /* isPrimary = */ true, c.components.sourceElementFactory.source(jClass)
)
val valueParameters = if (isAnnotation) createAnnotationConstructorParameters(constructorDescriptor)
else Collections.emptyList<ValueParameterDescriptor>()
constructorDescriptor.setHasSynthesizedParameterNames(false)
constructorDescriptor.initialize(valueParameters, getConstructorVisibility(classDescriptor))
constructorDescriptor.setHasStableParameterNames(true)
constructorDescriptor.returnType = classDescriptor.defaultType
c.components.javaResolverCache.recordConstructor(jClass, constructorDescriptor)
return constructorDescriptor
}
private fun getConstructorVisibility(classDescriptor: ClassDescriptor): Visibility {
val visibility = classDescriptor.visibility
if (visibility == JavaVisibilities.PROTECTED_STATIC_VISIBILITY) {
return JavaVisibilities.PROTECTED_AND_PACKAGE
}
return visibility
}
private fun createAnnotationConstructorParameters(constructor: ClassConstructorDescriptorImpl): List<ValueParameterDescriptor> {
val methods = jClass.methods
val result = ArrayList<ValueParameterDescriptor>(methods.size)
val attr = TypeUsage.COMMON.toAttributes(isForAnnotationParameter = true)
val (methodsNamedValue, otherMethods) = methods.
partition { it.name == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME }
assert(methodsNamedValue.size <= 1) { "There can't be more than one method named 'value' in annotation class: $jClass" }
val methodNamedValue = methodsNamedValue.firstOrNull()
if (methodNamedValue != null) {
val parameterNamedValueJavaType = methodNamedValue.returnType
val (parameterType, varargType) =
if (parameterNamedValueJavaType is JavaArrayType)
Pair(c.typeResolver.transformArrayType(parameterNamedValueJavaType, attr, isVararg = true),
c.typeResolver.transformJavaType(parameterNamedValueJavaType.componentType, attr))
else
Pair(c.typeResolver.transformJavaType(parameterNamedValueJavaType, attr), null)
result.addAnnotationValueParameter(constructor, 0, methodNamedValue, parameterType, varargType)
}
val startIndex = if (methodNamedValue != null) 1 else 0
for ((index, method) in otherMethods.withIndex()) {
val parameterType = c.typeResolver.transformJavaType(method.returnType, attr)
result.addAnnotationValueParameter(constructor, index + startIndex, method, parameterType, null)
}
return result
}
private fun MutableList<ValueParameterDescriptor>.addAnnotationValueParameter(
constructor: ConstructorDescriptor,
index: Int,
method: JavaMethod,
returnType: KotlinType,
varargElementType: KotlinType?
) {
add(ValueParameterDescriptorImpl(
constructor,
null,
index,
Annotations.EMPTY,
method.name,
// Parameters of annotation constructors in Java are never nullable
TypeUtils.makeNotNullable(returnType),
method.hasAnnotationParameterDefaultValue,
/* isCrossinline = */ false,
/* isNoinline = */ false,
// Nulls are not allowed in annotation arguments in Java
varargElementType?.let { TypeUtils.makeNotNullable(it) },
c.components.sourceElementFactory.source(method)
))
}
private val nestedClassIndex = c.storageManager.createLazyValue {
jClass.innerClassNames.toSet()
}
private val enumEntryIndex = c.storageManager.createLazyValue {
jClass.fields.filter { it.isEnumEntry }.associateBy { f -> f.name }
}
private val nestedClasses = c.storageManager.createMemoizedFunctionWithNullableValues {
name: Name ->
if (name !in nestedClassIndex()) {
val field = enumEntryIndex()[name]
if (field != null) {
val enumMemberNames: NotNullLazyValue<Set<Name>> = c.storageManager.createLazyValue {
getFunctionNames() + getVariableNames()
}
EnumEntrySyntheticClassDescriptor.create(
c.storageManager, ownerDescriptor, name, enumMemberNames, c.resolveAnnotations(field),
c.components.sourceElementFactory.source(field)
)
}
else null
}
else {
c.components.finder.findClass(ownerDescriptor.classId!!.createNestedClassId(name))?.let {
LazyJavaClassDescriptor(c, ownerDescriptor, it)
}
}
}
override fun getDispatchReceiverParameter(): ReceiverParameterDescriptor? =
DescriptorUtils.getDispatchReceiverParameterIfNeeded(ownerDescriptor)
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
recordLookup(name, location)
return nestedClasses(name)
}
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> {
recordLookup(name, location)
return super.getContributedFunctions(name, location)
}
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> {
recordLookup(name, location)
return super.getContributedVariables(name, location)
}
override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name>
= nestedClassIndex() + enumEntryIndex().keys
override fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> {
if (jClass.isAnnotationType) return getFunctionNames()
val result = LinkedHashSet(declaredMemberIndex().getFieldNames())
return ownerDescriptor.typeConstructor.supertypes.flatMapTo(result) { supertype ->
supertype.memberScope.getVariableNames()
}
}
override fun recordLookup(name: Name, location: LookupLocation) {
c.components.lookupTracker.record(location, ownerDescriptor, name)
}
override fun toString() = "Lazy Java member scope for " + jClass.fqName
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2015 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaPackage
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.storage.getValue
class LazyJavaPackageFragment(
outerContext: LazyJavaResolverContext,
private val jPackage: JavaPackage
) : PackageFragmentDescriptorImpl(outerContext.module, jPackage.fqName) {
private val c = outerContext.childForClassOrPackage(this)
internal val binaryClasses by c.storageManager.createLazyValue {
c.components.packageMapper.findPackageParts(fqName.asString()).mapNotNull { partName ->
val classId = ClassId.topLevel(JvmClassName.byInternalName(partName).fqNameForTopLevelClassMaybeWithDollars)
c.components.kotlinClassFinder.findKotlinClass(classId)?.let { partName to it }
}.toMap()
}
private val scope = JvmPackageScope(c, jPackage, this)
private val subPackages = c.storageManager.createRecursionTolerantLazyValue(
{ jPackage.subPackages.map(JavaPackage::fqName) },
// This breaks infinite recursion between loading Java descriptors and building light classes
onRecursiveCall = listOf()
)
override val annotations =
// Do not resolve package annotations if JSR-305 is disabled
if (c.components.annotationTypeQualifierResolver.disabled) Annotations.EMPTY
else c.resolveAnnotations(jPackage)
internal fun getSubPackageFqNames(): List<FqName> = subPackages()
internal fun findClassifierByJavaClass(jClass: JavaClass): ClassDescriptor? = scope.javaScope.findClassifierByJavaClass(jClass)
private val partToFacade by c.storageManager.createLazyValue {
val result = hashMapOf<JvmClassName, JvmClassName>()
kotlinClasses@for ((partInternalName, kotlinClass) in binaryClasses) {
val partName = JvmClassName.byInternalName(partInternalName)
val header = kotlinClass.classHeader
when (header.kind) {
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
result[partName] = JvmClassName.byInternalName(header.multifileClassName ?: continue@kotlinClasses)
}
KotlinClassHeader.Kind.FILE_FACADE -> {
result[partName] = partName
}
else -> {}
}
}
result
}
fun getFacadeNameForPartName(partName: JvmClassName): JvmClassName? = partToFacade[partName]
override fun getMemberScope() = scope
override fun toString() = "Lazy Java package fragment: $fqName"
override fun getSource(): SourceElement = KotlinJvmBinaryPackageSourceElement(this)
}
@@ -0,0 +1,164 @@
/*
* 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaPackage
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.storage.NullableLazyValue
import org.jetbrains.kotlin.utils.alwaysTrue
import java.util.*
class LazyJavaPackageScope(
c: LazyJavaResolverContext,
private val jPackage: JavaPackage,
override val ownerDescriptor: LazyJavaPackageFragment
) : LazyJavaStaticScope(c) {
// Null means that it's impossible to determine list of class names in package, i.e. in IDE where special finders exist
// But for compiler though we can determine full list of class names by getting all class-file names in classpath and sources
private val knownClassNamesInPackage: NullableLazyValue<Set<String>> = c.storageManager.createNullableLazyValue {
c.components.finder.knownClassNamesInPackage(ownerDescriptor.fqName)
}
private val classes = c.storageManager.createMemoizedFunctionWithNullableValues<FindClassRequest, ClassDescriptor> classByRequest@{ request ->
val requestClassId = ClassId(ownerDescriptor.fqName, request.name)
val kotlinBinaryClass =
// These branches should be semantically equal, but the first one could be faster
if (request.javaClass != null)
c.components.kotlinClassFinder.findKotlinClass(request.javaClass)
else
c.components.kotlinClassFinder.findKotlinClass(requestClassId)
val classId = kotlinBinaryClass?.classId
// Nested/local classes can be found when running in CLI in case when request.name looks like 'Outer$Inner'
// It happens because KotlinClassFinder searches through a file-based index that does not differ classes containing $-sign and nested ones
if (classId != null && (classId.isNestedClass || classId.isLocal)) return@classByRequest null
val kotlinResult = resolveKotlinBinaryClass(kotlinBinaryClass)
when (kotlinResult) {
is KotlinClassLookupResult.Found -> kotlinResult.descriptor
is KotlinClassLookupResult.SyntheticClass -> null
is KotlinClassLookupResult.NotFound -> {
val javaClass = request.javaClass ?: c.components.finder.findClass(requestClassId)
if (javaClass?.lightClassOriginKind == LightClassOriginKind.BINARY) {
throw IllegalStateException(
"Couldn't find kotlin binary class for light class created by kotlin binary file\n" +
"JavaClass: $javaClass\n" +
"ClassId: $requestClassId\n" +
"findKotlinClass(JavaClass) = ${c.components.kotlinClassFinder.findKotlinClass(javaClass)}\n" +
"findKotlinClass(ClassId) = ${c.components.kotlinClassFinder.findKotlinClass(requestClassId)}\n"
)
}
val actualFqName = javaClass?.fqName
if (actualFqName == null || actualFqName.isRoot || actualFqName.parent() != ownerDescriptor.fqName)
null
else
LazyJavaClassDescriptor(c, ownerDescriptor, javaClass)
}
}
}
private sealed class KotlinClassLookupResult {
class Found(val descriptor: ClassDescriptor) : KotlinClassLookupResult()
object NotFound : KotlinClassLookupResult()
object SyntheticClass : KotlinClassLookupResult()
}
private fun resolveKotlinBinaryClass(kotlinClass: KotlinJvmBinaryClass?): KotlinClassLookupResult =
when {
kotlinClass == null -> {
KotlinClassLookupResult.NotFound
}
kotlinClass.classHeader.kind == KotlinClassHeader.Kind.CLASS -> {
val descriptor = c.components.deserializedDescriptorResolver.resolveClass(kotlinClass)
if (descriptor != null) KotlinClassLookupResult.Found(descriptor) else KotlinClassLookupResult.NotFound
}
else -> {
// This is a package or interface DefaultImpls or something like that
KotlinClassLookupResult.SyntheticClass
}
}
// javaClass here is only for sake of optimizations
private class FindClassRequest(val name: Name, val javaClass: JavaClass?) {
override fun equals(other: Any?) = other is FindClassRequest && name == other.name
override fun hashCode() = name.hashCode()
}
override fun getContributedClassifier(name: Name, location: LookupLocation) = findClassifier(name, null)
private fun findClassifier(name: Name, javaClass: JavaClass?): ClassDescriptor? {
if (!SpecialNames.isSafeIdentifier(name)) return null
val knownClassNamesInPackage = knownClassNamesInPackage()
if (javaClass == null && knownClassNamesInPackage != null && name.asString() !in knownClassNamesInPackage) {
return null
}
return classes(FindClassRequest(name, javaClass))
}
internal fun findClassifierByJavaClass(javaClass: JavaClass) = findClassifier(javaClass.name, javaClass)
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> = emptyList()
override fun computeMemberIndex(): DeclaredMemberIndex = DeclaredMemberIndex.Empty
override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> {
// neither objects nor enum members can be in java package
if (!kindFilter.acceptsKinds(DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS_MASK)) return emptySet()
val knownClassNamesInPackage = knownClassNamesInPackage()
if (knownClassNamesInPackage != null) return knownClassNamesInPackage.mapTo(HashSet()) { Name.identifier(it) }
return jPackage.getClasses(nameFilter ?: alwaysTrue()).mapNotNullTo(linkedSetOf()) { klass ->
if (klass.lightClassOriginKind == LightClassOriginKind.SOURCE) null else klass.name
}
}
override fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> {
return emptySet()
}
override fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
}
override fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) = emptySet<Name>()
// we don't use implementation from super which caches all descriptors and does not use filters
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
return computeDescriptors(kindFilter, nameFilter, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
}
}
@@ -0,0 +1,368 @@
/*
* 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.childForMethod
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgumentValue
import org.jetbrains.kotlin.resolve.retainMostSpecificInEachOverridableGroup
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude.NonExtensions
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.storage.NotNullLazyValue
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberScopeImpl() {
protected abstract val ownerDescriptor: DeclarationDescriptor
// this lazy value is not used at all in LazyPackageFragmentScopeForJavaPackage because we do not use caching there
// but is placed in the base class to not duplicate code
private val allDescriptors = c.storageManager.createRecursionTolerantLazyValue<Collection<DeclarationDescriptor>>(
{ computeDescriptors(DescriptorKindFilter.ALL, MemberScope.ALL_NAME_FILTER, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) },
// This is to avoid the following recursive case:
// when computing getAllPackageNames() we ask the JavaPsiFacade for all subpackages of foo
// it, in turn, asks JavaElementFinder for subpackages of Kotlin package foo, which calls getAllPackageNames() recursively
// when on recursive call we return an empty collection, recursion collapses gracefully
listOf()
)
protected val declaredMemberIndex: NotNullLazyValue<DeclaredMemberIndex> = c.storageManager.createLazyValue { computeMemberIndex() }
protected abstract fun computeMemberIndex(): DeclaredMemberIndex
// Fake overrides, values()/valueOf(), etc.
protected abstract fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name)
protected abstract fun getDispatchReceiverParameter(): ReceiverParameterDescriptor?
private val functions = c.storageManager.createMemoizedFunction<Name, Collection<SimpleFunctionDescriptor>> {
name ->
val result = LinkedHashSet<SimpleFunctionDescriptor>()
for (method in declaredMemberIndex().findMethodsByName(name)) {
val descriptor = resolveMethodToFunctionDescriptor(method)
if (!descriptor.isVisibleAsFunction()) continue
c.components.javaResolverCache.recordMethod(method, descriptor)
result.add(descriptor)
}
result.retainMostSpecificInEachOverridableGroup()
computeNonDeclaredFunctions(result, name)
c.components.signatureEnhancement.enhanceSignatures(c, result).toList()
}
open protected fun JavaMethodDescriptor.isVisibleAsFunction() = true
protected data class MethodSignatureData(
val returnType: KotlinType,
val receiverType: KotlinType?,
val valueParameters: List<ValueParameterDescriptor>,
val typeParameters: List<TypeParameterDescriptor>,
val hasStableParameterNames: Boolean,
val errors: List<String>
)
protected abstract fun resolveMethodSignature(
method: JavaMethod,
methodTypeParameters: List<TypeParameterDescriptor>,
returnType: KotlinType,
valueParameters: List<ValueParameterDescriptor>
): MethodSignatureData
protected fun resolveMethodToFunctionDescriptor(method: JavaMethod): JavaMethodDescriptor {
val annotations = c.resolveAnnotations(method)
val functionDescriptorImpl = JavaMethodDescriptor.createJavaMethod(
ownerDescriptor, annotations, method.name, c.components.sourceElementFactory.source(method)
)
val c = c.childForMethod(functionDescriptorImpl, method)
val methodTypeParameters = method.typeParameters.map { p -> c.typeParameterResolver.resolveTypeParameter(p)!! }
val valueParameters = resolveValueParameters(c, functionDescriptorImpl, method.valueParameters)
val returnType = computeMethodReturnType(method, c)
val effectiveSignature = resolveMethodSignature(method, methodTypeParameters, returnType, valueParameters.descriptors)
functionDescriptorImpl.initialize(
effectiveSignature.receiverType,
getDispatchReceiverParameter(),
effectiveSignature.typeParameters,
effectiveSignature.valueParameters,
effectiveSignature.returnType,
Modality.convertFromFlags(method.isAbstract, !method.isFinal),
method.visibility,
if (effectiveSignature.receiverType != null)
mapOf(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER to valueParameters.descriptors.first())
else
emptyMap<FunctionDescriptor.UserDataKey<ValueParameterDescriptor>, ValueParameterDescriptor>()
)
functionDescriptorImpl.setParameterNamesStatus(effectiveSignature.hasStableParameterNames, valueParameters.hasSynthesizedNames)
if (effectiveSignature.errors.isNotEmpty()) {
c.components.signaturePropagator.reportSignatureErrors(functionDescriptorImpl, effectiveSignature.errors)
}
return functionDescriptorImpl
}
protected fun computeMethodReturnType(method: JavaMethod, c: LazyJavaResolverContext): KotlinType {
val annotationMethod = method.containingClass.isAnnotationType
val returnTypeAttrs = TypeUsage.COMMON.toAttributes(
isForAnnotationParameter = annotationMethod
)
return c.typeResolver.transformJavaType(method.returnType, returnTypeAttrs)
}
protected class ResolvedValueParameters(val descriptors: List<ValueParameterDescriptor>, val hasSynthesizedNames: Boolean)
protected fun resolveValueParameters(
c: LazyJavaResolverContext,
function: FunctionDescriptor,
jValueParameters: List<JavaValueParameter>
): ResolvedValueParameters {
var synthesizedNames = false
val usedNames = mutableSetOf<String>()
val descriptors = jValueParameters.withIndex().map { (index, javaParameter) ->
val annotations = c.resolveAnnotations(javaParameter)
val typeUsage = TypeUsage.COMMON.toAttributes()
val parameterName = annotations
.findAnnotation(JvmAnnotationNames.PARAMETER_NAME_FQ_NAME)
?.firstArgumentValue()
?.safeAs<String>()
val (outType, varargElementType) =
if (javaParameter.isVararg) {
val paramType = javaParameter.type as? JavaArrayType
?: throw AssertionError("Vararg parameter should be an array: $javaParameter")
val outType = c.typeResolver.transformArrayType(paramType, typeUsage, true)
outType to c.module.builtIns.getArrayElementType(outType)
}
else {
c.typeResolver.transformJavaType(javaParameter.type, typeUsage) to null
}
val name = if (function.name.asString() == "equals" &&
jValueParameters.size == 1 &&
c.module.builtIns.nullableAnyType == outType) {
// This is a hack to prevent numerous warnings on Kotlin classes that inherit Java classes: if you override "equals" in such
// class without this hack, you'll be warned that in the superclass the name is "p0" (regardless of the fact that it's
// "other" in Any)
// TODO: fix Java parameter name loading logic somehow (don't always load "p0", "p1", etc.)
Name.identifier("other")
}
else if (parameterName != null && parameterName.isNotEmpty() && usedNames.add(parameterName)) {
Name.identifier(parameterName)
}
else {
// TODO: parameter names may be drawn from attached sources, which is slow; it's better to make them lazy
val javaName = javaParameter.name
if (javaName == null) synthesizedNames = true
javaName ?: Name.identifier("p$index")
}
ValueParameterDescriptorImpl(
function,
null,
index,
annotations,
name,
outType,
/* declaresDefaultValue = */ false,
/* isCrossinline = */ false,
/* isNoinline = */ false,
varargElementType,
c.components.sourceElementFactory.source(javaParameter)
)
}.toList()
return ResolvedValueParameters(descriptors, synthesizedNames)
}
private val functionNamesLazy by c.storageManager.createLazyValue { computeFunctionNames(DescriptorKindFilter.FUNCTIONS, null) }
private val propertyNamesLazy by c.storageManager.createLazyValue { computePropertyNames(DescriptorKindFilter.VARIABLES, null) }
private val classNamesLazy by c.storageManager.createLazyValue { computeClassNames(DescriptorKindFilter.CLASSIFIERS, null) }
override fun getFunctionNames() = functionNamesLazy
override fun getVariableNames() = propertyNamesLazy
override fun getClassifierNames() = classNamesLazy
override fun definitelyDoesNotContainName(name: Name): Boolean {
return name !in functionNamesLazy && name !in propertyNamesLazy && name !in classNamesLazy
}
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> {
if (name !in getFunctionNames()) return emptyList()
return functions(name)
}
protected abstract fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name>
protected abstract fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>)
protected abstract fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name>
private val properties = c.storageManager.createMemoizedFunction {
name: Name ->
val properties = ArrayList<PropertyDescriptor>()
val field = declaredMemberIndex().findFieldByName(name)
if (field != null && !field.isEnumEntry) {
properties.add(resolveProperty(field))
}
computeNonDeclaredProperties(name, properties)
if (DescriptorUtils.isAnnotationClass(ownerDescriptor))
properties.toList()
else
c.components.signatureEnhancement.enhanceSignatures(c, properties).toList()
}
private fun resolveProperty(field: JavaField): PropertyDescriptor {
val propertyDescriptor = createPropertyDescriptor(field)
propertyDescriptor.initialize(null, null)
val propertyType = getPropertyType(field)
propertyDescriptor.setType(propertyType, listOf(), getDispatchReceiverParameter(), null as KotlinType?)
if (DescriptorUtils.shouldRecordInitializerForProperty(propertyDescriptor, propertyDescriptor.type)) {
propertyDescriptor.setCompileTimeInitializer(
c.storageManager.createNullableLazyValue {
c.components.javaPropertyInitializerEvaluator.getInitializerConstant(field, propertyDescriptor)
})
}
c.components.javaResolverCache.recordField(field, propertyDescriptor)
return propertyDescriptor
}
private fun createPropertyDescriptor(field: JavaField): PropertyDescriptorImpl {
val isVar = !field.isFinal
val annotations = c.resolveAnnotations(field)
return JavaPropertyDescriptor.create(
ownerDescriptor, annotations, Modality.FINAL, field.visibility, isVar, field.name,
c.components.sourceElementFactory.source(field), /* isConst = */ field.isFinalStatic
)
}
private val JavaField.isFinalStatic: Boolean
get() = isFinal && isStatic
private fun getPropertyType(field: JavaField): KotlinType {
// Fields do not have their own generic parameters.
// Simple static constants should not have flexible types.
val isNotNullable = !(field.isFinalStatic && field.hasConstantNotNullInitializer)
val propertyType = c.typeResolver.transformJavaType(
field.type,
TypeUsage.COMMON.toAttributes()
)
if (!isNotNullable) {
return TypeUtils.makeNotNullable(propertyType)
}
return propertyType
}
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> {
if (name !in getVariableNames()) return emptyList()
return properties(name)
}
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) = allDescriptors()
protected fun computeDescriptors(
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean,
location: LookupLocation
): List<DeclarationDescriptor> {
val result = LinkedHashSet<DeclarationDescriptor>()
if (kindFilter.acceptsKinds(DescriptorKindFilter.CLASSIFIERS_MASK)) {
for (name in computeClassNames(kindFilter, nameFilter)) {
if (nameFilter(name)) {
// Null signifies that a class found in Java is not present in Kotlin (e.g. package class)
result.addIfNotNull(getContributedClassifier(name, location))
}
}
}
if (kindFilter.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK) && !kindFilter.excludes.contains(NonExtensions)) {
for (name in computeFunctionNames(kindFilter, nameFilter)) {
if (nameFilter(name)) {
result.addAll(getContributedFunctions(name, location))
}
}
}
if (kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK) && !kindFilter.excludes.contains(NonExtensions)) {
for (name in computePropertyNames(kindFilter, nameFilter)) {
if (nameFilter(name)) {
result.addAll(getContributedVariables(name, location))
}
}
}
return result.toList()
}
protected abstract fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name>
override fun toString() = "Lazy scope for $ownerDescriptor"
override fun printScopeStructure(p: Printer) {
p.println(this::class.java.simpleName, " {")
p.pushIndent()
p.println("containingDeclaration: $ownerDescriptor")
p.popIndent()
p.println("}")
}
}
@@ -0,0 +1,137 @@
/*
* 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils.resolveOverridesForStaticMembers
import org.jetbrains.kotlin.load.java.descriptors.getParentJavaStaticClassScope
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValueOfMethod
import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValuesMethod
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.DFS
class LazyJavaStaticClassScope(
c: LazyJavaResolverContext,
private val jClass: JavaClass,
override val ownerDescriptor: LazyJavaClassDescriptor
) : LazyJavaStaticScope(c) {
override fun computeMemberIndex() = ClassDeclaredMemberIndex(jClass) { it.isStatic }
override fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) =
declaredMemberIndex().getMethodNames().toMutableSet().apply {
addAll(ownerDescriptor.getParentJavaStaticClassScope()?.getFunctionNames().orEmpty())
if (jClass.isEnum) {
addAll(listOf(DescriptorUtils.ENUM_VALUE_OF, DescriptorUtils.ENUM_VALUES))
}
}
override fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) =
declaredMemberIndex().getFieldNames().toMutableSet().apply {
flatMapJavaStaticSupertypesScopes(ownerDescriptor, this) { it.getVariableNames() }
}
override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> = emptySet()
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
// We don't need to track lookups here because we find nested/inner classes in LazyJavaClassMemberScope
return null
}
override fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
val functionsFromSupertypes = getStaticFunctionsFromJavaSuperClasses(name, ownerDescriptor)
result.addAll(resolveOverridesForStaticMembers(name, functionsFromSupertypes, result, ownerDescriptor, c.components.errorReporter))
if (jClass.isEnum) {
when (name) {
DescriptorUtils.ENUM_VALUE_OF -> result.add(createEnumValueOfMethod(ownerDescriptor))
DescriptorUtils.ENUM_VALUES -> result.add(createEnumValuesMethod(ownerDescriptor))
}
}
}
override fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>) {
val propertiesFromSupertypes = flatMapJavaStaticSupertypesScopes(ownerDescriptor, mutableSetOf()) {
it.getContributedVariables(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS)
}
if (result.isNotEmpty()) {
result.addAll(resolveOverridesForStaticMembers(
name, propertiesFromSupertypes, result, ownerDescriptor, c.components.errorReporter
))
}
else {
result.addAll(propertiesFromSupertypes.groupBy {
it.realOriginal
}.flatMap {
resolveOverridesForStaticMembers(name, it.value, result, ownerDescriptor, c.components.errorReporter)
})
}
}
private fun getStaticFunctionsFromJavaSuperClasses(name: Name, descriptor: ClassDescriptor): Set<SimpleFunctionDescriptor> {
val staticScope = descriptor.getParentJavaStaticClassScope() ?: return emptySet()
return staticScope.getContributedFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS).toSet()
}
private fun <R> flatMapJavaStaticSupertypesScopes(
root: ClassDescriptor,
result: MutableSet<R>,
onJavaStaticScope: (MemberScope) -> Collection<R>
): Set<R> {
DFS.dfs(listOf(root),
{
it.typeConstructor.supertypes.asSequence().mapNotNull {
supertype -> supertype.constructor.declarationDescriptor as? ClassDescriptor
}.asIterable()
},
object : DFS.AbstractNodeHandler<ClassDescriptor, Unit>() {
override fun beforeChildren(current: ClassDescriptor): Boolean {
if (current === root) return true
val staticScope = current.staticScope
if (staticScope is LazyJavaStaticScope) {
result.addAll(onJavaStaticScope(staticScope))
return false
}
return true
}
override fun result() {}
}
)
return result
}
private val PropertyDescriptor.realOriginal: PropertyDescriptor
get() {
if (this.kind.isReal) return this
return this.overriddenDescriptors.map { it.realOriginal }.distinct().single()
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2015 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
abstract class LazyJavaStaticScope(c: LazyJavaResolverContext) : LazyJavaScope(c) {
override fun getDispatchReceiverParameter() = null
override fun resolveMethodSignature(
method: JavaMethod, methodTypeParameters: List<TypeParameterDescriptor>, returnType: KotlinType,
valueParameters: List<ValueParameterDescriptor>
) = LazyJavaScope.MethodSignatureData(returnType, null, valueParameters, methodTypeParameters, false, emptyList())
override fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>) {
//no undeclared properties
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2016 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.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.impl.AbstractLazyTypeParameterDescriptor
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.Variance
class LazyJavaTypeParameterDescriptor(
private val c: LazyJavaResolverContext,
val javaTypeParameter: JavaTypeParameter,
index: Int,
containingDeclaration: DeclarationDescriptor
) : AbstractLazyTypeParameterDescriptor(
c.storageManager,
containingDeclaration,
javaTypeParameter.name,
Variance.INVARIANT,
/* isReified = */ false,
index,
SourceElement.NO_SOURCE, c.components.supertypeLoopChecker
) {
override val annotations = LazyJavaAnnotations(c, javaTypeParameter)
override fun resolveUpperBounds(): List<KotlinType> {
val bounds = javaTypeParameter.upperBounds
if (bounds.isEmpty()) {
return listOf(KotlinTypeFactory.flexibleType(
c.module.builtIns.anyType,
c.module.builtIns.nullableAnyType
))
}
return bounds.map {
c.typeResolver.transformJavaType(it, TypeUsage.COMMON.toAttributes(upperBoundForTypeParameter = this))
}
}
override fun reportSupertypeLoopError(type: KotlinType) {
// Do nothing
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2015 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.load.java.lazy
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
import org.jetbrains.kotlin.utils.mapToIndex
interface TypeParameterResolver {
object EMPTY : TypeParameterResolver {
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? = null
}
fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor?
}
class LazyJavaTypeParameterResolver(
private val c: LazyJavaResolverContext,
private val containingDeclaration: DeclarationDescriptor,
typeParameterOwner: JavaTypeParameterListOwner,
private val typeParametersIndexOffset: Int
) : TypeParameterResolver {
private val typeParameters: Map<JavaTypeParameter, Int> = typeParameterOwner.typeParameters.mapToIndex()
private val resolve = c.storageManager.createMemoizedFunctionWithNullableValues {
typeParameter: JavaTypeParameter ->
typeParameters[typeParameter]?.let { index ->
LazyJavaTypeParameterDescriptor(c.child(this), typeParameter, typeParametersIndexOffset + index, containingDeclaration)
}
}
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? {
return resolve(javaTypeParameter) ?: c.typeParameterResolver.resolveTypeParameter(javaTypeParameter)
}
}
@@ -0,0 +1,352 @@
/*
* Copyright 2010-2016 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.load.java.lazy.types
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.components.TypeUsage.COMMON
import org.jetbrains.kotlin.load.java.components.TypeUsage.SUPERTYPE
import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeFlexibility.*
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.Variance.*
import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.kotlin.utils.sure
private val JAVA_LANG_CLASS_FQ_NAME: FqName = FqName("java.lang.Class")
class JavaTypeResolver(
private val c: LazyJavaResolverContext,
private val typeParameterResolver: TypeParameterResolver
) {
fun transformJavaType(javaType: JavaType, attr: JavaTypeAttributes): KotlinType {
return when (javaType) {
is JavaPrimitiveType -> {
val primitiveType = javaType.type
if (primitiveType != null) c.module.builtIns.getPrimitiveKotlinType(primitiveType)
else c.module.builtIns.unitType
}
is JavaClassifierType -> transformJavaClassifierType(javaType, attr)
is JavaArrayType -> transformArrayType(javaType, attr)
// Top level type can be a wildcard only in case of broken Java code, but we should not fail with exceptions in such cases
is JavaWildcardType -> javaType.bound?.let { transformJavaType(it, attr) } ?: c.module.builtIns.defaultBound
else -> throw UnsupportedOperationException("Unsupported type: " + javaType)
}
}
fun transformArrayType(arrayType: JavaArrayType, attr: JavaTypeAttributes, isVararg: Boolean = false): KotlinType {
val javaComponentType = arrayType.componentType
val primitiveType = (javaComponentType as? JavaPrimitiveType)?.type
if (primitiveType != null) {
val jetType = c.module.builtIns.getPrimitiveArrayKotlinType(primitiveType)
return if (attr.isForAnnotationParameter)
jetType
else KotlinTypeFactory.flexibleType(jetType, jetType.makeNullableAsSpecified(true))
}
val componentType = transformJavaType(javaComponentType,
COMMON.toAttributes(attr.isForAnnotationParameter))
if (attr.isForAnnotationParameter) {
val projectionKind = if (isVararg) OUT_VARIANCE else INVARIANT
return c.module.builtIns.getArrayType(projectionKind, componentType)
}
return KotlinTypeFactory.flexibleType(
c.module.builtIns.getArrayType(INVARIANT, componentType),
c.module.builtIns.getArrayType(OUT_VARIANCE, componentType).makeNullableAsSpecified(true)
)
}
private fun transformJavaClassifierType(javaType: JavaClassifierType, attr: JavaTypeAttributes): KotlinType {
fun errorType() = ErrorUtils.createErrorType("Unresolved java class ${javaType.presentableText}")
val useFlexible = !attr.isForAnnotationParameter && attr.howThisTypeIsUsed != SUPERTYPE
val isRaw = javaType.isRaw
if (!isRaw && !useFlexible) {
return computeSimpleJavaClassifierType(javaType, attr, null) ?: errorType()
}
val lower =
computeSimpleJavaClassifierType(javaType, attr.withFlexibility(FLEXIBLE_LOWER_BOUND), lowerResult = null)
?: return errorType()
val upper =
computeSimpleJavaClassifierType(javaType, attr.withFlexibility(FLEXIBLE_UPPER_BOUND), lowerResult = lower)
?: return errorType()
return if (isRaw) {
RawTypeImpl(lower, upper)
}
else {
KotlinTypeFactory.flexibleType(lower, upper)
}
}
private fun computeSimpleJavaClassifierType(
javaType: JavaClassifierType, attr: JavaTypeAttributes,
lowerResult: SimpleType?
): SimpleType? {
val annotations =
lowerResult?.annotations ?: LazyJavaAnnotations(c, javaType)
val constructor = computeTypeConstructor(javaType, attr) ?: return null
val isNullable = attr.isNullable()
if (lowerResult?.constructor == constructor && !javaType.isRaw && isNullable) {
return lowerResult.makeNullableAsSpecified(true)
}
val arguments = computeArguments(javaType, attr, constructor)
return KotlinTypeFactory.simpleType(annotations, constructor, arguments, isNullable)
}
private fun computeTypeConstructor(javaType: JavaClassifierType, attr: JavaTypeAttributes): TypeConstructor? {
val classifier = javaType.classifier ?: return createNotFoundClass(javaType)
return when (classifier) {
is JavaClass -> {
val fqName = classifier.fqName.sure { "Class type should have a FQ name: $classifier" }
val classData = mapKotlinClass(javaType, attr, fqName) ?: c.components.moduleClassResolver.resolveClass(classifier)
classData?.typeConstructor ?: createNotFoundClass(javaType)
}
is JavaTypeParameter -> {
typeParameterResolver.resolveTypeParameter(classifier)?.typeConstructor
}
else -> throw IllegalStateException("Unknown classifier kind: $classifier")
}
}
// There's no way to extract precise type information in PSI when the type's classifier cannot be resolved.
// So we just take the canonical text of the type (which seems to be the only option at the moment), erase all type arguments
// and treat the resulting qualified name as if it references a simple top-level class.
// Note that this makes MISSING_DEPENDENCY_CLASS diagnostic messages not as precise as they could be in some corner cases.
private fun createNotFoundClass(javaType: JavaClassifierType): TypeConstructor {
val classId = ClassId.topLevel(FqName(javaType.classifierQualifiedName))
return c.components.deserializedDescriptorResolver.components.notFoundClasses.getClass(classId, listOf(0)).typeConstructor
}
private fun mapKotlinClass(javaType: JavaClassifierType, attr: JavaTypeAttributes, fqName: FqName): ClassDescriptor? {
if (attr.isForAnnotationParameter && fqName == JAVA_LANG_CLASS_FQ_NAME) {
return c.components.reflectionTypes.kClass
}
val javaToKotlin = JavaToKotlinClassMap
val kotlinDescriptor = javaToKotlin.mapJavaToKotlin(fqName, c.module.builtIns) ?: return null
if (javaToKotlin.isReadOnly(kotlinDescriptor)) {
if (attr.flexibility == FLEXIBLE_LOWER_BOUND ||
attr.howThisTypeIsUsed == SUPERTYPE ||
javaType.argumentsMakeSenseOnlyForMutableContainer(readOnlyContainer = kotlinDescriptor)) {
return javaToKotlin.convertReadOnlyToMutable(kotlinDescriptor)
}
}
return kotlinDescriptor
}
// Returns true for covariant read-only container that has mutable pair with invariant parameter
// List<in A> does not make sense, but MutableList<in A> does
// Same for Map<K, in V>
// But both Iterable<in A>, MutableIterable<in A> don't make sense as they are covariant, so return false
private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer(
readOnlyContainer: ClassDescriptor
): Boolean {
fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false
if (!typeArguments.lastOrNull().isSuperWildcard()) return false
val mutableLastParameterVariance = JavaToKotlinClassMap.convertReadOnlyToMutable(readOnlyContainer)
.typeConstructor.parameters.lastOrNull()?.variance ?: return false
return mutableLastParameterVariance != OUT_VARIANCE
}
private fun computeArguments(
javaType: JavaClassifierType,
attr: JavaTypeAttributes,
constructor: TypeConstructor
): List<TypeProjection> {
val isRaw = javaType.isRaw
val eraseTypeParameters =
isRaw ||
// This option is needed because sometimes we get weird versions of JDK classes in the class path,
// such as collections with no generics, so the Java types are not raw, formally, but they don't match with
// their Kotlin analogs, so we treat them as raw to avoid exceptions
(javaType.typeArguments.isEmpty() && !constructor.parameters.isEmpty())
val typeParameters = constructor.parameters
if (eraseTypeParameters) {
return typeParameters.map {
parameter ->
// Some activity for preventing recursion in cases like `class A<T extends A, F extends T>`
//
// When calculating upper bound of some parameter (attr.upperBoundOfTypeParameter),
// do not try to start upper bound calculation of it again.
// If we met such recursive dependency it means that upper bound of `attr.upperBoundOfTypeParameter` based effectively
// on the current class, so we can manually erase default type of current constructor.
//
// In example above corner cases are:
// - Calculating first argument for raw upper bound of T. It depends on T, so we just get A<*, *>
// - Calculating second argument for raw upper bound of T. It depends on F, that again depends on upper bound of T,
// so we get A<*, *>.
// Summary result for upper bound of T is `A<A<*, *>, A<*, *>>..A<out A<*, *>, out A<*, *>>`
val erasedUpperBound =
LazyWrappedType(c.storageManager) {
parameter.getErasedUpperBound(attr.upperBoundOfTypeParameter) {
constructor.declarationDescriptor!!.defaultType.replaceArgumentsWithStarProjections()
}
}
RawSubstitution.computeProjection(
parameter,
// if erasure happens due to invalid arguments number, use star projections instead
if (isRaw) attr else attr.withFlexibility(INFLEXIBLE),
erasedUpperBound
)
}.toList()
}
if (typeParameters.size != javaType.typeArguments.size) {
// Most of the time this means there is an error in the Java code
return typeParameters.map { p -> TypeProjectionImpl(ErrorUtils.createErrorType(p.name.asString())) }.toList()
}
return javaType.typeArguments.withIndex().map {
indexedArgument ->
val (i, javaTypeArgument) = indexedArgument
assert(i < typeParameters.size) {
"Argument index should be less then type parameters count, but $i > ${typeParameters.size}"
}
val parameter = typeParameters[i]
transformToTypeProjection(javaTypeArgument, COMMON.toAttributes(), parameter)
}.toList()
}
private fun transformToTypeProjection(
javaType: JavaType,
attr: JavaTypeAttributes,
typeParameter: TypeParameterDescriptor
): TypeProjection {
return when (javaType) {
is JavaWildcardType -> {
val bound = javaType.bound
val projectionKind = if (javaType.isExtends) OUT_VARIANCE else IN_VARIANCE
if (bound == null || projectionKind.isConflictingArgumentFor(typeParameter))
makeStarProjection(typeParameter, attr)
else {
createProjection(
type = transformJavaType(bound, COMMON.toAttributes()),
projectionKind = projectionKind,
typeParameterDescriptor = typeParameter
)
}
}
else -> TypeProjectionImpl(INVARIANT, transformJavaType(javaType, attr))
}
}
private fun Variance.isConflictingArgumentFor(typeParameter: TypeParameterDescriptor): Boolean {
if (typeParameter.variance == INVARIANT) return false
return this != typeParameter.variance
}
private fun JavaTypeAttributes.isNullable(): Boolean {
if (flexibility == FLEXIBLE_LOWER_BOUND) return false
// even if flexibility is FLEXIBLE_UPPER_BOUND it's still can be not nullable for supetypes and annotation parameters
return !isForAnnotationParameter && howThisTypeIsUsed != SUPERTYPE
}
}
internal fun makeStarProjection(
typeParameter: TypeParameterDescriptor,
attr: JavaTypeAttributes
): TypeProjection {
return if (attr.howThisTypeIsUsed == SUPERTYPE)
TypeProjectionImpl(typeParameter.starProjectionType())
else
StarProjectionImpl(typeParameter)
}
data class JavaTypeAttributes(
val howThisTypeIsUsed: TypeUsage,
val flexibility: JavaTypeFlexibility = INFLEXIBLE,
val isForAnnotationParameter: Boolean = false,
// Current type is upper bound of this type parameter
val upperBoundOfTypeParameter: TypeParameterDescriptor? = null
) {
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
}
enum class JavaTypeFlexibility {
INFLEXIBLE,
FLEXIBLE_UPPER_BOUND,
FLEXIBLE_LOWER_BOUND
}
fun TypeUsage.toAttributes(
isForAnnotationParameter: Boolean = false,
upperBoundForTypeParameter: TypeParameterDescriptor? = null
) = JavaTypeAttributes(
this,
isForAnnotationParameter = isForAnnotationParameter,
upperBoundOfTypeParameter = upperBoundForTypeParameter
)
// Definition:
// ErasedUpperBound(T : G<t>) = G<*> // UpperBound(T) is a type G<t> with arguments
// ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments
// ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F
internal fun TypeParameterDescriptor.getErasedUpperBound(
// Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound`
// E.g. `class A<T extends A, F extends A>`
// To prevent recursive calls return defaultValue() instead
potentiallyRecursiveTypeParameter: TypeParameterDescriptor? = null,
defaultValue: (() -> KotlinType) = { ErrorUtils.createErrorType("Can't compute erased upper bound of type parameter `$this`") }
): KotlinType {
if (this === potentiallyRecursiveTypeParameter) return defaultValue()
val firstUpperBound = upperBounds.first()
if (firstUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
return firstUpperBound.replaceArgumentsWithStarProjections()
}
val stopAt = potentiallyRecursiveTypeParameter ?: this
var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
while (current != stopAt) {
val nextUpperBound = current.upperBounds.first()
if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
return nextUpperBound.replaceArgumentsWithStarProjections()
}
current = nextUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
}
return defaultValue()
}
@@ -0,0 +1,167 @@
/*
* Copyright 2010-2016 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.load.java.lazy.types
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.builtIns
class RawTypeImpl(lowerBound: SimpleType, upperBound: SimpleType) : FlexibleType(lowerBound, upperBound), RawType {
init {
assert (KotlinTypeChecker.DEFAULT.isSubtypeOf(lowerBound, upperBound)) {
"Lower bound $lowerBound of a flexible type must be a subtype of the upper bound $upperBound"
}
}
override val delegate: SimpleType get() = lowerBound
override val memberScope: MemberScope
get() {
val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor
?: error("Incorrect classifier: ${constructor.declarationDescriptor}")
return classDescriptor.getMemberScope(RawSubstitution)
}
override fun replaceAnnotations(newAnnotations: Annotations)
= RawTypeImpl(lowerBound.replaceAnnotations(newAnnotations), upperBound.replaceAnnotations(newAnnotations))
override fun makeNullableAsSpecified(newNullability: Boolean)
= RawTypeImpl(lowerBound.makeNullableAsSpecified(newNullability), upperBound.makeNullableAsSpecified(newNullability))
override fun render(renderer: DescriptorRenderer, options: DescriptorRendererOptions): String {
fun onlyOutDiffers(first: String, second: String) = first == second.removePrefix("out ") || second == "*"
fun renderArguments(type: KotlinType) = type.arguments.map { renderer.renderTypeProjection(it) }
fun String.replaceArgs(newArgs: String): String {
if (!contains('<')) return this
return "${substringBefore('<')}<$newArgs>${substringAfterLast('>')}"
}
val lowerRendered = renderer.renderType(lowerBound)
val upperRendered = renderer.renderType(upperBound)
if (options.debugMode) {
return "raw ($lowerRendered..$upperRendered)"
}
if (upperBound.arguments.isEmpty()) return renderer.renderFlexibleType(lowerRendered, upperRendered, builtIns)
val lowerArgs = renderArguments(lowerBound)
val upperArgs = renderArguments(upperBound)
val newArgs = lowerArgs.joinToString(", ") { "(raw) $it" }
val newUpper =
if (lowerArgs.zip(upperArgs).all { onlyOutDiffers(it.first, it.second) })
upperRendered.replaceArgs(newArgs)
else upperRendered
val newLower = lowerRendered.replaceArgs(newArgs)
if (newLower == newUpper) return newLower
return renderer.renderFlexibleType(newLower, newUpper, builtIns)
}
}
internal object RawSubstitution : TypeSubstitution() {
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
private val lowerTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND)
private val upperTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND)
private fun eraseType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor
return when (declaration) {
is TypeParameterDescriptor -> eraseType(declaration.getErasedUpperBound())
is ClassDescriptor -> {
val (lower, isRawL) = eraseInflexibleBasedOnClassDescriptor(type.lowerIfFlexible(), declaration, lowerTypeAttr)
val (upper, isRawU) = eraseInflexibleBasedOnClassDescriptor(type.upperIfFlexible(), declaration, upperTypeAttr)
if (isRawL || isRawU) {
RawTypeImpl(lower, upper)
}
else {
KotlinTypeFactory.flexibleType(lower, upper)
}
}
else -> error("Unexpected declaration kind: $declaration")
}
}
// false means that type cannot be raw
private fun eraseInflexibleBasedOnClassDescriptor(
type: SimpleType, declaration: ClassDescriptor, attr: JavaTypeAttributes
): Pair<SimpleType, Boolean> {
if (type.constructor.parameters.isEmpty()) return type to false
if (KotlinBuiltIns.isArray(type)) {
val componentTypeProjection = type.arguments[0]
val arguments = listOf(
TypeProjectionImpl(componentTypeProjection.projectionKind, eraseType(componentTypeProjection.type))
)
return KotlinTypeFactory.simpleType(
type.annotations, type.constructor, arguments, type.isMarkedNullable
) to false
}
if (type.isError) return ErrorUtils.createErrorType("Raw error type: ${type.constructor}") to false
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
type.annotations, type.constructor,
type.constructor.parameters.map {
parameter ->
computeProjection(parameter, attr)
},
type.isMarkedNullable, declaration.getMemberScope(RawSubstitution)
) to true
}
fun computeProjection(
parameter: TypeParameterDescriptor,
attr: JavaTypeAttributes,
erasedUpperBound: KotlinType = parameter.getErasedUpperBound()
) = when (attr.flexibility) {
// Raw(List<T>) => (List<Any?>..List<*>)
// Raw(Enum<T>) => (Enum<Enum<*>>..Enum<out Enum<*>>)
// In the last case upper bound is equal to star projection `Enum<*>`,
// but we want to keep matching tree structure of flexible bounds (at least they should have the same size)
JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND -> TypeProjectionImpl(
// T : String -> String
// in T : String -> String
// T : Enum<T> -> Enum<*>
Variance.INVARIANT, erasedUpperBound
)
JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND, JavaTypeFlexibility.INFLEXIBLE -> {
if (!parameter.variance.allowsOutPosition)
// in T -> Comparable<Nothing>
TypeProjectionImpl(Variance.INVARIANT, parameter.builtIns.nothingType)
else if (erasedUpperBound.constructor.parameters.isNotEmpty())
// T : Enum<E> -> out Enum<*>
TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound)
else
// T : String -> *
makeStarProjection(parameter, attr)
}
}
override fun isEmpty() = false
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2015 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.load.java
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getPropertyNameCandidatesBySpecialGetterName
fun propertyNameByGetMethodName(methodName: Name): Name?
= propertyNameFromAccessorMethodName(methodName, "get") ?: propertyNameFromAccessorMethodName(methodName, "is", removePrefix = false)
fun propertyNameBySetMethodName(methodName: Name, withIsPrefix: Boolean): Name?
= propertyNameFromAccessorMethodName(methodName, "set", addPrefix = if (withIsPrefix) "is" else null)
fun propertyNamesBySetMethodName(methodName: Name)
= listOf(propertyNameBySetMethodName(methodName, false), propertyNameBySetMethodName(methodName, true)).filterNotNull()
private fun propertyNameFromAccessorMethodName(methodName: Name, prefix: String, removePrefix: Boolean = true, addPrefix: String? = null): Name? {
if (methodName.isSpecial) return null
val identifier = methodName.identifier
if (!identifier.startsWith(prefix)) return null
if (identifier.length == prefix.length) return null
if (identifier[prefix.length] in 'a'..'z') return null
if (addPrefix != null) {
assert(removePrefix)
return Name.identifier(addPrefix + identifier.removePrefix(prefix))
}
if (!removePrefix) return methodName
val name = identifier.removePrefix(prefix).decapitalizeSmart(asciiOnly = true)
if (!Name.isValidIdentifier(name)) return null
return Name.identifier(name)
}
fun getPropertyNamesCandidatesByAccessorName(name: Name): List<Name> {
val nameAsString = name.asString()
if (JvmAbi.isGetterName(nameAsString)) {
return listOfNotNull(propertyNameByGetMethodName(name))
}
if (JvmAbi.isSetterName(nameAsString)) {
return propertyNamesBySetMethodName(name)
}
return getPropertyNameCandidatesBySpecialGetterName(name)
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2015 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.load.java.sources
import org.jetbrains.kotlin.load.java.structure.JavaElement
import org.jetbrains.kotlin.descriptors.SourceElement
interface JavaSourceElementFactory {
fun source(javaElement: JavaElement): JavaSourceElement
}
interface JavaSourceElement: SourceElement {
val javaElement: JavaElement
}
@@ -0,0 +1,352 @@
/*
* Copyright 2010-2015 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.
*/
@file:JvmName("SpecialBuiltinMembers")
package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters
import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.load.kotlin.computeJvmSignature
import org.jetbrains.kotlin.load.kotlin.signatures
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES as BUILTIN_NAMES
private fun FqName.child(name: String): FqName = child(Name.identifier(name))
private fun FqNameUnsafe.childSafe(name: String): FqName = child(Name.identifier(name)).toSafe()
private data class NameAndSignature(val name: Name, val signature: String)
private fun String.method(name: String, parameters: String, returnType: String) =
NameAndSignature(
Name.identifier(name),
SignatureBuildingComponents.signature(this@method, "$name($parameters)$returnType"))
object BuiltinSpecialProperties {
val PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP = mapOf(
BUILTIN_NAMES._enum.childSafe("name") to Name.identifier("name"),
BUILTIN_NAMES._enum.childSafe("ordinal") to Name.identifier("ordinal"),
BUILTIN_NAMES.collection.child("size") to Name.identifier("size"),
BUILTIN_NAMES.map.child("size") to Name.identifier("size"),
BUILTIN_NAMES.charSequence.childSafe("length") to Name.identifier("length"),
BUILTIN_NAMES.map.child("keys") to Name.identifier("keySet"),
BUILTIN_NAMES.map.child("values") to Name.identifier("values"),
BUILTIN_NAMES.map.child("entries") to Name.identifier("entrySet")
)
private val GETTER_JVM_NAME_TO_PROPERTIES_SHORT_NAME_MAP: Map<Name, List<Name>> =
PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.entries
.map { Pair(it.key.shortName(), it.value) }
.groupBy({ it.second }, { it.first })
private val SPECIAL_FQ_NAMES = PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.keys
internal val SPECIAL_SHORT_NAMES = SPECIAL_FQ_NAMES.map(FqName::shortName).toSet()
fun hasBuiltinSpecialPropertyFqName(callableMemberDescriptor: CallableMemberDescriptor): Boolean {
if (callableMemberDescriptor.name !in SPECIAL_SHORT_NAMES) return false
return callableMemberDescriptor.hasBuiltinSpecialPropertyFqNameImpl()
}
private fun CallableMemberDescriptor.hasBuiltinSpecialPropertyFqNameImpl(): Boolean {
if (fqNameOrNull() in SPECIAL_FQ_NAMES && valueParameters.isEmpty()) return true
if (!KotlinBuiltIns.isBuiltIn(this)) return false
return overriddenDescriptors.any { hasBuiltinSpecialPropertyFqName(it) }
}
fun getPropertyNameCandidatesBySpecialGetterName(name1: Name): List<Name> =
GETTER_JVM_NAME_TO_PROPERTIES_SHORT_NAME_MAP[name1] ?: emptyList()
fun CallableMemberDescriptor.getBuiltinSpecialPropertyGetterName(): String? {
assert(KotlinBuiltIns.isBuiltIn(this)) { "This method is defined only for builtin members, but $this found" }
val descriptor = propertyIfAccessor.firstOverridden { hasBuiltinSpecialPropertyFqName(it) } ?: return null
return PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP[descriptor.fqNameSafe]?.asString()
}
}
object BuiltinMethodsWithSpecialGenericSignature {
private val ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES = setOf(
"containsAll", "removeAll", "retainAll"
).map { "java/util/Collection".method(it, "Ljava/util/Collection;", JvmPrimitiveType.BOOLEAN.desc) }
private val ERASED_COLLECTION_PARAMETER_SIGNATURES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.signature }
val ERASED_COLLECTION_PARAMETER_NAMES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.name.asString() }
enum class TypeSafeBarrierDescription(val defaultValue: Any?) {
NULL(null), INDEX(-1), FALSE(false),
MAP_GET_OR_DEFAULT(null) {
override fun checkParameter(index: Int) = index != 1
};
open fun checkParameter(index: Int) = true
}
private val GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP =
signatures {
mapOf(
javaUtil("Collection")
.method("contains", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE,
javaUtil("Collection")
.method("remove", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE,
javaUtil("Map")
.method("containsKey", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE,
javaUtil("Map")
.method("containsValue", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE,
javaUtil("Map")
.method("remove", "Ljava/lang/Object;Ljava/lang/Object;",
JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE,
javaUtil("Map")
.method("getOrDefault", "Ljava/lang/Object;Ljava/lang/Object;",
"Ljava/lang/Object;") to TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT,
javaUtil("Map")
.method("get", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL,
javaUtil("Map")
.method("remove", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL,
javaUtil("List")
.method("indexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX,
javaUtil("List")
.method("lastIndexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX
)
}
private val SIGNATURE_TO_DEFAULT_VALUES_MAP = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.mapKeys { it.key.signature }
private val ERASED_VALUE_PARAMETERS_SHORT_NAMES: Set<Name>
val ERASED_VALUE_PARAMETERS_SIGNATURES: Set<String>
init {
val allMethods = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.keys + ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES
ERASED_VALUE_PARAMETERS_SHORT_NAMES = allMethods.map { it.name }.toSet()
ERASED_VALUE_PARAMETERS_SIGNATURES = allMethods.map { it.signature }.toSet()
}
private val CallableMemberDescriptor.hasErasedValueParametersInJava: Boolean
get() = computeJvmSignature() in ERASED_VALUE_PARAMETERS_SIGNATURES
@JvmStatic
fun getOverriddenBuiltinFunctionWithErasedValueParametersInJava(
functionDescriptor: FunctionDescriptor
): FunctionDescriptor? {
if (!functionDescriptor.name.sameAsBuiltinMethodWithErasedValueParameters) return null
return functionDescriptor.firstOverridden { it.hasErasedValueParametersInJava } as FunctionDescriptor?
}
@JvmStatic
fun getDefaultValueForOverriddenBuiltinFunction(functionDescriptor: FunctionDescriptor): TypeSafeBarrierDescription? {
if (functionDescriptor.name !in ERASED_VALUE_PARAMETERS_SHORT_NAMES) return null
return functionDescriptor.firstOverridden {
it.computeJvmSignature() in SIGNATURE_TO_DEFAULT_VALUES_MAP.keys
}?.let { SIGNATURE_TO_DEFAULT_VALUES_MAP[it.computeJvmSignature()] }
}
val Name.sameAsBuiltinMethodWithErasedValueParameters: Boolean
get () = this in ERASED_VALUE_PARAMETERS_SHORT_NAMES
enum class SpecialSignatureInfo(val valueParametersSignature: String?, val isObjectReplacedWithTypeParameter: Boolean) {
ONE_COLLECTION_PARAMETER("Ljava/util/Collection<+Ljava/lang/Object;>;", false),
OBJECT_PARAMETER_NON_GENERIC(null, true),
OBJECT_PARAMETER_GENERIC("Ljava/lang/Object;", true)
}
fun CallableMemberDescriptor.isBuiltinWithSpecialDescriptorInJvm(): Boolean {
if (!KotlinBuiltIns.isBuiltIn(this)) return false
return getSpecialSignatureInfo()?.isObjectReplacedWithTypeParameter ?: false || doesOverrideBuiltinWithDifferentJvmName()
}
@JvmStatic
fun CallableMemberDescriptor.getSpecialSignatureInfo(): SpecialSignatureInfo? {
if (name !in ERASED_VALUE_PARAMETERS_SHORT_NAMES) return null
val builtinSignature = firstOverridden { it is FunctionDescriptor && it.hasErasedValueParametersInJava }?.computeJvmSignature()
?: return null
if (builtinSignature in ERASED_COLLECTION_PARAMETER_SIGNATURES) return SpecialSignatureInfo.ONE_COLLECTION_PARAMETER
val defaultValue = SIGNATURE_TO_DEFAULT_VALUES_MAP[builtinSignature]!!
return if (defaultValue == TypeSafeBarrierDescription.NULL)
// return type is some generic type as 'Map.get'
SpecialSignatureInfo.OBJECT_PARAMETER_GENERIC
else
SpecialSignatureInfo.OBJECT_PARAMETER_NON_GENERIC
}
}
object BuiltinMethodsWithDifferentJvmName {
// Note that signatures here are not real,
// e.g. 'java/lang/CharSequence.get(I)C' does not actually exist in JDK
// But it doesn't matter here, because signatures are only used to distinguish overloaded built-in definitions
private val REMOVE_AT_NAME_AND_SIGNATURE =
"java/util/List".method("removeAt", JvmPrimitiveType.INT.desc, "Ljava/lang/Object;")
private val NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP: Map<NameAndSignature, Name> = signatures {
mapOf(
javaLang("Number").method("toByte", "", JvmPrimitiveType.BYTE.desc) to Name.identifier("byteValue"),
javaLang("Number").method("toShort", "", JvmPrimitiveType.SHORT.desc) to Name.identifier("shortValue"),
javaLang("Number").method("toInt", "", JvmPrimitiveType.INT.desc) to Name.identifier("intValue"),
javaLang("Number").method("toLong", "", JvmPrimitiveType.LONG.desc) to Name.identifier("longValue"),
javaLang("Number").method("toFloat", "", JvmPrimitiveType.FLOAT.desc) to Name.identifier("floatValue"),
javaLang("Number").method("toDouble", "", JvmPrimitiveType.DOUBLE.desc) to Name.identifier("doubleValue"),
REMOVE_AT_NAME_AND_SIGNATURE to Name.identifier("remove"),
javaLang("CharSequence")
.method("get", JvmPrimitiveType.INT.desc, JvmPrimitiveType.CHAR.desc) to Name.identifier("charAt")
)
}
private val SIGNATURE_TO_JVM_REPRESENTATION_NAME: Map<String, Name> =
NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.mapKeys { it.key.signature }
val ORIGINAL_SHORT_NAMES: List<Name> = NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.keys.map { it.name }
private val JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP: Map<Name, List<Name>> =
NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.entries
.map { Pair(it.key.name, it.value) }
.groupBy({ it.second }, { it.first })
val Name.sameAsRenamedInJvmBuiltin: Boolean
get() = this in ORIGINAL_SHORT_NAMES
fun getJvmName(functionDescriptor: SimpleFunctionDescriptor): Name? {
return SIGNATURE_TO_JVM_REPRESENTATION_NAME[functionDescriptor.computeJvmSignature() ?: return null]
}
fun isBuiltinFunctionWithDifferentNameInJvm(functionDescriptor: SimpleFunctionDescriptor): Boolean {
return KotlinBuiltIns.isBuiltIn(functionDescriptor) && functionDescriptor.firstOverridden {
SIGNATURE_TO_JVM_REPRESENTATION_NAME.containsKey(functionDescriptor.computeJvmSignature())
} != null
}
fun getBuiltinFunctionNamesByJvmName(name: Name): List<Name> =
JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP[name] ?: emptyList()
val SimpleFunctionDescriptor.isRemoveAtByIndex: Boolean
get() = name.asString() == "removeAt" && computeJvmSignature() == REMOVE_AT_NAME_AND_SIGNATURE.signature
}
@Suppress("UNCHECKED_CAST")
fun <T : CallableMemberDescriptor> T.getOverriddenBuiltinWithDifferentJvmName(): T? {
if (name !in BuiltinMethodsWithDifferentJvmName.ORIGINAL_SHORT_NAMES
&& propertyIfAccessor.name !in BuiltinSpecialProperties.SPECIAL_SHORT_NAMES) return null
return when (this) {
is PropertyDescriptor, is PropertyAccessorDescriptor ->
firstOverridden { BuiltinSpecialProperties.hasBuiltinSpecialPropertyFqName(it.propertyIfAccessor) } as T?
is SimpleFunctionDescriptor ->
firstOverridden {
BuiltinMethodsWithDifferentJvmName.isBuiltinFunctionWithDifferentNameInJvm(it as SimpleFunctionDescriptor)
} as T?
else -> null
}
}
fun CallableMemberDescriptor.doesOverrideBuiltinWithDifferentJvmName(): Boolean = getOverriddenBuiltinWithDifferentJvmName() != null
@Suppress("UNCHECKED_CAST")
fun <T : CallableMemberDescriptor> T.getOverriddenSpecialBuiltin(): T? {
getOverriddenBuiltinWithDifferentJvmName()?.let { return it }
if (!name.sameAsBuiltinMethodWithErasedValueParameters) return null
return firstOverridden {
KotlinBuiltIns.isBuiltIn(it) && it.getSpecialSignatureInfo() != null
} as T?
}
// The subtle difference between getOverriddenBuiltinReflectingJvmDescriptor and getOverriddenSpecialBuiltin
// is that first one return descriptor reflecting JVM signature (JVM descriptor)
// E.g. it returns `contains(e: E): Boolean` instead of `contains(e: String): Boolean` for implementation of Collection<String>.contains
// Implementation differs by getting 'original' for collection methods with erased value parameters
// Also it ignores Collection<String>.containsAll overrides because they have the same JVM descriptor
@Suppress("UNCHECKED_CAST")
fun <T : CallableMemberDescriptor> T.getOverriddenBuiltinReflectingJvmDescriptor(): T? {
getOverriddenBuiltinWithDifferentJvmName()?.let { return it }
if (!name.sameAsBuiltinMethodWithErasedValueParameters) return null
return firstOverridden {
KotlinBuiltIns.isBuiltIn(it) && it.getSpecialSignatureInfo()?.isObjectReplacedWithTypeParameter ?: false
}?.original as T?
}
fun getJvmMethodNameIfSpecial(callableMemberDescriptor: CallableMemberDescriptor): String? {
val overriddenBuiltin = getOverriddenBuiltinThatAffectsJvmName(callableMemberDescriptor)?.propertyIfAccessor
?: return null
return when (overriddenBuiltin) {
is PropertyDescriptor -> overriddenBuiltin.getBuiltinSpecialPropertyGetterName()
is SimpleFunctionDescriptor -> BuiltinMethodsWithDifferentJvmName.getJvmName(overriddenBuiltin)?.asString()
else -> null
}
}
private fun getOverriddenBuiltinThatAffectsJvmName(
callableMemberDescriptor: CallableMemberDescriptor
): CallableMemberDescriptor? =
if (KotlinBuiltIns.isBuiltIn(callableMemberDescriptor)) callableMemberDescriptor.getOverriddenBuiltinWithDifferentJvmName()
else null
fun ClassDescriptor.hasRealKotlinSuperClassWithOverrideOf(
specialCallableDescriptor: CallableDescriptor
): Boolean {
val builtinContainerDefaultType = (specialCallableDescriptor.containingDeclaration as ClassDescriptor).defaultType
var superClassDescriptor = DescriptorUtils.getSuperClassDescriptor(this)
while (superClassDescriptor != null) {
if (superClassDescriptor !is JavaClassDescriptor) {
// Kotlin class
val doesOverrideBuiltinDeclaration =
TypeCheckingProcedure.findCorrespondingSupertype(superClassDescriptor.defaultType, builtinContainerDefaultType) != null
if (doesOverrideBuiltinDeclaration) {
return !KotlinBuiltIns.isBuiltIn(superClassDescriptor)
}
}
superClassDescriptor = DescriptorUtils.getSuperClassDescriptor(superClassDescriptor)
}
return false
}
// Util methods
val CallableMemberDescriptor.isFromJava: Boolean
get() = propertyIfAccessor.let { descriptor ->
(descriptor as? JavaCallableMemberDescriptor)?.containingDeclaration is JavaClassDescriptor
}
fun CallableMemberDescriptor.isFromJavaOrBuiltins() = isFromJava || KotlinBuiltIns.isBuiltIn(this)
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2015 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.load.java.structure
import org.jetbrains.kotlin.name.Name
interface JavaAnnotationArgument {
val name: Name?
}
interface JavaLiteralAnnotationArgument : JavaAnnotationArgument {
val value: Any?
}
interface JavaArrayAnnotationArgument : JavaAnnotationArgument {
fun getElements(): List<JavaAnnotationArgument>
}
interface JavaEnumValueAnnotationArgument : JavaAnnotationArgument {
val entryName: Name?
fun resolve(): JavaField?
}
interface JavaClassObjectAnnotationArgument : JavaAnnotationArgument {
fun getReferencedType(): JavaType
}
interface JavaAnnotationAsAnnotationArgument : JavaAnnotationArgument {
fun getAnnotation(): JavaAnnotation
}
@@ -0,0 +1,127 @@
/*
* Copyright 2010-2016 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.load.java.structure
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
interface JavaElement
interface JavaNamedElement : JavaElement {
val name: Name
}
interface JavaAnnotationOwner : JavaElement {
val annotations: Collection<JavaAnnotation>
fun findAnnotation(fqName: FqName): JavaAnnotation?
val isDeprecatedInJavaDoc: Boolean
}
interface JavaModifierListOwner : JavaElement {
val isAbstract: Boolean
val isStatic: Boolean
val isFinal: Boolean
val visibility: Visibility
}
interface JavaTypeParameterListOwner : JavaElement {
val typeParameters: List<JavaTypeParameter>
}
interface JavaAnnotation : JavaElement {
val arguments: Collection<JavaAnnotationArgument>
val classId: ClassId?
fun resolve(): JavaClass?
}
interface MapBasedJavaAnnotationOwner : JavaAnnotationOwner {
val annotationsByFqName: Map<FqName?, JavaAnnotation>
override fun findAnnotation(fqName: FqName) = annotationsByFqName[fqName]
override val isDeprecatedInJavaDoc: Boolean
get() = false
}
fun JavaAnnotationOwner.buildLazyValueForMap() = lazy {
annotations.associateBy { it.classId?.asSingleFqName() }
}
interface JavaPackage : JavaElement, JavaAnnotationOwner {
val fqName: FqName
val subPackages: Collection<JavaPackage>
fun getClasses(nameFilter: (Name) -> Boolean): Collection<JavaClass>
}
interface JavaClassifier : JavaNamedElement, JavaAnnotationOwner
interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierListOwner {
val fqName: FqName?
val supertypes: Collection<JavaClassifierType>
val innerClassNames: Collection<Name>
fun findInnerClass(name: Name): JavaClass?
val outerClass: JavaClass?
val isInterface: Boolean
val isAnnotationType: Boolean
val isEnum: Boolean
val lightClassOriginKind: LightClassOriginKind?
val methods: Collection<JavaMethod>
val fields: Collection<JavaField>
val constructors: Collection<JavaConstructor>
}
enum class LightClassOriginKind {
SOURCE, BINARY
}
interface JavaMember : JavaModifierListOwner, JavaAnnotationOwner, JavaNamedElement {
val containingClass: JavaClass
}
interface JavaMethod : JavaMember, JavaTypeParameterListOwner {
val valueParameters: List<JavaValueParameter>
val returnType: JavaType
val hasAnnotationParameterDefaultValue: Boolean
}
interface JavaField : JavaMember {
val isEnumEntry: Boolean
val type: JavaType
val initializerValue: Any?
val hasConstantNotNullInitializer: Boolean
}
interface JavaConstructor : JavaMember, JavaTypeParameterListOwner {
val valueParameters: List<JavaValueParameter>
}
interface JavaValueParameter : JavaAnnotationOwner {
val name: Name?
val type: JavaType
val isVararg: Boolean
}
interface JavaTypeParameter : JavaClassifier {
val upperBounds: Collection<JavaClassifierType>
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2016 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.load.java.structure
import org.jetbrains.kotlin.builtins.PrimitiveType
interface JavaType
interface JavaArrayType : JavaType {
val componentType: JavaType
}
interface JavaClassifierType : JavaType, JavaAnnotationOwner {
val classifier: JavaClassifier?
val typeArguments: List<JavaType>
val isRaw: Boolean
val classifierQualifiedName: String
val presentableText: String
}
interface JavaPrimitiveType : JavaType {
/** `null` means the `void` type. */
val type: PrimitiveType?
}
interface JavaWildcardType : JavaType {
val bound: JavaType?
val isExtends: Boolean
}
@@ -0,0 +1,245 @@
/*
* Copyright 2010-2016 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.load.java.typeEnhancement
import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.load.kotlin.signatures
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType.BOOLEAN
class TypeEnhancementInfo(val map: Map<Int, JavaTypeQualifiers>) {
constructor(vararg pairs: Pair<Int, JavaTypeQualifiers>) : this(mapOf(*pairs))
}
class PredefinedFunctionEnhancementInfo(
val returnTypeInfo: TypeEnhancementInfo? = null,
val parametersInfo: List<TypeEnhancementInfo?> = emptyList()
)
/** Type is always nullable: `T?` */
private val NULLABLE = JavaTypeQualifiers(NullabilityQualifier.NULLABLE, null, isNotNullTypeParameter = false)
/** Nullability depends on substitution, but the type is not platform: `T` */
private val NOT_PLATFORM = JavaTypeQualifiers(NullabilityQualifier.NOT_NULL, null, isNotNullTypeParameter = false)
/** Type is always non-nullable: `T & Any` */
private val NOT_NULLABLE = JavaTypeQualifiers(NullabilityQualifier.NOT_NULL, null, isNotNullTypeParameter = true)
val PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE = signatures {
val JLObject = javaLang("Object")
val JFPredicate = javaFunction("Predicate")
val JFFunction = javaFunction("Function")
val JFConsumer = javaFunction("Consumer")
val JFBiFunction = javaFunction("BiFunction")
val JFBiConsumer = javaFunction("BiConsumer")
val JFUnaryOperator = javaFunction("UnaryOperator")
val JUStream = javaUtil("stream/Stream")
val JUOptional = javaUtil("Optional")
enhancement {
forClass(javaUtil("Iterator")) {
function("forEachRemaining") {
parameter(JFConsumer, NOT_PLATFORM, NOT_PLATFORM)
}
}
forClass(javaLang("Iterable")) {
function("spliterator") {
returns(javaUtil("Spliterator"), NOT_PLATFORM, NOT_PLATFORM)
}
}
forClass(javaUtil("Collection")) {
function("removeIf") {
parameter(JFPredicate, NOT_PLATFORM, NOT_PLATFORM)
returns(BOOLEAN)
}
function("stream") {
returns(JUStream, NOT_PLATFORM, NOT_PLATFORM)
}
function("parallelStream") {
returns(JUStream, NOT_PLATFORM, NOT_PLATFORM)
}
}
forClass(javaUtil("List")) {
function("replaceAll") {
parameter(JFUnaryOperator, NOT_PLATFORM, NOT_PLATFORM)
}
}
forClass(javaUtil("Map")) {
function("forEach") {
parameter(JFBiConsumer, NOT_PLATFORM, NOT_PLATFORM, NOT_PLATFORM)
}
function("putIfAbsent") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
returns(JLObject, NULLABLE)
}
function("replace") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
returns(JLObject, NULLABLE)
}
function("replace") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
returns(BOOLEAN)
}
function("replaceAll") {
parameter(JFBiFunction, NOT_PLATFORM, NOT_PLATFORM, NOT_PLATFORM, NOT_PLATFORM)
}
function("compute") {
parameter(JLObject, NOT_PLATFORM)
parameter(JFBiFunction, NOT_PLATFORM, NOT_PLATFORM, NULLABLE, NULLABLE)
returns(JLObject, NULLABLE)
}
// while it is possible to return nullable value from lambda in computeIfAbsent,
// we deliberately make it just NOT_PLATFORM V in order to have the return type V and not V?
function("computeIfAbsent") {
parameter(JLObject, NOT_PLATFORM)
parameter(JFFunction, NOT_PLATFORM, NOT_PLATFORM, NOT_PLATFORM)
returns(JLObject, NOT_PLATFORM)
}
function("computeIfPresent") {
parameter(JLObject, NOT_PLATFORM)
parameter(JFBiFunction, NOT_PLATFORM, NOT_PLATFORM, NOT_NULLABLE, NULLABLE)
returns(JLObject, NULLABLE)
}
function("merge") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_NULLABLE)
parameter(JFBiFunction, NOT_PLATFORM, NOT_NULLABLE, NOT_NULLABLE, NULLABLE)
returns(JLObject, NULLABLE)
}
}
forClass(JUOptional) {
function("empty") {
returns(JUOptional, NOT_PLATFORM, NOT_NULLABLE)
}
function("of") {
parameter(JLObject, NOT_NULLABLE)
returns(JUOptional, NOT_PLATFORM, NOT_NULLABLE)
}
function("ofNullable") {
parameter(JLObject, NULLABLE)
returns(JUOptional, NOT_PLATFORM, NOT_NULLABLE)
}
function("get") {
returns(JLObject, NOT_NULLABLE)
}
function("ifPresent") {
parameter(JFConsumer, NOT_PLATFORM, NOT_NULLABLE)
}
}
forClass(javaLang("ref/Reference")) {
function("get") {
returns(JLObject, NULLABLE)
}
}
forClass(JFPredicate) {
function("test") {
parameter(JLObject, NOT_PLATFORM)
returns(BOOLEAN)
}
}
forClass(javaFunction("BiPredicate")) {
function("test") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
returns(BOOLEAN)
}
}
forClass(JFConsumer) {
function("accept") {
parameter(JLObject, NOT_PLATFORM)
}
}
forClass(JFBiConsumer) {
function("accept") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
}
}
forClass(JFFunction) {
function("apply") {
parameter(JLObject, NOT_PLATFORM)
returns(JLObject, NOT_PLATFORM)
}
}
forClass(JFBiFunction) {
function("apply") {
parameter(JLObject, NOT_PLATFORM)
parameter(JLObject, NOT_PLATFORM)
returns(JLObject, NOT_PLATFORM)
}
}
forClass(javaFunction("Supplier")) {
function("get") {
returns(JLObject, NOT_PLATFORM)
}
}
}
}
private inline fun enhancement(block: SignatureEnhancementBuilder.() -> Unit): Map<String, PredefinedFunctionEnhancementInfo>
= SignatureEnhancementBuilder().apply(block).build()
private class SignatureEnhancementBuilder {
private val signatures = mutableMapOf<String, PredefinedFunctionEnhancementInfo>()
inline fun forClass(internalName: String, block: ClassEnhancementBuilder.() -> Unit) =
ClassEnhancementBuilder(internalName).block()
inner class ClassEnhancementBuilder(val className: String) {
fun function(name: String, block: FunctionEnhancementBuilder.() -> Unit) {
signatures += FunctionEnhancementBuilder(name).apply(block).build()
}
inner class FunctionEnhancementBuilder(val functionName: String) {
private val parameters = mutableListOf<Pair<String, TypeEnhancementInfo?>>()
private var returnType: Pair<String, TypeEnhancementInfo?> = "V" to null
fun parameter(type: String, vararg pairs: Pair<Int, JavaTypeQualifiers>) {
parameters += type to
if (pairs.isEmpty()) null else TypeEnhancementInfo(*pairs)
}
fun parameter(type: String, vararg qualifiers: JavaTypeQualifiers) {
parameters += type to
if (qualifiers.isEmpty()) null else TypeEnhancementInfo(qualifiers.withIndex().associateBy({it.index}, {it.value}))
}
fun returns(type: String, vararg pairs: Pair<Int, JavaTypeQualifiers>) {
returnType = type to TypeEnhancementInfo(*pairs)
}
fun returns(type: String, vararg qualifiers: JavaTypeQualifiers) {
returnType = type to TypeEnhancementInfo(qualifiers.withIndex().associateBy({it.index}, {it.value}))
}
fun returns(type: JvmPrimitiveType) {
returnType = type.desc to null
}
fun build() = with (SignatureBuildingComponents) {
signature(className, jvmDescriptor(functionName, parameters.map { it.first }, returnType.first)) to
PredefinedFunctionEnhancementInfo(returnType.second, parameters.map { it.second })
}
}
}
fun build(): Map<String, PredefinedFunctionEnhancementInfo> = signatures
}
@@ -0,0 +1,449 @@
/*
* 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.load.java.typeEnhancement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.load.java.descriptors.*
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.copyWithNewDefaultTypeQualifiers
import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField
import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgumentValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.asFlexibleType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.unwrapEnhancement
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
data class NullabilityQualifierWithMigrationStatus(
val qualifier: NullabilityQualifier,
val isForWarningOnly: Boolean = false
)
class SignatureEnhancement(private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver) {
private fun AnnotationDescriptor.extractNullabilityTypeFromArgument(): NullabilityQualifierWithMigrationStatus? {
val enumEntryDescriptor = firstArgumentValue()
// if no argument is specified, use default value: NOT_NULL
?: return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
if (enumEntryDescriptor !is ClassDescriptor) return null
return when (enumEntryDescriptor.name.asString()) {
"ALWAYS" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
"MAYBE", "NEVER" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
"UNKNOWN" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.FORCE_FLEXIBILITY)
else -> null
}
}
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithMigrationStatus? {
extractNullabilityFromKnownAnnotations(annotationDescriptor)?.let { return it }
val typeQualifierAnnotation =
annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
?: return null
val jsr305State = annotationTypeQualifierResolver.resolveJsr305AnnotationState(annotationDescriptor)
if (jsr305State.isIgnore) return null
return extractNullabilityFromKnownAnnotations(typeQualifierAnnotation)?.copy(isForWarningOnly = jsr305State.isWarning)
}
private fun extractNullabilityFromKnownAnnotations(
annotationDescriptor: AnnotationDescriptor
): NullabilityQualifierWithMigrationStatus? {
val annotationFqName = annotationDescriptor.fqName ?: return null
return when (annotationFqName) {
in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
else -> null
}
}
fun <D : CallableMemberDescriptor> enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection<D>): Collection<D> {
return platformSignatures.map {
it.enhanceSignature(c)
}
}
private fun <D : CallableMemberDescriptor> D.enhanceSignature(c: LazyJavaResolverContext): D {
// TODO type parameters
// TODO use new type parameters while enhancing other types
// TODO Propagation into generic type arguments
if (this !is JavaCallableMemberDescriptor) return this
// Fake overrides with one overridden has been enhanced before
if (kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE && original.overriddenDescriptors.size == 1) return this
val memberContext = c.copyWithNewDefaultTypeQualifiers(annotations)
// When loading method as an override for a property, all annotations are stick to its getter
val annotationOwnerForMember =
if (this is JavaPropertyDescriptor && getter?.isDefault == false)
getter!!
else
this
val receiverTypeEnhancement =
if (extensionReceiverParameter != null)
partsForValueParameter(
parameterDescriptor =
annotationOwnerForMember.safeAs<FunctionDescriptor>()
?.getUserData(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER),
methodContext = memberContext
) { it.extensionReceiverParameter!!.type }.enhance()
else null
val predefinedEnhancementInfo =
(this as? JavaMethodDescriptor)
?.run { SignatureBuildingComponents.signature(this.containingDeclaration as ClassDescriptor, this.computeJvmDescriptor()) }
?.let { signature -> PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE[signature] }
predefinedEnhancementInfo?.let {
assert(it.parametersInfo.size == valueParameters.size) {
"Predefined enhancement info for $this has ${it.parametersInfo.size}, but ${valueParameters.size} expected"
}
}
val valueParameterEnhancements = annotationOwnerForMember.valueParameters.map {
p ->
val enhancementResult =partsForValueParameter(p, memberContext) { it.valueParameters[p.index].type }
.enhance(predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index))
val actualType = if (enhancementResult.wereChanges) enhancementResult.type else p.type
val hasDefaultValue = p.hasDefaultValueInAnnotation(actualType)
val wereChanges = enhancementResult.wereChanges || (hasDefaultValue != p.declaresDefaultValue())
ValueParameterEnhancementResult(enhancementResult.type, hasDefaultValue, wereChanges)
}
val returnTypeEnhancement =
parts(
typeContainer = annotationOwnerForMember, isCovariant = true,
containerContext = memberContext,
containerApplicabilityType =
if (this.safeAs<PropertyDescriptor>()?.isJavaField == true)
AnnotationTypeQualifierResolver.QualifierApplicabilityType.FIELD
else
AnnotationTypeQualifierResolver.QualifierApplicabilityType.METHOD_RETURN_TYPE
) { it.returnType!! }.enhance(predefinedEnhancementInfo?.returnTypeInfo)
if ((receiverTypeEnhancement?.wereChanges == true)
|| returnTypeEnhancement.wereChanges || valueParameterEnhancements.any { it.wereChanges }) {
@Suppress("UNCHECKED_CAST")
return this.enhance(receiverTypeEnhancement?.type,
valueParameterEnhancements.map { ValueParameterData(it.type, it.hasDefaultValue) }, returnTypeEnhancement.type) as D
}
return this
}
private fun ValueParameterDescriptor.hasDefaultValueInAnnotation(type: KotlinType): Boolean {
val defaultValue = getDefaultValueFromAnnotation()
return when (defaultValue) {
is StringDefaultValue -> type.lexicalCastFrom(defaultValue.value) != null
NullDefaultValue -> TypeUtils.acceptsNullable(type)
null -> declaresDefaultValue()
} && overriddenDescriptors.isEmpty()
}
private inner class SignatureParts(
private val typeContainer: Annotated?,
private val fromOverride: KotlinType,
private val fromOverridden: Collection<KotlinType>,
private val isCovariant: Boolean,
private val containerContext: LazyJavaResolverContext,
private val containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType
) {
fun enhance(predefined: TypeEnhancementInfo? = null): PartEnhancementResult {
val qualifiers = computeIndexedQualifiersForOverride()
val qualifiersWithPredefined: ((Int) -> JavaTypeQualifiers)? = predefined?.let {
{ index ->
predefined.map[index] ?: qualifiers(index)
}
}
return fromOverride.enhance(qualifiersWithPredefined ?: qualifiers)?.let { enhanced ->
PartEnhancementResult(enhanced, wereChanges = true)
} ?: PartEnhancementResult(fromOverride, wereChanges = false)
}
private fun KotlinType.extractQualifiers(): JavaTypeQualifiers {
val (lower, upper) =
if (this.isFlexible())
asFlexibleType().let { Pair(it.lowerBound, it.upperBound) }
else Pair(this, this)
val mapping = JavaToKotlinClassMap
return JavaTypeQualifiers(
when {
lower.isMarkedNullable -> NullabilityQualifier.NULLABLE
!upper.isMarkedNullable -> NullabilityQualifier.NOT_NULL
else -> null
},
when {
mapping.isReadOnly(lower) -> MutabilityQualifier.READ_ONLY
mapping.isMutable(upper) -> MutabilityQualifier.MUTABLE
else -> null
},
isNotNullTypeParameter = unwrap() is NotNullTypeParameter)
}
private fun KotlinType.extractQualifiersFromAnnotations(
isHeadTypeConstructor: Boolean,
defaultQualifiersForType: JavaTypeQualifiers?
): JavaTypeQualifiers {
val composedAnnotation =
if (isHeadTypeConstructor && typeContainer != null)
composeAnnotations(typeContainer.annotations, annotations)
else
annotations
fun <T : Any> List<FqName>.ifPresent(qualifier: T) =
if (any { composedAnnotation.findAnnotation(it) != null }) qualifier else null
fun <T : Any> uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null
val defaultTypeQualifier =
if (isHeadTypeConstructor)
containerContext.defaultTypeQualifiers?.get(containerApplicabilityType)
else
defaultQualifiersForType
val nullabilityInfo =
composedAnnotation.extractNullability()
?: defaultTypeQualifier?.nullability?.let {
NullabilityQualifierWithMigrationStatus(
defaultTypeQualifier.nullability,
defaultTypeQualifier.isNullabilityQualifierForWarning
)
}
return JavaTypeQualifiers(
nullabilityInfo?.qualifier,
uniqueNotNull(
READ_ONLY_ANNOTATIONS.ifPresent(
MutabilityQualifier.READ_ONLY
),
MUTABLE_ANNOTATIONS.ifPresent(
MutabilityQualifier.MUTABLE
)
),
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL && isTypeParameter(),
isNullabilityQualifierForWarning = nullabilityInfo?.isForWarningOnly == true
)
}
private fun Annotations.extractNullability(): NullabilityQualifierWithMigrationStatus? =
this.firstNotNullResult(this@SignatureEnhancement::extractNullability)
private fun computeIndexedQualifiersForOverride(): (Int) -> JavaTypeQualifiers {
val indexedFromSupertypes = fromOverridden.map { it.toIndexed() }
val indexedThisType = fromOverride.toIndexed()
// The covariant case may be hard, e.g. in the superclass the return may be Super<T>, but in the subclass it may be Derived, which
// is declared to extend Super<T>, and propagating data here is highly non-trivial, so we only look at the head type constructor
// (outermost type), unless the type in the subclass is interchangeable with the all the types in superclasses:
// e.g. we have (Mutable)List<String!>! in the subclass and { List<String!>, (Mutable)List<String>! } from superclasses
// Note that `this` is flexible here, so it's equal to it's bounds
val onlyHeadTypeConstructor = isCovariant && fromOverridden.any { !KotlinTypeChecker.DEFAULT.equalTypes(it, fromOverride) }
val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size
val computedResult = Array(treeSize) { index ->
val isHeadTypeConstructor = index == 0
assert(isHeadTypeConstructor || !onlyHeadTypeConstructor) { "Only head type constructors should be computed" }
val (qualifiers, defaultQualifiers) = indexedThisType[index]
val verticalSlice = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type }
// Only the head type constructor is safely co-variant
qualifiers.computeQualifiersForOverride(verticalSlice, defaultQualifiers, isHeadTypeConstructor)
}
return { index -> computedResult.getOrElse(index) { JavaTypeQualifiers.NONE } }
}
private fun KotlinType.toIndexed(): List<TypeAndDefaultQualifiers> {
val list = ArrayList<TypeAndDefaultQualifiers>(1)
fun add(type: KotlinType, ownerContext: LazyJavaResolverContext) {
val c = ownerContext.copyWithNewDefaultTypeQualifiers(type.annotations)
list.add(
TypeAndDefaultQualifiers(
type,
c.defaultTypeQualifiers
?.get(AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE)
)
)
for (arg in type.arguments) {
if (arg.isStarProjection) {
// TODO: sort out how to handle wildcards
list.add(TypeAndDefaultQualifiers(arg.type, null))
}
else {
add(arg.type, c)
}
}
}
add(this, containerContext)
return list
}
private fun KotlinType.computeQualifiersForOverride(
fromSupertypes: Collection<KotlinType>,
defaultQualifiersForType: JavaTypeQualifiers?,
isHeadTypeConstructor: Boolean
): JavaTypeQualifiers {
val superQualifiers = fromSupertypes.map { it.extractQualifiers() }
val mutabilityFromSupertypes = superQualifiers.mapNotNull { it.mutability }.toSet()
val nullabilityFromSupertypes = superQualifiers.mapNotNull { it.nullability }.toSet()
val nullabilityFromSupertypesWithWarning = fromSupertypes
.mapNotNull { it.unwrapEnhancement().extractQualifiers().nullability }
.toSet()
val own = extractQualifiersFromAnnotations(isHeadTypeConstructor, defaultQualifiersForType)
val ownNullability = own.takeIf { !it.isNullabilityQualifierForWarning }?.nullability
val ownNullabilityForWarning = own.nullability
val isCovariantPosition = isCovariant && isHeadTypeConstructor
val nullability = nullabilityFromSupertypes.select(ownNullability, isCovariantPosition)
val mutability =
mutabilityFromSupertypes
.select(MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability, isCovariantPosition)
val canChange = ownNullabilityForWarning != ownNullability || nullabilityFromSupertypesWithWarning != nullabilityFromSupertypes
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || superQualifiers.any { it.isNotNullTypeParameter }
if (nullability == null && canChange) {
val nullabilityWithWarning =
nullabilityFromSupertypesWithWarning.select(ownNullabilityForWarning, isCovariantPosition)
return createJavaTypeQualifiers(
nullabilityWithWarning, mutability,
forWarning = true, isAnyNonNullTypeParameter = isAnyNonNullTypeParameter
)
}
return createJavaTypeQualifiers(
nullability, mutability,
forWarning = nullability == null,
isAnyNonNullTypeParameter = isAnyNonNullTypeParameter
)
}
}
private open class PartEnhancementResult(val type: KotlinType, val wereChanges: Boolean)
private class ValueParameterEnhancementResult(
type: KotlinType,
val hasDefaultValue: Boolean,
wereChanges: Boolean
) : PartEnhancementResult(type, wereChanges)
private fun CallableMemberDescriptor.partsForValueParameter(
// TODO: investigate if it's really can be a null (check properties' with extension overrides in Java)
parameterDescriptor: ValueParameterDescriptor?,
methodContext: LazyJavaResolverContext,
collector: (CallableMemberDescriptor) -> KotlinType
) = parts(
parameterDescriptor, false,
parameterDescriptor?.let { methodContext.copyWithNewDefaultTypeQualifiers(it.annotations) } ?: methodContext,
AnnotationTypeQualifierResolver.QualifierApplicabilityType.VALUE_PARAMETER,
collector
)
private fun CallableMemberDescriptor.parts(
typeContainer: Annotated?,
isCovariant: Boolean,
containerContext: LazyJavaResolverContext,
containerApplicabilityType: AnnotationTypeQualifierResolver.QualifierApplicabilityType,
collector: (CallableMemberDescriptor) -> KotlinType
): SignatureParts {
return SignatureParts(
typeContainer,
collector(this),
this.overriddenDescriptors.map {
collector(it)
},
isCovariant,
// recompute default type qualifiers using type annotations
containerContext.copyWithNewDefaultTypeQualifiers(collector(this).annotations),
containerApplicabilityType
)
}
}
private fun createJavaTypeQualifiers(
nullability: NullabilityQualifier?,
mutability: MutabilityQualifier?,
forWarning: Boolean,
isAnyNonNullTypeParameter: Boolean
): JavaTypeQualifiers {
if (!isAnyNonNullTypeParameter || nullability != NullabilityQualifier.NOT_NULL) {
return JavaTypeQualifiers(nullability, mutability, false, forWarning)
}
return JavaTypeQualifiers(nullability, mutability, true, forWarning)
}
private fun <T : Any> Set<T>.select(low: T, high: T, own: T?, isCovariant: Boolean): T? {
if (isCovariant) {
val supertypeQualifier = if (low in this) low else if (high in this) high else null
return if (supertypeQualifier == low && own == high) null else own ?: supertypeQualifier
}
// isInvariant
val effectiveSet = own?.let { (this + own).toSet() } ?: this
// if this set contains exactly one element, it is the qualifier everybody agrees upon,
// otherwise (no qualifiers, or multiple qualifiers), there's no single such qualifier
// and all qualifiers are discarded
return effectiveSet.singleOrNull()
}
private fun Set<NullabilityQualifier>.select(own: NullabilityQualifier?, isCovariant: Boolean) =
if (own == NullabilityQualifier.FORCE_FLEXIBILITY)
NullabilityQualifier.FORCE_FLEXIBILITY
else
select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, own, isCovariant)
private data class TypeAndDefaultQualifiers(
val type: KotlinType,
val defaultQualifiers: JavaTypeQualifiers?
)
@@ -0,0 +1,250 @@
/*
* Copyright 2010-2016 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.load.java.typeEnhancement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.READ_ONLY
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier.NOT_NULL
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier.NULLABLE
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
// The index in the lambda is the position of the type component:
// Example: for `A<B, C<D, E>>`, indices go as follows: `0 - A<...>, 1 - B, 2 - C<D, E>, 3 - D, 4 - E`,
// which corresponds to the left-to-right breadth-first walk of the tree representation of the type.
// For flexible types, both bounds are indexed in the same way: `(A<B>..C<D>)` gives `0 - (A<B>..C<D>), 1 - B and D`.
fun KotlinType.enhance(qualifiers: (Int) -> JavaTypeQualifiers) = unwrap().enhancePossiblyFlexible(qualifiers, 0).typeIfChanged
fun KotlinType.hasEnhancedNullability()
= annotations.findAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) != null
private enum class TypeComponentPosition {
FLEXIBLE_LOWER,
FLEXIBLE_UPPER,
INFLEXIBLE
}
private open class Result(open val type: KotlinType, val subtreeSize: Int, val wereChanges: Boolean) {
val typeIfChanged: KotlinType? get() = type.takeIf { wereChanges }
}
private class SimpleResult(override val type: SimpleType, subtreeSize: Int, wereChanges: Boolean): Result(type, subtreeSize, wereChanges)
private fun UnwrappedType.enhancePossiblyFlexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int): Result {
if (isError) return Result(this, 1, false)
return when(this) {
is FlexibleType -> {
val lowerResult = lowerBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_LOWER)
val upperResult = upperBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_UPPER)
assert(lowerResult.subtreeSize == upperResult.subtreeSize) {
"Different tree sizes of bounds: " +
"lower = ($lowerBound, ${lowerResult.subtreeSize}), " +
"upper = ($upperBound, ${upperResult.subtreeSize})"
}
val wereChanges = lowerResult.wereChanges || upperResult.wereChanges
val enhancement = lowerResult.type.getEnhancement() ?: upperResult.type.getEnhancement()
val type = if (!wereChanges) this@enhancePossiblyFlexible
else when {
this is RawTypeImpl -> RawTypeImpl(lowerResult.type, upperResult.type)
else -> KotlinTypeFactory.flexibleType(lowerResult.type, upperResult.type)
}.wrapEnhancement(enhancement)
Result(
type,
lowerResult.subtreeSize,
wereChanges
)
}
is SimpleType -> enhanceInflexible(qualifiers, index, TypeComponentPosition.INFLEXIBLE)
}
}
private fun SimpleType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int, position: TypeComponentPosition): SimpleResult {
val shouldEnhance = position.shouldEnhance()
if (!shouldEnhance && arguments.isEmpty()) return SimpleResult(this, 1, false)
val originalClass = constructor.declarationDescriptor
?: return SimpleResult(this, 1, false)
val effectiveQualifiers = qualifiers(index)
val (enhancedClassifier, enhancedMutabilityAnnotations) = originalClass.enhanceMutability(effectiveQualifiers, position)
val typeConstructor = enhancedClassifier.typeConstructor
var globalArgIndex = index + 1
var wereChanges = enhancedMutabilityAnnotations != null
val enhancedArguments = arguments.mapIndexed {
localArgIndex, arg ->
if (arg.isStarProjection) {
globalArgIndex++
TypeUtils.makeStarProjection(enhancedClassifier.typeConstructor.parameters[localArgIndex])
}
else {
val enhanced = arg.type.unwrap().enhancePossiblyFlexible(qualifiers, globalArgIndex)
wereChanges = wereChanges || enhanced.wereChanges
globalArgIndex += enhanced.subtreeSize
createProjection(enhanced.type, arg.projectionKind, typeParameterDescriptor = typeConstructor.parameters[localArgIndex])
}
}
val (enhancedNullability, enhancedNullabilityAnnotations) = this.getEnhancedNullability(effectiveQualifiers, position)
wereChanges = wereChanges || enhancedNullabilityAnnotations != null
val subtreeSize = globalArgIndex - index
if (!wereChanges) return SimpleResult(this, subtreeSize, wereChanges = false)
val newAnnotations = listOf(
annotations,
enhancedMutabilityAnnotations,
enhancedNullabilityAnnotations
).filterNotNull().compositeAnnotationsOrSingle()
val enhancedType = KotlinTypeFactory.simpleType(
newAnnotations,
typeConstructor,
enhancedArguments,
enhancedNullability
)
val enhancement = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
val nullabilityForWarning = enhancedNullabilityAnnotations != null && effectiveQualifiers.isNullabilityQualifierForWarning
val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement
return SimpleResult(result as SimpleType, subtreeSize, wereChanges = true)
}
private fun List<Annotations>.compositeAnnotationsOrSingle() = when (size) {
0 -> error("At least one Annotations object expected")
1 -> single()
else -> CompositeAnnotations(this.toList())
}
private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE
private data class EnhancementResult<out T>(val result: T, val enhancementAnnotations: Annotations?)
private fun <T> T.noChange() = EnhancementResult(this, null)
private fun <T> T.enhancedNullability() = EnhancementResult(this, ENHANCED_NULLABILITY_ANNOTATIONS)
private fun <T> T.enhancedMutability() = EnhancementResult(this, ENHANCED_MUTABILITY_ANNOTATIONS)
private fun ClassifierDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): EnhancementResult<ClassifierDescriptor> {
if (!position.shouldEnhance()) return this.noChange()
if (this !is ClassDescriptor) return this.noChange() // mutability is not applicable for type parameters
val mapping = JavaToKotlinClassMap
when (qualifiers.mutability) {
READ_ONLY -> {
if (position == TypeComponentPosition.FLEXIBLE_LOWER && mapping.isMutable(this)) {
return mapping.convertMutableToReadOnly(this).enhancedMutability()
}
}
MUTABLE -> {
if (position == TypeComponentPosition.FLEXIBLE_UPPER && mapping.isReadOnly(this) ) {
return mapping.convertReadOnlyToMutable(this).enhancedMutability()
}
}
}
return this.noChange()
}
private fun KotlinType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): EnhancementResult<Boolean> {
if (!position.shouldEnhance()) return this.isMarkedNullable.noChange()
return when (qualifiers.nullability) {
NULLABLE -> true.enhancedNullability()
NOT_NULL -> false.enhancedNullability()
else -> this.isMarkedNullable.noChange()
}
}
private val ENHANCED_NULLABILITY_ANNOTATIONS = EnhancedTypeAnnotations(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION)
private val ENHANCED_MUTABILITY_ANNOTATIONS = EnhancedTypeAnnotations(JvmAnnotationNames.ENHANCED_MUTABILITY_ANNOTATION)
private class EnhancedTypeAnnotations(private val fqNameToMatch: FqName) : Annotations {
override fun isEmpty() = false
override fun findAnnotation(fqName: FqName) = when (fqName) {
fqNameToMatch -> EnhancedTypeAnnotationDescriptor
else -> null
}
override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) }
override fun getUseSiteTargetedAnnotations() = emptyList<AnnotationWithTarget>()
// Note, that this class may break Annotations contract (!isEmpty && iterator.isEmpty())
// It's a hack that we need unless we have stable "user data" in JetType
override fun iterator(): Iterator<AnnotationDescriptor> = emptyList<AnnotationDescriptor>().iterator()
}
private object EnhancedTypeAnnotationDescriptor : AnnotationDescriptor {
private fun throwError(): Nothing = error("No methods should be called on this descriptor. Only its presence matters")
override val type: KotlinType get() = throwError()
override val allValueArguments: Map<Name, ConstantValue<*>> get() = throwError()
override val source: SourceElement get() = throwError()
override fun toString() = "[EnhancedType]"
}
internal class NotNullTypeParameter(override val delegate: SimpleType) : CustomTypeVariable, DelegatingSimpleType() {
override val isTypeVariable: Boolean
get() = true
override fun substitutionResult(replacement: KotlinType): KotlinType {
val unwrappedType = replacement.unwrap()
if (!TypeUtils.isNullableType(unwrappedType) && !unwrappedType.isTypeParameter()) return unwrappedType
return when (unwrappedType) {
is SimpleType -> unwrappedType.prepareReplacement()
is FlexibleType -> KotlinTypeFactory.flexibleType(
unwrappedType.lowerBound.prepareReplacement(),
unwrappedType.upperBound.prepareReplacement()
).wrapEnhancement(unwrappedType.getEnhancement())
else -> error("Incorrect type: $unwrappedType")
}
}
override val isMarkedNullable: Boolean
get() = false
private fun SimpleType.prepareReplacement(): SimpleType {
val result = makeNullableAsSpecified(false)
if (!this.isTypeParameter()) return result
return NotNullTypeParameter(result)
}
override fun replaceAnnotations(newAnnotations: Annotations) = NotNullTypeParameter(delegate.replaceAnnotations(newAnnotations))
override fun makeNullableAsSpecified(newNullability: Boolean) =
if (newNullability) delegate.makeNullableAsSpecified(true) else this
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2016 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.load.java.typeEnhancement
enum class NullabilityQualifier {
NULLABLE,
NOT_NULL,
FORCE_FLEXIBILITY
}
enum class MutabilityQualifier {
READ_ONLY,
MUTABLE
}
class JavaTypeQualifiers internal constructor(
val nullability: NullabilityQualifier?,
val mutability: MutabilityQualifier?,
internal val isNotNullTypeParameter: Boolean,
internal val isNullabilityQualifierForWarning: Boolean = false
) {
companion object {
val NONE = JavaTypeQualifiers(null, null, false)
}
}
@@ -0,0 +1,63 @@
/*
* 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.load.java
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.extractRadix
sealed class JavaDefaultValue
class EnumEntry(val descriptor: ClassDescriptor) : JavaDefaultValue()
class Constant(val value: Any) : JavaDefaultValue()
fun KotlinType.lexicalCastFrom(value: String): JavaDefaultValue? {
val typeDescriptor = constructor.declarationDescriptor
if (typeDescriptor is ClassDescriptor && typeDescriptor.kind == ClassKind.ENUM_CLASS) {
val descriptor = typeDescriptor.unsubstitutedInnerClassesScope.getContributedClassifier(
Name.identifier(value),
NoLookupLocation.FROM_BACKEND
)
return if (descriptor is ClassDescriptor && descriptor.kind == ClassKind.ENUM_ENTRY) EnumEntry(descriptor) else null
}
val type = this.makeNotNullable()
val (number, radix) = extractRadix(value)
val result: Any? = try {
when {
KotlinBuiltIns.isBoolean(type) -> value.toBoolean()
KotlinBuiltIns.isChar(type) -> value.singleOrNull()
KotlinBuiltIns.isByte(type) -> number.toByteOrNull(radix)
KotlinBuiltIns.isShort(type) -> number.toShortOrNull(radix)
KotlinBuiltIns.isInt(type) -> number.toIntOrNull(radix)
KotlinBuiltIns.isLong(type) -> number.toLongOrNull(radix)
KotlinBuiltIns.isFloat(type) -> value.toFloatOrNull()
KotlinBuiltIns.isDouble(type) -> value.toDoubleOrNull()
KotlinBuiltIns.isString(type) -> value
else -> null
}
} catch (e: IllegalArgumentException) {
null
}
return if (result != null) Constant(result) else null
}
@@ -0,0 +1,370 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.serialization.Flags
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.*
import org.jetbrains.kotlin.serialization.jvm.ClassMapperLite
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.propertySignature
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.KotlinType
import java.util.*
abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any, T : Any>(
storageManager: StorageManager,
private val kotlinClassFinder: KotlinClassFinder
) : AnnotationAndConstantLoader<A, C, T> {
private val storage = storageManager.createMemoizedFunction<KotlinJvmBinaryClass, Storage<A, C>> {
kotlinClass ->
loadAnnotationsAndInitializers(kotlinClass)
}
protected abstract fun loadConstant(desc: String, initializer: Any): C?
protected abstract fun loadAnnotation(
annotationClassId: ClassId,
source: SourceElement,
result: MutableList<A>
): KotlinJvmBinaryClass.AnnotationArgumentVisitor?
protected abstract fun loadTypeAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): A
private fun loadAnnotationIfNotSpecial(
annotationClassId: ClassId,
source: SourceElement,
result: MutableList<A>
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
if (annotationClassId in SPECIAL_ANNOTATIONS) return null
return loadAnnotation(annotationClassId, source, result)
}
private fun ProtoContainer.Class.toBinaryClass(): KotlinJvmBinaryClass? =
(source as? KotlinJvmBinarySourceElement)?.binaryClass
protected open fun getCachedFileContent(kotlinClass: KotlinJvmBinaryClass): ByteArray? = null
override fun loadClassAnnotations(container: ProtoContainer.Class): List<A> {
val kotlinClass = container.toBinaryClass() ?: error("Class for loading annotations is not found: ${container.debugFqName()}")
val result = ArrayList<A>(1)
kotlinClass.loadClassAnnotations(object : KotlinJvmBinaryClass.AnnotationVisitor {
override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
return loadAnnotationIfNotSpecial(classId, source, result)
}
override fun visitEnd() {
}
}, getCachedFileContent(kotlinClass))
return result
}
override fun loadCallableAnnotations(container: ProtoContainer, proto: MessageLite, kind: AnnotatedCallableKind): List<T> {
if (kind == AnnotatedCallableKind.PROPERTY) {
proto as ProtoBuf.Property
val syntheticFunctionSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, synthetic = true)
val fieldSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, field = true)
val isConst = Flags.IS_CONST.get(proto.flags)
val propertyAnnotations = syntheticFunctionSignature?.let { sig ->
findClassAndLoadMemberAnnotations(container, sig, property = true, isConst = isConst)
}.orEmpty()
val fieldAnnotations = fieldSignature?.let { sig ->
findClassAndLoadMemberAnnotations(container, sig, property = true, field = true, isConst = isConst)
}.orEmpty()
// TODO: check delegate presence in some other way
return loadPropertyAnnotations(propertyAnnotations, fieldAnnotations,
if (fieldSignature?.signature?.contains(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX) ?: false) {
AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD
}
else {
AnnotationUseSiteTarget.FIELD
})
}
val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, kind) ?: return emptyList()
return transformAnnotations(findClassAndLoadMemberAnnotations(container, signature))
}
override fun loadEnumEntryAnnotations(container: ProtoContainer, proto: ProtoBuf.EnumEntry): List<A> {
val signature = MemberSignature.fromFieldNameAndDesc(
container.nameResolver.getString(proto.name),
ClassMapperLite.mapClass((container as ProtoContainer.Class).classId)
)
return findClassAndLoadMemberAnnotations(container, signature)
}
protected abstract fun loadPropertyAnnotations(propertyAnnotations: List<A>, fieldAnnotations: List<A>,
fieldUseSiteTarget: AnnotationUseSiteTarget): List<T>
protected abstract fun transformAnnotations(annotations: List<A>): List<T>
private fun findClassAndLoadMemberAnnotations(
container: ProtoContainer, signature: MemberSignature,
property: Boolean = false, field: Boolean = false, isConst: Boolean? = null
): List<A> {
val kotlinClass =
findClassWithAnnotationsAndInitializers(container, getSpecialCaseContainerClass(container, property, field, isConst))
?: return listOf()
return storage(kotlinClass).memberAnnotations[signature] ?: listOf()
}
override fun loadValueParameterAnnotations(
container: ProtoContainer,
callableProto: MessageLite,
kind: AnnotatedCallableKind,
parameterIndex: Int,
proto: ProtoBuf.ValueParameter
): List<A> {
val methodSignature = getCallableSignature(callableProto, container.nameResolver, container.typeTable, kind)
if (methodSignature != null) {
val index = parameterIndex + computeJvmParameterIndexShift(container, callableProto)
val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, index)
return findClassAndLoadMemberAnnotations(container, paramSignature)
}
return listOf()
}
private fun computeJvmParameterIndexShift(container: ProtoContainer, message: MessageLite): Int {
return when (message) {
is ProtoBuf.Function -> if (message.hasReceiver()) 1 else 0
is ProtoBuf.Property -> if (message.hasReceiver()) 1 else 0
is ProtoBuf.Constructor -> when {
(container as ProtoContainer.Class).kind == ProtoBuf.Class.Kind.ENUM_CLASS -> 2
container.isInner -> 1
else -> 0
}
else -> throw UnsupportedOperationException("Unsupported message: ${message::class.java}")
}
}
override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer,
proto: MessageLite,
kind: AnnotatedCallableKind
): List<A> {
val methodSignature = getCallableSignature(proto, container.nameResolver, container.typeTable, kind)
if (methodSignature != null) {
val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(methodSignature, 0)
return findClassAndLoadMemberAnnotations(container, paramSignature)
}
return emptyList()
}
override fun loadTypeAnnotations(proto: ProtoBuf.Type, nameResolver: NameResolver): List<A> {
return proto.getExtension(JvmProtoBuf.typeAnnotation).map { loadTypeAnnotation(it, nameResolver) }
}
override fun loadTypeParameterAnnotations(proto: ProtoBuf.TypeParameter, nameResolver: NameResolver): List<A> {
return proto.getExtension(JvmProtoBuf.typeParameterAnnotation).map { loadTypeAnnotation(it, nameResolver) }
}
override fun loadPropertyConstant(container: ProtoContainer, proto: ProtoBuf.Property, expectedType: KotlinType): C? {
val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, AnnotatedCallableKind.PROPERTY)
?: return null
val specialCase = getSpecialCaseContainerClass(container, property = true, field = true, isConst = Flags.IS_CONST.get(proto.flags))
val kotlinClass = findClassWithAnnotationsAndInitializers(container, specialCase) ?: return null
return storage(kotlinClass).propertyConstants[signature]
}
private fun findClassWithAnnotationsAndInitializers(
container: ProtoContainer, specialCase: KotlinJvmBinaryClass?
): KotlinJvmBinaryClass? {
return when {
specialCase != null -> specialCase
container is ProtoContainer.Class -> container.toBinaryClass()
else -> null
}
}
// TODO: do not use KotlinClassFinder#findKotlinClass here because it traverses the file system in the compiler
// Introduce an API in KotlinJvmBinaryClass to find a class nearby instead
private fun getSpecialCaseContainerClass(
container: ProtoContainer, property: Boolean, field: Boolean, isConst: Boolean?
): KotlinJvmBinaryClass? {
if (property) {
checkNotNull(isConst) { "isConst should not be null for property (container=$container)" }
if (container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.INTERFACE) {
return kotlinClassFinder.findKotlinClass(
container.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME))
)
}
if (isConst!! && container is ProtoContainer.Package) {
// Const properties in multifile classes are generated into the facade class
val facadeClassName = (container.source as? JvmPackagePartSource)?.facadeClassName
if (facadeClassName != null) {
// Converting '/' to '.' is fine here because the facade class has a top level ClassId
return kotlinClassFinder.findKotlinClass(ClassId.topLevel(FqName(facadeClassName.internalName.replace('/', '.'))))
}
}
}
if (field && container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
val outerClass = container.outerClass
if (outerClass != null && (outerClass.kind == ProtoBuf.Class.Kind.CLASS || outerClass.kind == ProtoBuf.Class.Kind.ENUM_CLASS)) {
// Backing fields of properties of a companion object in a class are generated in the outer class
return outerClass.toBinaryClass()
}
}
if (container is ProtoContainer.Package && container.source is JvmPackagePartSource) {
val jvmPackagePartSource = container.source as JvmPackagePartSource
return jvmPackagePartSource.knownJvmBinaryClass
?: kotlinClassFinder.findKotlinClass(jvmPackagePartSource.classId)
}
return null
}
private fun loadAnnotationsAndInitializers(kotlinClass: KotlinJvmBinaryClass): Storage<A, C> {
val memberAnnotations = HashMap<MemberSignature, MutableList<A>>()
val propertyConstants = HashMap<MemberSignature, C>()
kotlinClass.visitMembers(object : KotlinJvmBinaryClass.MemberVisitor {
override fun visitMethod(name: Name, desc: String): KotlinJvmBinaryClass.MethodAnnotationVisitor? {
return AnnotationVisitorForMethod(MemberSignature.fromMethodNameAndDesc(name.asString(), desc))
}
override fun visitField(name: Name, desc: String, initializer: Any?): KotlinJvmBinaryClass.AnnotationVisitor? {
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), desc)
if (initializer != null) {
val constant = loadConstant(desc, initializer)
if (constant != null) {
propertyConstants[signature] = constant
}
}
return MemberAnnotationVisitor(signature)
}
inner class AnnotationVisitorForMethod(signature: MemberSignature) : MemberAnnotationVisitor(signature), KotlinJvmBinaryClass.MethodAnnotationVisitor {
override fun visitParameterAnnotation(
index: Int, classId: ClassId, source: SourceElement
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
val paramSignature = MemberSignature.fromMethodSignatureAndParameterIndex(signature, index)
var result = memberAnnotations[paramSignature]
if (result == null) {
result = ArrayList<A>()
memberAnnotations[paramSignature] = result
}
return loadAnnotationIfNotSpecial(classId, source, result)
}
}
open inner class MemberAnnotationVisitor(protected val signature: MemberSignature) : KotlinJvmBinaryClass.AnnotationVisitor {
private val result = ArrayList<A>()
override fun visitAnnotation(classId: ClassId, source: SourceElement): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
return loadAnnotationIfNotSpecial(classId, source, result)
}
override fun visitEnd() {
if (result.isNotEmpty()) {
memberAnnotations[signature] = result
}
}
}
}, getCachedFileContent(kotlinClass))
return Storage(memberAnnotations, propertyConstants)
}
private fun getPropertySignature(
proto: ProtoBuf.Property,
nameResolver: NameResolver,
typeTable: TypeTable,
field: Boolean = false,
synthetic: Boolean = false
): MemberSignature? {
val signature =
if (proto.hasExtension(propertySignature)) proto.getExtension(propertySignature)
else return null
if (field) {
val (name, desc) = JvmProtoBufUtil.getJvmFieldSignature(proto, nameResolver, typeTable) ?: return null
return MemberSignature.fromFieldNameAndDesc(name, desc)
}
else if (synthetic && signature.hasSyntheticMethod()) {
return MemberSignature.fromMethod(nameResolver, signature.syntheticMethod)
}
return null
}
private fun getCallableSignature(
proto: MessageLite,
nameResolver: NameResolver,
typeTable: TypeTable,
kind: AnnotatedCallableKind
): MemberSignature? {
return when {
proto is ProtoBuf.Constructor -> {
MemberSignature.fromMethodNameAndDesc(JvmProtoBufUtil.getJvmConstructorSignature(proto, nameResolver, typeTable) ?: return null)
}
proto is ProtoBuf.Function -> {
MemberSignature.fromMethodNameAndDesc(JvmProtoBufUtil.getJvmMethodSignature(proto, nameResolver, typeTable) ?: return null)
}
proto is ProtoBuf.Property && proto.hasExtension(propertySignature) -> {
val signature = proto.getExtension(propertySignature)
when (kind) {
AnnotatedCallableKind.PROPERTY_GETTER -> MemberSignature.fromMethod(nameResolver, signature.getter)
AnnotatedCallableKind.PROPERTY_SETTER -> MemberSignature.fromMethod(nameResolver, signature.setter)
AnnotatedCallableKind.PROPERTY -> getPropertySignature(proto, nameResolver, typeTable, true, true)
else -> null
}
}
else -> null
}
}
private class Storage<out A, out C>(
val memberAnnotations: Map<MemberSignature, List<A>>,
val propertyConstants: Map<MemberSignature, C>
)
private companion object {
val SPECIAL_ANNOTATIONS = listOf(
JvmAnnotationNames.METADATA_FQ_NAME,
JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION,
JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION,
FqName("java.lang.annotation.Target"),
FqName("java.lang.annotation.Retention"),
FqName("java.lang.annotation.Documented")
).map(ClassId::topLevel).toSet()
}
}
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.AnnotationValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.ConstantValueFactory
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.AnnotationDeserializer
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.compact
import java.util.*
class BinaryClassAnnotationAndConstantLoaderImpl(
private val module: ModuleDescriptor,
private val notFoundClasses: NotFoundClasses,
storageManager: StorageManager,
kotlinClassFinder: KotlinClassFinder
) : AbstractBinaryClassAnnotationAndConstantLoader<AnnotationDescriptor, ConstantValue<*>, AnnotationWithTarget>(
storageManager, kotlinClassFinder
) {
private val annotationDeserializer = AnnotationDeserializer(module, notFoundClasses)
private val factory = ConstantValueFactory(module.builtIns)
override fun loadTypeAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): AnnotationDescriptor =
annotationDeserializer.deserializeAnnotation(proto, nameResolver)
override fun loadConstant(desc: String, initializer: Any): ConstantValue<*>? {
val normalizedValue: Any = if (desc in "ZBCS") {
val intValue = initializer as Int
when (desc) {
"Z" -> intValue != 0
"B" -> intValue.toByte()
"C" -> intValue.toChar()
"S" -> intValue.toShort()
else -> throw AssertionError(desc)
}
}
else {
initializer
}
return factory.createConstantValue(normalizedValue)
}
override fun loadPropertyAnnotations(
propertyAnnotations: List<AnnotationDescriptor>,
fieldAnnotations: List<AnnotationDescriptor>,
fieldUseSiteTarget: AnnotationUseSiteTarget
): List<AnnotationWithTarget> {
return propertyAnnotations.map { AnnotationWithTarget(it, null) } +
fieldAnnotations.map { AnnotationWithTarget(it, fieldUseSiteTarget) }
}
override fun transformAnnotations(annotations: List<AnnotationDescriptor>): List<AnnotationWithTarget> {
return annotations.map { AnnotationWithTarget(it, null) }
}
override fun loadAnnotation(
annotationClassId: ClassId,
source: SourceElement,
result: MutableList<AnnotationDescriptor>
): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
val annotationClass = resolveClass(annotationClassId)
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor {
private val arguments = HashMap<Name, ConstantValue<*>>()
override fun visit(name: Name?, value: Any?) {
if (name != null) {
arguments[name] = createConstant(name, value)
}
}
override fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name) {
arguments[name] = enumEntryValue(enumClassId, enumEntryName)
}
override fun visitArray(name: Name): AnnotationArrayArgumentVisitor? {
return object : KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor {
private val elements = ArrayList<ConstantValue<*>>()
override fun visit(value: Any?) {
elements.add(createConstant(name, value))
}
override fun visitEnum(enumClassId: ClassId, enumEntryName: Name) {
elements.add(enumEntryValue(enumClassId, enumEntryName))
}
override fun visitEnd() {
val parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass)
if (parameter != null) {
arguments[name] = factory.createArrayValue(elements.compact(), parameter.type)
}
}
}
}
override fun visitAnnotation(name: Name, classId: ClassId): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
val list = ArrayList<AnnotationDescriptor>()
val visitor = loadAnnotation(classId, SourceElement.NO_SOURCE, list)!!
return object: KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
override fun visitEnd() {
visitor.visitEnd()
arguments[name] = AnnotationValue(list.single())
}
}
}
// NOTE: see analogous code in AnnotationDeserializer
private fun enumEntryValue(enumClassId: ClassId, name: Name): ConstantValue<*> {
val enumClass = resolveClass(enumClassId)
if (enumClass.kind == ClassKind.ENUM_CLASS) {
val classifier = enumClass.unsubstitutedInnerClassesScope.getContributedClassifier(name, NoLookupLocation.FROM_JAVA_LOADER)
if (classifier is ClassDescriptor) {
return factory.createEnumValue(classifier)
}
}
return factory.createErrorValue("Unresolved enum entry: $enumClassId.$name")
}
override fun visitEnd() {
result.add(AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source))
}
private fun createConstant(name: Name?, value: Any?): ConstantValue<*> {
return factory.createConstantValue(value) ?:
factory.createErrorValue("Unsupported annotation argument: $name")
}
}
}
private fun resolveClass(classId: ClassId): ClassDescriptor {
return module.findNonGenericClassAcrossDependencies(classId, notFoundClasses)
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.descriptors.deserialization.AdditionalClassPartsProvider
import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentDeclarationFilter
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.lazy.LazyJavaPackageFragmentProvider
import org.jetbrains.kotlin.platform.JvmBuiltIns
import org.jetbrains.kotlin.serialization.deserialization.*
import org.jetbrains.kotlin.storage.StorageManager
// This class is needed only for easier injection: exact types of needed components are specified in the constructor here.
// Otherwise injector generator is not smart enough to deduce, for example, which package fragment provider DeserializationComponents needs
class DeserializationComponentsForJava(
storageManager: StorageManager,
moduleDescriptor: ModuleDescriptor,
configuration: DeserializationConfiguration,
classDataFinder: JavaClassDataFinder,
annotationAndConstantLoader: BinaryClassAnnotationAndConstantLoaderImpl,
packageFragmentProvider: LazyJavaPackageFragmentProvider,
notFoundClasses: NotFoundClasses,
errorReporter: ErrorReporter,
lookupTracker: LookupTracker,
contractDeserializer: ContractDeserializer
) {
val components: DeserializationComponents
init {
// currently built-ins may be not an instance of JvmBuiltIns only in case of built-ins serialization
val jvmBuiltIns = moduleDescriptor.builtIns as? JvmBuiltIns
components = DeserializationComponents(
storageManager, moduleDescriptor, configuration, classDataFinder, annotationAndConstantLoader, packageFragmentProvider,
LocalClassifierTypeSettings.Default, errorReporter, lookupTracker, JavaFlexibleTypeDeserializer,
emptyList(), notFoundClasses, contractDeserializer,
additionalClassPartsProvider = jvmBuiltIns?.settings ?: AdditionalClassPartsProvider.None,
platformDependentDeclarationFilter = jvmBuiltIns?.settings ?: PlatformDependentDeclarationFilter.NoPlatformDependent
)
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.ClassDataWithSource
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import javax.inject.Inject
class DeserializedDescriptorResolver {
lateinit var components: DeserializationComponents
// component dependency cycle
@Inject
fun setComponents(components: DeserializationComponentsForJava) {
this.components = components.components
}
private val skipMetadataVersionCheck: Boolean
get() = components.configuration.skipMetadataVersionCheck
fun resolveClass(kotlinClass: KotlinJvmBinaryClass): ClassDescriptor? {
val classData = readClassData(kotlinClass) ?: return null
return components.classDeserializer.deserializeClass(kotlinClass.classId, classData)
}
internal fun readClassData(kotlinClass: KotlinJvmBinaryClass): ClassDataWithSource? {
val data = readData(kotlinClass, KOTLIN_CLASS) ?: return null
val strings = kotlinClass.classHeader.strings ?: return null
val classData = parseProto(kotlinClass) {
JvmProtoBufUtil.readClassDataFrom(data, strings)
} ?: return null
val source = KotlinJvmBinarySourceElement(kotlinClass, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible)
return ClassDataWithSource(classData, source)
}
fun createKotlinPackagePartScope(descriptor: PackageFragmentDescriptor, kotlinClass: KotlinJvmBinaryClass): MemberScope? {
val data = readData(kotlinClass, KOTLIN_FILE_FACADE_OR_MULTIFILE_CLASS_PART) ?: return null
val strings = kotlinClass.classHeader.strings ?: return null
val (nameResolver, packageProto) = parseProto(kotlinClass) {
JvmProtoBufUtil.readPackageDataFrom(data, strings)
} ?: return null
val source = JvmPackagePartSource(kotlinClass, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible)
return DeserializedPackageMemberScope(descriptor, packageProto, nameResolver, source, components) {
// All classes are included into Java scope
emptyList()
}
}
private val KotlinJvmBinaryClass.incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>?
get() {
if (skipMetadataVersionCheck || classHeader.metadataVersion.isCompatible()) return null
return IncompatibleVersionErrorData(classHeader.metadataVersion, JvmMetadataVersion.INSTANCE, location, classId)
}
private val KotlinJvmBinaryClass.isPreReleaseInvisible: Boolean
get() = !components.configuration.skipPreReleaseCheck &&
!KotlinCompilerVersion.isPreRelease() &&
(classHeader.isPreRelease || classHeader.metadataVersion == KOTLIN_1_1_EAP_METADATA_VERSION)
internal fun readData(kotlinClass: KotlinJvmBinaryClass, expectedKinds: Set<KotlinClassHeader.Kind>): Array<String>? {
val header = kotlinClass.classHeader
return (header.data ?: header.incompatibleData)?.takeIf { header.kind in expectedKinds }
}
private inline fun <T : Any> parseProto(klass: KotlinJvmBinaryClass, block: () -> T): T? {
try {
try {
return block()
}
catch (e: InvalidProtocolBufferException) {
throw IllegalStateException("Could not read data from ${klass.location}", e)
}
}
catch (e: Throwable) {
if (skipMetadataVersionCheck || klass.classHeader.metadataVersion.isCompatible()) {
throw e
}
// TODO: log.warn
return null
}
}
companion object {
internal val KOTLIN_CLASS = setOf(KotlinClassHeader.Kind.CLASS)
private val KOTLIN_FILE_FACADE_OR_MULTIFILE_CLASS_PART =
setOf(KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART)
private val KOTLIN_1_1_EAP_METADATA_VERSION = JvmMetadataVersion(1, 1, 2)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.ClassDataWithSource
import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder
class JavaClassDataFinder(
internal val kotlinClassFinder: KotlinClassFinder,
private val deserializedDescriptorResolver: DeserializedDescriptorResolver
) : ClassDataFinder {
override fun findClassData(classId: ClassId): ClassDataWithSource? {
val kotlinClass = kotlinClassFinder.findKotlinClass(classId) ?: return null
assert(kotlinClass.classId == classId) {
"Class with incorrect id found: expected $classId, actual ${kotlinClass.classId}"
}
return deserializedDescriptorResolver.readClassData(kotlinClass)
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.SimpleType
object JavaFlexibleTypeDeserializer : FlexibleTypeDeserializer {
val id = "kotlin.jvm.PlatformType"
override fun create(proto: ProtoBuf.Type, flexibleId: String, lowerBound: SimpleType, upperBound: SimpleType): KotlinType {
if (flexibleId != id) return ErrorUtils.createErrorType("Error java flexible type with id: $flexibleId. ($lowerBound..$upperBound)")
if (proto.hasExtension(JvmProtoBuf.isRaw)) {
return RawTypeImpl(lowerBound, upperBound)
}
return KotlinTypeFactory.flexibleType(lowerBound, upperBound)
}
}
@@ -0,0 +1,497 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.builtins.BuiltInsInitializer
import org.jetbrains.kotlin.builtins.CloneableClassScope
import org.jetbrains.kotlin.builtins.JvmBuiltInClassDescriptorFactory
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl
import org.jetbrains.kotlin.descriptors.annotations.createDeprecatedAnnotation
import org.jetbrains.kotlin.descriptors.deserialization.AdditionalClassPartsProvider
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentDeclarationFilter
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.platform.createMappedTypeParametersSubstitution
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.LazyWrappedType
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.SmartSet
import java.io.Serializable
import java.util.*
open class JvmBuiltInsSettings(
private val moduleDescriptor: ModuleDescriptor,
storageManager: StorageManager,
deferredOwnerModuleDescriptor: () -> ModuleDescriptor,
isAdditionalBuiltInsFeatureSupported: () -> Boolean
) : AdditionalClassPartsProvider, PlatformDependentDeclarationFilter {
private val j2kClassMap = JavaToKotlinClassMap
private val ownerModuleDescriptor: ModuleDescriptor by lazy(deferredOwnerModuleDescriptor)
private val isAdditionalBuiltInsFeatureSupported: Boolean by lazy(isAdditionalBuiltInsFeatureSupported)
private val mockSerializableType = storageManager.createMockJavaIoSerializableType()
private val cloneableType by storageManager.createLazyValue {
ownerModuleDescriptor.findNonGenericClassAcrossDependencies(
JvmBuiltInClassDescriptorFactory.CLONEABLE_CLASS_ID,
NotFoundClasses(storageManager, ownerModuleDescriptor)
).defaultType
}
private val javaAnalogueClassesWithCustomSupertypeCache = storageManager.createCacheWithNotNullValues<FqName, ClassDescriptor>()
// Most this properties are lazy because they depends on KotlinBuiltIns initialization that depends on JvmBuiltInsSettings object
private val notConsideredDeprecation by storageManager.createLazyValue {
moduleDescriptor.builtIns.createDeprecatedAnnotation(
"This member is not fully supported by Kotlin compiler, so it may be absent or have different signature in next major version"
).let { AnnotationsImpl(listOf(it)) }
}
private fun StorageManager.createMockJavaIoSerializableType(): KotlinType {
val mockJavaIoPackageFragment = object : PackageFragmentDescriptorImpl(moduleDescriptor, FqName("java.io")) {
override fun getMemberScope() = MemberScope.Empty
}
//NOTE: can't reference anyType right away, because this is sometimes called when JvmBuiltIns are initializing
val superTypes = listOf(LazyWrappedType(this) { moduleDescriptor.builtIns.anyType })
val mockSerializableClass = ClassDescriptorImpl(
mockJavaIoPackageFragment, Name.identifier("Serializable"), Modality.ABSTRACT, ClassKind.INTERFACE, superTypes,
SourceElement.NO_SOURCE, /* isExternal = */ false
)
mockSerializableClass.initialize(MemberScope.Empty, emptySet(), null)
return mockSerializableClass.defaultType
}
override fun getSupertypes(classDescriptor: ClassDescriptor): Collection<KotlinType> {
val fqName = classDescriptor.fqNameUnsafe
return when {
isArrayOrPrimitiveArray(fqName) -> listOf(cloneableType, mockSerializableType)
isSerializableInJava(fqName) -> listOf(mockSerializableType)
else -> listOf()
}
}
override fun getFunctions(name: Name, classDescriptor: ClassDescriptor): Collection<SimpleFunctionDescriptor> {
if (name == CloneableClassScope.CLONE_NAME && classDescriptor is DeserializedClassDescriptor &&
KotlinBuiltIns.isArrayOrPrimitiveArray(classDescriptor)) {
// Do not create clone for arrays deserialized from metadata in the old (1.0) runtime, because clone is declared there anyway
if (classDescriptor.classProto.functionList.any { functionProto ->
classDescriptor.c.nameResolver.getName(functionProto.name) == CloneableClassScope.CLONE_NAME
}) {
return emptyList()
}
return listOf(createCloneForArray(
classDescriptor, cloneableType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
))
}
if (!isAdditionalBuiltInsFeatureSupported) return emptyList()
return getAdditionalFunctions(classDescriptor) {
it.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS)
}.mapNotNull {
additionalMember ->
val substitutedWithKotlinTypeParameters =
additionalMember.substitute(
createMappedTypeParametersSubstitution(
additionalMember.containingDeclaration as ClassDescriptor, classDescriptor).buildSubstitutor()
) as SimpleFunctionDescriptor
substitutedWithKotlinTypeParameters.newCopyBuilder().apply {
setOwner(classDescriptor)
setDispatchReceiverParameter(classDescriptor.thisAsReceiverParameter)
setPreserveSourceElement()
setSubstitution(UnsafeVarianceTypeSubstitution(moduleDescriptor.builtIns))
val memberStatus = additionalMember.getJdkMethodStatus()
when (memberStatus) {
JDKMemberStatus.BLACK_LIST -> {
// Black list methods in final class can't be overridden or called with 'super'
if (classDescriptor.isFinalClass) return@mapNotNull null
setHiddenForResolutionEverywhereBesideSupercalls()
}
JDKMemberStatus.NOT_CONSIDERED -> {
setAdditionalAnnotations(notConsideredDeprecation)
}
JDKMemberStatus.DROP -> return@mapNotNull null
JDKMemberStatus.WHITE_LIST -> Unit // Do nothing
}
}.build()!!
}
}
override fun getFunctionsNames(classDescriptor: ClassDescriptor): Set<Name> {
if (!isAdditionalBuiltInsFeatureSupported) return emptySet()
// NB: It's just an approximation that could be calculated relatively fast
// More precise computation would look like `getAdditionalFunctions` (and the measurements show that it would be rather slow)
return classDescriptor.getJavaAnalogue()?.unsubstitutedMemberScope?.getFunctionNames() ?: emptySet()
}
private fun getAdditionalFunctions(
classDescriptor: ClassDescriptor,
functionsByScope: (MemberScope) -> Collection<SimpleFunctionDescriptor>
): Collection<SimpleFunctionDescriptor> {
val javaAnalogueDescriptor = classDescriptor.getJavaAnalogue() ?: return emptyList()
val kotlinClassDescriptors = j2kClassMap.mapPlatformClass(javaAnalogueDescriptor.fqNameSafe, FallbackBuiltIns.Instance)
val kotlinMutableClassIfContainer = kotlinClassDescriptors.lastOrNull() ?: return emptyList()
val kotlinVersions = SmartSet.create(kotlinClassDescriptors.map { it.fqNameSafe })
val isMutable = j2kClassMap.isMutable(classDescriptor)
val fakeJavaClassDescriptor = javaAnalogueClassesWithCustomSupertypeCache.computeIfAbsent(javaAnalogueDescriptor.fqNameSafe) {
javaAnalogueDescriptor.copy(
javaResolverCache = JavaResolverCache.EMPTY,
additionalSupertypeClassDescriptor = kotlinMutableClassIfContainer)
}
val scope = fakeJavaClassDescriptor.unsubstitutedMemberScope
return functionsByScope(scope)
.filter { analogueMember ->
if (analogueMember.kind != CallableMemberDescriptor.Kind.DECLARATION) return@filter false
if (!analogueMember.visibility.isPublicAPI) return@filter false
if (KotlinBuiltIns.isDeprecated(analogueMember)) return@filter false
if (analogueMember.overriddenDescriptors.any {
it.containingDeclaration.fqNameSafe in kotlinVersions
}) return@filter false
!analogueMember.isMutabilityViolation(isMutable)
}
}
private fun createCloneForArray(
arrayClassDescriptor: DeserializedClassDescriptor,
cloneFromCloneable: SimpleFunctionDescriptor
): SimpleFunctionDescriptor = cloneFromCloneable.newCopyBuilder().apply {
setOwner(arrayClassDescriptor)
setVisibility(Visibilities.PUBLIC)
setReturnType(arrayClassDescriptor.defaultType)
setDispatchReceiverParameter(arrayClassDescriptor.thisAsReceiverParameter)
}.build()!!
private fun SimpleFunctionDescriptor.isMutabilityViolation(isMutable: Boolean): Boolean {
val owner = containingDeclaration as ClassDescriptor
val jvmDescriptor = computeJvmDescriptor()
if ((SignatureBuildingComponents.signature(owner, jvmDescriptor) in MUTABLE_METHOD_SIGNATURES) xor isMutable) return true
return DFS.ifAny<CallableMemberDescriptor>(
listOf(this),
{ it.original.overriddenDescriptors }
) {
overridden ->
overridden.kind == CallableMemberDescriptor.Kind.DECLARATION &&
j2kClassMap.isMutable(overridden.containingDeclaration as ClassDescriptor)
}
}
private fun FunctionDescriptor.getJdkMethodStatus(): JDKMemberStatus {
val owner = containingDeclaration as ClassDescriptor
val jvmDescriptor = computeJvmDescriptor()
var result: JDKMemberStatus? = null
return DFS.dfs<ClassDescriptor, JDKMemberStatus>(
listOf(owner),
{
// Search through mapped supertypes to determine that Set.toArray is in blacklist, while we have only
// Collection.toArray there explicitly
// Note, that we can't find j.u.Collection.toArray within overriddenDescriptors of j.u.Set.toArray
it.typeConstructor.supertypes.mapNotNull {
(it.constructor.declarationDescriptor?.original as? ClassDescriptor)?.getJavaAnalogue()
}
},
object : DFS.AbstractNodeHandler<ClassDescriptor, JDKMemberStatus>() {
override fun beforeChildren(javaClassDescriptor: ClassDescriptor): Boolean {
val signature = SignatureBuildingComponents.signature(javaClassDescriptor, jvmDescriptor)
when (signature) {
in BLACK_LIST_METHOD_SIGNATURES -> { result = JDKMemberStatus.BLACK_LIST }
in WHITE_LIST_METHOD_SIGNATURES -> { result = JDKMemberStatus.WHITE_LIST }
in DROP_LIST_METHOD_SIGNATURES -> { result = JDKMemberStatus.DROP }
}
return result == null
}
override fun result() = result ?: JDKMemberStatus.NOT_CONSIDERED
})
}
private enum class JDKMemberStatus {
BLACK_LIST, WHITE_LIST, NOT_CONSIDERED, DROP
}
private fun ClassDescriptor.getJavaAnalogue(): LazyJavaClassDescriptor? {
// Prevents recursive dependency: memberScope(Any) -> memberScope(Object) -> memberScope(Any)
// No additional members should be added to Any
if (KotlinBuiltIns.isAny(this)) return null
// Optimization: only classes under kotlin.* can have Java analogues
if (!KotlinBuiltIns.isUnderKotlinPackage(this)) return null
val fqName = fqNameUnsafe
if (!fqName.isSafe) return null
val javaAnalogueFqName = j2kClassMap.mapKotlinToJava(fqName)?.asSingleFqName() ?: return null
return ownerModuleDescriptor.resolveClassByFqName(javaAnalogueFqName, NoLookupLocation.FROM_BUILTINS) as? LazyJavaClassDescriptor
}
override fun getConstructors(classDescriptor: ClassDescriptor): Collection<ClassConstructorDescriptor> {
if (classDescriptor.kind != ClassKind.CLASS || !isAdditionalBuiltInsFeatureSupported) return emptyList()
val javaAnalogueDescriptor = classDescriptor.getJavaAnalogue() ?: return emptyList()
val defaultKotlinVersion =
j2kClassMap.mapJavaToKotlin(javaAnalogueDescriptor.fqNameSafe, FallbackBuiltIns.Instance) ?: return emptyList()
val substitutor = createMappedTypeParametersSubstitution(defaultKotlinVersion, javaAnalogueDescriptor).buildSubstitutor()
fun ConstructorDescriptor.isEffectivelyTheSameAs(javaConstructor: ConstructorDescriptor) =
OverridingUtil.getBothWaysOverridability(this, javaConstructor.substitute(substitutor)) ==
OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
return javaAnalogueDescriptor.constructors.filter {
javaConstructor ->
javaConstructor.visibility.isPublicAPI &&
defaultKotlinVersion.constructors.none { it.isEffectivelyTheSameAs(javaConstructor) } &&
!javaConstructor.isTrivialCopyConstructorFor(classDescriptor) &&
!KotlinBuiltIns.isDeprecated(javaConstructor) &&
SignatureBuildingComponents.signature(javaAnalogueDescriptor, javaConstructor.computeJvmDescriptor()) !in BLACK_LIST_CONSTRUCTOR_SIGNATURES
}.map {
javaConstructor ->
javaConstructor.newCopyBuilder().apply {
setOwner(classDescriptor)
setReturnType(classDescriptor.defaultType)
setPreserveSourceElement()
setSubstitution(substitutor.substitution)
if (SignatureBuildingComponents.signature(javaAnalogueDescriptor, javaConstructor.computeJvmDescriptor()) !in WHITE_LIST_CONSTRUCTOR_SIGNATURES) {
setAdditionalAnnotations(notConsideredDeprecation)
}
}.build() as ClassConstructorDescriptor
}
}
override fun isFunctionAvailable(classDescriptor: ClassDescriptor, functionDescriptor: SimpleFunctionDescriptor): Boolean {
val javaAnalogueClassDescriptor = classDescriptor.getJavaAnalogue() ?: return true
if (!functionDescriptor.annotations.hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME)) return true
if (!isAdditionalBuiltInsFeatureSupported) return false
val jvmDescriptor = functionDescriptor.computeJvmDescriptor()
return javaAnalogueClassDescriptor
.unsubstitutedMemberScope
.getContributedFunctions(functionDescriptor.name, NoLookupLocation.FROM_BUILTINS)
.any { it.computeJvmDescriptor() == jvmDescriptor }
}
private fun ConstructorDescriptor.isTrivialCopyConstructorFor(classDescriptor: ClassDescriptor): Boolean =
valueParameters.size == 1 &&
valueParameters.single().type.constructor.declarationDescriptor?.fqNameUnsafe == classDescriptor.fqNameUnsafe
companion object {
fun isSerializableInJava(fqName: FqNameUnsafe): Boolean {
if (isArrayOrPrimitiveArray(fqName)) {
return true
}
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(fqName) ?: return false
val classViaReflection = try {
Class.forName(javaClassId.asSingleFqName().asString())
}
catch (e: ClassNotFoundException) {
return false
}
return Serializable::class.java.isAssignableFrom(classViaReflection)
}
private fun isArrayOrPrimitiveArray(fqName: FqNameUnsafe): Boolean {
return fqName == KotlinBuiltIns.FQ_NAMES.array || KotlinBuiltIns.isPrimitiveArray(fqName)
}
val DROP_LIST_METHOD_SIGNATURES: Set<String> =
SignatureBuildingComponents.inJavaUtil(
"Collection",
"toArray()[Ljava/lang/Object;", "toArray([Ljava/lang/Object;)[Ljava/lang/Object;") +
"java/lang/annotation/Annotation.annotationType()Ljava/lang/Class;"
val BLACK_LIST_METHOD_SIGNATURES: Set<String> =
signatures {
buildPrimitiveValueMethodsSet() +
inJavaUtil("List", "sort(Ljava/util/Comparator;)V") +
inJavaLang("String",
"codePointAt(I)I", "codePointBefore(I)I", "codePointCount(II)I", "compareToIgnoreCase(Ljava/lang/String;)I",
"concat(Ljava/lang/String;)Ljava/lang/String;", "contains(Ljava/lang/CharSequence;)Z",
"contentEquals(Ljava/lang/CharSequence;)Z", "contentEquals(Ljava/lang/StringBuffer;)Z",
"endsWith(Ljava/lang/String;)Z", "equalsIgnoreCase(Ljava/lang/String;)Z", "getBytes()[B", "getBytes(II[BI)V",
"getBytes(Ljava/lang/String;)[B", "getBytes(Ljava/nio/charset/Charset;)[B", "getChars(II[CI)V",
"indexOf(I)I", "indexOf(II)I", "indexOf(Ljava/lang/String;)I", "indexOf(Ljava/lang/String;I)I",
"intern()Ljava/lang/String;", "isEmpty()Z", "lastIndexOf(I)I", "lastIndexOf(II)I",
"lastIndexOf(Ljava/lang/String;)I", "lastIndexOf(Ljava/lang/String;I)I", "matches(Ljava/lang/String;)Z",
"offsetByCodePoints(II)I", "regionMatches(ILjava/lang/String;II)Z", "regionMatches(ZILjava/lang/String;II)Z",
"replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", "replace(CC)Ljava/lang/String;",
"replaceFirst(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
"replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;",
"split(Ljava/lang/String;I)[Ljava/lang/String;", "split(Ljava/lang/String;)[Ljava/lang/String;",
"startsWith(Ljava/lang/String;I)Z", "startsWith(Ljava/lang/String;)Z", "substring(II)Ljava/lang/String;",
"substring(I)Ljava/lang/String;", "toCharArray()[C", "toLowerCase()Ljava/lang/String;",
"toLowerCase(Ljava/util/Locale;)Ljava/lang/String;", "toUpperCase()Ljava/lang/String;",
"toUpperCase(Ljava/util/Locale;)Ljava/lang/String;", "trim()Ljava/lang/String;") +
inJavaLang("Double", "isInfinite()Z", "isNaN()Z") +
inJavaLang("Float", "isInfinite()Z", "isNaN()Z") +
inJavaLang("Enum", "getDeclaringClass()Ljava/lang/Class;", "finalize()V")
}
private fun buildPrimitiveValueMethodsSet(): Set<String> =
signatures {
listOf(JvmPrimitiveType.BOOLEAN, JvmPrimitiveType.CHAR).flatMapTo(LinkedHashSet()) {
inJavaLang(it.wrapperFqName.shortName().asString(), "${it.javaKeywordName}Value()${it.desc}")
}
}
val WHITE_LIST_METHOD_SIGNATURES: Set<String> =
signatures {
inJavaLang("CharSequence",
"codePoints()Ljava/util/stream/IntStream;", "chars()Ljava/util/stream/IntStream;") +
inJavaUtil("Iterator",
"forEachRemaining(Ljava/util/function/Consumer;)V") +
inJavaLang("Iterable",
"forEach(Ljava/util/function/Consumer;)V", "spliterator()Ljava/util/Spliterator;") +
inJavaLang("Throwable",
"setStackTrace([Ljava/lang/StackTraceElement;)V", "fillInStackTrace()Ljava/lang/Throwable;",
"getLocalizedMessage()Ljava/lang/String;", "printStackTrace()V", "printStackTrace(Ljava/io/PrintStream;)V",
"printStackTrace(Ljava/io/PrintWriter;)V", "getStackTrace()[Ljava/lang/StackTraceElement;",
"initCause(Ljava/lang/Throwable;)Ljava/lang/Throwable;", "getSuppressed()[Ljava/lang/Throwable;",
"addSuppressed(Ljava/lang/Throwable;)V") +
inJavaUtil("Collection",
"spliterator()Ljava/util/Spliterator;", "parallelStream()Ljava/util/stream/Stream;",
"stream()Ljava/util/stream/Stream;", "removeIf(Ljava/util/function/Predicate;)Z") +
inJavaUtil("List",
"replaceAll(Ljava/util/function/UnaryOperator;)V") +
inJavaUtil("Map",
"getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
"forEach(Ljava/util/function/BiConsumer;)V", "replaceAll(Ljava/util/function/BiFunction;)V",
"merge(Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;",
"computeIfPresent(Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;",
"putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
"replace(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z",
"replace(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
"computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;",
"compute(Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;")
}
val MUTABLE_METHOD_SIGNATURES: Set<String> =
signatures {
inJavaUtil("Collection", "removeIf(Ljava/util/function/Predicate;)Z") +
inJavaUtil("List", "replaceAll(Ljava/util/function/UnaryOperator;)V", "sort(Ljava/util/Comparator;)V") +
inJavaUtil("Map",
"computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;",
"computeIfPresent(Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;",
"compute(Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;",
"merge(Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;",
"putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
"remove(Ljava/lang/Object;Ljava/lang/Object;)Z", "replaceAll(Ljava/util/function/BiFunction;)V",
"replace(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
"replace(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z")
}
val BLACK_LIST_CONSTRUCTOR_SIGNATURES: Set<String> =
signatures {
buildPrimitiveStringConstructorsSet() +
inJavaLang("Float", *constructors("D")) +
inJavaLang("String", *constructors(
"[C", "[CII", "[III", "[BIILjava/lang/String;",
"[BIILjava/nio/charset/Charset;",
"[BLjava/lang/String;",
"[BLjava/nio/charset/Charset;",
"[BII", "[B",
"Ljava/lang/StringBuffer;",
"Ljava/lang/StringBuilder;"
))
}
val WHITE_LIST_CONSTRUCTOR_SIGNATURES: Set<String> =
signatures {
inJavaLang("Throwable", *constructors("Ljava/lang/String;Ljava/lang/Throwable;ZZ"))
}
private fun buildPrimitiveStringConstructorsSet(): Set<String> =
signatures {
listOf(JvmPrimitiveType.BOOLEAN, JvmPrimitiveType.BYTE, JvmPrimitiveType.DOUBLE, JvmPrimitiveType.FLOAT,
JvmPrimitiveType.BYTE, JvmPrimitiveType.INT, JvmPrimitiveType.LONG, JvmPrimitiveType.SHORT
).flatMapTo(LinkedHashSet()) {
// java/lang/<Wrapper>.<init>(Ljava/lang/String;)V
inJavaLang(it.wrapperFqName.shortName().asString(), *constructors("Ljava/lang/String;"))
}
}
}
}
private class FallbackBuiltIns private constructor() : KotlinBuiltIns(LockBasedStorageManager()) {
init {
createBuiltInsModule()
}
companion object {
private val initializer = BuiltInsInitializer {
FallbackBuiltIns()
}
@JvmStatic
val Instance: KotlinBuiltIns
get() = initializer.get()
}
override fun getPlatformDependentDeclarationFilter() = PlatformDependentDeclarationFilter.All
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
/**
* The version of the metadata serialized by the compiler and deserialized by the compiler and reflection.
* This version includes the version of the core protobuf messages (descriptors.proto) as well as JVM extensions (jvm_descriptors.proto).
*/
class JvmMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
// NOTE: 1.1 is incompatible with 1.0 and hence with any other version except 1.1.*
override fun isCompatible() =
this.major == 1 && this.minor == 1
companion object {
@JvmField
val INSTANCE = JvmMetadataVersion(1, 1, 9)
@JvmField
val INVALID_VERSION = JvmMetadataVersion()
}
}
@@ -0,0 +1,136 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.StringTableTypes.Record.Operation.*
import java.util.*
class JvmNameResolver(
private val types: JvmProtoBuf.StringTableTypes,
private val strings: Array<String>
) : NameResolver {
private val localNameIndices = types.localNameList.run { if (isEmpty()) emptySet() else toSet() }
// Here we expand the 'range' field of the Record message for simplicity to a list of records
private val records: List<Record> = ArrayList<Record>().apply {
val records = types.recordList
this.ensureCapacity(records.size)
for (record in records) {
repeat(record.range) {
this.add(record)
}
}
this.trimToSize()
}
override fun getString(index: Int): String {
val record = records[index]
var string = when {
record.hasString() -> record.string
record.hasPredefinedIndex() && record.predefinedIndex in PREDEFINED_STRINGS.indices ->
PREDEFINED_STRINGS[record.predefinedIndex]
else -> strings[index]
}
if (record.substringIndexCount >= 2) {
val (begin, end) = record.substringIndexList
if (0 <= begin && begin <= end && end <= string.length) {
string = string.substring(begin, end)
}
}
if (record.replaceCharCount >= 2) {
val (from, to) = record.replaceCharList
string = string.replace(from.toChar(), to.toChar())
}
when (record.operation ?: NONE) {
NONE -> {
// Do nothing
}
INTERNAL_TO_CLASS_ID -> {
string = string.replace('$', '.')
}
DESC_TO_CLASS_ID -> {
if (string.length >= 2) {
string = string.substring(1, string.length - 1)
}
string = string.replace('$', '.')
}
}
return string
}
override fun getName(index: Int) = Name.guessByFirstCharacter(getString(index))
override fun getClassId(index: Int): ClassId {
val string = getString(index)
val lastSlash = string.lastIndexOf('/')
val packageName =
if (lastSlash < 0) FqName.ROOT
else FqName(string.substring(0, lastSlash).replace('/', '.'))
val className = FqName(string.substring(lastSlash + 1))
return ClassId(packageName, className, index in localNameIndices)
}
companion object {
val PREDEFINED_STRINGS = listOf(
"kotlin/Any",
"kotlin/Nothing",
"kotlin/Unit",
"kotlin/Throwable",
"kotlin/Number",
"kotlin/Byte", "kotlin/Double", "kotlin/Float", "kotlin/Int",
"kotlin/Long", "kotlin/Short", "kotlin/Boolean", "kotlin/Char",
"kotlin/CharSequence",
"kotlin/String",
"kotlin/Comparable",
"kotlin/Enum",
"kotlin/Array",
"kotlin/ByteArray", "kotlin/DoubleArray", "kotlin/FloatArray", "kotlin/IntArray",
"kotlin/LongArray", "kotlin/ShortArray", "kotlin/BooleanArray", "kotlin/CharArray",
"kotlin/Cloneable",
"kotlin/Annotation",
"kotlin/collections/Iterable", "kotlin/collections/MutableIterable",
"kotlin/collections/Collection", "kotlin/collections/MutableCollection",
"kotlin/collections/List", "kotlin/collections/MutableList",
"kotlin/collections/Set", "kotlin/collections/MutableSet",
"kotlin/collections/Map", "kotlin/collections/MutableMap",
"kotlin/collections/Map.Entry", "kotlin/collections/MutableMap.MutableEntry",
"kotlin/collections/Iterator", "kotlin/collections/MutableIterator",
"kotlin/collections/ListIterator", "kotlin/collections/MutableListIterator"
)
private val PREDEFINED_STRINGS_MAP = PREDEFINED_STRINGS.withIndex().associateBy({ it.value }, { it.index })
fun getPredefinedStringIndex(string: String): Int? = PREDEFINED_STRINGS_MAP[string]
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.descriptors.SourceFile
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
class JvmPackagePartSource(
val className: JvmClassName,
val facadeClassName: JvmClassName?,
override val incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
override val isPreReleaseInvisible: Boolean = false,
val knownJvmBinaryClass: KotlinJvmBinaryClass? = null
) : DeserializedContainerSource {
constructor(
kotlinClass: KotlinJvmBinaryClass,
incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
isPreReleaseInvisible: Boolean = false
) : this(
JvmClassName.byClassId(kotlinClass.classId),
kotlinClass.classHeader.multifileClassName?.let {
if (it.isNotEmpty()) JvmClassName.byInternalName(it) else null
},
incompatibility,
isPreReleaseInvisible,
kotlinClass
)
override val presentableString: String
get() = "Class '${classId.asSingleFqName().asString()}'"
val simpleName: Name get() = Name.identifier(className.internalName.substringAfterLast('/'))
val classId: ClassId get() = ClassId(className.packageFqName, simpleName)
override fun toString() = "${this::class.java.simpleName}: $className"
override fun getContainingFile(): SourceFile = SourceFile.NO_SOURCE_FILE
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.KotlinMetadataFinder
interface KotlinClassFinder : KotlinMetadataFinder {
fun findKotlinClass(classId: ClassId): KotlinJvmBinaryClass?
fun findKotlinClass(javaClass: JavaClass): KotlinJvmBinaryClass?
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
interface KotlinJvmBinaryClass {
val classId: ClassId
/**
* @return path to the class file (to be reported to the user upon error)
*/
val location: String
fun loadClassAnnotations(visitor: AnnotationVisitor, cachedContents: ByteArray?)
fun visitMembers(visitor: MemberVisitor, cachedContents: ByteArray?)
val classHeader: KotlinClassHeader
interface MemberVisitor {
// TODO: abstract signatures for methods and fields instead of ASM 'desc' strings?
fun visitMethod(name: Name, desc: String): MethodAnnotationVisitor?
fun visitField(name: Name, desc: String, initializer: Any?): AnnotationVisitor?
}
interface AnnotationVisitor {
fun visitAnnotation(classId: ClassId, source: SourceElement): AnnotationArgumentVisitor?
fun visitEnd()
}
interface MethodAnnotationVisitor : AnnotationVisitor {
fun visitParameterAnnotation(index: Int, classId: ClassId, source: SourceElement): AnnotationArgumentVisitor?
}
interface AnnotationArgumentVisitor {
// TODO: class literals
fun visit(name: Name?, value: Any?)
fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name)
fun visitAnnotation(name: Name, classId: ClassId): AnnotationArgumentVisitor?
fun visitArray(name: Name): AnnotationArrayArgumentVisitor?
fun visitEnd()
}
interface AnnotationArrayArgumentVisitor {
fun visit(value: Any?)
fun visitEnum(enumClassId: ClassId, enumEntryName: Name)
fun visitEnd()
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.SourceFile
import org.jetbrains.kotlin.load.java.descriptors.getImplClassNameForDeserialized
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
class KotlinJvmBinaryPackageSourceElement(
private val packageFragment: LazyJavaPackageFragment
) : SourceElement {
override fun toString() = "$packageFragment: ${packageFragment.binaryClasses.keys}"
override fun getContainingFile(): SourceFile = SourceFile.NO_SOURCE_FILE
fun getRepresentativeBinaryClass(): KotlinJvmBinaryClass {
return packageFragment.binaryClasses.values.first()
}
fun getContainingBinaryClass(descriptor: DeserializedMemberDescriptor): KotlinJvmBinaryClass? {
val name = descriptor.getImplClassNameForDeserialized() ?: return null
return packageFragment.binaryClasses[name.internalName]
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.descriptors.SourceFile
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
class KotlinJvmBinarySourceElement(
val binaryClass: KotlinJvmBinaryClass,
override val incompatibility: IncompatibleVersionErrorData<JvmMetadataVersion>? = null,
override val isPreReleaseInvisible: Boolean = false
) : DeserializedContainerSource {
override val presentableString: String
get() = "Class '${binaryClass.classId.asSingleFqName().asString()}'"
override fun getContainingFile(): SourceFile = SourceFile.NO_SOURCE_FILE
override fun toString() = "${this::class.java.simpleName}: $binaryClass"
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
// The purpose of this class is to hold a unique signature of either a method or a field, so that annotations on a member can be put
// into a map indexed by these signatures
data class MemberSignature private constructor(internal val signature: String) {
companion object {
@JvmStatic
fun fromMethod(nameResolver: NameResolver, signature: JvmProtoBuf.JvmMethodSignature): MemberSignature {
return fromMethodNameAndDesc(nameResolver.getString(signature.name), nameResolver.getString(signature.desc))
}
@JvmStatic
fun fromMethodNameAndDesc(name: String, desc: String): MemberSignature {
return MemberSignature(name + desc)
}
@JvmStatic
fun fromMethodNameAndDesc(namePlusDesc: String): MemberSignature {
return MemberSignature(namePlusDesc)
}
@JvmStatic
fun fromFieldNameAndDesc(name: String, desc: String): MemberSignature {
return MemberSignature(name + "#" + desc)
}
@JvmStatic
fun fromMethodSignatureAndParameterIndex(signature: MemberSignature, index: Int): MemberSignature {
return MemberSignature(signature.signature + "@" + index)
}
}
}
@@ -0,0 +1,218 @@
/*
* Copyright 2010-2015 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.load.kotlin
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
import org.jetbrains.kotlin.serialization.jvm.JvmPackageTable
import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.io.IOException
class ModuleMapping private constructor(val packageFqName2Parts: Map<String, PackageParts>, private val debugName: String) {
fun findPackageParts(packageFqName: String): PackageParts? {
return packageFqName2Parts[packageFqName]
}
override fun toString() = debugName
companion object {
@JvmField
val MAPPING_FILE_EXT: String = "kotlin_module"
@JvmField
val EMPTY: ModuleMapping = ModuleMapping(emptyMap(), "EMPTY")
@JvmField
val CORRUPTED: ModuleMapping = ModuleMapping(emptyMap(), "CORRUPTED")
fun create(
bytes: ByteArray?,
debugName: String,
configuration: DeserializationConfiguration
): ModuleMapping {
if (bytes == null) {
return EMPTY
}
val stream = DataInputStream(ByteArrayInputStream(bytes))
val versionNumber = try {
IntArray(stream.readInt()) { stream.readInt() }
}
catch (e: IOException) {
return CORRUPTED
}
val version = JvmMetadataVersion(*versionNumber)
if (configuration.skipMetadataVersionCheck || version.isCompatible()) {
val table = JvmPackageTable.PackageTable.parseFrom(stream) ?: return EMPTY
val result = linkedMapOf<String, PackageParts>()
for (proto in table.packagePartsList) {
val packageFqName = proto.packageFqName
val packageParts = result.getOrPut(packageFqName) { PackageParts(packageFqName) }
for ((index, partShortName) in proto.shortClassNameList.withIndex()) {
val multifileFacadeId = proto.multifileFacadeShortNameIdList.getOrNull(index)?.minus(1)
val facadeShortName = multifileFacadeId?.let(proto.multifileFacadeShortNameList::getOrNull)
val facadeInternalName = facadeShortName?.let { internalNameOf(packageFqName, it) }
packageParts.addPart(internalNameOf(packageFqName, partShortName), facadeInternalName)
}
if (configuration.isJvmPackageNameSupported) {
for ((index, partShortName) in proto.classWithJvmPackageNameShortNameList.withIndex()) {
val packageId = proto.classWithJvmPackageNamePackageIdList.getOrNull(index)
?: proto.classWithJvmPackageNamePackageIdList.lastOrNull()
?: continue
val jvmPackageName = table.jvmPackageNameList.getOrNull(packageId) ?: continue
packageParts.addPart(internalNameOf(jvmPackageName, partShortName), null)
}
}
}
for (proto in table.metadataPartsList) {
val packageParts = result.getOrPut(proto.packageFqName) { PackageParts(proto.packageFqName) }
proto.shortClassNameList.forEach(packageParts::addMetadataPart)
}
return ModuleMapping(result, debugName)
}
else {
// TODO: consider reporting "incompatible ABI version" error for package parts
}
return EMPTY
}
}
}
private fun internalNameOf(packageFqName: String, className: String): String =
JvmClassName.byFqNameWithoutInnerClasses(FqName(packageFqName).child(Name.identifier(className))).internalName
class PackageParts(val packageFqName: String) {
// JVM internal name of package part -> JVM internal name of the corresponding multifile facade (or null, if it's not a multifile part)
private val packageParts = linkedMapOf<String, String?>()
val parts: Set<String> get() = packageParts.keys
// Short names of .kotlin_metadata package parts
val metadataParts: Set<String> = linkedSetOf()
fun addPart(partInternalName: String, facadeInternalName: String?) {
packageParts[partInternalName] = facadeInternalName
}
fun removePart(internalName: String) {
packageParts.remove(internalName)
}
fun addMetadataPart(shortName: String) {
(metadataParts as MutableSet /* see KT-14663 */).add(shortName)
}
fun addTo(builder: JvmPackageTable.PackageTable.Builder) {
if (parts.isNotEmpty()) {
builder.addPackageParts(JvmPackageTable.PackageParts.newBuilder().apply {
packageFqName = this@PackageParts.packageFqName
val packageInternalName = packageFqName.replace('.', '/')
val (partsWithinPackage, partsOutsidePackage) = parts.partition { partInternalName ->
partInternalName.packageName == packageInternalName
}
writePartsWithinPackage(partsWithinPackage)
writePartsOutsidePackage(partsOutsidePackage, builder)
})
}
if (metadataParts.isNotEmpty()) {
builder.addMetadataParts(JvmPackageTable.PackageParts.newBuilder().apply {
packageFqName = this@PackageParts.packageFqName
addAllShortClassName(metadataParts.sorted())
})
}
}
private fun JvmPackageTable.PackageParts.Builder.writePartsWithinPackage(parts: List<String>) {
val facadeNameToId = mutableMapOf<String, Int>()
for ((facadeInternalName, partInternalNames) in parts.groupBy { getMultifileFacadeName(it) }.toSortedMap(nullsLast())) {
for (partInternalName in partInternalNames.sorted()) {
addShortClassName(partInternalName.className)
if (facadeInternalName != null) {
addMultifileFacadeShortNameId(1 + facadeNameToId.getOrPut(facadeInternalName.className) { facadeNameToId.size })
}
}
}
for ((facadeId, facadeName) in facadeNameToId.values.zip(facadeNameToId.keys).sortedBy(Pair<Int, String>::first)) {
assert(facadeId == multifileFacadeShortNameCount) { "Multifile facades are loaded incorrectly: $facadeNameToId" }
addMultifileFacadeShortName(facadeName)
}
}
// Writes information about package parts which have a different JVM package from the Kotlin package (with the help of @JvmPackageName)
private fun JvmPackageTable.PackageParts.Builder.writePartsOutsidePackage(
parts: List<String>,
packageTableBuilder: JvmPackageTable.PackageTable.Builder
) {
val packageIds = mutableListOf<Int>()
for ((packageInternalName, partsInPackage) in parts.groupBy { it.packageName }.toSortedMap()) {
val packageFqName = packageInternalName.replace('/', '.')
if (packageFqName !in packageTableBuilder.jvmPackageNameList) {
packageTableBuilder.addJvmPackageName(packageFqName)
}
val packageId = packageTableBuilder.jvmPackageNameList.indexOf(packageFqName)
for (part in partsInPackage.map { it.className }.sorted()) {
addClassWithJvmPackageNameShortName(part)
packageIds.add(packageId)
}
}
// See PackageParts#class_with_jvm_package_name_package_id in jvm_package_table.proto for description of this optimization
while (packageIds.size > 1 && packageIds[packageIds.size - 1] == packageIds[packageIds.size - 2]) {
packageIds.removeAt(packageIds.size - 1)
}
addAllClassWithJvmPackageNamePackageId(packageIds)
}
private val String.packageName: String get() = substringBeforeLast('/', "")
private val String.className: String get() = substringAfterLast('/')
fun getMultifileFacadeName(partInternalName: String): String? = packageParts[partInternalName]
operator fun plusAssign(other: PackageParts) {
for ((partInternalName, facadeInternalName) in other.packageParts) {
addPart(partInternalName, facadeInternalName)
}
other.metadataParts.forEach(this::addMetadataPart)
}
override fun equals(other: Any?) =
other is PackageParts &&
other.packageFqName == packageFqName && other.packageParts == packageParts && other.metadataParts == metadataParts
override fun hashCode() =
(packageFqName.hashCode() * 31 + packageParts.hashCode()) * 31 + metadataParts.hashCode()
override fun toString() =
(parts + metadataParts).toString()
}
@@ -0,0 +1,134 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
class TypeMappingMode private constructor(
val needPrimitiveBoxing: Boolean = true,
val isForAnnotationParameter: Boolean = false,
// Here DeclarationSiteWildcards means wildcard generated because of declaration-site variance
val skipDeclarationSiteWildcards: Boolean = false,
val skipDeclarationSiteWildcardsIfPossible: Boolean = false,
private val genericArgumentMode: TypeMappingMode? = null,
val kotlinCollectionsToJavaCollections: Boolean = true,
private val genericContravariantArgumentMode: TypeMappingMode? = genericArgumentMode,
private val genericInvariantArgumentMode: TypeMappingMode? = genericArgumentMode
) {
companion object {
/**
* kotlin.Int is mapped to Ljava/lang/Integer;
*/
@JvmField
val GENERIC_ARGUMENT = TypeMappingMode()
/**
* kotlin.Int is mapped to I
*/
@JvmField
val DEFAULT = TypeMappingMode(genericArgumentMode = GENERIC_ARGUMENT, needPrimitiveBoxing = false)
/**
* kotlin.Int is mapped to Ljava/lang/Integer;
* No projections allowed in immediate arguments
*/
@JvmField
val SUPER_TYPE = TypeMappingMode(skipDeclarationSiteWildcards = true, genericArgumentMode = GENERIC_ARGUMENT)
@JvmField
val SUPER_TYPE_KOTLIN_COLLECTIONS_AS_IS = TypeMappingMode(
skipDeclarationSiteWildcards = true,
genericArgumentMode = GENERIC_ARGUMENT,
kotlinCollectionsToJavaCollections = false
)
/**
* kotlin.reflect.KClass mapped to java.lang.Class
* Other types mapped as DEFAULT
*/
@JvmField
val VALUE_FOR_ANNOTATION = TypeMappingMode(
isForAnnotationParameter = true,
needPrimitiveBoxing = false,
genericArgumentMode = TypeMappingMode(isForAnnotationParameter = true, genericArgumentMode = GENERIC_ARGUMENT))
@JvmStatic
fun getModeForReturnTypeNoGeneric(
isAnnotationMethod: Boolean
) = if (isAnnotationMethod) VALUE_FOR_ANNOTATION else DEFAULT
@JvmStatic
fun getOptimalModeForValueParameter(
type: KotlinType
) = getOptimalModeForSignaturePart(type, isForAnnotationParameter = false, canBeUsedInSupertypePosition = true)
@JvmStatic
fun getOptimalModeForReturnType(
type: KotlinType,
isAnnotationMethod: Boolean
) = getOptimalModeForSignaturePart(type, isForAnnotationParameter = isAnnotationMethod, canBeUsedInSupertypePosition = false)
private fun getOptimalModeForSignaturePart(
type: KotlinType,
isForAnnotationParameter: Boolean,
canBeUsedInSupertypePosition: Boolean
): TypeMappingMode {
if (type.arguments.isEmpty()) return DEFAULT
val contravariantArgumentMode =
if (!canBeUsedInSupertypePosition)
TypeMappingMode(
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = false,
skipDeclarationSiteWildcardsIfPossible = true)
else
null
val invariantArgumentMode =
if (canBeUsedInSupertypePosition)
getOptimalModeForSignaturePart(type, isForAnnotationParameter, canBeUsedInSupertypePosition = false)
else
null
return TypeMappingMode(
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = !canBeUsedInSupertypePosition,
skipDeclarationSiteWildcardsIfPossible = true,
genericContravariantArgumentMode = contravariantArgumentMode,
genericInvariantArgumentMode = invariantArgumentMode)
}
@JvmStatic
fun createWithConstantDeclarationSiteWildcardsMode(
skipDeclarationSiteWildcards: Boolean,
isForAnnotationParameter: Boolean,
fallbackMode: TypeMappingMode? = null
) = TypeMappingMode(
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = skipDeclarationSiteWildcards,
genericArgumentMode = fallbackMode)
}
fun toGenericArgumentMode(effectiveVariance: Variance): TypeMappingMode =
when (effectiveVariance) {
Variance.IN_VARIANCE -> genericContravariantArgumentMode ?: this
Variance.INVARIANT -> genericInvariantArgumentMode ?: this
else -> genericArgumentMode ?: this
}
}
@@ -0,0 +1,102 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl
import org.jetbrains.kotlin.descriptors.annotations.BuiltInAnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations
import org.jetbrains.kotlin.types.*
internal class UnsafeVarianceTypeSubstitution(builtIns: KotlinBuiltIns) : TypeSubstitution() {
private val unsafeVarianceAnnotations = AnnotationsImpl(listOf(
BuiltInAnnotationDescriptor(builtIns, KotlinBuiltIns.FQ_NAMES.unsafeVariance, emptyMap())
))
override fun get(key: KotlinType) = null
override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance): KotlinType {
val unsafeVariancePaths = mutableListOf<List<Int>>()
IndexedTypeHolder(topLevelType).checkTypePosition(
position,
{ _, indexedTypeHolder, _ ->
unsafeVariancePaths.add(indexedTypeHolder.argumentIndices)
},
customVariance = { null })
return topLevelType.unwrap().annotatePartsWithUnsafeVariance(unsafeVariancePaths)
}
private fun UnwrappedType.annotatePartsWithUnsafeVariance(unsafeVariancePaths: Collection<List<Int>>): UnwrappedType {
if (unsafeVariancePaths.isEmpty()) return this
return when (this) {
is FlexibleType ->
KotlinTypeFactory.flexibleType(
lowerBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 0)),
upperBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 1))
).inheritEnhancement(this)
is SimpleType -> annotatePartsWithUnsafeVariance(unsafeVariancePaths)
}
}
private fun SimpleType.annotatePartsWithUnsafeVariance(unsafeVariancePaths: Collection<List<Int>>): SimpleType {
if (unsafeVariancePaths.isEmpty()) return this
// if root is unsafe
if (emptyList<Int>() in unsafeVariancePaths) {
return replaceAnnotations(composeAnnotations(annotations, unsafeVarianceAnnotations))
}
return replace(newArguments = arguments.withIndex().map {
val (index, argument) = it
if (argument.isStarProjection) return@map argument
TypeProjectionImpl(
argument.projectionKind,
argument.type.unwrap().annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, index)))
})
}
private fun subPathsWithIndex(paths: Collection<List<Int>>, index: Int) = paths.filter { it[0] == index }.map { it.subList(1, it.size) }
private class IndexedTypeHolder(
override val type: KotlinType,
val argumentIndices: List<Int> = emptyList()
) : TypeHolder<IndexedTypeHolder> {
override val flexibleBounds: Pair<IndexedTypeHolder, IndexedTypeHolder>? get() =
if (type.isFlexible())
Pair(
IndexedTypeHolder(type.lowerIfFlexible(), argumentIndices + 0),
IndexedTypeHolder(type.upperIfFlexible(), argumentIndices + 1))
else null
override val arguments: List<TypeHolderArgument<IndexedTypeHolder>>
get() = type.arguments.withIndex().map { projectionWithIndex ->
val (index, projection) = projectionWithIndex
object : TypeHolderArgument<IndexedTypeHolder> {
override val projection: TypeProjection
get() = projection
override val typeParameter: TypeParameterDescriptor?
get() = type.constructor.parameters[index]
override val holder: IndexedTypeHolder
get() = IndexedTypeHolder(projection.type, argumentIndices + index)
}
}
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2015 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.load.kotlin.header
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.MultifileClassKind.DELEGATING
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.MultifileClassKind.INHERITING
class KotlinClassHeader(
val kind: KotlinClassHeader.Kind,
val metadataVersion: JvmMetadataVersion,
val bytecodeVersion: JvmBytecodeBinaryVersion,
val data: Array<String>?,
val incompatibleData: Array<String>?,
val strings: Array<String>?,
val extraString: String?,
val extraInt: Int,
val packageName: String?
) {
// See kotlin.Metadata
enum class Kind(val id: Int) {
UNKNOWN(0),
CLASS(1),
FILE_FACADE(2),
SYNTHETIC_CLASS(3),
MULTIFILE_CLASS(4),
MULTIFILE_CLASS_PART(5);
companion object {
private val entryById = values().associateBy(Kind::id)
@JvmStatic
fun getById(id: Int) = entryById[id] ?: UNKNOWN
}
}
enum class MultifileClassKind {
DELEGATING,
INHERITING;
}
val multifileClassName: String?
get() = extraString.takeIf { kind == Kind.MULTIFILE_CLASS_PART }
val multifilePartNames: List<String>
get() = data.takeIf { kind == Kind.MULTIFILE_CLASS }?.asList().orEmpty()
// TODO: use in incremental compilation
val multifileClassKind: MultifileClassKind?
get() = if (kind == Kind.MULTIFILE_CLASS || kind == Kind.MULTIFILE_CLASS_PART) {
if ((extraInt and JvmAnnotationNames.METADATA_MULTIFILE_PARTS_INHERIT_FLAG) != 0)
INHERITING
else
DELEGATING
}
else
null
val isPreRelease: Boolean
get() = (extraInt and JvmAnnotationNames.METADATA_PRE_RELEASE_FLAG) != 0
val isScript: Boolean
get() = (extraInt and JvmAnnotationNames.METADATA_SCRIPT_FLAG) != 0
override fun toString() = "$kind version=$metadataVersion"
}
@@ -0,0 +1,312 @@
/*
* Copyright 2010-2015 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.load.kotlin.header;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.SourceElement;
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion;
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.*;
import static org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.*;
import static org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.*;
public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
private static final boolean IGNORE_OLD_METADATA = "true".equals(System.getProperty("kotlin.ignore.old.metadata"));
private static final Map<ClassId, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<ClassId, KotlinClassHeader.Kind>();
static {
// TODO: delete this at some point
HEADER_KINDS.put(ClassId.topLevel(new FqName("kotlin.jvm.internal.KotlinClass")), CLASS);
HEADER_KINDS.put(ClassId.topLevel(new FqName("kotlin.jvm.internal.KotlinFileFacade")), FILE_FACADE);
HEADER_KINDS.put(ClassId.topLevel(new FqName("kotlin.jvm.internal.KotlinMultifileClass")), MULTIFILE_CLASS);
HEADER_KINDS.put(ClassId.topLevel(new FqName("kotlin.jvm.internal.KotlinMultifileClassPart")), MULTIFILE_CLASS_PART);
HEADER_KINDS.put(ClassId.topLevel(new FqName("kotlin.jvm.internal.KotlinSyntheticClass")), SYNTHETIC_CLASS);
}
private JvmMetadataVersion metadataVersion = null;
private JvmBytecodeBinaryVersion bytecodeVersion = null;
private String extraString = null;
private int extraInt = 0;
private String packageName = null;
private String[] data = null;
private String[] strings = null;
private String[] incompatibleData = null;
private KotlinClassHeader.Kind headerKind = null;
@Nullable
public KotlinClassHeader createHeader() {
if (headerKind == null) {
return null;
}
if (!metadataVersion.isCompatible()) {
incompatibleData = data;
}
if (metadataVersion == null || !metadataVersion.isCompatible()) {
data = null;
}
else if (shouldHaveData() && data == null) {
// This means that the annotation is found and its ABI version is compatible, but there's no "data" string array in it.
// We tell the outside world that there's really no annotation at all
return null;
}
return new KotlinClassHeader(
headerKind,
metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION,
bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION,
data,
incompatibleData,
strings,
extraString,
extraInt,
packageName
);
}
private boolean shouldHaveData() {
return headerKind == CLASS ||
headerKind == FILE_FACADE ||
headerKind == MULTIFILE_CLASS_PART;
}
@Nullable
@Override
public AnnotationArgumentVisitor visitAnnotation(@NotNull ClassId classId, @NotNull SourceElement source) {
FqName fqName = classId.asSingleFqName();
if (fqName.equals(METADATA_FQ_NAME)) {
return new KotlinMetadataArgumentVisitor();
}
if (IGNORE_OLD_METADATA) return null;
if (headerKind != null) {
// Ignore all Kotlin annotations except the first found
return null;
}
KotlinClassHeader.Kind newKind = HEADER_KINDS.get(classId);
if (newKind != null) {
headerKind = newKind;
return new OldDeprecatedAnnotationArgumentVisitor();
}
return null;
}
@Override
public void visitEnd() {
}
private class KotlinMetadataArgumentVisitor implements AnnotationArgumentVisitor {
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name == null) return;
String string = name.asString();
if (KIND_FIELD_NAME.equals(string)) {
if (value instanceof Integer) {
headerKind = KotlinClassHeader.Kind.getById((Integer) value);
}
}
else if (METADATA_VERSION_FIELD_NAME.equals(string)) {
if (value instanceof int[]) {
metadataVersion = new JvmMetadataVersion((int[]) value);
}
}
else if (BYTECODE_VERSION_FIELD_NAME.equals(string)) {
if (value instanceof int[]) {
bytecodeVersion = new JvmBytecodeBinaryVersion((int[]) value);
}
}
else if (METADATA_EXTRA_STRING_FIELD_NAME.equals(string)) {
if (value instanceof String) {
extraString = (String) value;
}
}
else if (METADATA_EXTRA_INT_FIELD_NAME.equals(string)) {
if (value instanceof Integer) {
extraInt = (Integer) value;
}
}
else if (METADATA_PACKAGE_NAME_FIELD_NAME.equals(string)) {
if (value instanceof String) {
packageName = (String) value;
}
}
}
@Override
@Nullable
public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
String string = name.asString();
if (METADATA_DATA_FIELD_NAME.equals(string)) {
return dataArrayVisitor();
}
else if (METADATA_STRINGS_FIELD_NAME.equals(string)) {
return stringsArrayVisitor();
}
else {
return null;
}
}
@NotNull
private AnnotationArrayArgumentVisitor dataArrayVisitor() {
return new CollectStringArrayAnnotationVisitor() {
@Override
protected void visitEnd(@NotNull String[] result) {
data = result;
}
};
}
@NotNull
private AnnotationArrayArgumentVisitor stringsArrayVisitor() {
return new CollectStringArrayAnnotationVisitor() {
@Override
protected void visitEnd(@NotNull String[] result) {
strings = result;
}
};
}
@Override
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
}
@Nullable
@Override
public AnnotationArgumentVisitor visitAnnotation(@NotNull Name name, @NotNull ClassId classId) {
return null;
}
@Override
public void visitEnd() {
}
}
private class OldDeprecatedAnnotationArgumentVisitor implements AnnotationArgumentVisitor {
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name == null) return;
String string = name.asString();
if ("version".equals(string)) {
if (value instanceof int[]) {
metadataVersion = new JvmMetadataVersion((int[]) value);
// If there's no bytecode binary version in the class file, we assume it to be equal to the metadata version
if (bytecodeVersion == null) {
bytecodeVersion = new JvmBytecodeBinaryVersion((int[]) value);
}
}
}
else if ("multifileClassName".equals(string)) {
extraString = value instanceof String ? (String) value : null;
}
}
@Override
@Nullable
public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
String string = name.asString();
if ("data".equals(string) || "filePartClassNames".equals(string)) {
return dataArrayVisitor();
}
else if ("strings".equals(string)) {
return stringsArrayVisitor();
}
else {
return null;
}
}
@NotNull
private AnnotationArrayArgumentVisitor dataArrayVisitor() {
return new CollectStringArrayAnnotationVisitor() {
@Override
protected void visitEnd(@NotNull String[] data) {
ReadKotlinClassHeaderAnnotationVisitor.this.data = data;
}
};
}
@NotNull
private AnnotationArrayArgumentVisitor stringsArrayVisitor() {
return new CollectStringArrayAnnotationVisitor() {
@Override
protected void visitEnd(@NotNull String[] data) {
strings = data;
}
};
}
@Override
public void visitEnum(@NotNull Name name, @NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
}
@Nullable
@Override
public AnnotationArgumentVisitor visitAnnotation(@NotNull Name name, @NotNull ClassId classId) {
return null;
}
@Override
public void visitEnd() {
}
}
private abstract static class CollectStringArrayAnnotationVisitor implements AnnotationArrayArgumentVisitor {
private final List<String> strings;
public CollectStringArrayAnnotationVisitor() {
this.strings = new ArrayList<String>();
}
@Override
public void visit(@Nullable Object value) {
if (value instanceof String) {
strings.add((String) value);
}
}
@Override
public void visitEnum(@NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
}
@Override
public void visitEnd() {
//noinspection SSBasedInspection
visitEnd(strings.toArray(new String[strings.size()]));
}
protected abstract void visitEnd(@NotNull String[] data);
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.ClassId
import java.util.*
inline fun <T> signatures(block: SignatureBuildingComponents.() -> T) = with(SignatureBuildingComponents, block)
object SignatureBuildingComponents {
fun javaLang(name: String) = "java/lang/$name"
fun javaUtil(name: String) = "java/util/$name"
fun javaFunction(name: String) = "java/util/function/$name"
fun constructors(vararg signatures: String) = signatures.map { "<init>($it)V" }.toTypedArray()
fun inJavaLang(name: String, vararg signatures: String) = inClass(javaLang(name), *signatures)
fun inJavaUtil(name: String, vararg signatures: String) = inClass(javaUtil(name), *signatures)
fun inClass(internalName: String, vararg signatures: String) = signatures.mapTo(LinkedHashSet()) { internalName + "." + it }
fun signature(classDescriptor: ClassDescriptor, jvmDescriptor: String) = signature(classDescriptor.internalName, jvmDescriptor)
fun signature(classId: ClassId, jvmDescriptor: String) = signature(classId.internalName, jvmDescriptor)
fun signature(internalName: String, jvmDescriptor: String) = internalName + "." + jvmDescriptor
fun jvmDescriptor(name: String, vararg parameters: String, ret: String = "V") =
jvmDescriptor(name, parameters.asList(), ret)
fun jvmDescriptor(name: String, parameters: List<String>, ret: String = "V") =
"$name(${parameters.joinToString("") { escapeClassName(it) }})${escapeClassName(internalName = ret)}"
private fun escapeClassName(internalName: String) = if (internalName.length > 1) "L$internalName;" else internalName
}
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
import org.jetbrains.kotlin.load.java.isFromJavaOrBuiltins
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.KotlinType
fun FunctionDescriptor.computeJvmDescriptor(withReturnType: Boolean = true)
= StringBuilder().apply {
append(if (this@computeJvmDescriptor is ConstructorDescriptor) "<init>" else name.asString())
append("(")
valueParameters.forEach {
appendErasedType(it.type)
}
append(")")
if (withReturnType) {
if (hasVoidReturnType(this@computeJvmDescriptor)) {
append("V")
}
else {
appendErasedType(returnType!!)
}
}
}.toString()
// Boxing is only necessary for 'remove(E): Boolean' of a MutableCollection<Int> implementation
// Otherwise this method might clash with 'remove(I): E' defined in the java.util.List JDK interface (mapped to kotlin 'removeAt')
fun forceSingleValueParameterBoxing(f: CallableDescriptor): Boolean {
if (f !is FunctionDescriptor) return false
if (f.valueParameters.size != 1 || f.isFromJavaOrBuiltins() || f.name.asString() != "remove") return false
if ((f.original.valueParameters.single().type.mapToJvmType() as? JvmType.Primitive)?.jvmPrimitiveType != JvmPrimitiveType.INT) return false
val overridden =
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f)
?: return false
val overriddenParameterType = overridden.original.valueParameters.single().type.mapToJvmType()
return overridden.containingDeclaration.fqNameUnsafe == KotlinBuiltIns.FQ_NAMES.mutableCollection.toUnsafe()
&& overriddenParameterType is JvmType.Object && overriddenParameterType.internalName == "java/lang/Object"
}
// This method only returns not-null for class methods
internal fun CallableDescriptor.computeJvmSignature(): String? = signatures {
if (DescriptorUtils.isLocal(this@computeJvmSignature)) return null
val classDescriptor = containingDeclaration as? ClassDescriptor ?: return null
if (classDescriptor.name.isSpecial) return null
signature(
classDescriptor,
(original as? SimpleFunctionDescriptor ?: return null).computeJvmDescriptor()
)
}
internal val ClassDescriptor.internalName: String
get() {
JavaToKotlinClassMap.mapKotlinToJava(fqNameSafe.toUnsafe())?.let {
return JvmClassName.byClassId(it).internalName
}
return computeInternalName(this)
}
internal val ClassId.internalName: String
get() {
return JvmClassName.byClassId(JavaToKotlinClassMap.mapKotlinToJava(asSingleFqName().toUnsafe()) ?: this).internalName
}
private fun StringBuilder.appendErasedType(type: KotlinType) {
append(type.mapToJvmType())
}
internal fun KotlinType.mapToJvmType() =
mapType(this, JvmTypeFactoryImpl, TypeMappingMode.DEFAULT, TypeMappingConfigurationImpl, descriptorTypeWriter = null)
sealed class JvmType {
// null means 'void'
class Primitive(val jvmPrimitiveType: JvmPrimitiveType?) : JvmType()
class Object(val internalName: String) : JvmType()
class Array(val elementType: JvmType) : JvmType()
override fun toString() = JvmTypeFactoryImpl.toString(this)
}
private object JvmTypeFactoryImpl : JvmTypeFactory<JvmType> {
override fun boxType(possiblyPrimitiveType: JvmType) =
when {
possiblyPrimitiveType is JvmType.Primitive && possiblyPrimitiveType.jvmPrimitiveType != null ->
createObjectType(
JvmClassName.byFqNameWithoutInnerClasses(
possiblyPrimitiveType.jvmPrimitiveType.wrapperFqName).internalName)
else -> possiblyPrimitiveType
}
override fun createFromString(representation: String): JvmType {
assert(representation.length > 0) { "empty string as JvmType" }
val firstChar = representation[0]
JvmPrimitiveType.values().firstOrNull { it.desc[0] == firstChar }?.let {
return JvmType.Primitive(it)
}
return when (firstChar) {
'V' -> JvmType.Primitive(null)
'[' -> JvmType.Array(createFromString(representation.substring(1)))
else -> {
assert(firstChar == 'L' && representation.endsWith(';')) {
"Type that is not primitive nor array should be Object, but '$representation' was found"
}
JvmType.Object(representation.substring(1, representation.length - 1))
}
}
}
override fun createObjectType(internalName: String) = JvmType.Object(internalName)
override fun toString(type: JvmType): String =
when (type) {
is JvmType.Array -> "[" + toString(type.elementType)
is JvmType.Primitive -> type.jvmPrimitiveType?.desc ?: "V"
is JvmType.Object -> "L" + type.internalName + ";"
}
override val javaLangClassType: JvmType
get() = createObjectType("java/lang/Class")
}
internal object TypeMappingConfigurationImpl : TypeMappingConfiguration<JvmType> {
override fun commonSupertype(types: Collection<KotlinType>): KotlinType {
throw AssertionError("There should be no intersection type in existing descriptors, but found: " + types.joinToString())
}
override fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor) = null
override fun getPredefinedInternalNameForClass(classDescriptor: ClassDescriptor): String? = null
override fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor) {
// DO nothing
}
}
@@ -0,0 +1,288 @@
/*
* Copyright 2010-2016 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.load.kotlin
import org.jetbrains.kotlin.builtins.FAKE_CONTINUATION_CLASS_DESCRIPTOR
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.builtins.transformSuspendFunctionToRuntimeFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.kotlin.utils.DO_NOTHING_3
interface JvmTypeFactory<T : Any> {
fun boxType(possiblyPrimitiveType: T): T
fun createFromString(representation: String): T
fun createObjectType(internalName: String): T
fun toString(type: T): String
val javaLangClassType: T
}
private fun <T : Any> JvmTypeFactory<T>.boxTypeIfNeeded(possiblyPrimitiveType: T, needBoxedType: Boolean) =
if (needBoxedType) boxType(possiblyPrimitiveType) else possiblyPrimitiveType
interface TypeMappingConfiguration<out T : Any> {
private companion object {
private val DEFAULT_INNER_CLASS_NAME_FACTORY = fun(outer: String, inner: String) = outer + "$" + inner
}
val innerClassNameFactory: (outer: String, inner: String) -> String
get() = DEFAULT_INNER_CLASS_NAME_FACTORY
fun commonSupertype(types: Collection<@JvmSuppressWildcards KotlinType>): KotlinType
fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor): T?
fun getPredefinedInternalNameForClass(classDescriptor: ClassDescriptor): String?
fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor)
}
const val NON_EXISTENT_CLASS_NAME = "error/NonExistentClass"
private val CONTINUATION_INTERNAL_NAME =
JvmClassName.byClassId(ClassId.topLevel(DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME)).internalName
fun <T : Any> mapType(
kotlinType: KotlinType,
factory: JvmTypeFactory<T>,
mode: TypeMappingMode,
typeMappingConfiguration: TypeMappingConfiguration<T>,
descriptorTypeWriter: JvmDescriptorTypeWriter<T>?,
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3
): T {
if (kotlinType.isSuspendFunctionType) {
return mapType(
transformSuspendFunctionToRuntimeFunctionType(kotlinType),
factory, mode, typeMappingConfiguration, descriptorTypeWriter,
writeGenericType
)
}
mapBuiltInType(kotlinType, factory, mode, typeMappingConfiguration)?.let { builtInType ->
val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing)
writeGenericType(kotlinType, jvmType, mode)
return jvmType
}
val constructor = kotlinType.constructor
if (constructor is IntersectionTypeConstructor) {
val commonSupertype = typeMappingConfiguration.commonSupertype(constructor.supertypes)
// interface In<in E>
// open class A : In<A>
// open class B : In<B>
// commonSupertype(A, B) = In<A & B>
// So replace arguments with star-projections to prevent infinite recursive mapping
// It's not very important because such types anyway are prohibited in declarations
return mapType(
commonSupertype.replaceArgumentsWithStarProjections(),
factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType)
}
val descriptor =
constructor.declarationDescriptor
?: throw UnsupportedOperationException("no descriptor for type constructor of " + kotlinType)
when {
ErrorUtils.isError(descriptor) -> {
val jvmType = factory.createObjectType(NON_EXISTENT_CLASS_NAME)
typeMappingConfiguration.processErrorType(kotlinType, descriptor as ClassDescriptor)
descriptorTypeWriter?.writeClass(jvmType)
return jvmType
}
descriptor is ClassDescriptor && KotlinBuiltIns.isArray(kotlinType) -> {
if (kotlinType.arguments.size != 1) {
throw UnsupportedOperationException("arrays must have one type argument")
}
val memberProjection = kotlinType.arguments[0]
val memberType = memberProjection.type
val arrayElementType: T
if (memberProjection.projectionKind === Variance.IN_VARIANCE) {
arrayElementType = factory.createObjectType("java/lang/Object")
descriptorTypeWriter?.apply {
writeArrayType()
writeClass(arrayElementType)
writeArrayEnd()
}
}
else {
descriptorTypeWriter?.writeArrayType()
arrayElementType =
mapType(
memberType, factory,
mode.toGenericArgumentMode(memberProjection.projectionKind),
typeMappingConfiguration, descriptorTypeWriter, writeGenericType)
descriptorTypeWriter?.writeArrayEnd()
}
return factory.createFromString("[" + factory.toString(arrayElementType))
}
descriptor is ClassDescriptor -> {
val jvmType =
if (mode.isForAnnotationParameter && KotlinBuiltIns.isKClass(descriptor)) {
factory.javaLangClassType
}
else {
typeMappingConfiguration.getPredefinedTypeForClass(descriptor.original)
?: run {
// refer to enum entries by enum type in bytecode unless ASM_TYPE is written
val enumClassIfEnumEntry = if (descriptor.kind == ClassKind.ENUM_ENTRY)
descriptor.containingDeclaration as ClassDescriptor
else descriptor
factory.createObjectType(computeInternalName(enumClassIfEnumEntry.original, typeMappingConfiguration))
}
}
writeGenericType(kotlinType, jvmType, mode)
return jvmType
}
descriptor is TypeParameterDescriptor -> {
val type = mapType(getRepresentativeUpperBound(descriptor),
factory, mode, typeMappingConfiguration, writeGenericType = DO_NOTHING_3, descriptorTypeWriter = null)
descriptorTypeWriter?.writeTypeVariable(descriptor.getName(), type)
return type
}
else -> throw UnsupportedOperationException("Unknown type " + kotlinType)
}
}
fun hasVoidReturnType(descriptor: CallableDescriptor): Boolean {
if (descriptor is ConstructorDescriptor) return true
return KotlinBuiltIns.isUnit(descriptor.returnType!!) && !TypeUtils.isNullableType(descriptor.returnType!!)
&& descriptor !is PropertyGetterDescriptor
}
private fun <T : Any> mapBuiltInType(
type: KotlinType,
typeFactory: JvmTypeFactory<T>,
mode: TypeMappingMode,
typeMappingConfiguration: TypeMappingConfiguration<T>
): T? {
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
if (descriptor === FAKE_CONTINUATION_CLASS_DESCRIPTOR) {
return typeFactory.createObjectType(CONTINUATION_INTERNAL_NAME)
}
val primitiveType = KotlinBuiltIns.getPrimitiveType(descriptor)
if (primitiveType != null) {
val jvmType = typeFactory.createFromString(JvmPrimitiveType.get(primitiveType).desc)
val isNullableInJava = TypeUtils.isNullableType(type) || type.hasEnhancedNullability()
return typeFactory.boxTypeIfNeeded(jvmType, isNullableInJava)
}
val arrayElementType = KotlinBuiltIns.getPrimitiveArrayType(descriptor)
if (arrayElementType != null) {
return typeFactory.createFromString("[" + JvmPrimitiveType.get(arrayElementType).desc)
}
if (KotlinBuiltIns.isUnderKotlinPackage(descriptor)) {
val classId = JavaToKotlinClassMap.mapKotlinToJava(descriptor.fqNameUnsafe)
if (classId != null) {
if (!mode.kotlinCollectionsToJavaCollections &&
JavaToKotlinClassMap.mutabilityMappings.any { it.javaClass == classId }) return null
return typeFactory.createObjectType(JvmClassName.byClassId(classId, typeMappingConfiguration).internalName)
}
}
return null
}
fun computeInternalName(
klass: ClassDescriptor,
typeMappingConfiguration: TypeMappingConfiguration<*> = TypeMappingConfigurationImpl
): String {
val container = klass.containingDeclaration
val name = SpecialNames.safeIdentifier(klass.name).identifier
if (container is PackageFragmentDescriptor) {
val fqName = container.fqName
return if (fqName.isRoot) name else fqName.asString().replace('.', '/') + '/' + name
}
val containerClass = container as? ClassDescriptor ?:
throw IllegalArgumentException("Unexpected container: $container for $klass")
val containerInternalName =
typeMappingConfiguration.getPredefinedInternalNameForClass(containerClass) ?:
computeInternalName(containerClass, typeMappingConfiguration)
return typeMappingConfiguration.innerClassNameFactory(containerInternalName, name)
}
private fun getRepresentativeUpperBound(descriptor: TypeParameterDescriptor): KotlinType {
val upperBounds = descriptor.upperBounds
assert(!upperBounds.isEmpty()) { "Upper bounds should not be empty: " + descriptor }
return upperBounds.firstOrNull {
val classDescriptor = it.constructor.declarationDescriptor as? ClassDescriptor ?: return@firstOrNull false
classDescriptor.kind != ClassKind.INTERFACE && classDescriptor.kind != ClassKind.ANNOTATION_CLASS
} ?: upperBounds.first()
}
open class JvmDescriptorTypeWriter<T : Any>(private val jvmTypeFactory: JvmTypeFactory<T>) {
private var jvmCurrentTypeArrayLevel: Int = 0
protected var jvmCurrentType: T? = null
private set
protected fun clearCurrentType() {
jvmCurrentType = null
jvmCurrentTypeArrayLevel = 0
}
open fun writeArrayType() {
if (jvmCurrentType == null) {
++jvmCurrentTypeArrayLevel
}
}
open fun writeArrayEnd() {
}
open public fun writeClass(objectType: T) {
writeJvmTypeAsIs(objectType)
}
protected fun writeJvmTypeAsIs(type: T) {
if (jvmCurrentType == null) {
jvmCurrentType = jvmTypeFactory.createFromString("[".repeat(jvmCurrentTypeArrayLevel) + jvmTypeFactory.toString(type))
}
}
open fun writeTypeVariable(name: Name, type: T) {
writeJvmTypeAsIs(type)
}
}
@@ -0,0 +1,225 @@
/*
* Copyright 2010-2015 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.platform
import org.jetbrains.kotlin.builtins.CompanionObjectMapping
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import java.util.*
object JavaToKotlinClassMap : PlatformToKotlinClassMap {
private val javaToKotlin = HashMap<FqNameUnsafe, ClassId>()
private val kotlinToJava = HashMap<FqNameUnsafe, ClassId>()
private val mutableToReadOnly = HashMap<FqNameUnsafe, FqName>()
private val readOnlyToMutable = HashMap<FqNameUnsafe, FqName>()
// describes mapping for a java class that has separate readOnly and mutable equivalents in Kotlin
data class PlatformMutabilityMapping(
val javaClass: ClassId,
val kotlinReadOnly: ClassId,
val kotlinMutable: ClassId
)
private inline fun <reified T> mutabilityMapping(kotlinReadOnly: ClassId, kotlinMutable: FqName): PlatformMutabilityMapping {
val mutableClassId = ClassId(kotlinReadOnly.packageFqName, kotlinMutable.tail(kotlinReadOnly.packageFqName), false)
return PlatformMutabilityMapping(classId(T::class.java), kotlinReadOnly, mutableClassId)
}
val mutabilityMappings = listOf(
mutabilityMapping<Iterable<*>>(ClassId.topLevel(FQ_NAMES.iterable), FQ_NAMES.mutableIterable),
mutabilityMapping<Iterator<*>>(ClassId.topLevel(FQ_NAMES.iterator), FQ_NAMES.mutableIterator),
mutabilityMapping<Collection<*>>(ClassId.topLevel(FQ_NAMES.collection), FQ_NAMES.mutableCollection),
mutabilityMapping<List<*>>(ClassId.topLevel(FQ_NAMES.list), FQ_NAMES.mutableList),
mutabilityMapping<Set<*>>(ClassId.topLevel(FQ_NAMES.set), FQ_NAMES.mutableSet),
mutabilityMapping<ListIterator<*>>(ClassId.topLevel(FQ_NAMES.listIterator), FQ_NAMES.mutableListIterator),
mutabilityMapping<Map<*, *>>(ClassId.topLevel(FQ_NAMES.map), FQ_NAMES.mutableMap),
mutabilityMapping<Map.Entry<*, *>>(
ClassId.topLevel(FQ_NAMES.map).createNestedClassId(FQ_NAMES.mapEntry.shortName()), FQ_NAMES.mutableMapEntry
)
)
init {
addTopLevel(Any::class.java, FQ_NAMES.any)
addTopLevel(String::class.java, FQ_NAMES.string)
addTopLevel(CharSequence::class.java, FQ_NAMES.charSequence)
addTopLevel(Throwable::class.java, FQ_NAMES.throwable)
addTopLevel(Cloneable::class.java, FQ_NAMES.cloneable)
addTopLevel(Number::class.java, FQ_NAMES.number)
addTopLevel(Comparable::class.java, FQ_NAMES.comparable)
addTopLevel(Enum::class.java, FQ_NAMES._enum)
addTopLevel(Annotation::class.java, FQ_NAMES.annotation)
for (platformCollection in mutabilityMappings) {
addMapping(platformCollection)
}
for (jvmType in JvmPrimitiveType.values()) {
add(ClassId.topLevel(jvmType.wrapperFqName),
ClassId.topLevel(KotlinBuiltIns.getPrimitiveFqName(jvmType.primitiveType)))
}
for (classId in CompanionObjectMapping.allClassesWithIntrinsicCompanions()) {
add(ClassId.topLevel(FqName("kotlin.jvm.internal." + classId.shortClassName.asString() + "CompanionObject")),
classId.createNestedClassId(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT))
}
// TODO: support also functions with >= 23 parameters
for (i in 0..22) {
add(ClassId.topLevel(FqName("kotlin.jvm.functions.Function" + i)), KotlinBuiltIns.getFunctionClassId(i))
val kFunction = FunctionClassDescriptor.Kind.KFunction
val kFun = kFunction.packageFqName.toString() + "." + kFunction.classNamePrefix
addKotlinToJava(FqName(kFun + i), ClassId.topLevel(FqName(kFun)))
}
addKotlinToJava(FQ_NAMES.nothing.toSafe(), classId(Void::class.java))
}
/**
* E.g.
* java.lang.String -> kotlin.String
* java.lang.Integer -> kotlin.Int
* kotlin.jvm.internal.IntCompanionObject -> kotlin.Int.Companion
* java.util.List -> kotlin.List
* java.util.Map.Entry -> kotlin.Map.Entry
* java.lang.Void -> null
* kotlin.jvm.functions.Function3 -> kotlin.Function3
*/
fun mapJavaToKotlin(fqName: FqName): ClassId? {
return javaToKotlin[fqName.toUnsafe()]
}
fun mapJavaToKotlin(fqName: FqName, builtIns: KotlinBuiltIns): ClassDescriptor? {
val kotlinClassId = mapJavaToKotlin(fqName)
return if (kotlinClassId != null) builtIns.getBuiltInClassByFqName(kotlinClassId.asSingleFqName()) else null
}
/**
* E.g.
* kotlin.Throwable -> java.lang.Throwable
* kotlin.Int -> java.lang.Integer
* kotlin.Int.Companion -> kotlin.jvm.internal.IntCompanionObject
* kotlin.Nothing -> java.lang.Void
* kotlin.IntArray -> null
* kotlin.Function3 -> kotlin.jvm.functions.Function3
* kotlin.reflect.KFunction3 -> kotlin.reflect.KFunction
*/
fun mapKotlinToJava(kotlinFqName: FqNameUnsafe): ClassId? {
return kotlinToJava[kotlinFqName]
}
private fun addMapping(platformMutabilityMapping: PlatformMutabilityMapping) {
val (javaClassId, readOnlyClassId, mutableClassId) = platformMutabilityMapping
add(javaClassId, readOnlyClassId)
addKotlinToJava(mutableClassId.asSingleFqName(), javaClassId)
val readOnlyFqName = readOnlyClassId.asSingleFqName()
val mutableFqName = mutableClassId.asSingleFqName()
mutableToReadOnly.put(mutableClassId.asSingleFqName().toUnsafe(), readOnlyFqName)
readOnlyToMutable.put(readOnlyFqName.toUnsafe(), mutableFqName)
}
private fun add(javaClassId: ClassId, kotlinClassId: ClassId) {
addJavaToKotlin(javaClassId, kotlinClassId)
addKotlinToJava(kotlinClassId.asSingleFqName(), javaClassId)
}
private fun addTopLevel(javaClass: Class<*>, kotlinFqName: FqNameUnsafe) {
addTopLevel(javaClass, kotlinFqName.toSafe())
}
private fun addTopLevel(javaClass: Class<*>, kotlinFqName: FqName) {
add(classId(javaClass), ClassId.topLevel(kotlinFqName))
}
private fun addJavaToKotlin(javaClassId: ClassId, kotlinClassId: ClassId) {
javaToKotlin.put(javaClassId.asSingleFqName().toUnsafe(), kotlinClassId)
}
private fun addKotlinToJava(kotlinFqNameUnsafe: FqName, javaClassId: ClassId) {
kotlinToJava.put(kotlinFqNameUnsafe.toUnsafe(), javaClassId)
}
fun isJavaPlatformClass(fqName: FqName): Boolean = mapJavaToKotlin(fqName) != null
fun mapPlatformClass(fqName: FqName, builtIns: KotlinBuiltIns): Collection<ClassDescriptor> {
val kotlinAnalog = mapJavaToKotlin(fqName, builtIns) ?: return emptySet()
val kotlinMutableAnalogFqName = readOnlyToMutable[kotlinAnalog.fqNameUnsafe] ?: return setOf(kotlinAnalog)
return Arrays.asList(kotlinAnalog, builtIns.getBuiltInClassByFqName(kotlinMutableAnalogFqName))
}
override fun mapPlatformClass(classDescriptor: ClassDescriptor): Collection<ClassDescriptor> {
val className = DescriptorUtils.getFqName(classDescriptor)
return if (className.isSafe)
mapPlatformClass(className.toSafe(), classDescriptor.builtIns)
else
emptySet<ClassDescriptor>()
}
fun isMutable(mutable: ClassDescriptor): Boolean = mutableToReadOnly.containsKey(DescriptorUtils.getFqName(mutable))
fun isMutable(type: KotlinType): Boolean {
val classDescriptor = TypeUtils.getClassDescriptor(type)
return classDescriptor != null && isMutable(classDescriptor)
}
fun isReadOnly(readOnly: ClassDescriptor): Boolean = readOnlyToMutable.containsKey(DescriptorUtils.getFqName(readOnly))
fun isReadOnly(type: KotlinType): Boolean {
val classDescriptor = TypeUtils.getClassDescriptor(type)
return classDescriptor != null && isReadOnly(classDescriptor)
}
fun convertMutableToReadOnly(mutable: ClassDescriptor): ClassDescriptor {
return convertToOppositeMutability(mutable, mutableToReadOnly, "mutable")
}
fun convertReadOnlyToMutable(readOnly: ClassDescriptor): ClassDescriptor {
return convertToOppositeMutability(readOnly, readOnlyToMutable, "read-only")
}
private fun classId(clazz: Class<*>): ClassId {
assert(!clazz.isPrimitive && !clazz.isArray) { "Invalid class: " + clazz }
val outer = clazz.declaringClass
return if (outer == null)
ClassId.topLevel(FqName(clazz.canonicalName))
else
classId(outer).createNestedClassId(Name.identifier(clazz.simpleName))
}
private fun convertToOppositeMutability(
descriptor: ClassDescriptor,
map: Map<FqNameUnsafe, FqName>,
mutabilityKindName: String
): ClassDescriptor {
val oppositeClassFqName = map[DescriptorUtils.getFqName(descriptor)] ?: throw IllegalArgumentException("Given class $descriptor is not a $mutabilityKindName collection")
return descriptor.builtIns.getBuiltInClassByFqName(oppositeClassFqName)
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2015 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.platform
import org.jetbrains.kotlin.builtins.JvmBuiltInClassDescriptorFactory
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.deserialization.AdditionalClassPartsProvider
import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentDeclarationFilter
import org.jetbrains.kotlin.load.kotlin.JvmBuiltInsSettings
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.utils.sure
class JvmBuiltIns @JvmOverloads constructor(
storageManager: StorageManager,
loadBuiltInsFromCurrentClassLoader: Boolean = true
) : KotlinBuiltIns(storageManager) {
// Module containing JDK classes or having them among dependencies
private var ownerModuleDescriptor: ModuleDescriptor? = null
private var isAdditionalBuiltInsFeatureSupported: Boolean = true
fun initialize(moduleDescriptor: ModuleDescriptor, isAdditionalBuiltInsFeatureSupported: Boolean) {
assert(ownerModuleDescriptor == null) { "JvmBuiltins repeated initialization" }
this.ownerModuleDescriptor = moduleDescriptor
this.isAdditionalBuiltInsFeatureSupported = isAdditionalBuiltInsFeatureSupported
}
val settings: JvmBuiltInsSettings by storageManager.createLazyValue {
JvmBuiltInsSettings(
builtInsModule, storageManager,
{ ownerModuleDescriptor.sure { "JvmBuiltins has not been initialized properly" } },
{
ownerModuleDescriptor.sure { "JvmBuiltins has not been initialized properly" }
isAdditionalBuiltInsFeatureSupported
}
)
}
init {
if (loadBuiltInsFromCurrentClassLoader) {
createBuiltInsModule()
}
}
override fun getPlatformDependentDeclarationFilter(): PlatformDependentDeclarationFilter = settings
override fun getAdditionalClassPartsProvider(): AdditionalClassPartsProvider = settings
override fun getClassDescriptorFactories() =
super.getClassDescriptorFactories() + JvmBuiltInClassDescriptorFactory(storageManager, builtInsModule)
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2016 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.platform
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
fun createMappedTypeParametersSubstitution(from: ClassDescriptor, to: ClassDescriptor): TypeConstructorSubstitution {
assert(from.declaredTypeParameters.size == to.declaredTypeParameters.size) {
"$from and $to should have same number of type parameters, " +
"but ${from.declaredTypeParameters.size} / ${to.declaredTypeParameters.size} found"
}
return TypeConstructorSubstitution.createByConstructorsMap(
from.declaredTypeParameters.map(TypeParameterDescriptor::getTypeConstructor).zip(
to.declaredTypeParameters.map { it.defaultType.asTypeProjection() }
).toMap())
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2015 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.resolve.jvm
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
import org.jetbrains.kotlin.load.java.lazy.LazyJavaPackageFragmentProvider
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
class JavaDescriptorResolver(
val packageFragmentProvider: LazyJavaPackageFragmentProvider,
private val javaResolverCache: JavaResolverCache
) {
fun resolveClass(javaClass: JavaClass): ClassDescriptor? {
val fqName = javaClass.fqName
if (fqName != null && javaClass.lightClassOriginKind == LightClassOriginKind.SOURCE) {
return javaResolverCache.getClassResolvedFromSource(fqName)
}
javaClass.outerClass?.let { outerClass ->
val outerClassScope = resolveClass(outerClass)?.unsubstitutedInnerClassesScope
return outerClassScope?.getContributedClassifier(javaClass.name, NoLookupLocation.FROM_JAVA_LOADER) as? ClassDescriptor
}
if (fqName == null) return null
return packageFragmentProvider.getPackageFragments(fqName.parent()).firstOrNull()?.findClassifierByJavaClass(javaClass)
}
}
@@ -0,0 +1,148 @@
/*
* Copyright 2010-2015 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.resolve.jvm;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.load.kotlin.TypeMappingConfiguration;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import java.util.regex.Pattern;
public class JvmClassName {
@NotNull
public static JvmClassName byInternalName(@NotNull String internalName) {
return new JvmClassName(internalName);
}
@NotNull
public static JvmClassName byClassId(@NotNull ClassId classId) {
return byClassId(classId, null);
}
@NotNull
public static JvmClassName byClassId(@NotNull ClassId classId, @Nullable TypeMappingConfiguration<?> typeMappingConfiguration) {
FqName packageFqName = classId.getPackageFqName();
String[] relativeClassNameSegments = classId.getRelativeClassName().asString().split(Pattern.quote("."));
String relativeClassName;
if (relativeClassNameSegments.length == 1) {
relativeClassName = relativeClassNameSegments[0];
}
else if (relativeClassNameSegments.length > 1 && typeMappingConfiguration != null) {
Function2<String, String, String> innerClassNameFactory = typeMappingConfiguration.getInnerClassNameFactory();
relativeClassName = innerClassNameFactory.invoke(relativeClassNameSegments[0], relativeClassNameSegments[1]);
for (int i = 2; i < relativeClassNameSegments.length; ++i) {
relativeClassName = innerClassNameFactory.invoke(relativeClassName, relativeClassNameSegments[i]);
}
}
else {
// Default behavior if we don't have an inner class name factory
relativeClassName = classId.getRelativeClassName().asString().replace('.', '$');
}
return packageFqName.isRoot()
? new JvmClassName(relativeClassName)
: new JvmClassName(packageFqName.asString().replace('.', '/') + "/" + relativeClassName);
}
/**
* WARNING: fq name cannot be uniquely mapped to JVM class name.
*/
@NotNull
public static JvmClassName byFqNameWithoutInnerClasses(@NotNull FqName fqName) {
JvmClassName r = new JvmClassName(fqName.asString().replace('.', '/'));
r.fqName = fqName;
return r;
}
@NotNull
public static JvmClassName byFqNameWithoutInnerClasses(@NotNull String fqName) {
return byFqNameWithoutInnerClasses(new FqName(fqName));
}
// Internal name: kotlin/Map$Entry
// FqName: kotlin.Map.Entry
private final String internalName;
private FqName fqName;
private JvmClassName(@NotNull String internalName) {
this.internalName = internalName;
}
/**
* WARNING: internal name cannot be reliably converted to FQ name.
*
* This method treats all dollar characters ('$') in the internal name as inner class separators.
* So it _will work incorrectly_ for classes where dollar characters are a part of the identifier.
*
* E.g. JvmClassName("org/foo/bar/Baz$quux").getFqNameForClassNameWithoutDollars() -> FqName("org.foo.bar.Baz.quux")
*/
@NotNull
public FqName getFqNameForClassNameWithoutDollars() {
if (fqName == null) {
this.fqName = new FqName(internalName.replace('$', '.').replace('/', '.'));
}
return fqName;
}
/**
* WARNING: internal name cannot be reliably converted to FQ name.
*
* This method treats all dollar characters ('$') in the internal name as a part of the identifier.
* So it _will work incorrectly_ for inner classes.
*
* E.g. JvmClassName("org/foo/bar/Baz$quux").getFqNameForTopLevelClassMaybeWithDollars() -> FqName("org.foo.bar.Baz$quux")
*/
@NotNull
public FqName getFqNameForTopLevelClassMaybeWithDollars() {
return new FqName(internalName.replace('/', '.'));
}
@NotNull
public FqName getPackageFqName() {
int lastSlash = internalName.lastIndexOf("/");
if (lastSlash == -1) return FqName.ROOT;
return new FqName(internalName.substring(0, lastSlash).replace('/', '.'));
}
@NotNull
public String getInternalName() {
return internalName;
}
@Override
public String toString() {
return internalName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return internalName.equals(((JvmClassName) o).internalName);
}
@Override
public int hashCode() {
return internalName.hashCode();
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2015 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.resolve.jvm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.name.FqName;
import java.util.*;
public enum JvmPrimitiveType {
BOOLEAN(PrimitiveType.BOOLEAN, "boolean", "Z", "java.lang.Boolean"),
CHAR(PrimitiveType.CHAR, "char", "C", "java.lang.Character"),
BYTE(PrimitiveType.BYTE, "byte", "B", "java.lang.Byte"),
SHORT(PrimitiveType.SHORT, "short", "S", "java.lang.Short"),
INT(PrimitiveType.INT, "int", "I", "java.lang.Integer"),
FLOAT(PrimitiveType.FLOAT, "float", "F", "java.lang.Float"),
LONG(PrimitiveType.LONG, "long", "J", "java.lang.Long"),
DOUBLE(PrimitiveType.DOUBLE, "double", "D", "java.lang.Double"),
;
private static final Set<FqName> WRAPPERS_CLASS_NAMES;
private static final Map<String, JvmPrimitiveType> TYPE_BY_NAME;
private static final Map<PrimitiveType, JvmPrimitiveType> TYPE_BY_PRIMITIVE_TYPE;
static {
WRAPPERS_CLASS_NAMES = new HashSet<FqName>();
TYPE_BY_NAME = new HashMap<String, JvmPrimitiveType>();
TYPE_BY_PRIMITIVE_TYPE = new EnumMap<PrimitiveType, JvmPrimitiveType>(PrimitiveType.class);
for (JvmPrimitiveType type : values()) {
WRAPPERS_CLASS_NAMES.add(type.getWrapperFqName());
TYPE_BY_NAME.put(type.getJavaKeywordName(), type);
TYPE_BY_PRIMITIVE_TYPE.put(type.getPrimitiveType(), type);
}
}
public static boolean isWrapperClassName(@NotNull FqName className) {
return WRAPPERS_CLASS_NAMES.contains(className);
}
@NotNull
public static JvmPrimitiveType get(@NotNull String name) {
JvmPrimitiveType result = TYPE_BY_NAME.get(name);
if (result == null) {
throw new AssertionError("Non-primitive type name passed: " + name);
}
return result;
}
@NotNull
public static JvmPrimitiveType get(@NotNull PrimitiveType type) {
return TYPE_BY_PRIMITIVE_TYPE.get(type);
}
private final PrimitiveType primitiveType;
private final String name;
private final String desc;
private final FqName wrapperFqName;
JvmPrimitiveType(@NotNull PrimitiveType primitiveType, @NotNull String name, @NotNull String desc, @NotNull String wrapperClassName) {
this.primitiveType = primitiveType;
this.name = name;
this.desc = desc;
this.wrapperFqName = new FqName(wrapperClassName);
}
@NotNull
public PrimitiveType getPrimitiveType() {
return primitiveType;
}
@NotNull
public String getJavaKeywordName() {
return name;
}
@NotNull
public String getDesc() {
return desc;
}
@NotNull
public FqName getWrapperFqName() {
return wrapperFqName;
}
}
@@ -0,0 +1,274 @@
/*
* Copyright 2010-2015 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.jvm;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.kotlin.serialization.jvm.UtfEncodingKt.MAX_UTF8_INFO_LENGTH;
public class BitEncoding {
private static final boolean FORCE_8TO7_ENCODING = "true".equals(System.getProperty("kotlin.jvm.serialization.use8to7"));
private static final char _8TO7_MODE_MARKER = (char) -1;
private BitEncoding() {
}
/**
* Converts a byte array of serialized data to an array of {@code String} satisfying JVM annotation value argument restrictions:
* <ol>
* <li>Each string's length should be no more than 65535</li>
* <li>UTF-8 representation of each string cannot contain bytes in the range 0xf0..0xff</li>
* </ol>
*/
@NotNull
public static String[] encodeBytes(@NotNull byte[] data) {
// TODO: try both encodings here and choose the best one (with the smallest size)
if (!FORCE_8TO7_ENCODING) {
return UtfEncodingKt.bytesToStrings(data);
}
byte[] bytes = encode8to7(data);
// Since 0x0 byte is encoded as two bytes in the Modified UTF-8 (0xc0 0x80) and zero is rather common to byte arrays, we increment
// every byte by one modulo max byte value, so that the less common value 0x7f will be represented as two bytes instead.
addModuloByte(bytes, 1);
return splitBytesToStringArray(bytes);
}
/**
* Converts a byte array to another byte array, every element of which is in the range 0x0..0x7f.
*
* The conversion is equivalent to the following: input bytes are combined into one long bit string. This big string is then split into
* groups of 7 bits. Each resulting 7-bit chunk is then converted to a byte (with a leading bit = 0). The last chunk may have less than
* 7 bits, it's prepended with zeros to form a byte. The result is then the array of these bytes, each of which is obviously in the
* range 0x0..0x7f.
*
* Suppose the input of 4 bytes is given (bytes are listed from the beginning to the end, each byte from the least significant bit to
* the most significant bit, bits within each byte are numbered):
*
* 01234567 01234567 01234567 01234567
*
* The output for this kind of input will be of the following form ('#' represents a zero bit):
*
* 0123456# 7012345# 6701234# 5670123# 4567####
*/
@NotNull
private static byte[] encode8to7(@NotNull byte[] data) {
// ceil(data.length * 8 / 7)
int resultLength = (data.length * 8 + 6) / 7;
byte[] result = new byte[resultLength];
// We maintain a pointer to the bit in the input, which is represented by two numbers: index of the current byte in the input and
// the index of a bit inside this byte (0 is least significant, 7 is most significant)
int byteIndex = 0;
int bit = 0;
// Write all resulting bytes except the last one. To do this we need to collect exactly 7 bits, starting from the current, into a
// byte. In almost all cases these 7 bits can be collected from two parts: the first is several (at least one) most significant bits
// from the current byte, the second is several (maybe zero) least significant bits from the next byte. The special case is when the
// current bit is the first (least significant) bit in its byte (bit == 0): then the 7 needed bits are just the 7 least significant
// of the current byte.
for (int i = 0; i < resultLength - 1; i++) {
if (bit == 0) {
result[i] = (byte) (data[byteIndex] & 0x7f);
bit = 7;
continue;
}
int firstPart = (data[byteIndex] & 0xff) >>> bit;
int newBit = (bit + 7) & 7;
int secondPart = (data[++byteIndex] & ((1 << newBit) - 1)) << 8 - bit;
result[i] = (byte) (firstPart + secondPart);
bit = newBit;
}
// Write the last byte, which is just several most significant bits of the last byte in the input, padded with zeros
if (resultLength > 0) {
assert bit != 0 : "The last chunk cannot start from the input byte since otherwise at least one bit will remain unprocessed";
assert byteIndex == data.length - 1 : "The last 7-bit chunk should be encoded from the last input byte: " +
byteIndex + " != " + (data.length - 1);
result[resultLength - 1] = (byte) ((data[byteIndex] & 0xff) >>> bit);
}
return result;
}
private static void addModuloByte(@NotNull byte[] data, int increment) {
for (int i = 0, n = data.length; i < n; i++) {
data[i] = (byte) ((data[i] + increment) & 0x7f);
}
}
/**
* Converts a big byte array into the array of strings, where each string, when written to the constant pool table in bytecode, produces
* a byte array of not more than MAX_UTF8_INFO_LENGTH. Each byte, except those which are 0x0, occupies exactly one byte in the constant
* pool table. Zero bytes occupy two bytes in the table each.
*
* When strings are constructed from the array of bytes here, they are encoded in the platform's default encoding. This is fine: the
* conversion to the Modified UTF-8 (which here would be equivalent to replacing each 0x0 with 0xc0 0x80) will happen later by ASM, when
* it writes these strings to the bytecode
*/
@NotNull
private static String[] splitBytesToStringArray(@NotNull byte[] data) {
List<String> result = new ArrayList<String>();
// The offset where the currently processed string starts
int off = 0;
// The effective length the bytes of the current string would occupy in the constant pool table.
// 2 because the first char is -1 which denotes the encoding mode and occupies two bytes in Modified UTF-8
int len = 2;
boolean encodingModeAdded = false;
for (int i = 0, n = data.length; i < n; i++) {
// When the effective length reaches at least MAX - 1, we add the current string to the result. Note that the effective length
// is at most MAX here: non-zero bytes occupy 1 byte and zero bytes occupy 2 bytes, so we couldn't jump over more than one byte
if (len >= MAX_UTF8_INFO_LENGTH - 1) {
assert len <= MAX_UTF8_INFO_LENGTH : "Produced strings cannot contain more than " + MAX_UTF8_INFO_LENGTH + " bytes: " + len;
String string = new String(data, off, i - off);
if (!encodingModeAdded) {
encodingModeAdded = true;
result.add(_8TO7_MODE_MARKER + string);
}
else {
result.add(string);
}
off = i;
len = 0;
}
if (data[i] == 0) {
len += 2;
}
else {
len++;
}
}
if (len >= 0) {
result.add(new String(data, off, data.length - off));
}
return result.toArray(new String[result.size()]);
}
/**
* Converts encoded array of {@code String} obtained by {@link BitEncoding#encodeBytes(byte[])} back to a byte array.
*/
@NotNull
public static byte[] decodeBytes(@NotNull String[] data) {
if (data.length > 0 && !data[0].isEmpty()) {
char possibleMarker = data[0].charAt(0);
if (possibleMarker == UtfEncodingKt.UTF8_MODE_MARKER) {
return UtfEncodingKt.stringsToBytes(dropMarker(data));
}
if (possibleMarker == _8TO7_MODE_MARKER) {
data = dropMarker(data);
}
}
byte[] bytes = combineStringArrayIntoBytes(data);
// Adding 0x7f modulo max byte value is equivalent to subtracting 1 the same modulo, which is inverse to what happens in encodeBytes
addModuloByte(bytes, 0x7f);
return decode7to8(bytes);
}
@NotNull
private static String[] dropMarker(@NotNull String[] data) {
// Clone because the clients should be able to use the passed array for their own purposes.
// This is cheap because the size of the array is 1 or 2 almost always.
String[] result = data.clone();
result[0] = result[0].substring(1);
return result;
}
/**
* Combines the array of strings resulted from encodeBytes() into one long byte array
*/
@NotNull
private static byte[] combineStringArrayIntoBytes(@NotNull String[] data) {
int resultLength = 0;
for (String s : data) {
assert s.length() <= MAX_UTF8_INFO_LENGTH : "String is too long: " + s.length();
resultLength += s.length();
}
byte[] result = new byte[resultLength];
int p = 0;
for (String s : data) {
for (int i = 0, n = s.length(); i < n; i++) {
result[p++] = (byte) s.charAt(i);
}
}
return result;
}
/**
* Decodes the byte array resulted from encode8to7().
*
* Each byte of the input array has at most 7 valuable bits of information. So the decoding is equivalent to the following: least
* significant 7 bits of all input bytes are combined into one long bit string. This bit string is then split into groups of 8 bits,
* each of which forms a byte in the output. If there are any leftovers, they are ignored, since they were added just as a padding and
* do not comprise a full byte.
*
* Suppose the following encoded byte array is given (bits are numbered the same way as in encode8to7() doc):
*
* 01234567 01234567 01234567 01234567
*
* The output of the following form would be produced:
*
* 01234560 12345601 23456012
*
* Note how all most significant bits and leftovers are dropped, since they don't contain any useful information
*/
@NotNull
private static byte[] decode7to8(@NotNull byte[] data) {
// floor(7 * data.length / 8)
int resultLength = 7 * data.length / 8;
byte[] result = new byte[resultLength];
// We maintain a pointer to an input bit in the same fashion as in encode8to7(): it's represented as two numbers: index of the
// current byte in the input and index of the bit in the byte
int byteIndex = 0;
int bit = 0;
// A resulting byte is comprised of 8 bits, starting from the current bit. Since each input byte only "contains 7 bytes", a
// resulting byte always consists of two parts: several most significant bits of the current byte and several least significant bits
// of the next byte
for (int i = 0; i < resultLength; i++) {
int firstPart = (data[byteIndex] & 0xff) >>> bit;
byteIndex++;
int secondPart = (data[byteIndex] & ((1 << (bit + 1)) - 1)) << 7 - bit;
result[i] = (byte) (firstPart + secondPart);
if (bit == 6) {
byteIndex++;
bit = 0;
}
else {
bit++;
}
}
return result;
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2015 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.jvm
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
// The purpose of this class is to map Kotlin classes to JVM bytecode desc strings, as KotlinTypeMapper does in the backend.
// It's used as an optimization during serialization/deserialization: if there's no JVM signature for a method/property/constructor,
// it means that the JVM signature should be trivially computable from the Kotlin signature with this class.
// It's not required to support everything in KotlinTypeMapper, but the more it does, the more we save on JVM signatures in proto metadata.
// Note that improving the behavior of this class may break binary compatibility of code compiled by Kotlin, because it may make
// the new compiler skip writing the signatures it now thinks are trivial, and the old compiler would recreate them incorrectly.
object ClassMapperLite {
@JvmStatic
fun mapClass(classId: ClassId): String {
val internalName = classId.asString().replace('.', '$')
val simpleName = internalName.removePrefix("kotlin/")
if (simpleName != internalName) {
for (jvmPrimitive in JvmPrimitiveType.values()) {
val primitiveType = jvmPrimitive.primitiveType
if (simpleName == primitiveType.typeName.asString()) return jvmPrimitive.desc
if (simpleName == primitiveType.arrayTypeName.asString()) return "[" + jvmPrimitive.desc
}
if (simpleName == KotlinBuiltIns.FQ_NAMES.unit.shortName().asString()) return "V"
}
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(classId.asSingleFqName().toUnsafe())
if (javaClassId != null) {
return "L" + javaClassId.asString().replace('.', '$') + ";"
}
return "L$internalName;"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
/*
* Copyright 2010-2015 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.jvm
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite
import org.jetbrains.kotlin.serialization.ClassData
import org.jetbrains.kotlin.serialization.PackageData
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.*
import java.io.ByteArrayInputStream
object JvmProtoBufUtil {
val EXTENSION_REGISTRY: ExtensionRegistryLite = run {
val registry = ExtensionRegistryLite.newInstance()
JvmProtoBuf.registerAllExtensions(registry)
registry
}
@JvmStatic fun readClassDataFrom(data: Array<String>, strings: Array<String>): ClassData =
readClassDataFrom(BitEncoding.decodeBytes(data), strings)
@JvmStatic fun readClassDataFrom(bytes: ByteArray, strings: Array<String>): ClassData {
val input = ByteArrayInputStream(bytes)
val nameResolver = JvmNameResolver(JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, EXTENSION_REGISTRY), strings)
val classProto = ProtoBuf.Class.parseFrom(input, EXTENSION_REGISTRY)
return ClassData(nameResolver, classProto)
}
@JvmStatic fun readPackageDataFrom(data: Array<String>, strings: Array<String>): PackageData =
readPackageDataFrom(BitEncoding.decodeBytes(data), strings)
@JvmStatic fun readPackageDataFrom(bytes: ByteArray, strings: Array<String>): PackageData {
val input = ByteArrayInputStream(bytes)
val nameResolver = JvmNameResolver(JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, EXTENSION_REGISTRY), strings)
val packageProto = ProtoBuf.Package.parseFrom(input, EXTENSION_REGISTRY)
return PackageData(nameResolver, packageProto)
}
// returns JVM signature in the format: "equals(Ljava/lang/Object;)Z"
fun getJvmMethodSignature(
proto: ProtoBuf.Function,
nameResolver: NameResolver,
typeTable: TypeTable
): String? {
val signature =
if (proto.hasExtension(JvmProtoBuf.methodSignature)) proto.getExtension(JvmProtoBuf.methodSignature) else null
val name = if (signature != null && signature.hasName()) signature.name else proto.name
val desc = if (signature != null && signature.hasDesc()) {
nameResolver.getString(signature.desc)
}
else {
val parameterTypes = listOfNotNull(proto.receiverType(typeTable)) + proto.valueParameterList.map { it.type(typeTable) }
val parametersDesc = parameterTypes.map { mapTypeDefault(it, nameResolver) ?: return null }
val returnTypeDesc = mapTypeDefault(proto.returnType(typeTable), nameResolver) ?: return null
parametersDesc.joinToString(separator = "", prefix = "(", postfix = ")") + returnTypeDesc
}
return nameResolver.getString(name) + desc
}
fun getJvmConstructorSignature(
proto: ProtoBuf.Constructor,
nameResolver: NameResolver,
typeTable: TypeTable
): String? {
val signature =
if (proto.hasExtension(JvmProtoBuf.constructorSignature)) proto.getExtension(JvmProtoBuf.constructorSignature) else null
val desc = if (signature != null && signature.hasDesc()) {
nameResolver.getString(signature.desc)
}
else {
proto.valueParameterList.map {
mapTypeDefault(it.type(typeTable), nameResolver) ?: return null
}.joinToString(separator = "", prefix = "(", postfix = ")V")
}
return "<init>" + desc
}
fun getJvmFieldSignature(
proto: ProtoBuf.Property,
nameResolver: NameResolver,
typeTable: TypeTable
): PropertySignature? {
val signature =
if (proto.hasExtension(JvmProtoBuf.propertySignature)) proto.getExtension(JvmProtoBuf.propertySignature) else return null
val field =
if (signature.hasField()) signature.field else null
val name = if (field != null && field.hasName()) field.name else proto.name
val desc =
if (field != null && field.hasDesc()) nameResolver.getString(field.desc)
else mapTypeDefault(proto.returnType(typeTable), nameResolver) ?: return null
return PropertySignature(nameResolver.getString(name), desc)
}
data class PropertySignature(val name: String, val desc: String)
private fun mapTypeDefault(type: ProtoBuf.Type, nameResolver: NameResolver): String? {
return if (type.hasClassName()) ClassMapperLite.mapClass(nameResolver.getClassId(type.className)) else null
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2016 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.jvm
import java.util.*
// The maximum possible length of the byte array in the CONSTANT_Utf8_info structure in the bytecode, as per JVMS7 4.4.7
const val MAX_UTF8_INFO_LENGTH = 65535
const val UTF8_MODE_MARKER = 0.toChar()
fun bytesToStrings(bytes: ByteArray): Array<String> {
val result = ArrayList<String>(1)
val buffer = StringBuilder()
var bytesInBuffer = 0
buffer.append(UTF8_MODE_MARKER)
// Zeros effectively occupy two bytes because each 0x0 is converted to 0x80 0xc0 in Modified UTF-8, see JVMS7 4.4.7
bytesInBuffer += 2
for (b in bytes) {
val c = b.toInt() and 0xFF // 0 <= c <= 255
buffer.append(c.toChar())
if (0 < b && b <= 127) {
bytesInBuffer++
}
else {
bytesInBuffer += 2
}
if (bytesInBuffer >= MAX_UTF8_INFO_LENGTH - 1) {
result.add(buffer.toString())
buffer.setLength(0)
bytesInBuffer = 0
}
}
if (!buffer.isEmpty()) {
result.add(buffer.toString())
}
return result.toTypedArray()
}
fun stringsToBytes(strings: Array<String>): ByteArray {
val resultLength = strings.sumBy { it.length }
val result = ByteArray(resultLength)
var i = 0
for (s in strings) {
for (si in 0..s.length - 1) {
result[i++] = s[si].toByte()
}
}
assert(i == result.size) { "Should have reached the end" }
return result
}