Drop Cloneable in JS, synthesize it at compile-time on JVM

Use the same approach that is used for creating function type classes
(Function{0,1,...}) + add Cloneable to supertypes of Array and primitive arrays

 #KT-5537 Fixed
This commit is contained in:
Alexander Udalov
2016-10-13 11:53:18 +03:00
parent 0f4b10d37d
commit 035d6156a7
24 changed files with 376 additions and 173 deletions
@@ -0,0 +1,90 @@
/*
* 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.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.utils.Printer
import java.util.*
class CloneableClassScope(storageManager: StorageManager, private val classDescriptor: ClassDescriptor) : MemberScopeImpl() {
private val allDescriptors by storageManager.createLazyValue {
val cloneFunction =
SimpleFunctionDescriptorImpl.create(classDescriptor, Annotations.EMPTY, CLONE_NAME, DECLARATION, SourceElement.NO_SOURCE)
cloneFunction.initialize(null, classDescriptor.thisAsReceiverParameter, emptyList(), emptyList(), classDescriptor.builtIns.anyType,
Modality.OPEN, Visibilities.PROTECTED)
listOf(cloneFunction) + createFakeOverrides()
}
private val functionNames = setOf(CLONE_NAME) + classDescriptor.builtIns.anyType.memberScope.getFunctionNames()
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> =
if (name in functionNames) allDescriptors.filter { it.name == name } else emptySet()
override fun getContributedDescriptors(
kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> = allDescriptors
override fun getFunctionNames(): Set<Name> = functionNames
private fun createFakeOverrides(): List<SimpleFunctionDescriptor> {
val result = ArrayList<SimpleFunctionDescriptor>(3)
val allSuperDescriptors = classDescriptor.typeConstructor.supertypes
.flatMap { it.memberScope.getContributedDescriptors() }
.filterIsInstance<SimpleFunctionDescriptor>()
for ((name, descriptors) in allSuperDescriptors.groupBy { it.name }) {
OverridingUtil.generateOverridesInFunctionGroup(
name,
/* membersFromSupertypes = */ descriptors,
/* membersFromCurrent = */ emptyList(),
classDescriptor,
object : NonReportingOverrideStrategy() {
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, null)
result.add(fakeOverride as SimpleFunctionDescriptor)
}
override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
error("Conflict in scope of $classDescriptor: $fromSuper vs $fromCurrent")
}
}
)
}
return result
}
override fun printScopeStructure(p: Printer) {
p.println("kotlin.Cloneable member scope")
}
companion object {
internal val CLONE_NAME = Name.identifier("clone")
}
}
@@ -0,0 +1,62 @@
/*
* 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.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.ClassDescriptorFactory
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
class JvmBuiltInClassDescriptorFactory(
storageManager: StorageManager,
private val moduleDescriptor: ModuleDescriptor
) : ClassDescriptorFactory {
private val cloneable by storageManager.createLazyValue {
ClassDescriptorImpl(
moduleDescriptor.getPackage(KOTLIN_FQ_NAME).fragments.single(),
CLONEABLE_NAME, Modality.ABSTRACT, ClassKind.INTERFACE, listOf(moduleDescriptor.builtIns.anyType),
SourceElement.NO_SOURCE
).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())
}
}
@@ -17,6 +17,8 @@
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.AnnotationDescriptorImpl
@@ -28,6 +30,7 @@ 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
@@ -65,6 +68,9 @@ open class JvmBuiltInsSettings(
private val isAdditionalBuiltInsFeatureSupported: Boolean by lazy(isAdditionalBuiltInsFeatureSupported)
private val mockSerializableType = storageManager.createMockJavaIoSerializableType()
private val cloneableType by storageManager.createLazyValue {
moduleDescriptor.builtIns.getBuiltInClassByName(JvmBuiltInClassDescriptorFactory.CLONEABLE_CLASS_ID.shortClassName).defaultType
}
private val javaAnalogueClassesWithCustomSupertypeCache = storageManager.createCacheWithNotNullValues<FqName, ClassDescriptor>()
@@ -114,58 +120,66 @@ open class JvmBuiltInsSettings(
}
override fun getSupertypes(classDescriptor: DeserializedClassDescriptor): Collection<KotlinType> {
if (isSerializableInJava(classDescriptor.fqNameSafe)) {
return listOf(mockSerializableType)
val fqName = classDescriptor.fqNameUnsafe
return when {
isArrayOrPrimitiveArray(fqName) -> listOf(cloneableType, mockSerializableType)
isSerializableInJava(fqName) -> listOf(mockSerializableType)
else -> listOf()
}
else return listOf()
}
override fun getFunctions(name: Name, classDescriptor: DeserializedClassDescriptor): Collection<SimpleFunctionDescriptor> =
getAdditionalFunctions(classDescriptor) {
it.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS)
}
.mapNotNull {
additionalMember ->
val substitutedWithKotlinTypeParameters =
additionalMember.substitute(
createMappedTypeParametersSubstitution(
additionalMember.containingDeclaration as ClassDescriptor, classDescriptor).buildSubstitutor()
) as SimpleFunctionDescriptor
override fun getFunctions(name: Name, classDescriptor: DeserializedClassDescriptor): Collection<SimpleFunctionDescriptor> {
if (name == CloneableClassScope.CLONE_NAME && KotlinBuiltIns.isArrayOrPrimitiveArray(classDescriptor)) {
return listOf(createCloneForArray(
classDescriptor, cloneableType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
))
}
substitutedWithKotlinTypeParameters.newCopyBuilder().apply {
setOwner(classDescriptor)
setDispatchReceiverParameter(classDescriptor.thisAsReceiverParameter)
setPreserveSourceElement()
setSubstitution(UnsafeVarianceTypeSubstitution(moduleDescriptor.builtIns))
return getAdditionalFunctions(classDescriptor) {
it.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS)
}.mapNotNull {
additionalMember ->
val substitutedWithKotlinTypeParameters =
additionalMember.substitute(
createMappedTypeParametersSubstitution(
additionalMember.containingDeclaration as ClassDescriptor, classDescriptor).buildSubstitutor()
) as SimpleFunctionDescriptor
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()
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 -> {
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
JDKMemberStatus.NOT_CONSIDERED -> {
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
else {
setAdditionalAnnotations(notConsideredDeprecation)
}
}
JDKMemberStatus.DROP -> return@mapNotNull null
JDKMemberStatus.WHITE_LIST -> {
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
else {
setAdditionalAnnotations(notConsideredDeprecation)
}
}
}.build()!!
}
JDKMemberStatus.DROP -> return@mapNotNull null
JDKMemberStatus.WHITE_LIST -> {
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
}
}
}.build()!!
}
}
override fun getFunctionsNames(classDescriptor: DeserializedClassDescriptor): Set<Name> =
// NB: It's just an approximation that could be calculated relatively fast
@@ -206,6 +220,15 @@ open class JvmBuiltInsSettings(
}
}
private fun createCloneForArray(
arrayClassDescriptor: DeserializedClassDescriptor,
cloneFromCloneable: SimpleFunctionDescriptor
): SimpleFunctionDescriptor = cloneFromCloneable.newCopyBuilder().apply {
setOwner(arrayClassDescriptor)
setVisibility(Visibilities.PUBLIC)
setReturnType(arrayClassDescriptor.defaultType)
}.build()!!
private fun SimpleFunctionDescriptor.isMutabilityViolation(isMutable: Boolean): Boolean {
val owner = containingDeclaration as ClassDescriptor
val jvmDescriptor = computeJvmDescriptor()
@@ -323,12 +346,11 @@ open class JvmBuiltInsSettings(
valueParameters.single().type.constructor.declarationDescriptor?.fqNameUnsafe == classDescriptor.fqNameUnsafe
companion object {
fun isSerializableInJava(classFqName: FqName): Boolean {
val fqNameUnsafe = classFqName.toUnsafe()
if (fqNameUnsafe == KotlinBuiltIns.FQ_NAMES.array || KotlinBuiltIns.isPrimitiveArray(fqNameUnsafe)) {
fun isSerializableInJava(fqName: FqNameUnsafe): Boolean {
if (isArrayOrPrimitiveArray(fqName)) {
return true
}
val javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqNameUnsafe) ?: return false
val javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName) ?: return false
val classViaReflection = try {
Class.forName(javaClassId.asSingleFqName().asString())
}
@@ -338,6 +360,10 @@ open class JvmBuiltInsSettings(
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",
@@ -16,6 +16,7 @@
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.load.kotlin.JvmBuiltInsSettings
@@ -49,4 +50,7 @@ class JvmBuiltIns(storageManager: StorageManager) : KotlinBuiltIns(storageManage
}
override fun getAdditionalClassPartsProvider() = settings
override fun getClassDescriptorFactories() =
super.getClassDescriptorFactories() + JvmBuiltInClassDescriptorFactory(storageManager, builtInsModule)
}