/*
* 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.psi2ir.generators
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getSuperCallExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitutor
import java.lang.AssertionError
fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? =
receiver?.let { generateReceiver(ktDefaultElement, receiver) }
fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): IntermediateValue =
generateReceiver(ktDefaultElement.startOffset, ktDefaultElement.endOffset, receiver)
fun StatementGenerator.generateReceiver(defaultStartOffset: Int, defaultEndOffset: Int, receiver: ReceiverValue): IntermediateValue {
val irReceiverType = receiver.type.toIrType()
if (receiver is TransientReceiver) return TransientReceiverValue(irReceiverType)
return generateDelegatedValue(irReceiverType) {
val receiverExpression: IrExpression = when (receiver) {
is ImplicitClassReceiver -> {
val receiverClassDescriptor = receiver.classDescriptor
if (shouldGenerateReceiverAsSingletonReference(receiverClassDescriptor))
generateSingletonReference(receiverClassDescriptor, defaultStartOffset, defaultEndOffset, receiver.type)
else
IrGetValueImpl(
defaultStartOffset, defaultEndOffset, irReceiverType,
context.symbolTable.referenceValueParameter(receiverClassDescriptor.thisAsReceiverParameter)
)
}
is ThisClassReceiver ->
generateThisOrSuperReceiver(receiver, receiver.classDescriptor)
is SuperCallReceiverValue ->
generateThisOrSuperReceiver(receiver, receiver.thisType.constructor.declarationDescriptor as ClassDescriptor)
is ExpressionReceiver ->
generateExpression(receiver.expression)
is ClassValueReceiver ->
IrGetObjectValueImpl(
receiver.expression.startOffset, receiver.expression.endOffset, irReceiverType,
context.symbolTable.referenceClass(receiver.classQualifier.descriptor as ClassDescriptor)
)
is ExtensionReceiver ->
IrGetValueImpl(
defaultStartOffset, defaultStartOffset, irReceiverType,
context.symbolTable.referenceValueParameter(receiver.declarationDescriptor.extensionReceiverParameter!!)
)
else ->
TODO("Receiver: ${receiver::class.java.simpleName}")
}
if (receiverExpression is IrExpressionWithCopy)
RematerializableValue(receiverExpression)
else
OnceExpressionValue(receiverExpression)
}
}
fun StatementGenerator.generateSingletonReference(
descriptor: ClassDescriptor,
startOffset: Int,
endOffset: Int,
type: KotlinType
): IrDeclarationReference {
val irType = type.toIrType()
return when {
DescriptorUtils.isObject(descriptor) ->
IrGetObjectValueImpl(
startOffset, endOffset, irType,
context.symbolTable.referenceClass(descriptor)
)
DescriptorUtils.isEnumEntry(descriptor) ->
IrGetEnumValueImpl(
startOffset, endOffset, irType,
context.symbolTable.referenceEnumEntry(descriptor)
)
else -> {
val companionObjectDescriptor = descriptor.companionObjectDescriptor
?: throw java.lang.AssertionError("Class value without companion object: $descriptor")
IrGetObjectValueImpl(
startOffset, endOffset, irType,
context.symbolTable.referenceClass(companionObjectDescriptor)
)
}
}
}
private fun StatementGenerator.shouldGenerateReceiverAsSingletonReference(receiverClassDescriptor: ClassDescriptor): Boolean {
val scopeOwner = this.scopeOwner
return receiverClassDescriptor.kind.isSingleton &&
scopeOwner != receiverClassDescriptor && // For anonymous initializers
!(scopeOwner is CallableMemberDescriptor && scopeOwner.containingDeclaration == receiverClassDescriptor) // Members of object
}
private fun StatementGenerator.generateThisOrSuperReceiver(receiver: ReceiverValue, classDescriptor: ClassDescriptor): IrExpression {
val expressionReceiver =
receiver as? ExpressionReceiver ?: throw AssertionError("'this' or 'super' receiver should be an expression receiver")
val ktReceiver = expressionReceiver.expression
return IrGetValueImpl(
ktReceiver.startOffset, ktReceiver.endOffset,
expressionReceiver.type.toIrType(),
context.symbolTable.referenceValueParameter(classDescriptor.thisAsReceiverParameter)
)
}
fun StatementGenerator.generateBackingFieldReceiver(
startOffset: Int,
endOffset: Int,
resolvedCall: ResolvedCall<*>?,
fieldDescriptor: SyntheticFieldDescriptor
): IntermediateValue? {
val receiver = resolvedCall?.dispatchReceiver ?: fieldDescriptor.getDispatchReceiverForBackend() ?: return null
return this.generateReceiver(startOffset, endOffset, receiver)
}
fun StatementGenerator.generateCallReceiver(
ktDefaultElement: KtElement,
calleeDescriptor: CallableDescriptor,
dispatchReceiver: ReceiverValue?,
extensionReceiver: ReceiverValue?,
isSafe: Boolean,
isAssignmentReceiver: Boolean = false
): CallReceiver {
val dispatchReceiverValue: IntermediateValue?
val extensionReceiverValue: IntermediateValue?
when (calleeDescriptor) {
is ImportedFromObjectCallableDescriptor<*> -> {
assert(dispatchReceiver == null) {
"Call for member imported from object $calleeDescriptor has non-null dispatch receiver $dispatchReceiver"
}
dispatchReceiverValue =
generateReceiverForCalleeImportedFromObject(ktDefaultElement.startOffset, ktDefaultElement.endOffset, calleeDescriptor)
extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver)
}
is TypeAliasConstructorDescriptor -> {
assert(!(dispatchReceiver != null && extensionReceiver != null)) {
"Type alias constructor call for $calleeDescriptor can't have both dispatch receiver and extension receiver: " +
"$dispatchReceiver, $extensionReceiver"
}
dispatchReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver ?: dispatchReceiver)
extensionReceiverValue = null
}
else -> {
dispatchReceiverValue = generateReceiverOrNull(ktDefaultElement, dispatchReceiver)
extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver)
}
}
return when {
!isSafe ->
SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue)
extensionReceiverValue != null || dispatchReceiverValue != null ->
SafeCallReceiver(
this, ktDefaultElement.startOffset, ktDefaultElement.endOffset,
extensionReceiverValue, dispatchReceiverValue, isAssignmentReceiver
)
else ->
throw AssertionError("Safe call should have an explicit receiver: ${ktDefaultElement.text}")
}
}
private fun StatementGenerator.generateReceiverForCalleeImportedFromObject(
startOffset: Int,
endOffset: Int,
calleeDescriptor: ImportedFromObjectCallableDescriptor<*>
): ExpressionValue {
val objectDescriptor = calleeDescriptor.containingObject
val objectType = objectDescriptor.defaultType.toIrType()
return generateExpressionValue(objectType) {
IrGetObjectValueImpl(
startOffset, endOffset, objectType,
context.symbolTable.referenceClass(objectDescriptor)
)
}
}
fun StatementGenerator.generateVarargExpression(
varargArgument: VarargValueArgument,
valueParameter: ValueParameterDescriptor
): IrExpression? {
if (varargArgument.arguments.isEmpty()) {
return null
}
val varargStartOffset = varargArgument.arguments.fold(Int.MAX_VALUE) { minStartOffset, argument ->
Math.min(minStartOffset, argument.asElement().startOffset)
}
val varargEndOffset = varargArgument.arguments.fold(Int.MIN_VALUE) { maxEndOffset, argument ->
Math.max(maxEndOffset, argument.asElement().endOffset)
}
val varargElementType =
valueParameter.varargElementType ?: throw AssertionError("Vararg argument for non-vararg parameter $valueParameter")
val irVararg = IrVarargImpl(varargStartOffset, varargEndOffset, valueParameter.type.toIrType(), varargElementType.toIrType())
for (argument in varargArgument.arguments) {
val ktArgumentExpression = argument.getArgumentExpression()
?: throw AssertionError("No argument expression for vararg element ${argument.asElement().text}")
val irVarargElement =
if (argument.getSpreadElement() != null)
IrSpreadElementImpl(
ktArgumentExpression.startOffset, ktArgumentExpression.endOffset,
generateExpression(ktArgumentExpression)
)
else
generateExpression(ktArgumentExpression)
irVararg.addElement(irVarargElement)
}
return irVararg
}
fun StatementGenerator.generateValueArgument(
valueArgument: ResolvedValueArgument,
valueParameter: ValueParameterDescriptor
): IrExpression? =
when (valueArgument) {
is DefaultValueArgument ->
null
is ExpressionValueArgument ->
generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!)
is VarargValueArgument ->
generateVarargExpression(valueArgument, valueParameter)
else ->
TODO("Unexpected valueArgument: ${valueArgument::class.java.simpleName}")
}
fun Generator.getSuperQualifier(resolvedCall: ResolvedCall<*>): ClassDescriptor? {
val superCallExpression = getSuperCallExpression(resolvedCall.call) ?: return null
return getOrFail(BindingContext.REFERENCE_TARGET, superCallExpression.instanceReference) as ClassDescriptor
}
fun StatementGenerator.pregenerateCall(resolvedCall: ResolvedCall<*>): CallBuilder {
if (resolvedCall.isExtensionInvokeCall()) {
return pregenerateExtensionInvokeCall(resolvedCall)
}
val call = pregenerateCallReceivers(resolvedCall)
pregenerateValueArguments(call, resolvedCall)
return call
}
fun getTypeArguments(resolvedCall: ResolvedCall<*>?): Map<TypeParameterDescriptor, KotlinType>? {
if (resolvedCall == null) return null
val descriptor = resolvedCall.resultingDescriptor
if (descriptor.typeParameters.isEmpty()) return null
return resolvedCall.typeArguments
}
fun StatementGenerator.pregenerateExtensionInvokeCall(resolvedCall: ResolvedCall<*>): CallBuilder {
val extensionInvoke = resolvedCall.resultingDescriptor
val functionNClass = extensionInvoke.containingDeclaration as? ClassDescriptor
?: throw AssertionError("'invoke' should be a class member: $extensionInvoke")
val unsubstitutedPlainInvokes =
functionNClass.unsubstitutedMemberScope.getContributedFunctions(extensionInvoke.name, NoLookupLocation.FROM_BACKEND)
val unsubstitutedPlainInvoke = unsubstitutedPlainInvokes.singleOrNull()
?: throw AssertionError("There should be a single 'invoke' in FunctionN class: $unsubstitutedPlainInvokes")
assert(unsubstitutedPlainInvoke.typeParameters.isEmpty()) {
"'operator fun invoke' should have no type parameters: $unsubstitutedPlainInvoke"
}
val expectedValueParametersCount = extensionInvoke.valueParameters.size + 1
assert(unsubstitutedPlainInvoke.valueParameters.size == expectedValueParametersCount) {
"Plain 'invoke' should have $expectedValueParametersCount value parameters, got ${unsubstitutedPlainInvoke.valueParameters}"
}
val functionNType = extensionInvoke.dispatchReceiverParameter!!.type
val plainInvoke = unsubstitutedPlainInvoke.substitute(TypeSubstitutor.create(functionNType))
?: throw AssertionError("Substitution failed for $unsubstitutedPlainInvoke, type=$functionNType")
val ktCallElement = resolvedCall.call.callElement
val call = CallBuilder(
resolvedCall,
plainInvoke,
typeArguments = null, // FunctionN#invoke has no type parameters of its own
isExtensionInvokeCall = true
)
val functionReceiverValue = run {
val dispatchReceiver =
resolvedCall.dispatchReceiver ?: throw AssertionError("Extension 'invoke' call should have a dispatch receiver")
generateReceiver(ktCallElement, dispatchReceiver)
}
val extensionInvokeReceiverValue = run {
val extensionReceiver =
resolvedCall.extensionReceiver ?: throw AssertionError("Extension 'invoke' call should have an extension receiver")
generateReceiver(ktCallElement, extensionReceiver)
}
call.callReceiver =
if (resolvedCall.call.isSafeCall())
SafeExtensionInvokeCallReceiver(
this, ktCallElement.startOffset, ktCallElement.endOffset,
call, functionReceiverValue, extensionInvokeReceiverValue
)
else
ExtensionInvokeCallReceiver(call, functionReceiverValue, extensionInvokeReceiverValue)
call.irValueArgumentsByIndex[0] = null
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
val valueParameter = call.descriptor.valueParameters[index]
call.irValueArgumentsByIndex[index + 1] = generateValueArgument(valueArgument, valueParameter)
}
return call
}
private fun ResolvedCall<*>.isExtensionInvokeCall(): Boolean {
val callee = resultingDescriptor as? SimpleFunctionDescriptor ?: return false
if (callee.name.asString() != "invoke") return false
val dispatchReceiverType = callee.dispatchReceiverParameter?.type ?: return false
if (!dispatchReceiverType.isBuiltinFunctionalType) return false
return extensionReceiver != null
}
private fun StatementGenerator.pregenerateValueArguments(call: CallBuilder, resolvedCall: ResolvedCall<*>) {
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
val valueParameter = call.descriptor.valueParameters[index]
call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument, valueParameter)
}
}
fun StatementGenerator.pregenerateCallReceivers(resolvedCall: ResolvedCall<*>): CallBuilder {
val call = unwrapCallableDescriptorAndTypeArguments(resolvedCall)
call.callReceiver = generateCallReceiver(
resolvedCall.call.callElement,
resolvedCall.resultingDescriptor,
resolvedCall.dispatchReceiver,
resolvedCall.extensionReceiver,
isSafe = resolvedCall.call.isSafeCall()
)
call.superQualifier = getSuperQualifier(resolvedCall)
return call
}
fun unwrapCallableDescriptorAndTypeArguments(resolvedCall: ResolvedCall<*>): CallBuilder {
val originalDescriptor = resolvedCall.resultingDescriptor
val candidateDescriptor = resolvedCall.candidateDescriptor
val unwrappedDescriptor = when (originalDescriptor) {
is ImportedFromObjectCallableDescriptor<*> -> originalDescriptor.callableFromObject
is TypeAliasConstructorDescriptor -> originalDescriptor.underlyingConstructorDescriptor
else -> originalDescriptor
}
val originalTypeArguments = resolvedCall.typeArguments
val unsubstitutedUnwrappedDescriptor = unwrappedDescriptor.original
val unsubstitutedUnwrappedTypeParameters = unsubstitutedUnwrappedDescriptor.typeParameters
val unwrappedTypeArguments = when (originalDescriptor) {
is ImportedFromObjectCallableDescriptor<*> -> {
assert(originalDescriptor.typeParameters.size == unsubstitutedUnwrappedTypeParameters.size) {
"Mismatching original / unwrapped type parameters: " +
"originalDescriptor: $originalDescriptor; " +
"unsubstitutedUnwrappedDescriptor: $unsubstitutedUnwrappedDescriptor"
}
if (unsubstitutedUnwrappedTypeParameters.isEmpty())
null
else
unsubstitutedUnwrappedTypeParameters.associate {
val originalTypeParameter = candidateDescriptor.typeParameters[it.index]
val originalTypeArgument = originalTypeArguments[originalTypeParameter]
?: throw AssertionError("No type argument for $originalTypeParameter")
it to originalTypeArgument
}
}
is TypeAliasConstructorDescriptor -> {
val substitutedType = originalDescriptor.returnType
if (substitutedType.arguments.isEmpty())
null
else
unsubstitutedUnwrappedTypeParameters.associate {
it to substitutedType.arguments[it.index].type
}
}
else -> {
if (originalTypeArguments.keys.all { it.containingDeclaration == unsubstitutedUnwrappedDescriptor })
originalTypeArguments.takeIf { it.isNotEmpty() }
else {
assert(unsubstitutedUnwrappedTypeParameters.size == originalTypeArguments.size) {
"Mismatching type parameters and type arguments: " +
"unsubstitutedUnwrappedDescriptor: $unsubstitutedUnwrappedDescriptor; " +
"originalDescriptor: $originalDescriptor; " +
"originalTypeArguments: $originalTypeArguments"
}
if (unsubstitutedUnwrappedTypeParameters.isEmpty())
null
else {
originalTypeArguments.keys.associate { originalTypeParameter ->
val unwrappedTypeParameter = unsubstitutedUnwrappedTypeParameters[originalTypeParameter.index]
val originalTypeArgument = originalTypeArguments[originalTypeParameter]
?: throw AssertionError("No type argument for $unwrappedTypeParameter <= $originalTypeParameter")
unwrappedTypeParameter to originalTypeArgument
}
}
}
}
}
return CallBuilder(resolvedCall, unwrappedDescriptor, unwrappedTypeArguments)
}