When expression.
Fix object & enum references.
This commit is contained in:
committed by
Dmitry Petrov
parent
d6040d8570
commit
b96626fd4c
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.psi2ir.generators
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
@@ -50,6 +51,10 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return createTemporary(ktExpression, irExpression, nameHint)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String?): IrVariable {
|
||||||
val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, nameHint)
|
val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, nameHint)
|
||||||
putValue(ktExpression, VariableLValue(irTmpVar))
|
putValue(ktExpression, VariableLValue(irTmpVar))
|
||||||
return irTmpVar
|
return irTmpVar
|
||||||
@@ -75,12 +80,16 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
receiverValues[receiver] = irValue
|
receiverValues[receiver] = irValue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putValue(valueArgument: ValueParameterDescriptor, irValue: IrValue) {
|
fun putValue(parameter: ValueParameterDescriptor, irValue: IrValue) {
|
||||||
valueArgumentValues[valueArgument] = irValue
|
valueArgumentValues[parameter] = irValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun valueOf(ktExpression: KtExpression) = expressionValues[ktExpression]?.load()
|
||||||
|
fun valueOf(receiver: ReceiverValue) = receiverValues[receiver]?.load()
|
||||||
|
fun valueOf(parameter: ValueParameterDescriptor) = valueArgumentValues[parameter]?.load()
|
||||||
|
|
||||||
fun generateCall(
|
fun generateCall(
|
||||||
ktExpression: KtExpression,
|
ktElement: KtElement,
|
||||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||||
operator: IrOperator? = null,
|
operator: IrOperator? = null,
|
||||||
superQualifier: ClassDescriptor? = null
|
superQualifier: ClassDescriptor? = null
|
||||||
@@ -91,15 +100,15 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
return when (descriptor) {
|
return when (descriptor) {
|
||||||
is PropertyDescriptor ->
|
is PropertyDescriptor ->
|
||||||
IrGetPropertyExpressionImpl(
|
IrGetPropertyExpressionImpl(
|
||||||
ktExpression.startOffset, ktExpression.endOffset,
|
ktElement.startOffset, ktElement.endOffset,
|
||||||
returnType,
|
returnType,
|
||||||
resolvedCall.call.isSafeCall(), descriptor
|
resolvedCall.call.isSafeCall(), descriptor
|
||||||
).apply {
|
).apply {
|
||||||
dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
|
dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
|
||||||
extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
|
extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
|
||||||
}
|
}
|
||||||
is FunctionDescriptor ->
|
is FunctionDescriptor ->
|
||||||
generateFunctionCall(descriptor, ktExpression, returnType, operator, resolvedCall, superQualifier)
|
generateFunctionCall(descriptor, ktElement, returnType, operator, resolvedCall, superQualifier)
|
||||||
else ->
|
else ->
|
||||||
TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}")
|
TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}")
|
||||||
}
|
}
|
||||||
@@ -121,21 +130,21 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
|
|
||||||
private fun generateFunctionCall(
|
private fun generateFunctionCall(
|
||||||
descriptor: FunctionDescriptor,
|
descriptor: FunctionDescriptor,
|
||||||
ktExpression: KtExpression,
|
ktElement: KtElement,
|
||||||
resultType: KotlinType?,
|
resultType: KotlinType?,
|
||||||
operator: IrOperator?,
|
operator: IrOperator?,
|
||||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||||
superQualifier: ClassDescriptor?
|
superQualifier: ClassDescriptor?
|
||||||
): IrExpression {
|
): IrExpression {
|
||||||
val irCall = IrCallExpressionImpl(
|
val irCall = IrCallExpressionImpl(
|
||||||
ktExpression.startOffset, ktExpression.endOffset, resultType,
|
ktElement.startOffset, ktElement.endOffset, resultType,
|
||||||
descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier
|
descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier
|
||||||
)
|
)
|
||||||
irCall.dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
|
irCall.dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
|
||||||
irCall.extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
|
irCall.extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
|
||||||
|
|
||||||
return if (resolvedCall.requiresArgumentReordering()) {
|
return if (resolvedCall.requiresArgumentReordering()) {
|
||||||
generateCallWithArgumentReordering(irCall, ktExpression, resolvedCall, resultType)
|
generateCallWithArgumentReordering(irCall, ktElement, resolvedCall, resultType)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
irCall.apply {
|
irCall.apply {
|
||||||
@@ -152,7 +161,7 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
|
|
||||||
private fun generateCallWithArgumentReordering(
|
private fun generateCallWithArgumentReordering(
|
||||||
irCall: IrCallExpression,
|
irCall: IrCallExpression,
|
||||||
ktExpression: KtExpression,
|
ktElement: KtElement,
|
||||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||||
resultType: KotlinType?
|
resultType: KotlinType?
|
||||||
): IrExpression {
|
): IrExpression {
|
||||||
@@ -161,8 +170,8 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values
|
val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values
|
||||||
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
|
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
|
||||||
|
|
||||||
val hasResult = isUsedAsExpression(ktExpression)
|
val hasResult = isUsedAsExpression(ktElement)
|
||||||
val irBlock = IrBlockExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, resultType, hasResult,
|
val irBlock = IrBlockExpressionImpl(ktElement.startOffset, ktElement.endOffset, resultType, hasResult,
|
||||||
IrOperator.SYNTHETIC_BLOCK)
|
IrOperator.SYNTHETIC_BLOCK)
|
||||||
|
|
||||||
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
|
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
|
||||||
@@ -189,22 +198,22 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
return irBlock
|
return irBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?, receiverParameterDescriptor: ReceiverParameterDescriptor?) =
|
fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?, receiverParameterDescriptor: ReceiverParameterDescriptor?) =
|
||||||
generateReceiver(ktExpression, receiver, receiverParameterDescriptor?.type)
|
generateReceiver(ktElement, receiver, receiverParameterDescriptor?.type)
|
||||||
|
|
||||||
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?, expectedType: KotlinType?) =
|
fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?, expectedType: KotlinType?) =
|
||||||
generateReceiver(ktExpression, receiver)?.toExpectedType(expectedType)
|
generateReceiver(ktElement, receiver)?.toExpectedType(expectedType)
|
||||||
|
|
||||||
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? =
|
fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?): IrExpression? =
|
||||||
if (receiver == null)
|
if (receiver == null)
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
receiverValues[receiver]?.load() ?: doGenerateReceiver(ktExpression, receiver)
|
receiverValues[receiver]?.load() ?: doGenerateReceiver(ktElement, receiver)
|
||||||
|
|
||||||
fun doGenerateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? =
|
fun doGenerateReceiver(ktElement: KtElement, receiver: ReceiverValue?): IrExpression? =
|
||||||
when (receiver) {
|
when (receiver) {
|
||||||
is ImplicitClassReceiver ->
|
is ImplicitClassReceiver ->
|
||||||
IrThisExpressionImpl(ktExpression.startOffset, ktExpression.startOffset, receiver.type, receiver.classDescriptor)
|
IrThisExpressionImpl(ktElement.startOffset, ktElement.startOffset, receiver.type, receiver.classDescriptor)
|
||||||
is ThisClassReceiver ->
|
is ThisClassReceiver ->
|
||||||
(receiver as? ExpressionReceiver)?.expression?.let { receiverExpression ->
|
(receiver as? ExpressionReceiver)?.expression?.let { receiverExpression ->
|
||||||
IrThisExpressionImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor)
|
IrThisExpressionImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor)
|
||||||
@@ -215,7 +224,7 @@ class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
|||||||
IrGetObjectValueExpressionImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type,
|
IrGetObjectValueExpressionImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type,
|
||||||
receiver.classQualifier.descriptor)
|
receiver.classQualifier.descriptor)
|
||||||
is ExtensionReceiver ->
|
is ExtensionReceiver ->
|
||||||
IrGetExtensionReceiverExpressionImpl(ktExpression.startOffset, ktExpression.startOffset, receiver.type,
|
IrGetExtensionReceiverExpressionImpl(ktElement.startOffset, ktElement.startOffset, receiver.type,
|
||||||
receiver.declarationDescriptor.extensionReceiverParameter!!)
|
receiver.declarationDescriptor.extensionReceiverParameter!!)
|
||||||
null ->
|
null ->
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrDummyExpression
|
import org.jetbrains.kotlin.ir.expressions.IrDummyExpression
|
||||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
@@ -54,8 +55,8 @@ fun IrGenerator.getExpectedTypeForLastInferredCall(key: KtExpression): KotlinTyp
|
|||||||
fun IrGenerator.getInferredTypeWithSmarcastsOrFail(key: KtExpression): KotlinType =
|
fun IrGenerator.getInferredTypeWithSmarcastsOrFail(key: KtExpression): KotlinType =
|
||||||
getInferredTypeWithSmartcasts(key) ?: TODO("No type for expression: ${key.text}")
|
getInferredTypeWithSmartcasts(key) ?: TODO("No type for expression: ${key.text}")
|
||||||
|
|
||||||
fun IrGenerator.isUsedAsExpression(ktExpression: KtExpression) =
|
fun IrGenerator.isUsedAsExpression(ktElement: KtElement) =
|
||||||
get(BindingContext.USED_AS_EXPRESSION, ktExpression) ?: false
|
get(BindingContext.USED_AS_EXPRESSION, ktElement) ?: false
|
||||||
|
|
||||||
fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? =
|
fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? =
|
||||||
key.getResolvedCall(context.bindingContext)
|
key.getResolvedCall(context.bindingContext)
|
||||||
|
|||||||
+38
-27
@@ -31,8 +31,11 @@ import org.jetbrains.kotlin.psi2ir.toExpectedType
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||||
import org.jetbrains.kotlin.resolve.constants.IntValue
|
import org.jetbrains.kotlin.resolve.constants.IntValue
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.NullValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
|
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
|
||||||
@@ -140,6 +143,8 @@ class StatementGenerator(
|
|||||||
IrLiteralExpressionImpl.string(expression.startOffset, expression.endOffset, constantType, constantValue.value)
|
IrLiteralExpressionImpl.string(expression.startOffset, expression.endOffset, constantType, constantValue.value)
|
||||||
is IntValue ->
|
is IntValue ->
|
||||||
IrLiteralExpressionImpl.int(expression.startOffset, expression.endOffset, constantType, constantValue.value)
|
IrLiteralExpressionImpl.int(expression.startOffset, expression.endOffset, constantType, constantValue.value)
|
||||||
|
is NullValue ->
|
||||||
|
IrLiteralExpressionImpl.nullLiteral(expression.startOffset, expression.endOffset, constantType)
|
||||||
else ->
|
else ->
|
||||||
TODO("handle other literal types: ${constantValue.type}")
|
TODO("handle other literal types: ${constantValue.type}")
|
||||||
}
|
}
|
||||||
@@ -166,40 +171,45 @@ class StatementGenerator(
|
|||||||
IrLiteralExpressionImpl.string(entry.startOffset, entry.endOffset, context.builtIns.stringType, entry.text)
|
IrLiteralExpressionImpl.string(entry.startOffset, entry.endOffset, context.builtIns.stringType, entry.text)
|
||||||
|
|
||||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Nothing?): IrExpression {
|
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Nothing?): IrExpression {
|
||||||
val resolvedCall = getResolvedCall(expression)
|
val resolvedCall = getResolvedCall(expression)!!
|
||||||
|
|
||||||
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||||
TODO("Unexpected VariableAsFunctionResolvedCall")
|
TODO("Unexpected VariableAsFunctionResolvedCall")
|
||||||
}
|
}
|
||||||
|
|
||||||
val descriptor = resolvedCall?.resultingDescriptor
|
val descriptor = resolvedCall.resultingDescriptor
|
||||||
|
|
||||||
return when (descriptor) {
|
return generateExpressionForReferencedDescriptor(descriptor, expression, resolvedCall)
|
||||||
is ClassDescriptor ->
|
|
||||||
if (DescriptorUtils.isObject(descriptor))
|
|
||||||
IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
|
|
||||||
else if (DescriptorUtils.isEnumEntry(descriptor))
|
|
||||||
IrGetEnumValueExpressionImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
|
|
||||||
else {
|
|
||||||
val companionObjectDescriptor = descriptor.companionObjectDescriptor
|
|
||||||
?: error("Class value without companion object: $descriptor")
|
|
||||||
IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset,
|
|
||||||
descriptor.classValueType,
|
|
||||||
companionObjectDescriptor)
|
|
||||||
}
|
|
||||||
is PropertyDescriptor -> {
|
|
||||||
CallGenerator(this).generateCall(expression, resolvedCall)
|
|
||||||
}
|
|
||||||
is VariableDescriptor ->
|
|
||||||
IrGetVariableExpressionImpl(expression.startOffset, expression.endOffset, descriptor)
|
|
||||||
else ->
|
|
||||||
IrDummyExpression(
|
|
||||||
expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression),
|
|
||||||
expression.getReferencedName() + ": ${descriptor?.name} ${descriptor?.javaClass?.simpleName}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateExpressionForReferencedDescriptor(descriptor: DeclarationDescriptor, expression: KtExpression, resolvedCall: ResolvedCall<*>): IrExpression =
|
||||||
|
when (descriptor) {
|
||||||
|
is FakeCallableDescriptorForObject ->
|
||||||
|
generateExpressionForReferencedDescriptor(descriptor.getReferencedDescriptor(), expression, resolvedCall)
|
||||||
|
is ClassDescriptor ->
|
||||||
|
if (DescriptorUtils.isObject(descriptor))
|
||||||
|
IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
|
||||||
|
else if (DescriptorUtils.isEnumEntry(descriptor))
|
||||||
|
IrGetEnumValueExpressionImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
|
||||||
|
else {
|
||||||
|
val companionObjectDescriptor = descriptor.companionObjectDescriptor
|
||||||
|
?: error("Class value without companion object: $descriptor")
|
||||||
|
IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset,
|
||||||
|
descriptor.classValueType,
|
||||||
|
companionObjectDescriptor)
|
||||||
|
}
|
||||||
|
is PropertyDescriptor -> {
|
||||||
|
CallGenerator(this).generateCall(expression, resolvedCall)
|
||||||
|
}
|
||||||
|
is VariableDescriptor ->
|
||||||
|
IrGetVariableExpressionImpl(expression.startOffset, expression.endOffset, descriptor)
|
||||||
|
else ->
|
||||||
|
IrDummyExpression(
|
||||||
|
expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression),
|
||||||
|
expression.text + ": ${descriptor.name} ${descriptor.javaClass.simpleName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitCallExpression(expression: KtCallExpression, data: Nothing?): IrStatement {
|
override fun visitCallExpression(expression: KtCallExpression, data: Nothing?): IrStatement {
|
||||||
val resolvedCall = getResolvedCall(expression) ?: TODO("No resolved call for call expression")
|
val resolvedCall = getResolvedCall(expression) ?: TODO("No resolved call for call expression")
|
||||||
|
|
||||||
@@ -280,5 +290,6 @@ class StatementGenerator(
|
|||||||
return irWhen
|
return irWhen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitWhenExpression(expression: KtWhenExpression, data: Nothing?): IrStatement =
|
||||||
|
WhenExpressionGenerator(this).generate(expression)
|
||||||
}
|
}
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
import org.jetbrains.kotlin.psi2ir.load
|
||||||
|
import org.jetbrains.kotlin.psi2ir.toExpectedType
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
|
||||||
|
class WhenExpressionGenerator(val statementGenerator: StatementGenerator) : IrGenerator {
|
||||||
|
override val context: GeneratorContext get() = statementGenerator.context
|
||||||
|
|
||||||
|
fun generate(expression: KtWhenExpression): IrExpression {
|
||||||
|
val conditionsGenerator = CallGenerator(statementGenerator)
|
||||||
|
|
||||||
|
val irSubject = expression.subjectExpression?.let {
|
||||||
|
conditionsGenerator.createTemporary(it, statementGenerator.generateExpression(it), "subject")
|
||||||
|
}
|
||||||
|
|
||||||
|
val resultType = getInferredTypeWithSmartcasts(expression)
|
||||||
|
|
||||||
|
val irWhen = IrWhenExpressionImpl(expression.startOffset, expression.endOffset, resultType, irSubject)
|
||||||
|
|
||||||
|
for (ktEntry in expression.entries) {
|
||||||
|
if (ktEntry.isElse) {
|
||||||
|
irWhen.elseExpression = statementGenerator.generateExpression(ktEntry.expression!!).toExpectedType(resultType)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val irBranch = IrBranchImpl(ktEntry.startOffset, ktEntry.endOffset)
|
||||||
|
|
||||||
|
for (ktCondition in ktEntry.conditions) {
|
||||||
|
val irCondition =
|
||||||
|
if (irSubject != null)
|
||||||
|
generateWhenConditionWithSubject(ktCondition, conditionsGenerator, irSubject)
|
||||||
|
else
|
||||||
|
generateWhenConditionNoSubject(ktCondition)
|
||||||
|
irBranch.addCondition(irCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
irBranch.result = statementGenerator.generateExpression(ktEntry.expression!!).toExpectedType(resultType)
|
||||||
|
|
||||||
|
irWhen.addBranch(irBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return irWhen
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateWhenConditionNoSubject(ktCondition: KtWhenCondition): IrExpression =
|
||||||
|
statementGenerator.generateExpression((ktCondition as KtWhenConditionWithExpression).expression!!)
|
||||||
|
.toExpectedType(context.builtIns.booleanType)
|
||||||
|
|
||||||
|
private fun generateWhenConditionWithSubject(
|
||||||
|
ktCondition: KtWhenCondition,
|
||||||
|
conditionsGenerator: CallGenerator,
|
||||||
|
irSubject: IrVariable
|
||||||
|
): IrExpression {
|
||||||
|
return when (ktCondition) {
|
||||||
|
is KtWhenConditionWithExpression ->
|
||||||
|
generateEqualsCondition(irSubject, ktCondition)
|
||||||
|
is KtWhenConditionInRange ->
|
||||||
|
generateInRangeCondition(conditionsGenerator, ktCondition)
|
||||||
|
is KtWhenConditionIsPattern ->
|
||||||
|
generateIsPatternCondition(irSubject, ktCondition)
|
||||||
|
else ->
|
||||||
|
throw AssertionError("Unexpected 'when' condition: ${ktCondition.text}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateIsPatternCondition(irSubject: IrVariable, ktCondition: KtWhenConditionIsPattern): IrExpression {
|
||||||
|
val isType = getOrFail(BindingContext.TYPE, ktCondition.typeReference)
|
||||||
|
return IrTypeOperatorExpressionImpl(
|
||||||
|
ktCondition.startOffset, ktCondition.endOffset, context.builtIns.booleanType,
|
||||||
|
IrTypeOperator.INSTANCEOF, isType, irSubject.load()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateInRangeCondition(conditionsGenerator: CallGenerator, ktCondition: KtWhenConditionInRange): IrExpression {
|
||||||
|
val inResolvedCall = getResolvedCall(ktCondition.operationReference)!!
|
||||||
|
val inOperator = getIrBinaryOperator(ktCondition.operationReference.getReferencedNameElementType())
|
||||||
|
val irInCall = conditionsGenerator.generateCall(ktCondition, inResolvedCall, inOperator)
|
||||||
|
return when (inOperator) {
|
||||||
|
IrOperator.IN -> irInCall
|
||||||
|
IrOperator.NOT_IN ->
|
||||||
|
IrUnaryOperatorExpressionImpl(ktCondition.startOffset, ktCondition.endOffset, context.builtIns.booleanType,
|
||||||
|
IrOperator.EXCL, null, irInCall)
|
||||||
|
else -> throw AssertionError("Expected 'in' or '!in', got $inOperator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateEqualsCondition(irSubject: IrVariable, ktCondition: KtWhenConditionWithExpression): IrBinaryOperatorExpressionImpl =
|
||||||
|
IrBinaryOperatorExpressionImpl(
|
||||||
|
ktCondition.startOffset, ktCondition.endOffset,
|
||||||
|
context.builtIns.booleanType, IrOperator.EQEQ, null,
|
||||||
|
irSubject.load(), statementGenerator.generateExpression(ktCondition.expression!!)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -27,5 +27,4 @@ const val MODULE_SLOT = 0
|
|||||||
const val INITIALIZER_SLOT = 0
|
const val INITIALIZER_SLOT = 0
|
||||||
const val WHEN_SUBJECT_VARIABLE_SLOT = -1
|
const val WHEN_SUBJECT_VARIABLE_SLOT = -1
|
||||||
const val WHEN_ELSE_EXPRESSION_SLOT = -2
|
const val WHEN_ELSE_EXPRESSION_SLOT = -2
|
||||||
const val BRANCH_CONDITION_SLOT = -1
|
const val BRANCH_RESULT_SLOT = -1
|
||||||
const val BRANCH_RESULT_SLOT = -2
|
|
||||||
@@ -58,6 +58,9 @@ class IrLiteralExpressionImpl<out T> (
|
|||||||
|
|
||||||
fun int(startOffset: Int, endOffset: Int, type: KotlinType, value: Int): IrLiteralExpressionImpl<Int> =
|
fun int(startOffset: Int, endOffset: Int, type: KotlinType, value: Int): IrLiteralExpressionImpl<Int> =
|
||||||
IrLiteralExpressionImpl(startOffset, endOffset, type, IrLiteralKind.Int, value)
|
IrLiteralExpressionImpl(startOffset, endOffset, type, IrLiteralKind.Int, value)
|
||||||
|
|
||||||
|
fun nullLiteral(startOffset: Int, endOffset: Int, type: KotlinType): IrLiteralExpressionImpl<Nothing?> =
|
||||||
|
IrLiteralExpressionImpl(startOffset, endOffset, type, IrLiteralKind.Null, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.*
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface IrWhenExpression : IrExpression {
|
interface IrWhenExpression : IrExpression {
|
||||||
@@ -31,8 +32,10 @@ interface IrWhenExpression : IrExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IrBranch : IrElement {
|
interface IrBranch : IrElement {
|
||||||
var condition: IrExpression
|
val conditions: List<IrExpression>
|
||||||
var result: IrExpression
|
var result: IrExpression
|
||||||
|
|
||||||
|
fun addCondition(expression: IrExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
class IrWhenExpressionImpl(
|
class IrWhenExpressionImpl(
|
||||||
@@ -44,7 +47,7 @@ class IrWhenExpressionImpl(
|
|||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
type: KotlinType?,
|
type: KotlinType?,
|
||||||
subject: IrVariable
|
subject: IrVariable?
|
||||||
) : this(startOffset, endOffset, type) {
|
) : this(startOffset, endOffset, type) {
|
||||||
this.subject = subject
|
this.subject = subject
|
||||||
}
|
}
|
||||||
@@ -121,19 +124,17 @@ class IrWhenExpressionImpl(
|
|||||||
|
|
||||||
class IrBranchImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOffset, endOffset), IrBranch {
|
class IrBranchImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOffset, endOffset), IrBranch {
|
||||||
constructor(startOffset: Int, endOffset: Int, condition: IrExpression, result: IrExpression) : this(startOffset, endOffset) {
|
constructor(startOffset: Int, endOffset: Int, condition: IrExpression, result: IrExpression) : this(startOffset, endOffset) {
|
||||||
this.condition = condition
|
|
||||||
this.result = result
|
this.result = result
|
||||||
|
addCondition(condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var conditionImpl: IrExpression? = null
|
override val conditions: MutableList<IrExpression> = SmartList()
|
||||||
override var condition: IrExpression
|
|
||||||
get() = conditionImpl!!
|
override fun addCondition(expression: IrExpression) {
|
||||||
set(value) {
|
expression.assertDetached()
|
||||||
value.assertDetached()
|
expression.setTreeLocation(this, conditions.size)
|
||||||
conditionImpl?.detach()
|
conditions.add(expression)
|
||||||
conditionImpl = value
|
}
|
||||||
value.setTreeLocation(this, BRANCH_CONDITION_SLOT)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var resultImpl: IrExpression? = null
|
private var resultImpl: IrExpression? = null
|
||||||
override var result: IrExpression
|
override var result: IrExpression
|
||||||
@@ -147,17 +148,19 @@ class IrBranchImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOffset
|
|||||||
|
|
||||||
override fun getChild(slot: Int): IrElement? =
|
override fun getChild(slot: Int): IrElement? =
|
||||||
when (slot) {
|
when (slot) {
|
||||||
BRANCH_CONDITION_SLOT -> condition
|
|
||||||
BRANCH_RESULT_SLOT -> result
|
BRANCH_RESULT_SLOT -> result
|
||||||
else -> null
|
else -> conditions.getOrNull(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChild(slot: Int, newChild: IrElement) {
|
override fun replaceChild(slot: Int, newChild: IrElement) {
|
||||||
when (slot) {
|
when (slot) {
|
||||||
BRANCH_CONDITION_SLOT ->
|
|
||||||
condition = newChild.assertCast()
|
|
||||||
BRANCH_RESULT_SLOT ->
|
BRANCH_RESULT_SLOT ->
|
||||||
result = newChild.assertCast()
|
result = newChild.assertCast()
|
||||||
|
in conditions.indices -> {
|
||||||
|
conditions[slot].detach()
|
||||||
|
conditions[slot] = newChild.assertCast()
|
||||||
|
newChild.setTreeLocation(this, slot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +168,7 @@ class IrBranchImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOffset
|
|||||||
visitor.visitBranch(this, data)
|
visitor.visitBranch(this, data)
|
||||||
|
|
||||||
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
|
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
|
||||||
conditionImpl?.accept(visitor, data)
|
conditions.forEach { it.accept(visitor, data) }
|
||||||
resultImpl?.accept(visitor, data)
|
resultImpl?.accept(visitor, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
|
|||||||
subject.accept(this, "\$subject:")
|
subject.accept(this, "\$subject:")
|
||||||
}
|
}
|
||||||
for (branch in expression.branches) {
|
for (branch in expression.branches) {
|
||||||
branch.condition.accept(this, "if")
|
branch.conditions.forEach { it.accept(this, "if") }
|
||||||
branch.result.accept(this, "then")
|
branch.result.accept(this, "then")
|
||||||
}
|
}
|
||||||
expression.elseExpression?.accept(this, "else")
|
expression.elseExpression?.accept(this, "else")
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ IrFunction public fun B.test(): kotlin.Unit
|
|||||||
BLOCK type=<no-type> hasResult=false operator=null
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK
|
BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK
|
||||||
VAR val tmp0: A
|
VAR val tmp0: A
|
||||||
GET_VAR A type=A operator=null
|
GET_OBJECT A type=A
|
||||||
VAR val x: kotlin.Int
|
VAR val x: kotlin.Int
|
||||||
CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1)
|
CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1)
|
||||||
$this: $RECEIVER of: test type=B
|
$this: $RECEIVER of: test type=B
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
enum class Enum { A }
|
||||||
|
object A
|
||||||
|
val a = 0
|
||||||
|
class Z {
|
||||||
|
companion object
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test1() = Enum.A
|
||||||
|
fun test2() = A
|
||||||
|
fun test3() = a
|
||||||
|
fun test4() = Z
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
IrFile /values.kt
|
||||||
|
DUMMY Enum
|
||||||
|
DUMMY A
|
||||||
|
IrProperty public val a: kotlin.Int = 0 getter=null setter=null
|
||||||
|
IrExpressionBody
|
||||||
|
LITERAL Int type=kotlin.Int value='0'
|
||||||
|
DUMMY Z
|
||||||
|
IrFunction public fun test1(): Enum
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
RETURN type=<no-type>
|
||||||
|
GET_ENUM_VALUE A type=Enum
|
||||||
|
IrFunction public fun test2(): A
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
RETURN type=<no-type>
|
||||||
|
GET_OBJECT A type=A
|
||||||
|
IrFunction public fun test3(): kotlin.Int
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
RETURN type=<no-type>
|
||||||
|
GET_PROPERTY .a type=kotlin.Int operator=null
|
||||||
|
IrFunction public fun test4(): Z.Companion
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
RETURN type=<no-type>
|
||||||
|
GET_OBJECT Companion type=Z.Companion
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
object A
|
||||||
|
|
||||||
|
fun test(x: Any?) =
|
||||||
|
when (x) {
|
||||||
|
null -> "null"
|
||||||
|
A -> "A"
|
||||||
|
is String -> "String"
|
||||||
|
in setOf<Nothing>() -> "nothingness?"
|
||||||
|
else -> "something"
|
||||||
|
}
|
||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
IrFile /when.kt
|
||||||
|
DUMMY A
|
||||||
|
IrFunction public fun test(/*0*/ x: kotlin.Any?): kotlin.String
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
RETURN type=<no-type>
|
||||||
|
WHEN subject=tmp0_subject type=kotlin.String
|
||||||
|
$subject:: VAR val tmp0_subject: kotlin.Any?
|
||||||
|
GET_VAR x type=kotlin.Any? operator=null
|
||||||
|
if: BINARY_OP operator=EQEQ type=kotlin.Boolean related=null
|
||||||
|
GET_VAR tmp0_subject type=kotlin.Any? operator=null
|
||||||
|
LITERAL Null type=kotlin.Nothing? value='null'
|
||||||
|
then: LITERAL String type=kotlin.String value='null'
|
||||||
|
if: BINARY_OP operator=EQEQ type=kotlin.Boolean related=null
|
||||||
|
GET_VAR tmp0_subject type=kotlin.Any? operator=null
|
||||||
|
GET_OBJECT A type=A
|
||||||
|
then: LITERAL String type=kotlin.String value='A'
|
||||||
|
if: TYPE_OP operator=INSTANCEOF typeOperand=kotlin.String
|
||||||
|
GET_VAR tmp0_subject type=kotlin.Any? operator=null
|
||||||
|
then: LITERAL String type=kotlin.String value='String'
|
||||||
|
if: CALL .contains type=kotlin.Boolean operator=IN
|
||||||
|
$receiver: CALL .setOf type=kotlin.collections.Set<kotlin.Nothing> operator=null
|
||||||
|
element: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Any
|
||||||
|
GET_VAR tmp0_subject type=kotlin.Any? operator=null
|
||||||
|
then: LITERAL String type=kotlin.String value='nothingness?'
|
||||||
|
else: LITERAL String type=kotlin.String value='something'
|
||||||
@@ -214,4 +214,16 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/typeOperators.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/typeOperators.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("values.kt")
|
||||||
|
public void testValues() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/values.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("when.kt")
|
||||||
|
public void testWhen() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/when.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user