Inline safe call chains.
This commit is contained in:
committed by
Dmitry Petrov
parent
3f256fab67
commit
dc4bb3015c
@@ -23,25 +23,28 @@ import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
|||||||
import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator
|
import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator
|
||||||
import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation
|
import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation
|
||||||
import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks
|
import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks
|
||||||
|
import org.jetbrains.kotlin.psi2ir.transformations.inlineSafeCallChains
|
||||||
import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts
|
import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
|
||||||
class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
|
class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
|
||||||
class Configuration(
|
class Configuration(
|
||||||
val shouldInlineDesugaredBlocks: Boolean = false,
|
val shouldInlineDesugaredBlocks: Boolean = false,
|
||||||
val shouldFoldStringConcatenation: Boolean = true
|
val shouldFoldStringConcatenation: Boolean = true,
|
||||||
|
val shouldInlineSafeCallChains: Boolean = true
|
||||||
)
|
)
|
||||||
|
|
||||||
fun generateModule(moduleDescriptor: ModuleDescriptor, ktFiles: List<KtFile>, bindingContext: BindingContext): IrModule {
|
fun generateModule(moduleDescriptor: ModuleDescriptor, ktFiles: List<KtFile>, bindingContext: BindingContext): IrModule {
|
||||||
val irGeneratorContext = GeneratorContext(moduleDescriptor, bindingContext)
|
val context = GeneratorContext(moduleDescriptor, bindingContext)
|
||||||
val irModule = ModuleGenerator(irGeneratorContext).generateModule(ktFiles)
|
val irModule = ModuleGenerator(context).generateModule(ktFiles)
|
||||||
postprocess(irModule)
|
postprocess(irModule, context)
|
||||||
return irModule
|
return irModule
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocess(irModule: IrModule) {
|
private fun postprocess(irModule: IrModule, context: GeneratorContext) {
|
||||||
insertImplicitCasts(irModule.irBuiltins.builtIns, irModule)
|
insertImplicitCasts(irModule.irBuiltins.builtIns, irModule)
|
||||||
if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irModule)
|
if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irModule)
|
||||||
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irModule)
|
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irModule)
|
||||||
|
if (configuration.shouldInlineSafeCallChains) inlineSafeCallChains(context, irModule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -93,7 +93,7 @@ class BranchingExpressionGenerator(val statementGenerator: StatementGenerator) :
|
|||||||
generateWhenConditionWithSubject(ktCondition, irSubject)
|
generateWhenConditionWithSubject(ktCondition, irSubject)
|
||||||
else
|
else
|
||||||
generateWhenConditionNoSubject(ktCondition)
|
generateWhenConditionNoSubject(ktCondition)
|
||||||
irBranchCondition = irBranchCondition?.let { whenComma(it, irCondition) } ?: irCondition
|
irBranchCondition = irBranchCondition?.let { context.whenComma(it, irCondition) } ?: irCondition
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||||
|
|
||||||
fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): Value? =
|
fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? =
|
||||||
receiver?.let { generateReceiver(ktDefaultElement, receiver) }
|
receiver?.let { generateReceiver(ktDefaultElement, receiver) }
|
||||||
|
|
||||||
fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): Value {
|
fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): IntermediateValue {
|
||||||
if (receiver is TransientReceiver) {
|
if (receiver is TransientReceiver) {
|
||||||
return TransientReceiverValue(ktDefaultElement.text, receiver.type)
|
return TransientReceiverValue(ktDefaultElement.text, receiver.type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
|||||||
import org.jetbrains.kotlin.psi2ir.intermediate.CallBuilder
|
import org.jetbrains.kotlin.psi2ir.intermediate.CallBuilder
|
||||||
import org.jetbrains.kotlin.psi2ir.intermediate.getValueArgumentsInParameterOrder
|
import org.jetbrains.kotlin.psi2ir.intermediate.getValueArgumentsInParameterOrder
|
||||||
import org.jetbrains.kotlin.psi2ir.intermediate.isValueArgumentReorderingRequired
|
import org.jetbrains.kotlin.psi2ir.intermediate.isValueArgumentReorderingRequired
|
||||||
import org.jetbrains.kotlin.psi2ir.intermediate.Value
|
import org.jetbrains.kotlin.psi2ir.intermediate.IntermediateValue
|
||||||
import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary
|
import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
@@ -126,7 +126,7 @@ class CallGenerator(
|
|||||||
valueArgumentsToValueParameters[valueArgument] = valueParameter
|
valueArgumentsToValueParameters[valueArgument] = valueParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
val irArgumentValues = HashMap<ValueParameterDescriptor, Value>()
|
val irArgumentValues = HashMap<ValueParameterDescriptor, IntermediateValue>()
|
||||||
|
|
||||||
for (valueArgument in valueArgumentsInEvaluationOrder) {
|
for (valueArgument in valueArgumentsInEvaluationOrder) {
|
||||||
val valueParameter = valueArgumentsToValueParameters[valueArgument]!!
|
val valueParameter = valueArgumentsToValueParameters[valueArgument]!!
|
||||||
|
|||||||
+3
-3
@@ -121,7 +121,7 @@ class OperatorExpressionGenerator(
|
|||||||
val irArgument0Value = createRematerializableOrTemporary(scope, irArgument0, irBlock, "elvis_lhs")
|
val irArgument0Value = createRematerializableOrTemporary(scope, irArgument0, irBlock, "elvis_lhs")
|
||||||
irBlock.addStatement(IrIfThenElseImpl(
|
irBlock.addStatement(IrIfThenElseImpl(
|
||||||
expression.startOffset, expression.endOffset, returnType,
|
expression.startOffset, expression.endOffset, returnType,
|
||||||
equalsNull(expression.startOffset, expression.endOffset, irArgument0Value.load()),
|
context.equalsNull(expression.startOffset, expression.endOffset, irArgument0Value.load()),
|
||||||
irArgument1,
|
irArgument1,
|
||||||
irArgument0Value.load()
|
irArgument0Value.load()
|
||||||
))
|
))
|
||||||
@@ -133,9 +133,9 @@ class OperatorExpressionGenerator(
|
|||||||
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
|
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
|
||||||
return when (irOperator) {
|
return when (irOperator) {
|
||||||
IrOperator.OROR ->
|
IrOperator.OROR ->
|
||||||
oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
|
context.oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
|
||||||
IrOperator.ANDAND ->
|
IrOperator.ANDAND ->
|
||||||
andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
|
context.andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
|
||||||
else ->
|
else ->
|
||||||
throw AssertionError("Unexpected binary boolean operator $irOperator")
|
throw AssertionError("Unexpected binary boolean operator $irOperator")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.psi2ir.generators
|
package org.jetbrains.kotlin.psi2ir.generators
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
|
||||||
fun primitiveOp1(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, irOperator: IrOperator,
|
fun primitiveOp1(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, irOperator: IrOperator,
|
||||||
@@ -27,30 +28,30 @@ fun primitiveOp2(startOffset: Int, endOffset: Int, primitiveOpDescriptor: Callab
|
|||||||
argument1: IrExpression, argument2: IrExpression): IrExpression =
|
argument1: IrExpression, argument2: IrExpression): IrExpression =
|
||||||
IrBinaryPrimitiveImpl(startOffset, endOffset, irOperator, primitiveOpDescriptor, argument1, argument2)
|
IrBinaryPrimitiveImpl(startOffset, endOffset, irOperator, primitiveOpDescriptor, argument1, argument2)
|
||||||
|
|
||||||
fun Generator.constNull(startOffset: Int, endOffset: Int): IrExpression =
|
fun GeneratorContext.constNull(startOffset: Int, endOffset: Int): IrExpression =
|
||||||
IrConstImpl.constNull(startOffset, endOffset, context.builtIns.nullableNothingType)
|
IrConstImpl.constNull(startOffset, endOffset, builtIns.nullableNothingType)
|
||||||
|
|
||||||
fun Generator.equalsNull(startOffset: Int, endOffset: Int, argument: IrExpression): IrExpression =
|
fun GeneratorContext.equalsNull(startOffset: Int, endOffset: Int, argument: IrExpression): IrExpression =
|
||||||
primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrOperator.EQEQ,
|
primitiveOp2(startOffset, endOffset, irBuiltIns.eqeq, IrOperator.EQEQ,
|
||||||
argument, constNull(startOffset, endOffset))
|
argument, constNull(startOffset, endOffset))
|
||||||
|
|
||||||
// a || b == if (a) true else b
|
// a || b == if (a) true else b
|
||||||
fun Generator.oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
|
fun GeneratorContext.oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
|
||||||
IrIfThenElseImpl(startOffset, endOffset, context.builtIns.booleanType,
|
IrIfThenElseImpl(startOffset, endOffset, builtIns.booleanType,
|
||||||
a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type!!), b,
|
a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type!!), b,
|
||||||
operator)
|
operator)
|
||||||
|
|
||||||
fun Generator.oror(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
|
fun GeneratorContext.oror(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
|
||||||
oror(b.startOffset, b.endOffset, a, b, operator)
|
oror(b.startOffset, b.endOffset, a, b, operator)
|
||||||
|
|
||||||
fun Generator.whenComma(a: IrExpression, b: IrExpression): IrWhen =
|
fun GeneratorContext.whenComma(a: IrExpression, b: IrExpression): IrWhen =
|
||||||
oror(a, b, IrOperator.WHEN_COMMA)
|
oror(a, b, IrOperator.WHEN_COMMA)
|
||||||
|
|
||||||
// a && b == if (a) b else false
|
// a && b == if (a) b else false
|
||||||
fun Generator.andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
|
fun GeneratorContext.andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
|
||||||
IrIfThenElseImpl(startOffset, endOffset, context.builtIns.booleanType,
|
IrIfThenElseImpl(startOffset, endOffset, builtIns.booleanType,
|
||||||
a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type!!),
|
a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type!!),
|
||||||
operator)
|
operator)
|
||||||
|
|
||||||
fun Generator.andand(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
|
fun GeneratorContext.andand(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
|
||||||
andand(b.startOffset, b.endOffset, a, b, operator)
|
andand(b.startOffset, b.endOffset, a, b, operator)
|
||||||
+1
-1
@@ -82,7 +82,7 @@ class StatementGenerator(
|
|||||||
return irBlock
|
return irBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
fun declareComponentVariablesInBlock(multiDeclaration: KtDestructuringDeclaration, irBlock: IrBlockImpl, containerValue: Value) {
|
fun declareComponentVariablesInBlock(multiDeclaration: KtDestructuringDeclaration, irBlock: IrBlockImpl, containerValue: IntermediateValue) {
|
||||||
val callGenerator = CallGenerator(this)
|
val callGenerator = CallGenerator(this)
|
||||||
for ((index, ktEntry) in multiDeclaration.entries.withIndex()) {
|
for ((index, ktEntry) in multiDeclaration.entries.withIndex()) {
|
||||||
val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry)
|
val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry)
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ class ArrayAccessAssignmentReceiver(
|
|||||||
return callGenerator.generateCall(startOffset, endOffset, indexedSetCall, IrOperator.EQ)
|
return callGenerator.generateCall(startOffset, endOffset, indexedSetCall, IrOperator.EQ)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallBuilder.fillArrayAndIndexArguments(arrayValue: Value, indexValues: List<Value>) {
|
private fun CallBuilder.fillArrayAndIndexArguments(arrayValue: IntermediateValue, indexValues: List<IntermediateValue>) {
|
||||||
setExplicitReceiverValue(arrayValue)
|
setExplicitReceiverValue(arrayValue)
|
||||||
indexValues.forEachIndexed { i, irIndexValue ->
|
indexValues.forEachIndexed { i, irIndexValue ->
|
||||||
irValueArgumentsByIndex[i] = irIndexValue.load()
|
irValueArgumentsByIndex[i] = irIndexValue.load()
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ val CallBuilder.explicitReceiverParameter: ReceiverParameterDescriptor? get() =
|
|||||||
val CallBuilder.explicitReceiverType: KotlinType? get() =
|
val CallBuilder.explicitReceiverType: KotlinType? get() =
|
||||||
explicitReceiverParameter?.type
|
explicitReceiverParameter?.type
|
||||||
|
|
||||||
fun CallBuilder.setExplicitReceiverValue(explicitReceiverValue: Value) {
|
fun CallBuilder.setExplicitReceiverValue(explicitReceiverValue: IntermediateValue) {
|
||||||
val previousCallReceiver = callReceiver
|
val previousCallReceiver = callReceiver
|
||||||
callReceiver = object : CallReceiver {
|
callReceiver = object : CallReceiver {
|
||||||
override fun call(withDispatchAndExtensionReceivers: (Value?, Value?) -> IrExpression): IrExpression {
|
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
|
||||||
return previousCallReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
|
return previousCallReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
|
||||||
val newDispatchReceiverValue = if (hasExtensionReceiver) dispatchReceiverValue else explicitReceiverValue
|
val newDispatchReceiverValue = if (hasExtensionReceiver) dispatchReceiverValue else explicitReceiverValue
|
||||||
val newExtensionReceiverValue = if (hasExtensionReceiver) explicitReceiverValue else null
|
val newExtensionReceiverValue = if (hasExtensionReceiver) explicitReceiverValue else null
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class OnceCallValue(
|
|||||||
val statementGenerator: StatementGenerator,
|
val statementGenerator: StatementGenerator,
|
||||||
val call: CallBuilder,
|
val call: CallBuilder,
|
||||||
val operator: IrOperator? = null
|
val operator: IrOperator? = null
|
||||||
): Value {
|
): IntermediateValue {
|
||||||
private var instantiated = false
|
private var instantiated = false
|
||||||
|
|
||||||
override fun load(): IrExpression {
|
override fun load(): IrExpression {
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.ir.util.render
|
|||||||
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
class OnceExpressionValue(val irExpression: IrExpression) : Value {
|
class OnceExpressionValue(val irExpression: IrExpression) : IntermediateValue {
|
||||||
init {
|
init {
|
||||||
irExpression.assertDetached()
|
irExpression.assertDetached()
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -21,14 +21,14 @@ import org.jetbrains.kotlin.ir.expressions.*
|
|||||||
import org.jetbrains.kotlin.psi2ir.generators.Scope
|
import org.jetbrains.kotlin.psi2ir.generators.Scope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
class RematerializableValue(val irExpression: IrExpressionWithCopy) : Value {
|
class RematerializableValue(val irExpression: IrExpressionWithCopy) : IntermediateValue {
|
||||||
override val type: KotlinType?
|
override val type: KotlinType?
|
||||||
get() = irExpression.type
|
get() = irExpression.type
|
||||||
|
|
||||||
override fun load(): IrExpression = irExpression.copy()
|
override fun load(): IrExpression = irExpression.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRematerializableValue(irExpression: IrExpression): Value? =
|
fun createRematerializableValue(irExpression: IrExpression): IntermediateValue? =
|
||||||
(irExpression as? IrExpressionWithCopy)?.let { RematerializableValue(it) }
|
(irExpression as? IrExpressionWithCopy)?.let { RematerializableValue(it) }
|
||||||
|
|
||||||
inline fun createRematerializableOrTemporary(
|
inline fun createRematerializableOrTemporary(
|
||||||
@@ -36,7 +36,7 @@ inline fun createRematerializableOrTemporary(
|
|||||||
irExpression: IrExpression,
|
irExpression: IrExpression,
|
||||||
nameHint: String? = null,
|
nameHint: String? = null,
|
||||||
addVariable: (IrVariable) -> Unit
|
addVariable: (IrVariable) -> Unit
|
||||||
): Value {
|
): IntermediateValue {
|
||||||
val rematerializable = createRematerializableValue(irExpression)
|
val rematerializable = createRematerializableValue(irExpression)
|
||||||
if (rematerializable != null) {
|
if (rematerializable != null) {
|
||||||
return rematerializable
|
return rematerializable
|
||||||
@@ -47,7 +47,7 @@ inline fun createRematerializableOrTemporary(
|
|||||||
return VariableLValue(temporaryVariable)
|
return VariableLValue(temporaryVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRematerializableOrTemporary(scope: Scope, irExpression: IrExpression, block: IrBlockImpl, nameHint: String? = null): Value =
|
fun createRematerializableOrTemporary(scope: Scope, irExpression: IrExpression, block: IrBlockImpl, nameHint: String? = null): IntermediateValue =
|
||||||
createRematerializableOrTemporary(scope, irExpression, nameHint) {
|
createRematerializableOrTemporary(scope, irExpression, nameHint) {
|
||||||
block.addStatement(it)
|
block.addStatement(it)
|
||||||
}
|
}
|
||||||
+6
-6
@@ -31,14 +31,14 @@ class SafeCallReceiver(
|
|||||||
val startOffset: Int,
|
val startOffset: Int,
|
||||||
val endOffset: Int,
|
val endOffset: Int,
|
||||||
val explicitReceiver: IrExpression,
|
val explicitReceiver: IrExpression,
|
||||||
val implicitDispatchReceiverValue: Value?
|
val implicitDispatchReceiverValue: IntermediateValue?
|
||||||
) : CallReceiver {
|
) : CallReceiver {
|
||||||
override fun call(withDispatchAndExtensionReceivers: (Value?, Value?) -> IrExpression): IrExpression {
|
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
|
||||||
val irTmp = generator.scope.createTemporaryVariable(explicitReceiver, "safe_receiver")
|
val irTmp = generator.scope.createTemporaryVariable(explicitReceiver, "safe_receiver")
|
||||||
val safeReceiverValue = VariableLValue(irTmp)
|
val safeReceiverValue = VariableLValue(irTmp)
|
||||||
|
|
||||||
val dispatchReceiverValue: Value
|
val dispatchReceiverValue: IntermediateValue
|
||||||
val extensionReceiverValue: Value?
|
val extensionReceiverValue: IntermediateValue?
|
||||||
if (implicitDispatchReceiverValue != null) {
|
if (implicitDispatchReceiverValue != null) {
|
||||||
dispatchReceiverValue = implicitDispatchReceiverValue
|
dispatchReceiverValue = implicitDispatchReceiverValue
|
||||||
extensionReceiverValue = safeReceiverValue
|
extensionReceiverValue = safeReceiverValue
|
||||||
@@ -56,8 +56,8 @@ class SafeCallReceiver(
|
|||||||
irBlock.addStatement(irTmp)
|
irBlock.addStatement(irTmp)
|
||||||
|
|
||||||
val irIfThenElse = IrIfThenElseImpl(startOffset, endOffset, resultType,
|
val irIfThenElse = IrIfThenElseImpl(startOffset, endOffset, resultType,
|
||||||
generator.equalsNull(startOffset, endOffset, safeReceiverValue.load()),
|
generator.context.equalsNull(startOffset, endOffset, safeReceiverValue.load()),
|
||||||
generator.constNull(startOffset, endOffset),
|
generator.context.constNull(startOffset, endOffset),
|
||||||
irResult,
|
irResult,
|
||||||
IrOperator.SAFE_CALL)
|
IrOperator.SAFE_CALL)
|
||||||
irBlock.addStatement(irIfThenElse)
|
irBlock.addStatement(irIfThenElse)
|
||||||
|
|||||||
+3
-3
@@ -19,10 +19,10 @@ package org.jetbrains.kotlin.psi2ir.intermediate
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
|
||||||
class SimpleCallReceiver(
|
class SimpleCallReceiver(
|
||||||
val dispatchReceiverValue: Value?,
|
val dispatchReceiverValue: IntermediateValue?,
|
||||||
val extensionReceiverValue: Value?
|
val extensionReceiverValue: IntermediateValue?
|
||||||
) : CallReceiver {
|
) : CallReceiver {
|
||||||
override fun call(withDispatchAndExtensionReceivers: (Value?, Value?) -> IrExpression): IrExpression {
|
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
|
||||||
return withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
|
return withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.psi2ir.intermediate
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
class TransientReceiverValue(val description: String, override val type: KotlinType?): Value {
|
class TransientReceiverValue(val description: String, override val type: KotlinType?): IntermediateValue {
|
||||||
override fun load(): IrExpression {
|
override fun load(): IrExpression {
|
||||||
throw AssertionError("Transient receiver should not be instantiated: $description")
|
throw AssertionError("Transient receiver should not be instantiated: $description")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ package org.jetbrains.kotlin.psi2ir.intermediate
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
interface Value {
|
interface IntermediateValue {
|
||||||
fun load(): IrExpression
|
fun load(): IrExpression
|
||||||
val type: KotlinType?
|
val type: KotlinType?
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LValue : Value {
|
interface LValue : IntermediateValue {
|
||||||
fun store(irExpression: IrExpression): IrExpression
|
fun store(irExpression: IrExpression): IrExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,5 +34,5 @@ interface AssignmentReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CallReceiver {
|
interface CallReceiver {
|
||||||
fun call(withDispatchAndExtensionReceivers: (Value?, Value?) -> IrExpression): IrExpression
|
fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression
|
||||||
}
|
}
|
||||||
|
|||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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.transformations
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.detach
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.replaceWith
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.psi2ir.containsNull
|
||||||
|
import org.jetbrains.kotlin.psi2ir.defaultLoad
|
||||||
|
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
||||||
|
import org.jetbrains.kotlin.psi2ir.generators.constNull
|
||||||
|
import org.jetbrains.kotlin.psi2ir.generators.equalsNull
|
||||||
|
import org.jetbrains.kotlin.psi2ir.intermediate.OnceExpressionValue
|
||||||
|
|
||||||
|
fun inlineSafeCallChains(context: GeneratorContext, element: IrElement) {
|
||||||
|
element.accept(InlineSafeCallChains(context), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
class InlineSafeCallChains(val context: GeneratorContext) : IrElementVisitor<Unit, Nothing?> {
|
||||||
|
override fun visitElement(element: IrElement, data: Nothing?) {
|
||||||
|
element.acceptChildren(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBlock(expression: IrBlock, data: Nothing?) {
|
||||||
|
expression.acceptChildren(this, data)
|
||||||
|
|
||||||
|
if (expression.operator == IrOperator.SAFE_CALL) {
|
||||||
|
val safeCall = getSafeCallInfo(expression) ?: return
|
||||||
|
val innerSafeCall = (safeCall.receiverValue as? IrBlock)?.let { getSafeCallInfo(it) } ?: return
|
||||||
|
rewriteSafeCallChain(safeCall, innerSafeCall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rewriteSafeCallChain(outer: SafeCallInfo, inner: SafeCallInfo) {
|
||||||
|
val innerNestedCallReturnType = inner.nestedCall.type ?: return
|
||||||
|
if (innerNestedCallReturnType.containsNull()) return
|
||||||
|
|
||||||
|
outer.root.replaceWith {
|
||||||
|
val newBlock = IrBlockImpl(it.startOffset, it.endOffset, it.type, it.hasResult, IrOperator.SAFE_CALL)
|
||||||
|
newBlock.addStatement(inner.receiverVariable.detach())
|
||||||
|
outer.nestedCall.acceptChildren(
|
||||||
|
ReplaceTemporaryVariable(outer.receiverVariable, OnceExpressionValue(inner.nestedCall.detach())),
|
||||||
|
null)
|
||||||
|
newBlock.addStatement(IrIfThenElseImpl(
|
||||||
|
it.startOffset, it.endOffset, it.type,
|
||||||
|
context.equalsNull(it.startOffset, it.endOffset, inner.receiverVariable.defaultLoad()),
|
||||||
|
context.constNull(it.startOffset, it.endOffset),
|
||||||
|
outer.nestedCall.detach(),
|
||||||
|
IrOperator.SAFE_CALL))
|
||||||
|
newBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SafeCallInfo(val root: IrBlock, val receiverVariable: IrVariable, val nestedCall: IrExpression) {
|
||||||
|
val receiverValue = receiverVariable.initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSafeCallInfo(block: IrBlock): SafeCallInfo? {
|
||||||
|
if (block.operator != IrOperator.SAFE_CALL) return null
|
||||||
|
val receiverVariable = block.statements[0] as? IrVariable ?: return null
|
||||||
|
if (receiverVariable.initializer == null) return null
|
||||||
|
val nestedCall = (block.statements[1] as? IrWhen)?.elseBranch ?: return null
|
||||||
|
return SafeCallInfo(block, receiverVariable, nestedCall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.transformations
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrGetVariable
|
||||||
|
import org.jetbrains.kotlin.ir.replaceWith
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.psi2ir.intermediate.IntermediateValue
|
||||||
|
|
||||||
|
class ReplaceTemporaryVariable(val from: IrVariable, val to: IntermediateValue) : IrElementVisitor<Unit, Nothing?> {
|
||||||
|
override fun visitElement(element: IrElement, data: Nothing?) {
|
||||||
|
element.acceptChildren(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitGetVariable(expression: IrGetVariable, data: Nothing?) {
|
||||||
|
if (expression.descriptor == from.descriptor) {
|
||||||
|
expression.replaceWith { to.load() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class C {
|
||||||
|
fun foo(): C = this
|
||||||
|
fun bar(): C? = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(nc: C?) =
|
||||||
|
nc?.foo()?.bar()?.foo()?.foo()
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
IrFile /chainOfSafeCalls.kt
|
||||||
|
DUMMY C
|
||||||
|
IrFunction public fun test(/*0*/ nc: C?): C?
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
RETURN type=<no-type>
|
||||||
|
BLOCK type=C? hasResult=true operator=SAFE_CALL
|
||||||
|
VAR val tmp2_safe_receiver: C?
|
||||||
|
BLOCK type=C? hasResult=true operator=SAFE_CALL
|
||||||
|
VAR val tmp0_safe_receiver: C?
|
||||||
|
GET_VAR nc type=C? operator=null
|
||||||
|
WHEN type=C? operator=SAFE_CALL
|
||||||
|
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
|
||||||
|
arg0: GET_VAR tmp0_safe_receiver type=C? operator=null
|
||||||
|
arg1: CONST Null type=kotlin.Nothing? value='null'
|
||||||
|
then: CONST Null type=kotlin.Nothing? value='null'
|
||||||
|
else: CALL .bar type=C? operator=null
|
||||||
|
$this: CALL .foo type=C operator=null
|
||||||
|
$this: GET_VAR tmp0_safe_receiver type=C? operator=null
|
||||||
|
WHEN type=C? operator=SAFE_CALL
|
||||||
|
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
|
||||||
|
arg0: GET_VAR tmp2_safe_receiver type=C? operator=null
|
||||||
|
arg1: CONST Null type=kotlin.Nothing? value='null'
|
||||||
|
then: CONST Null type=kotlin.Nothing? value='null'
|
||||||
|
else: CALL .foo type=C operator=null
|
||||||
|
$this: CALL .foo type=C operator=null
|
||||||
|
$this: GET_VAR tmp2_safe_receiver type=C? operator=null
|
||||||
+5
-13
@@ -29,21 +29,13 @@ IrFile /variableAsFunctionCall.kt
|
|||||||
BLOCK type=<no-type> hasResult=false operator=null
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
RETURN type=<no-type>
|
RETURN type=<no-type>
|
||||||
BLOCK type=kotlin.String? hasResult=true operator=SAFE_CALL
|
BLOCK type=kotlin.String? hasResult=true operator=SAFE_CALL
|
||||||
VAR val tmp1_safe_receiver: (() -> kotlin.String)?
|
VAR val tmp0_safe_receiver: kotlin.String?
|
||||||
BLOCK type=(() -> kotlin.String)? hasResult=true operator=SAFE_CALL
|
GET_VAR ns type=kotlin.String? operator=null
|
||||||
VAR val tmp0_safe_receiver: kotlin.String?
|
|
||||||
GET_VAR ns type=kotlin.String? operator=null
|
|
||||||
WHEN type=(() -> kotlin.String)? operator=SAFE_CALL
|
|
||||||
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
|
|
||||||
arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
|
|
||||||
arg1: CONST Null type=kotlin.Nothing? value='null'
|
|
||||||
then: CONST Null type=kotlin.Nothing? value='null'
|
|
||||||
else: CALL .k type=() -> kotlin.String operator=null
|
|
||||||
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
|
|
||||||
WHEN type=kotlin.String? operator=SAFE_CALL
|
WHEN type=kotlin.String? operator=SAFE_CALL
|
||||||
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
|
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
|
||||||
arg0: GET_VAR tmp1_safe_receiver type=(() -> kotlin.String)? operator=null
|
arg0: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
|
||||||
arg1: CONST Null type=kotlin.Nothing? value='null'
|
arg1: CONST Null type=kotlin.Nothing? value='null'
|
||||||
then: CONST Null type=kotlin.Nothing? value='null'
|
then: CONST Null type=kotlin.Nothing? value='null'
|
||||||
else: CALL .invoke type=kotlin.String operator=null
|
else: CALL .invoke type=kotlin.String operator=null
|
||||||
$this: GET_VAR tmp1_safe_receiver type=(() -> kotlin.String)? operator=null
|
$this: CALL .k type=() -> kotlin.String operator=null
|
||||||
|
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
|
||||||
|
|||||||
@@ -107,6 +107,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("chainOfSafeCalls.kt")
|
||||||
|
public void testChainOfSafeCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/chainOfSafeCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("conventionComparisons.kt")
|
@TestMetadata("conventionComparisons.kt")
|
||||||
public void testConventionComparisons() throws Exception {
|
public void testConventionComparisons() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/conventionComparisons.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/conventionComparisons.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user