All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.synthetic.SamAdapterFunctionsScope.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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.synthetic

import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.synthetic.FunctionInterfaceAdapterExtensionFunctionDescriptor
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.record
import org.jetbrains.kotlin.load.java.descriptors.JavaClassConstructorDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
import org.jetbrains.kotlin.load.java.sam.SamAdapterDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.util.isCallableReference
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.calls.inference.wrapWithCapturingSubstitution
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.sam.*
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.SyntheticScope
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.findCorrespondingSupertype
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.utils.addIfNotNull
import kotlin.properties.Delegates

interface SamAdapterExtensionFunctionDescriptor : FunctionDescriptor, FunctionInterfaceAdapterExtensionFunctionDescriptor {
    override val baseDescriptorForSynthetic: FunctionDescriptor
}

class SamAdapterFunctionsScope(
    storageManager: StorageManager,
    private val samResolver: SamConversionResolver,
    private val samConversionOracle: SamConversionOracle,
    private val deprecationResolver: DeprecationResolver,
    private val lookupTracker: LookupTracker,
    private val samViaSyntheticScopeDisabled: Boolean,
    private val allowNonSpreadArraysForVarargAfterSam: Boolean
) : SyntheticScope.Default() {
    private val extensionForFunction =
        storageManager.createMemoizedFunctionWithNullableValues { function ->
            extensionForFunctionNotCached(function)
        }

    private val samAdapterForStaticFunction =
        storageManager.createMemoizedFunction> { function ->
            JavaSingleAbstractMethodUtils.createSamAdapterFunction(
                function,
                samResolver,
                samConversionOracle,
                allowNonSpreadArraysForVarargAfterSam
            )
        }

    private val samConstructorForClassifier =
        storageManager.createMemoizedFunction { classifier ->
            createSamConstructorFunction(classifier.containingDeclaration, classifier, samResolver, samConversionOracle)
        }

    private val samConstructorForJavaConstructor =
        storageManager.createMemoizedFunction { constructor ->
            JavaSingleAbstractMethodUtils.createSamAdapterConstructor(
                constructor,
                samResolver,
                samConversionOracle,
                allowNonSpreadArraysForVarargAfterSam
            ) as ClassConstructorDescriptor
        }

    private val samConstructorForTypeAliasConstructor =
        storageManager.createMemoizedFunctionWithNullableValues, TypeAliasConstructorDescriptor> { (constructor, typeAliasDescriptor) ->
            TypeAliasConstructorDescriptorImpl.createIfAvailable(storageManager, typeAliasDescriptor, constructor)
        }

    private fun extensionForFunctionNotCached(function: FunctionDescriptor): FunctionDescriptor? {
        if (!function.visibility.isVisibleOutside()) return null
        if (!function.hasJavaOriginInHierarchy()) return null //TODO: should we go into base at all?
        if (!JavaSingleAbstractMethodUtils.isSamAdapterNecessary(function)) return null
        if (function.returnType == null) return null
        if (deprecationResolver.isHiddenInResolution(function)) return null
        return SamAdapterExtensionFunctionDescriptorImpl.create(
            function,
            samResolver,
            samConversionOracle,
            allowNonSpreadArraysForVarargAfterSam
        )
    }

    override fun getSyntheticMemberFunctions(
        receiverTypes: Collection,
        name: Name,
        location: LookupLocation
    ): Collection {
        var result: SmartList? = null
        for (type in receiverTypes) {
            for (function in type.memberScope.getContributedFunctions(name, location)) {
                if (samViaSyntheticScopeDisabled && !function.hasNothingTypeInParameters) {
                    if (!function.shouldGenerateCandidateForVarargAfterSamAndHasVararg && !location.isCallableReference()) continue
                }

                val extension = extensionForFunction(function.original)?.substituteForReceiverType(type)
                if (extension != null) {
                    recordSamLookupsForParameters(function, location)
                    if (result == null) {
                        result = SmartList()
                    }
                    result.add(extension)
                }
            }
        }
        return when {
            result == null -> emptyList()
            result.size > 1 -> result.toSet()
            else -> result
        }
    }

    // TODO: replace this logic with a proper conversion in SamTypeConversions
    private fun LookupLocation.isCallableReference(): Boolean {
        if (this !is KotlinLookupLocation) return false
        return element.isCallableReference()
    }

    private fun recordSamLookupsForParameters(function: FunctionDescriptor, location: LookupLocation) {
        for (valueParameter in function.valueParameters) {
            recordSamLookupsToClassifier(valueParameter.type.constructor.declarationDescriptor ?: continue, location)
        }
    }

    private val FunctionDescriptor.shouldGenerateCandidateForVarargAfterSamAndHasVararg
        get() = allowNonSpreadArraysForVarargAfterSam && valueParameters.lastOrNull()?.isVararg == true

    private val FunctionDescriptor.hasNothingTypeInParameters
        get() = valueParameters.any { it.type.isNothing() && !it.original.type.isNothing() }

    private fun FunctionDescriptor.substituteForReceiverType(receiverType: KotlinType): FunctionDescriptor? {
        val containingClass = containingDeclaration as? ClassDescriptor ?: return null
        val correspondingSupertype = findCorrespondingSupertype(receiverType, containingClass.defaultType) ?: return null

        return substitute(
            TypeConstructorSubstitution
                .create(correspondingSupertype)
                .wrapWithCapturingSubstitution(needApproximation = true)
                .buildSubstitutor()
        )
    }

    override fun getSyntheticMemberFunctions(receiverTypes: Collection): Collection {
        return receiverTypes.flatMapTo(LinkedHashSet()) { type ->
            type.memberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
                .filterIsInstance()
                .run {
                    if (samViaSyntheticScopeDisabled) filter { it.shouldGenerateCandidateForVarargAfterSamAndHasVararg } else this
                }
                .mapNotNull {
                    extensionForFunction(it.original)?.substituteForReceiverType(type)
                }
        }
    }

    override fun getSyntheticStaticFunctions(
        contributedFunctions: Collection,
        location: LookupLocation
    ): Collection {
        return getSamFunctions(contributedFunctions, location)
    }

    override fun getSyntheticConstructors(
        contributedClassifier: ClassifierDescriptor,
        location: LookupLocation
    ): Collection {
        recordSamLookupsToClassifier(contributedClassifier, location)

        return getAllSamConstructors(contributedClassifier)
    }

    private fun recordSamLookupsToClassifier(classifier: ClassifierDescriptor, location: LookupLocation) {
        if (classifier !is JavaClassDescriptor || classifier.kind != ClassKind.INTERFACE) return
        // TODO: We should also record SAM lookups even when the interface is not SAM
        if (!JavaSingleAbstractMethodUtils.isSamType(classifier.defaultType)) return

        lookupTracker.record(location, classifier, SAM_LOOKUP_NAME)
    }

    override fun getSyntheticStaticFunctions(functionDescriptors: Collection): Collection {
        return getSamFunctions(functionDescriptors, location = null)
    }

    override fun getSyntheticConstructors(classifierDescriptors: Collection): Collection {
        val classifiers = classifierDescriptors.filterIsInstance()

        return classifiers.flatMap { getAllSamConstructors(it) }
    }

    override fun getSyntheticConstructor(constructor: ConstructorDescriptor): ConstructorDescriptor? {
        if (samViaSyntheticScopeDisabled && !constructor.shouldGenerateCandidateForVarargAfterSamAndHasVararg) return null

        return when (constructor) {
            is JavaClassConstructorDescriptor -> createJavaSamAdapterConstructor(constructor)
            is TypeAliasConstructorDescriptor -> {
                val underlyingConstructor = constructor.underlyingConstructorDescriptor
                if (underlyingConstructor !is JavaClassConstructorDescriptor) return null
                val underlyingSamConstructor = createJavaSamAdapterConstructor(underlyingConstructor) ?: return null

                samConstructorForTypeAliasConstructor(Pair(underlyingSamConstructor, constructor.typeAliasDescriptor))
            }
            else -> null
        }
    }

    private fun createJavaSamAdapterConstructor(constructor: JavaClassConstructorDescriptor): ClassConstructorDescriptor? {
        if (!JavaSingleAbstractMethodUtils.isSamAdapterNecessary(constructor)) return null
        return samConstructorForJavaConstructor(constructor)
    }

    private fun getSamFunctions(
        functions: Collection,
        location: LookupLocation?
    ): List> {
        return functions.mapNotNull { function ->
            if (function !is JavaMethodDescriptor) return@mapNotNull null
            if (function.dispatchReceiverParameter != null) return@mapNotNull null // consider only statics
            if (!JavaSingleAbstractMethodUtils.isSamAdapterNecessary(function)) return@mapNotNull null
            if (samViaSyntheticScopeDisabled && !function.shouldGenerateCandidateForVarargAfterSamAndHasVararg)
                return@mapNotNull null

            location?.let { recordSamLookupsForParameters(function, it) }

            samAdapterForStaticFunction(function)
        }
    }

    private fun getAllSamConstructors(classifier: ClassifierDescriptor): List =
        getSamAdaptersFromConstructors(classifier).also {
            it.addIfNotNull(getSamConstructor(classifier))
        }

    private fun getSamAdaptersFromConstructors(classifier: ClassifierDescriptor): MutableList {
        if (classifier !is JavaClassDescriptor) return SmartList()

        return SmartList().apply {
            for (constructor in classifier.constructors) {
                val samConstructor = getSyntheticConstructor(constructor) ?: continue
                add(samConstructor)
            }
        }
    }

    private fun getSamConstructor(classifier: ClassifierDescriptor): SamConstructorDescriptor? {
        if (classifier is TypeAliasDescriptor) {
            return getTypeAliasSamConstructor(classifier)
        }

        if (classifier !is LazyJavaClassDescriptor || classifier.defaultFunctionTypeForSamInterface == null) return null

        return samConstructorForClassifier(classifier)
    }

    private fun getTypeAliasSamConstructor(classifier: TypeAliasDescriptor): SamConstructorDescriptor? {
        val classDescriptor = classifier.classDescriptor ?: return null
        if (classDescriptor !is LazyJavaClassDescriptor || classDescriptor.defaultFunctionTypeForSamInterface == null) return null

        return createTypeAliasSamConstructorFunction(
            classifier, samConstructorForClassifier(classDescriptor), samResolver, samConversionOracle
        )
    }

    private class SamAdapterExtensionFunctionDescriptorImpl(
        containingDeclaration: DeclarationDescriptor,
        original: SimpleFunctionDescriptor?,
        annotations: Annotations,
        name: Name,
        kind: CallableMemberDescriptor.Kind,
        source: SourceElement
    ) : SamAdapterExtensionFunctionDescriptor,
        SimpleFunctionDescriptorImpl(containingDeclaration, original, annotations, name, kind, source) {

        override var baseDescriptorForSynthetic: FunctionDescriptor by Delegates.notNull()
            private set

        private val fromSourceFunctionTypeParameters: Map by lazy {
            baseDescriptorForSynthetic.typeParameters.zip(typeParameters).toMap()
        }

        companion object {
            fun create(
                sourceFunction: FunctionDescriptor,
                samResolver: SamConversionResolver,
                samConversionOracle: SamConversionOracle,
                allowNonSpreadArraysForVarargAfterSam: Boolean
            ): SamAdapterExtensionFunctionDescriptorImpl {
                val descriptor = SamAdapterExtensionFunctionDescriptorImpl(
                    sourceFunction.containingDeclaration,
                    null,
                    sourceFunction.annotations,
                    sourceFunction.name,
                    CallableMemberDescriptor.Kind.SYNTHESIZED,
                    sourceFunction.original.source
                )
                descriptor.baseDescriptorForSynthetic = sourceFunction

                val sourceTypeParams = (sourceFunction.typeParameters).toMutableList()
                val ownerClass = sourceFunction.containingDeclaration as ClassDescriptor

                val typeParameters = ArrayList(sourceTypeParams.size)
                val typeSubstitutor =
                    DescriptorSubstitutor.substituteTypeParameters(sourceTypeParams, TypeSubstitution.EMPTY, descriptor, typeParameters)

                val returnType = typeSubstitutor.safeSubstitute(sourceFunction.returnType!!, Variance.INVARIANT)
                val valueParameters = JavaSingleAbstractMethodUtils.createValueParametersForSamAdapter(
                    sourceFunction, descriptor, typeSubstitutor, samResolver, samConversionOracle, allowNonSpreadArraysForVarargAfterSam
                )

                val visibility = syntheticVisibility(sourceFunction, isUsedForExtension = false)

                descriptor.initialize(
                    null, ownerClass.thisAsReceiverParameter, emptyList(), typeParameters, valueParameters, returnType,
                    Modality.FINAL, visibility
                )

                descriptor.isOperator = sourceFunction.isOperator
                descriptor.isInfix = sourceFunction.isInfix

                return descriptor
            }
        }

        override fun hasStableParameterNames() = baseDescriptorForSynthetic.hasStableParameterNames()
        override fun hasSynthesizedParameterNames() = baseDescriptorForSynthetic.hasSynthesizedParameterNames()

        override fun createSubstitutedCopy(
            newOwner: DeclarationDescriptor,
            original: FunctionDescriptor?,
            kind: CallableMemberDescriptor.Kind,
            newName: Name?,
            annotations: Annotations,
            source: SourceElement
        ): SamAdapterExtensionFunctionDescriptorImpl {
            return SamAdapterExtensionFunctionDescriptorImpl(
                containingDeclaration, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind, source
            ).apply {
                baseDescriptorForSynthetic = this@SamAdapterExtensionFunctionDescriptorImpl.baseDescriptorForSynthetic
            }
        }

        override fun newCopyBuilder(substitutor: TypeSubstitutor): CopyConfiguration =
            super.newCopyBuilder(substitutor).setOriginal(this.original)

        override fun doSubstitute(configuration: CopyConfiguration): FunctionDescriptor? {
            val descriptor = super.doSubstitute(configuration) as SamAdapterExtensionFunctionDescriptorImpl? ?: return null
            val original = configuration.original
                ?: throw UnsupportedOperationException("doSubstitute with no original should not be called for synthetic extension $this")

            original as SamAdapterExtensionFunctionDescriptorImpl
            assert(original.original == original) { "original in doSubstitute should have no other original" }

            val sourceFunctionSubstitutor =
                CompositionTypeSubstitution(configuration.substitution, fromSourceFunctionTypeParameters).buildSubstitutor()

            descriptor.baseDescriptorForSynthetic = original.baseDescriptorForSynthetic.substitute(sourceFunctionSubstitutor) ?: return null
            return descriptor
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy