Safe calls.

This commit is contained in:
Dmitry Petrov
2016-08-22 18:13:21 +03:00
committed by Dmitry Petrov
parent 985f3b20c7
commit 594a553a04
69 changed files with 1567 additions and 1093 deletions
@@ -19,9 +19,13 @@ package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.containsNull
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isNullabilityFlexible
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.upperIfFlexible
fun IrVariable.defaultLoad(): IrExpression =
IrGetVariableImpl(startOffset, endOffset, descriptor)
IrGetVariableImpl(startOffset, endOffset, descriptor)
@@ -0,0 +1,58 @@
/*
* 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
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.upperIfFlexible
fun KotlinType.containsNull() =
KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.nullableNothingType, this.upperIfFlexible())
fun KtElement.deparenthesize(): KtElement =
if (this is KtExpression) KtPsiUtil.safeDeparenthesize(this) else this
val CallableDescriptor.explicitReceiverType: KotlinType?
get() {
extensionReceiverParameter?.let { return it.type }
dispatchReceiverParameter?.let { return it.type }
return null
}
fun ResolvedCall<*>.isValueArgumentReorderingRequired(): Boolean {
var lastValueParameterIndex = -1
for (valueArgument in call.valueArguments) {
val argumentMapping = getArgumentMapping(valueArgument)
if (argumentMapping !is ArgumentMatch || argumentMapping.isError()) {
throw AssertionError("Value argument in function call is mapped with error")
}
val argumentIndex = argumentMapping.valueParameter.index
if (argumentIndex < lastValueParameterIndex) {
return true
}
lastValueParameterIndex = argumentIndex
}
return false
}
@@ -17,13 +17,13 @@
package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrModule
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator
import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation
import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks
import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts
import org.jetbrains.kotlin.resolve.BindingContext
class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
@@ -39,8 +39,9 @@ class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
return irModule
}
private fun postprocess(irElement: IrElement) {
if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irElement)
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irElement)
private fun postprocess(irModule: IrModule) {
insertImplicitCasts(irModule.irBuiltins.builtIns, irModule)
if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irModule)
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irModule)
}
}
@@ -0,0 +1,112 @@
/*
* 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.expressions.*
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.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.scopes.receivers.*
fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? =
receiver?.let { generateReceiver(ktDefaultElement, receiver) }
fun StatementGenerator.generateReceiver(ktDefaultElement: KtElement, receiver: ReceiverValue): IntermediateValue {
val receiverExpression = when (receiver) {
is ImplicitClassReceiver ->
IrThisReferenceImpl(ktDefaultElement.startOffset, ktDefaultElement.startOffset, receiver.type, receiver.classDescriptor)
is ThisClassReceiver ->
(receiver as? ExpressionReceiver)?.expression?.let { receiverExpression ->
IrThisReferenceImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type,
receiver.classDescriptor)
} ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver")
is ExpressionReceiver ->
generateExpression(receiver.expression)
is ClassValueReceiver ->
IrGetObjectValueImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type,
receiver.classQualifier.descriptor)
is ExtensionReceiver ->
IrGetExtensionReceiverImpl(ktDefaultElement.startOffset, ktDefaultElement.startOffset, receiver.type,
receiver.declarationDescriptor.extensionReceiverParameter!!)
else ->
TODO("Receiver: ${receiver.javaClass.simpleName}")
}
return if (receiverExpression is IrExpressionWithCopy)
RematerializableValue(receiverExpression)
else
OnceExpressionValue(receiverExpression)
}
fun StatementGenerator.generateCallReceiver(
ktDefaultElement: KtElement,
dispatchReceiver: ReceiverValue?,
extensionReceiver: ReceiverValue?,
isSafe: Boolean
) : CallReceiver {
val dispatchReceiverValue = generateReceiverOrNull(ktDefaultElement, dispatchReceiver)
val extensionReceiverValue = generateReceiverOrNull(ktDefaultElement, extensionReceiver)
if (!isSafe) {
return SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue)
}
else if (extensionReceiverValue != null) {
return SafeCallReceiver(this, ktDefaultElement.startOffset, ktDefaultElement.endOffset,
extensionReceiverValue.load(), dispatchReceiverValue)
}
else if (dispatchReceiverValue != null) {
return SafeCallReceiver(this, ktDefaultElement.startOffset, ktDefaultElement.endOffset,
dispatchReceiverValue.load(), null)
}
else {
return throw AssertionError("Safe call should have an explicit receiver: ${ktDefaultElement.text}")
}
}
fun StatementGenerator.generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
when (valueArgument) {
is DefaultValueArgument ->
null
is ExpressionValueArgument ->
generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!)
is VarargValueArgument ->
createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg")
else ->
TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}")
}
fun StatementGenerator.pregenerateCall(resolvedCall: ResolvedCall<*>): PregeneratedCall {
val call = pregenerateCallReceivers(resolvedCall)
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument)
}
return call
}
fun StatementGenerator.pregenerateCallReceivers(resolvedCall: ResolvedCall<*>): PregeneratedCall {
val call = PregeneratedCall(resolvedCall)
val ktDefaultCallElement = resolvedCall.call.callElement
call.callReceiver = generateCallReceiver(ktDefaultCallElement, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall())
return call
}
@@ -16,109 +16,104 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
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.psi2ir.intermediate.PregeneratedCall
import org.jetbrains.kotlin.psi2ir.intermediate.getValueArgumentsInParameterOrder
import org.jetbrains.kotlin.psi2ir.intermediate.isValueArgumentReorderingRequired
import org.jetbrains.kotlin.psi2ir.intermediate.IntermediateValue
import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.types.KotlinType
import java.util.*
class CallGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase<StatementGenerator>(parentGenerator) {
class CallGenerator(
override val context: GeneratorContext,
override val scope: Scope
) : BodyGenerator {
constructor(parent: BodyGenerator) : this(parent.context, parent.scope)
fun generateCall(
ktElement: KtElement,
resolvedCall: ResolvedCall<out CallableDescriptor>,
operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null
startOffset: Int,
endOffset: Int,
call: PregeneratedCall,
operator: IrOperator? = null
): IrExpression {
val descriptor = resolvedCall.resultingDescriptor
val descriptor = call.descriptor
return when (descriptor) {
is PropertyDescriptor ->
generatePropertyGetterCall(descriptor, ktElement, resolvedCall)
generatePropertyGetterCall(descriptor, startOffset, endOffset, call)
is FunctionDescriptor ->
generateFunctionCall(descriptor, ktElement, operator, resolvedCall, superQualifier)
generateFunctionCall(descriptor, startOffset, endOffset, operator, call)
else ->
TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}")
}
}
private fun CallGenerator.generatePropertyGetterCall(
private fun generatePropertyGetterCall(
descriptor: PropertyDescriptor,
ktElement: KtElement,
resolvedCall: ResolvedCall<*>
): IrGetterCallImpl {
val returnType = getReturnType(resolvedCall)
val dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
val extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
return IrGetterCallImpl(ktElement.startOffset, ktElement.endOffset,
returnType, descriptor.getter!!, resolvedCall.call.isSafeCall(),
dispatchReceiver, extensionReceiver, IrOperator.GET_PROPERTY)
}
private fun ResolvedCall<*>.requiresArgumentReordering(): Boolean {
var lastValueParameterIndex = -1
for (valueArgument in call.valueArguments) {
val argumentMapping = getArgumentMapping(valueArgument)
if (argumentMapping !is ArgumentMatch || argumentMapping.isError()) {
error("Value argument in function call is mapped with error")
}
val argumentIndex = argumentMapping.valueParameter.index
if (argumentIndex < lastValueParameterIndex) return true
lastValueParameterIndex = argumentIndex
startOffset: Int,
endOffset: Int,
call: PregeneratedCall
): IrExpression {
return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
IrGetterCallImpl(startOffset, endOffset, descriptor.getter!!,
dispatchReceiverValue?.load(),
extensionReceiverValue?.load(),
IrOperator.GET_PROPERTY)
}
return false
}
private fun generateFunctionCall(
descriptor: FunctionDescriptor,
ktElement: KtElement,
startOffset: Int,
endOffset: Int,
operator: IrOperator?,
resolvedCall: ResolvedCall<out CallableDescriptor>,
superQualifier: ClassDescriptor?
call: PregeneratedCall
): IrExpression {
val returnType = descriptor.returnType
val irCall = IrCallImpl(
ktElement.startOffset, ktElement.endOffset, returnType,
descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier
)
irCall.dispatchReceiver = generateReceiver(ktElement, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter)
irCall.extensionReceiver = generateReceiver(ktElement, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter)
return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
val irCall = IrCallImpl(startOffset, endOffset, returnType, descriptor, operator, call.superQualifier)
irCall.dispatchReceiver = dispatchReceiverValue?.load()
irCall.extensionReceiver = extensionReceiverValue?.load()
return if (resolvedCall.requiresArgumentReordering()) {
generateCallWithArgumentReordering(irCall, ktElement, resolvedCall, returnType)
}
else {
val valueArguments = resolvedCall.valueArgumentsByIndex
for (index in valueArguments!!.indices) {
val valueArgument = valueArguments[index]
val valueParameter = descriptor.valueParameters[index]
val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue
irCall.putArgument(index, irArgument)
}
irCall
val irCallWithReordering =
if (call.isValueArgumentReorderingRequired()) {
generateCallWithArgumentReordering(irCall, startOffset, endOffset, call, returnType)
}
else {
val valueArguments = call.getValueArgumentsInParameterOrder()
for ((index, valueArgument) in valueArguments.withIndex()) {
irCall.putArgument(index, valueArgument)
}
irCall
}
irCallWithReordering
}
}
private fun generateCallWithArgumentReordering(
irCall: IrCall,
ktElement: KtElement,
resolvedCall: ResolvedCall<out CallableDescriptor>,
startOffset: Int,
endOffset: Int,
call: PregeneratedCall,
resultType: KotlinType?
): IrExpression {
// TODO use IrLetExpression?
val resolvedCall = call.original
val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
val hasResult = isUsedAsExpression(ktElement)
val irBlock = IrBlockImpl(ktElement.startOffset, ktElement.endOffset, resultType, hasResult,
IrOperator.SYNTHETIC_BLOCK)
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, true, IrOperator.SYNTHETIC_BLOCK)
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
@@ -126,86 +121,28 @@ class CallGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorB
valueArgumentsToValueParameters[valueArgument] = valueParameter
}
val reorderingScope = Scope(this.scope)
val irArgumentValues = HashMap<ValueParameterDescriptor, IntermediateValue>()
for (valueArgument in valueArgumentsInEvaluationOrder) {
val valueParameter = valueArgumentsToValueParameters[valueArgument]!!
val irArgument = generateValueArgument(valueArgument, valueParameter) ?: continue
val irTmpArg = reorderingScope.introduceTemporary(valueParameter, irArgument)
irBlock.addIfNotNull(irTmpArg)
val irArgument = call.getValueArgument(valueParameter) ?: continue
val irArgumentValue = createRematerializableOrTemporary(scope, irArgument, irBlock, valueParameter.name.asString())
irArgumentValues[valueParameter] = irArgumentValue
}
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
val valueParameter = valueParameters[index]
val irGetTemporary = reorderingScope.valueOf(valueParameter)!!
irCall.putArgument(index, toExpectedType(irGetTemporary, valueParameter.type))
irCall.putArgument(index, irArgumentValues[valueParameter]?.load())
}
irBlock.addStatement(irCall)
return irBlock
}
fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?, receiverParameterDescriptor: ReceiverParameterDescriptor?) =
generateReceiver(ktElement, receiver, receiverParameterDescriptor?.type)
fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?, expectedType: KotlinType?) =
toExpectedTypeOrNull(generateReceiver(ktElement, receiver), expectedType)
fun generateReceiver(ktElement: KtElement, receiver: ReceiverValue?): IrExpression? =
if (receiver == null)
null
else
scope.valueOf(receiver) ?: doGenerateReceiver(ktElement, receiver)
fun doGenerateReceiver(ktElement: KtElement, receiver: ReceiverValue?): IrExpression? =
when (receiver) {
is ImplicitClassReceiver ->
IrThisReferenceImpl(ktElement.startOffset, ktElement.startOffset, receiver.type, receiver.classDescriptor)
is ThisClassReceiver ->
(receiver as? ExpressionReceiver)?.expression?.let { receiverExpression ->
IrThisReferenceImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor)
} ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver")
is ExpressionReceiver ->
generateExpression(receiver.expression)
is ClassValueReceiver ->
IrGetObjectValueImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type,
receiver.classQualifier.descriptor)
is ExtensionReceiver ->
IrGetExtensionReceiverImpl(ktElement.startOffset, ktElement.startOffset, receiver.type,
receiver.declarationDescriptor.extensionReceiverParameter!!)
null ->
null
else ->
TODO("Receiver: ${receiver.javaClass.simpleName}")
}
fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
generateValueArgument(valueArgument, valueParameterDescriptor, valueParameterDescriptor.type)
fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor, expectedType: KotlinType): IrExpression? =
if (valueParameterDescriptor.varargElementType != null)
doGenerateValueArgument(valueArgument, valueParameterDescriptor)
else
toExpectedTypeOrNull(doGenerateValueArgument(valueArgument, valueParameterDescriptor), expectedType)
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
if (valueArgument is DefaultValueArgument)
null
else
scope.valueOf(valueParameterDescriptor) ?: doGenerateValueArgument(valueArgument)
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
when (valueArgument) {
is ExpressionValueArgument ->
generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!)
is VarargValueArgument ->
createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg")
else ->
TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}")
}
private fun generateExpression(ktExpression: KtExpression): IrExpression =
scope.valueOf(ktExpression) ?: parentGenerator.generateExpression(ktExpression)
}
fun CallGenerator.generateCall(ktElement: KtElement, call: PregeneratedCall, operator: IrOperator? = null) =
generateCall(ktElement.startOffset, ktElement.endOffset, call, operator)
fun CallGenerator.generateCall(irExpression: IrExpression, call: PregeneratedCall, operator: IrOperator? = null) =
generateCall(irExpression.startOffset, irExpression.endOffset, call, operator)
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
class DeclarationGenerator(override val context: GeneratorContext) : IrGenerator {
class DeclarationGenerator(override val context: GeneratorContext) : Generator {
fun generateAnnotationEntries(annotationEntries: List<KtAnnotationEntry>) {
// TODO create IrAnnotation's for each KtAnnotationEntry
}
@@ -25,13 +25,13 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val context: GeneratorContext): IrBodyGenerator {
override val scope = Scope.rootScope(scopeOwner, this)
class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val context: GeneratorContext): BodyGenerator {
override val scope = Scope(scopeOwner)
fun generateFunctionBody(ktBody: KtExpression): IrExpression {
resetInternalContext()
val irBodyExpression = createStatementGenerator().generateExpressionWithExpectedType(ktBody, scopeOwner.returnType)
val irBodyExpression = createStatementGenerator().generateExpression(ktBody)
val irBodyExpressionAsBlock =
if (ktBody is KtBlockExpression)
@@ -52,7 +52,7 @@ class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val c
}
fun generatePropertyInitializerBody(ktInitializer: KtExpression): IrExpression =
createStatementGenerator().generateExpressionWithExpectedType(ktInitializer, scopeOwner.returnType!!)
createStatementGenerator().generateExpression(ktInitializer)
private fun createStatementGenerator() =
StatementGenerator(context, scopeOwner, this, scope)
@@ -35,35 +35,38 @@ import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import java.lang.AssertionError
import java.lang.RuntimeException
interface IrGenerator {
interface Generator {
val context: GeneratorContext
}
fun <K, V : Any> IrGenerator.get(slice: ReadOnlySlice<K, V>, key: K): V? =
interface BodyGenerator : Generator {
val scope: Scope
}
fun <K, V : Any> Generator.get(slice: ReadOnlySlice<K, V>, key: K): V? =
context.bindingContext[slice, key]
fun <K, V : Any> IrGenerator.getOrFail(slice: ReadOnlySlice<K, V>, key: K): V =
fun <K, V : Any> Generator.getOrFail(slice: ReadOnlySlice<K, V>, key: K): V =
context.bindingContext[slice, key] ?: throw RuntimeException("No $slice for $key")
inline fun <K, V : Any> IrGenerator.getOrFail(slice: ReadOnlySlice<K, V>, key: K, message: (K) -> String): V =
inline fun <K, V : Any> Generator.getOrFail(slice: ReadOnlySlice<K, V>, key: K, message: (K) -> String): V =
context.bindingContext[slice, key] ?: throw RuntimeException(message(key))
fun IrGenerator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? =
fun Generator.getInferredTypeWithSmartcasts(key: KtExpression): KotlinType? =
context.bindingContext.getType(key)
fun IrGenerator.getExpectedTypeForLastInferredCall(key: KtExpression): KotlinType? =
get(BindingContext.EXPECTED_EXPRESSION_TYPE, key)
fun Generator.getInferredTypeWithSmartcastsOrFail(key: KtExpression): KotlinType =
getInferredTypeWithSmartcasts(key) ?: throw AssertionError("No type for expression: ${key.text}")
fun IrGenerator.getInferredTypeWithSmarcastsOrFail(key: KtExpression): KotlinType =
getInferredTypeWithSmartcasts(key) ?: TODO("No type for expression: ${key.text}")
fun IrGenerator.isUsedAsExpression(ktElement: KtElement) =
fun Generator.isUsedAsExpression(ktElement: KtElement) =
get(BindingContext.USED_AS_EXPRESSION, ktElement) ?: false
fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? =
fun Generator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? =
key.getResolvedCall(context.bindingContext)
fun IrGenerator.getReturnType(key: KtExpression): KotlinType? {
fun Generator.getReturnType(key: KtExpression): KotlinType? {
val resolvedCall = getResolvedCall(key)
if (resolvedCall != null) {
return getReturnType(resolvedCall)
@@ -78,21 +81,21 @@ fun IrGenerator.getReturnType(key: KtExpression): KotlinType? {
}
fun getReturnType(resolvedCall: ResolvedCall<*>): KotlinType {
val descriptor = resolvedCall.resultingDescriptor
val returnType = getReturnType(resolvedCall.resultingDescriptor)
return if (resolvedCall.call.isSafeCall()) returnType.makeNullable() else returnType
}
fun getReturnType(descriptor: CallableDescriptor): KotlinType {
return when (descriptor) {
is ClassDescriptor ->
descriptor.classValueType ?: throw AssertionError("Class descriptor without companion object: $descriptor")
is CallableDescriptor -> {
val returnType = descriptor.returnType ?: throw AssertionError("Callable descriptor without return type: $descriptor")
if (resolvedCall.call.isSafeCall())
returnType.makeNullable()
else
returnType
descriptor.returnType ?: throw AssertionError("Callable descriptor without return type: $descriptor")
}
else ->
throw AssertionError("Unexpected desciptor in resolved call: $descriptor")
throw AssertionError("Unexpected descriptor in resolved call: $descriptor")
}
}
fun IrGenerator.createDummyExpression(ktExpression: KtExpression, description: String): IrDummyExpression =
fun Generator.createDummyExpression(ktExpression: KtExpression, description: String): IrDummyExpression =
IrDummyExpression(ktExpression.startOffset, ktExpression.endOffset, getInferredTypeWithSmartcasts(ktExpression), description)
@@ -1,97 +0,0 @@
/*
* 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.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.assertCast
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.SmartList
class IrBlockBuilder(val startOffset: Int, val endOffset: Int, val irOperator: IrOperator, val generator: IrBodyGenerator) {
private val statements = SmartList<IrStatement>()
private var resultType: KotlinType? = null
private var hasResult = false
val scope: Scope get() = generator.scope
fun <T : IrStatement?> add(irStatement: T): T {
if (irStatement != null) {
statements.add(irStatement)
}
return irStatement
}
fun <T : IrExpression> result(irExpression: T): T {
resultType = irExpression.type
hasResult = true
statements.add(irExpression)
return irExpression
}
fun build() =
if (statements.size == 1)
statements[0]
else {
val block = IrBlockImpl(startOffset, endOffset, resultType, hasResult, irOperator)
statements.forEach { block.addStatement(it) }
block
}
}
fun IrBodyGenerator.block(ktElement: KtElement, irOperator: IrOperator, body: IrBlockBuilder.() -> Unit): IrExpression =
IrBlockBuilder(ktElement.startOffset, ktElement.endOffset, irOperator, this).apply(body).build().assertCast()
fun IrBodyGenerator.block(irExpression: IrExpression, irOperator: IrOperator, body: IrBlockBuilder.() -> Unit): IrExpression =
IrBlockBuilder(irExpression.startOffset, irExpression.endOffset, irOperator, this).apply(body).build().assertCast()
fun IrBlockBuilder.constType(constKind: IrConstKind<*>): KotlinType =
when (constKind) {
IrConstKind.Null -> generator.context.builtIns.nullableNothingType
IrConstKind.Boolean -> generator.context.builtIns.anyType
IrConstKind.Byte -> generator.context.builtIns.byteType
IrConstKind.Short -> generator.context.builtIns.shortType
IrConstKind.Int -> generator.context.builtIns.intType
IrConstKind.Long -> generator.context.builtIns.longType
IrConstKind.String -> generator.context.builtIns.stringType
IrConstKind.Float -> generator.context.builtIns.floatType
IrConstKind.Double -> generator.context.builtIns.doubleType
}
fun <T> IrBlockBuilder.const(constKind: IrConstKind<T>, value: T) =
IrConstImpl(startOffset, endOffset, constType(constKind), constKind, value)
fun IrBlockBuilder.constNull() =
const(IrConstKind.Null, null)
fun IrBlockBuilder.op0(operatorDescriptor: CallableDescriptor) =
IrNullaryOperatorImpl(startOffset, endOffset, irOperator, operatorDescriptor)
fun IrBlockBuilder.op1(operatorDescriptor: CallableDescriptor, argument: IrExpression) =
IrUnaryOperatorImpl(startOffset, endOffset, irOperator, operatorDescriptor, argument)
fun IrBlockBuilder.op2(operatorDescriptor: CallableDescriptor, argument1: IrExpression, argument2: IrExpression) =
IrBinaryOperatorImpl(startOffset, endOffset, irOperator, operatorDescriptor, argument1, argument2)
fun IrBlockBuilder.equalsNull(argument: IrExpression) =
op2(generator.context.irBuiltIns.eqeq, argument, constNull())
fun IrBlockBuilder.ifThenElse(type: KotlinType?, condition: IrExpression, thenBranch: IrExpression, elseBranch: IrExpression) =
IrIfThenElseImpl(startOffset, endOffset, type, condition, thenBranch, elseBranch, irOperator)
@@ -1,68 +0,0 @@
/*
* 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.KotlinBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isNullabilityFlexible
interface IrBodyGenerator : IrGenerator {
val scope: Scope
}
abstract class IrChildBodyGeneratorBase<out T : IrBodyGenerator>(
val parentGenerator: T
) : IrBodyGenerator {
override val context: GeneratorContext get() = parentGenerator.context
override val scope: Scope = Scope(parentGenerator.scope)
}
fun IrBodyGenerator.toExpectedType(irExpression: IrExpression, expectedType: KotlinType?): IrExpression {
if (irExpression is IrBlock && !irExpression.hasResult) return irExpression
if (expectedType == null) return irExpression
if (KotlinBuiltIns.isUnit(expectedType)) return irExpression // TODO expose coercion to Unit in IR?
val valueType = irExpression.type ?: throw AssertionError("expectedType != null, valueType == null: $this")
if (valueType.isNullabilityFlexible() && !expectedType.isMarkedNullable) {
return block(irExpression, IrOperator.IMPLICIT_NOTNULL) {
add(scope.introduceTemporary(irExpression))
result(ifThenElse(expectedType,
equalsNull(scope.valueOf(irExpression)!!),
op0(context.irBuiltIns.throwNpe),
scope.valueOf(irExpression)!!))
}
}
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) {
return IrTypeOperatorCallImpl(irExpression.startOffset, irExpression.endOffset, expectedType,
IrTypeOperator.IMPLICIT_CAST, expectedType, irExpression)
}
return irExpression
}
fun StatementGenerator.generateExpressionWithExpectedType(ktExpression: KtExpression, expectedType: KotlinType?) =
toExpectedType(generateExpression(ktExpression), expectedType)
fun IrBodyGenerator.toExpectedTypeOrNull(irExpression: IrExpression?, expectedType: KotlinType?): IrExpression? =
irExpression?.let { toExpectedType(it, expectedType) }
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
class ModuleGenerator(override val context: GeneratorContext) : IrGenerator {
class ModuleGenerator(override val context: GeneratorContext) : Generator {
fun generateModule(ktFiles: List<KtFile>): IrModule {
val irDeclarationGenerator = DeclarationGenerator(context)
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.psi2ir.generators.operators
package org.jetbrains.kotlin.psi2ir.generators
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.ir.expressions.IrOperator
@@ -25,15 +25,19 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.defaultLoad
import org.jetbrains.kotlin.psi2ir.generators.operators.*
import org.jetbrains.kotlin.psi2ir.generators.values.*
import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.lang.AssertionError
class OperatorExpressionGenerator(
val statementGenerator: StatementGenerator
) : BodyGenerator {
override val scope: Scope get() = statementGenerator.scope
override val context: GeneratorContext get() = statementGenerator.context
class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase<StatementGenerator>(parentGenerator) {
fun generatePrefixExpression(expression: KtPrefixExpression): IrExpression {
val ktOperator = expression.operationReference.getReferencedNameElementType()
val irOperator = getPrefixOperator(ktOperator)
@@ -72,7 +76,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
}
return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, resultType, irOperator, rhsType,
parentGenerator.generateExpression(expression.left))
statementGenerator.generateExpression(expression.left))
}
fun generateInstanceOfExpression(expression: KtIsExpression): IrStatement {
@@ -81,7 +85,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
val againstType = getOrFail(BindingContext.TYPE, expression.typeReference)
return IrTypeOperatorCallImpl(expression.startOffset, expression.endOffset, context.builtIns.booleanType, irOperator,
againstType, parentGenerator.generateExpression(expression.leftHandSide))
againstType, statementGenerator.generateExpression(expression.leftHandSide))
}
fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression {
@@ -110,25 +114,28 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateElvis(expression: KtBinaryExpression): IrExpression {
val specialCallForElvis = getResolvedCall(expression)!!
val returnType = specialCallForElvis.resultingDescriptor.returnType!!
val irArgument0 = parentGenerator.generateExpressionWithExpectedType(expression.left!!, returnType.makeNullable())
val irArgument1 = parentGenerator.generateExpressionWithExpectedType(expression.right!!, returnType)
return block(expression, IrOperator.ELVIS) {
add(scope.introduceTemporary(irArgument0))
result(ifThenElse(returnType,
equalsNull(scope.valueOf(irArgument0)!!),
irArgument1,
scope.valueOf(irArgument0)!!))
}
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, returnType, true, IrOperator.ELVIS)
val irArgument0Value = createRematerializableOrTemporary(scope, irArgument0, irBlock, "elvis_lhs")
irBlock.addStatement(IrIfThenElseImpl(
expression.startOffset, expression.endOffset, returnType,
equalsNull(expression.startOffset, expression.endOffset, irArgument0Value.load()),
irArgument1,
irArgument0Value.load()
))
return irBlock
}
private fun generateBinaryBooleanOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val irArgument0 = parentGenerator.generateExpressionWithExpectedType(expression.left!!, context.builtIns.booleanType)
val irArgument1 = parentGenerator.generateExpressionWithExpectedType(expression.right!!, context.builtIns.booleanType)
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
return when (irOperator) {
IrOperator.OROR ->
IrIfThenElseImpl.oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
IrOperator.ANDAND ->
IrIfThenElseImpl.andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
andand(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
else ->
throw AssertionError("Unexpected binary boolean operator $irOperator")
}
@@ -137,7 +144,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateInOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val containsCall = getResolvedCall(expression)!!
val irContainsCall = CallGenerator(parentGenerator).generateCall(expression, containsCall, irOperator)
val irContainsCall = CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(containsCall), irOperator)
return when (irOperator) {
IrOperator.IN ->
@@ -152,8 +159,8 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
}
private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val irArgument0 = parentGenerator.generateExpression(expression.left!!)
val irArgument1 = parentGenerator.generateExpression(expression.right!!)
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irIdentityEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset, irOperator, context.irBuiltIns.eqeqeq,
@@ -172,8 +179,8 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
}
private fun generateEqualityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val irArgument0 = parentGenerator.generateExpression(expression.left!!)
val irArgument1 = parentGenerator.generateExpression(expression.right!!)
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irEquals = IrBinaryOperatorImpl(expression.startOffset, expression.endOffset,
irOperator, context.irBuiltIns.eqeq, irArgument0, irArgument1)
@@ -193,8 +200,7 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateComparisonOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val compareToCall = getResolvedCall(expression)!!
val irCallGenerator = CallGenerator(parentGenerator)
val irCompareToCall = irCallGenerator.generateCall(expression, compareToCall, irOperator)
val irCompareToCall = CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(compareToCall), irOperator)
val compareToZeroDescriptor = when (irOperator) {
IrOperator.LT -> context.irBuiltIns.lt0
@@ -210,99 +216,102 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
private fun generateBinaryOperatorAsCall(expression: KtBinaryExpression, irOperator: IrOperator?): IrExpression {
val operatorCall = getResolvedCall(expression)!!
return CallGenerator(parentGenerator).generateCall(expression, operatorCall, irOperator)
return CallGenerator(this).generateCall(expression, statementGenerator.pregenerateCall(operatorCall), irOperator)
}
private fun generatePrefixIncrementDecrementOperator(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression {
val opResolvedCall = getResolvedCall(expression)!!
val ktBaseExpression = expression.baseExpression!!
val irLValue = generateLValue(ktBaseExpression, irOperator)
val operatorCall = getResolvedCall(expression)!!
val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, irOperator)
if (irLValue is IrLValueWithAugmentedStore) {
return irLValue.prefixAugmentedStore(operatorCall, irOperator)
return irAssignmentReceiver.assign { irLValue ->
val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator)
// VAR tmp = [lhs].inc()
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
val irOpCall = CallGenerator(this).generateCall(expression, opCall, irOperator)
val irTmp = statementGenerator.scope.createTemporaryVariable(irOpCall)
irBlock.addStatement(irTmp)
// [lhs] = tmp
irBlock.addStatement(irLValue.store(irTmp.defaultLoad()))
// ^ tmp
irBlock.addStatement(irTmp.defaultLoad())
irBlock
}
val opCallGenerator = CallGenerator(parentGenerator).apply { scope.putValue(ktBaseExpression, irLValue) }
val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator)
val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator)
val irTmp = parentGenerator.scope.createTemporaryVariable(irOpCall)
irBlock.addStatement(irTmp)
irBlock.addStatement(irLValue.store(irTmp.defaultLoad()))
irBlock.addStatement(irTmp.defaultLoad())
return irBlock
}
private fun generatePostfixIncrementDecrementOperator(expression: KtPostfixExpression, irOperator: IrOperator): IrExpression {
val opResolvedCall = getResolvedCall(expression)!!
val ktBaseExpression = expression.baseExpression!!
val irLValue = generateLValue(ktBaseExpression, irOperator)
val operatorCall = getResolvedCall(expression)!!
val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, irOperator)
if (irLValue is IrLValueWithAugmentedStore) {
return irLValue.postfixAugmentedStore(operatorCall, irOperator)
return irAssignmentReceiver.assign { irLValue ->
val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator)
// VAR tmp = [lhs]
val irTmp = scope.createTemporaryVariable(irLValue.load())
irBlock.addStatement(irTmp)
// [lhs] = tmp.inc()
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(VariableLValue(irTmp))
val irOpCall = CallGenerator(this).generateCall(expression, opCall, irOperator)
irBlock.addStatement(irLValue.store(irOpCall))
// ^ tmp
irBlock.addStatement(irTmp.defaultLoad())
irBlock
}
val irBlock = IrBlockImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator)
val opCallGenerator = CallGenerator(parentGenerator)
val irTmp = parentGenerator.scope.createTemporaryVariable(irLValue.load())
irBlock.addStatement(irTmp)
opCallGenerator.scope.putValue(ktBaseExpression, VariableLValue(this, irTmp, irOperator))
val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator)
irBlock.addStatement(irLValue.store(irOpCall))
irBlock.addStatement(irTmp.defaultLoad())
return irBlock
}
private fun generatePrefixOperatorAsCall(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression {
val resolvedCall = getResolvedCall(expression)!!
return CallGenerator(parentGenerator).generateCall(expression, resolvedCall, irOperator)
return CallGenerator(statementGenerator).generateCall(expression, statementGenerator.pregenerateCall(resolvedCall), irOperator)
}
private fun generateAugmentedAssignment(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
val ktLeft = expression.left!!
val irLValue = generateLValue(ktLeft, irOperator)
val operatorCall = getResolvedCall(expression)!!
val opResolvedCall = getResolvedCall(expression)!!
val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false
val ktLeft = expression.left!!
val ktRight = expression.right!!
val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, irOperator)
if (isSimpleAssignment && irLValue is IrLValueWithAugmentedStore) {
return irLValue.augmentedStore(operatorCall, irOperator, parentGenerator.generateExpression(expression.right!!))
}
return irAssignmentReceiver.assign { irLValue ->
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
opCall.irValueArgumentsByIndex[0] = statementGenerator.generateExpression(ktRight)
val irOpCall = CallGenerator(this).generateCall(expression, opCall, irOperator)
val opCallGenerator = CallGenerator(parentGenerator).apply { scope.putValue(ktLeft, irLValue) }
val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator)
return if (isSimpleAssignment) {
// Set( Op( Get(), RHS ) )
irLValue.store(irOpCall)
}
else {
// Op( Get(), RHS )
irOpCall
if (isSimpleAssignment) {
// Set( Op( Get(), RHS ) )
irLValue.store(irOpCall)
}
else {
// Op( Get(), RHS )
irOpCall
}
}
}
private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
val ktLeft = expression.left!!
val ktRight = expression.right!!
val irLValue = generateLValue(ktLeft, IrOperator.EQ)
return irLValue.store(parentGenerator.generateExpression(ktRight))
val irRhs = statementGenerator.generateExpression(expression.right!!)
val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, IrOperator.EQ)
return if (irAssignmentReceiver is IntermediateReference)
irAssignmentReceiver.store(irRhs)
else
irAssignmentReceiver.assign { irLValue -> irLValue.store(irRhs) }
}
private fun generateLValue(ktLeft: KtExpression, irOperator: IrOperator?): IrLValue {
private fun generateAssignmentReceiver(ktLeft: KtExpression, irOperator: IrOperator): AssignmentReceiver {
if (ktLeft is KtArrayAccessExpression) {
val irArrayValue = parentGenerator.generateExpression(ktLeft.arrayExpression!!)
val indexExpressions = ktLeft.indexExpressions.map { it to parentGenerator.generateExpression(it) }
val indexedGetCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
val indexedSetCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft)
val type = indexedGetCall?.run { resultingDescriptor.returnType }
?: indexedSetCall?.run { resultingDescriptor.valueParameters.last().type }
?: throw AssertionError("Either 'get' or 'set' call should be present for an indexed LValue: ${ktLeft.text}")
return IndexedLValue(parentGenerator, ktLeft, irOperator,
irArrayValue, type, indexExpressions, indexedGetCall, indexedSetCall)
return generateArrayAccessAssignmentReceiver(ktLeft, irOperator)
}
val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS")
@@ -313,20 +322,31 @@ class OperatorExpressionGenerator(parentGenerator: StatementGenerator) : IrChild
if (descriptor.isDelegated)
TODO("Delegated local variable")
else
VariableLValue(this, ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator)
is PropertyDescriptor ->
CallGenerator(parentGenerator).run {
PropertyLValue(
this,
ktLeft, irOperator, descriptor,
generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter),
generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter),
resolvedCall.call.isSafeCall()
)
}
VariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator)
is PropertyDescriptor -> {
val propertyReceiver = statementGenerator.generateCallReceiver(
ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall()
)
SimplePropertyLValue(scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, propertyReceiver)
}
else ->
TODO("Other cases of LHS")
}
}
private fun generateArrayAccessAssignmentReceiver(ktLeft: KtArrayAccessExpression, irOperator: IrOperator): ArrayAccessAssignmentReceiver {
val irArray = statementGenerator.generateExpression(ktLeft.arrayExpression!!)
val irIndexExpressions = ktLeft.indexExpressions.map { statementGenerator.generateExpression(it) }
val indexedGetResolvedCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
val indexedGetCall = indexedGetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
val indexedSetResolvedCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft)
val indexedSetCall = indexedSetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
return ArrayAccessAssignmentReceiver(irArray, irIndexExpressions, indexedGetCall, indexedSetCall,
CallGenerator(statementGenerator),
ktLeft.startOffset, ktLeft.endOffset, irOperator)
}
}
@@ -0,0 +1,56 @@
/*
* 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.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.expressions.*
fun primitiveOp1(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, irOperator: IrOperator,
argument: IrExpression): IrExpression =
IrUnaryOperatorImpl(startOffset, endOffset, irOperator, primitiveOpDescriptor, argument)
fun primitiveOp2(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, irOperator: IrOperator,
argument1: IrExpression, argument2: IrExpression): IrExpression =
IrBinaryOperatorImpl(startOffset, endOffset, irOperator, primitiveOpDescriptor, argument1, argument2)
fun Generator.constNull(startOffset: Int, endOffset: Int): IrExpression =
IrConstImpl.constNull(startOffset, endOffset, context.builtIns.nullableNothingType)
fun Generator.equalsNull(startOffset: Int, endOffset: Int, argument: IrExpression): IrExpression =
primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrOperator.EQEQ,
argument, constNull(startOffset, endOffset))
// a || b == if (a) true else b
fun Generator.oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
IrIfThenElseImpl(startOffset, endOffset, context.builtIns.booleanType,
a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type!!), b,
operator)
fun Generator.oror(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
oror(b.startOffset, b.endOffset, a, b, operator)
fun Generator.whenComma(a: IrExpression, b: IrExpression): IrWhen =
oror(a, b, IrOperator.WHEN_COMMA)
// a && b == if (a) b else false
fun Generator.andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
IrIfThenElseImpl(startOffset, endOffset, context.builtIns.booleanType,
a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type!!),
operator)
fun Generator.andand(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
andand(b.startOffset, b.endOffset, a, b, operator)
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginKind
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.IrVariableImpl
@@ -25,42 +24,12 @@ import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi2ir.generators.values.IrValue
import org.jetbrains.kotlin.psi2ir.generators.values.VariableLValue
import org.jetbrains.kotlin.psi2ir.generators.values.createRematerializableValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import java.util.*
import java.lang.AssertionError
class Scope private constructor(val scopeOwner: DeclarationDescriptor, val parent: Scope? = null) {
internal lateinit var generator: IrBodyGenerator
constructor(parent: Scope) : this(parent.scopeOwner, parent) {
this.generator = parent.generator
}
private var lastTemporaryIndex: Int = parent?.lastTemporaryIndex ?: 0
private fun nextTemporaryIndex(): Int = parent?.nextTemporaryIndex() ?: lastTemporaryIndex++
private val values = HashMap<Any, IrValue>()
private inline fun introduceTemporary(irExpression: IrExpression, nameHint: String?, register: (IrValue) -> Unit): IrVariable? {
val rematerializable = createRematerializableValue(irExpression)
return if (rematerializable != null) {
register(rematerializable)
null
}
else {
createTemporary(irExpression, nameHint, register)
}
}
private inline fun createTemporary(irExpression: IrExpression, nameHint: String?, register: (IrValue) -> Unit): IrVariable {
val irTemporary = createTemporaryVariable(irExpression, nameHint)
register(VariableLValue(generator, irTemporary))
return irTemporary
}
class Scope(val scopeOwner: DeclarationDescriptor) {
private var lastTemporaryIndex: Int = 0
private fun nextTemporaryIndex(): Int = lastTemporaryIndex++
private fun createDescriptorForTemporaryVariable(type: KotlinType, nameHint: String? = null): IrTemporaryVariableDescriptor =
IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier(getNameForTemporary(nameHint)), type)
@@ -79,52 +48,4 @@ class Scope private constructor(val scopeOwner: DeclarationDescriptor, val paren
),
irExpression
)
fun introduceTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String? = null): IrVariable? =
introduceTemporary(irExpression, nameHint) { putValue(ktExpression, it) }
fun introduceTemporary(valueParameterDescriptor: ValueParameterDescriptor, irExpression: IrExpression): IrVariable? =
introduceTemporary(irExpression, valueParameterDescriptor.name.asString()) { putValue(valueParameterDescriptor, it) }
fun introduceTemporary(irExpression: IrExpression): IrVariable? =
introduceTemporary(irExpression, null) { putValue(irExpression, it) }
fun createTemporary(ktExpression: KtExpression, irExpression: IrExpression, nameHint: String?): IrVariable =
createTemporary(irExpression, nameHint) { putValue(ktExpression, it) }
fun putValue(ktExpression: KtExpression, irValue: IrValue) {
values[ktExpression] = irValue
}
fun putValue(receiver: ReceiverValue, irValue: IrValue) {
values[receiver] = irValue
}
fun putValue(parameter: ValueParameterDescriptor, irValue: IrValue) {
values[parameter] = irValue
}
fun putValue(irExpression: IrExpression, irValue: IrValue) {
values[irExpression] = irValue
}
fun valueOf(ktExpression: KtExpression): IrExpression? =
values[ktExpression]?.load() ?: parent?.valueOf(ktExpression)
fun valueOf(receiver: ReceiverValue): IrExpression? =
values[receiver]?.load() ?: parent?.valueOf(receiver)
fun valueOf(parameter: ValueParameterDescriptor): IrExpression? =
values[parameter]?.load() ?: parent?.valueOf(parameter)
fun valueOf(irExpression: IrExpression): IrExpression? =
values[irExpression]?.load() ?: parent?.valueOf(irExpression)
companion object {
fun rootScope(scopeOwner: DeclarationDescriptor, generator: IrBodyGenerator): Scope {
val scope = Scope(scopeOwner)
scope.generator = generator
return scope
}
}
}
@@ -26,7 +26,8 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.deparenthesize
import org.jetbrains.kotlin.psi2ir.generators.values.VariableLValue
import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary
import org.jetbrains.kotlin.psi2ir.intermediate.setExplicitReceiverValue
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -41,14 +42,14 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.constant
import java.lang.AssertionError
class StatementGenerator(
override val context: GeneratorContext,
val scopeOwner: DeclarationDescriptor,
val expressionBodyGenerator: ExpressionBodyGenerator,
@Suppress("unused") val expressionBodyGenerator: ExpressionBodyGenerator,
override val scope: Scope
) : KtVisitor<IrStatement, Nothing?>(), IrBodyGenerator {
) : KtVisitor<IrStatement, Nothing?>(), BodyGenerator {
fun generateExpression(ktExpression: KtExpression): IrExpression =
ktExpression.genExpr()
@@ -67,10 +68,7 @@ class StatementGenerator(
val variableDescriptor = getOrFail(BindingContext.VARIABLE, property)
val irLocalVariable = IrVariableImpl(property.startOffset, property.endOffset, IrDeclarationOriginKind.DEFINED, variableDescriptor)
irLocalVariable.initializer = property.initializer?.let {
toExpectedType(it.genExpr(), variableDescriptor.type)
}
irLocalVariable.initializer = property.initializer?.genExpr()
return irLocalVariable
}
@@ -79,16 +77,19 @@ class StatementGenerator(
val irBlock = IrBlockImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, false, IrOperator.SYNTHETIC_BLOCK)
val ktInitializer = multiDeclaration.initializer!!
val irTmpInitializer = scope.createTemporaryVariable(ktInitializer.genExpr())
irBlock.addStatement(irTmpInitializer)
val irTmpInitializerValue = createRematerializableOrTemporary(scope, ktInitializer.genExpr(), irBlock, "container")
val irCallGenerator = CallGenerator(this)
irCallGenerator.scope.putValue(ktInitializer, VariableLValue(this, irTmpInitializer))
for ((index, ktEntry) in multiDeclaration.entries.withIndex()) {
val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry)
val componentSubstitutedCall = pregenerateCall(componentResolvedCall)
componentSubstitutedCall.setExplicitReceiverValue(irTmpInitializerValue)
val componentVariable = getOrFail(BindingContext.VARIABLE, ktEntry)
val irComponentCall = irCallGenerator.generateCall(ktEntry, componentResolvedCall, IrOperator.COMPONENT_N.withIndex(index + 1))
val irComponentCall = irCallGenerator.generateCall(ktEntry.startOffset, ktEntry.endOffset, componentSubstitutedCall,
IrOperator.COMPONENT_N.withIndex(index + 1))
val irComponentVar = IrVariableImpl(ktEntry.startOffset, ktEntry.endOffset, IrDeclarationOriginKind.DEFINED,
componentVariable, irComponentCall)
irBlock.addStatement(irComponentVar)
@@ -106,13 +107,8 @@ class StatementGenerator(
override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrStatement {
val returnTarget = getReturnExpressionTarget(expression)
val irReturnedExpression = expression.returnedExpression?.let {
toExpectedType(it.genExpr(), returnTarget.returnType)
}
return IrReturnImpl(
expression.startOffset, expression.endOffset,
returnTarget, irReturnedExpression
)
val irReturnedExpression = expression.returnedExpression?.genExpr()
return IrReturnImpl(expression.startOffset, expression.endOffset, returnTarget, irReturnedExpression)
}
private fun getReturnExpressionTarget(expression: KtReturnExpression): CallableDescriptor =
@@ -137,7 +133,7 @@ class StatementGenerator(
override fun visitConstantExpression(expression: KtConstantExpression, data: Nothing?): IrExpression {
val compileTimeConstant = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext)
?: error("KtConstantExpression was not evaluated: ${expression.text}")
val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmarcastsOrFail(expression))
val constantValue = compileTimeConstant.toConstantValue(getInferredTypeWithSmartcastsOrFail(expression))
val constantType = constantValue.type
return when (constantValue) {
@@ -163,7 +159,7 @@ class StatementGenerator(
return entry0.genExpr()
}
}
entries.size == 0 -> return IrConstImpl.string(expression.startOffset, expression.endOffset, getInferredTypeWithSmarcastsOrFail(expression), "")
entries.size == 0 -> return IrConstImpl.string(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcastsOrFail(expression), "")
}
val irStringTemplate = IrStringConcatenationImpl(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression))
@@ -191,19 +187,19 @@ class StatementGenerator(
is FakeCallableDescriptorForObject ->
generateExpressionForReferencedDescriptor(descriptor.getReferencedDescriptor(), expression, resolvedCall)
is ClassDescriptor ->
if (DescriptorUtils.isObject(descriptor))
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
else if (DescriptorUtils.isEnumEntry(descriptor))
IrGetEnumValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
else {
val companionObjectDescriptor = descriptor.companionObjectDescriptor
?: error("Class value without companion object: $descriptor")
IrGetObjectValueImpl(expression.startOffset, expression.endOffset,
descriptor.classValueType,
companionObjectDescriptor)
when {
DescriptorUtils.isObject(descriptor) ->
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
DescriptorUtils.isEnumEntry(descriptor) ->
IrGetEnumValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType, descriptor)
else -> {
IrGetObjectValueImpl(expression.startOffset, expression.endOffset, descriptor.classValueType,
descriptor.companionObjectDescriptor ?: throw AssertionError("Class value without companion object: $descriptor"))
}
}
is PropertyDescriptor -> {
CallGenerator(this).generateCall(expression, resolvedCall)
// TODO safe calls
CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall))
}
is VariableDescriptor ->
IrGetVariableImpl(expression.startOffset, expression.endOffset, descriptor)
@@ -221,7 +217,9 @@ class StatementGenerator(
TODO("VariableAsFunctionResolvedCall = variable call + invoke call")
}
return CallGenerator(this).generateCall(expression, resolvedCall)
// TODO safe calls
return CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall))
}
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression, data: Nothing?): IrStatement =
@@ -275,8 +273,8 @@ class StatementGenerator(
var irElseBranch: IrExpression? = null
whenBranches@while (true) {
val irCondition = generateExpressionWithExpectedType(ktLastIf.condition!!, context.builtIns.booleanType)
val irThenBranch = generateExpressionWithExpectedType(ktLastIf.then!!, resultType)
val irCondition = ktLastIf.condition!!.genExpr()
val irThenBranch = ktLastIf.then!!.genExpr()
irBranches.add(Pair(irCondition, irThenBranch))
val ktElse = ktLastIf.`else`?.deparenthesize()
@@ -284,7 +282,7 @@ class StatementGenerator(
null -> break@whenBranches
is KtIfExpression -> ktLastIf = ktElse
is KtExpression -> {
irElseBranch = generateExpressionWithExpectedType(ktElse, resultType)
irElseBranch = ktElse.genExpr()
break@whenBranches
}
else -> throw AssertionError("Unexpected else expression: ${ktElse.text}")
@@ -312,3 +310,5 @@ class StatementGenerator(
}
@@ -22,16 +22,17 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.defaultLoad
import org.jetbrains.kotlin.psi2ir.generators.operators.getInfixOperator
import org.jetbrains.kotlin.psi2ir.generators.getInfixOperator
import org.jetbrains.kotlin.resolve.BindingContext
import java.lang.AssertionError
class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBodyGeneratorBase<StatementGenerator>(parentGenerator) {
fun generate(expression: KtWhenExpression): IrExpression {
val conditionsGenerator = CallGenerator(parentGenerator)
class WhenExpressionGenerator(val statementGenerator: StatementGenerator) : BodyGenerator {
override val scope: Scope get() = statementGenerator.scope
override val context: GeneratorContext get() = statementGenerator.context
fun generate(expression: KtWhenExpression): IrExpression {
val irSubject = expression.subjectExpression?.let {
conditionsGenerator.scope.createTemporary(it, parentGenerator.generateExpression(it), "subject")
scope.createTemporaryVariable(statementGenerator.generateExpression(it), "subject")
}
val resultType = getInferredTypeWithSmartcasts(expression)
@@ -40,7 +41,7 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
for (ktEntry in expression.entries) {
if (ktEntry.isElse) {
irWhen.elseBranch = parentGenerator.generateExpressionWithExpectedType(ktEntry.expression!!, resultType)
irWhen.elseBranch = statementGenerator.generateExpression(ktEntry.expression!!)
break
}
@@ -48,14 +49,14 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
for (ktCondition in ktEntry.conditions) {
val irCondition =
if (irSubject != null)
generateWhenConditionWithSubject(ktCondition, conditionsGenerator, irSubject)
generateWhenConditionWithSubject(ktCondition, irSubject)
else
generateWhenConditionNoSubject(ktCondition)
irBranchCondition = irBranchCondition?.let { IrIfThenElseImpl.whenComma(it, irCondition) } ?: irCondition
irBranchCondition = irBranchCondition?.let { whenComma(it, irCondition) } ?: irCondition
}
val irBranchResult = parentGenerator.generateExpressionWithExpectedType(ktEntry.expression!!, resultType)
val irBranchResult = statementGenerator.generateExpression(ktEntry.expression!!)
irWhen.addBranch(irBranchCondition!!, irBranchResult)
}
@@ -85,19 +86,14 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
}
private fun generateWhenConditionNoSubject(ktCondition: KtWhenCondition): IrExpression =
parentGenerator.generateExpressionWithExpectedType((ktCondition as KtWhenConditionWithExpression).expression!!,
context.builtIns.booleanType)
statementGenerator.generateExpression((ktCondition as KtWhenConditionWithExpression).expression!!)
private fun generateWhenConditionWithSubject(
ktCondition: KtWhenCondition,
conditionsGenerator: CallGenerator,
irSubject: IrVariable
): IrExpression {
private fun generateWhenConditionWithSubject(ktCondition: KtWhenCondition, irSubject: IrVariable): IrExpression {
return when (ktCondition) {
is KtWhenConditionWithExpression ->
generateEqualsCondition(irSubject, ktCondition)
is KtWhenConditionInRange ->
generateInRangeCondition(conditionsGenerator, ktCondition)
generateInRangeCondition(irSubject, ktCondition)
is KtWhenConditionIsPattern ->
generateIsPatternCondition(irSubject, ktCondition)
else ->
@@ -113,10 +109,11 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
)
}
private fun generateInRangeCondition(conditionsGenerator: CallGenerator, ktCondition: KtWhenConditionInRange): IrExpression {
val inResolvedCall = getResolvedCall(ktCondition.operationReference)!!
private fun generateInRangeCondition(irSubject: IrVariable, ktCondition: KtWhenConditionInRange): IrExpression {
val inCall = statementGenerator.pregenerateCall(getResolvedCall(ktCondition.operationReference)!!)
inCall.irValueArgumentsByIndex[0] = irSubject.defaultLoad()
val inOperator = getInfixOperator(ktCondition.operationReference.getReferencedNameElementType())
val irInCall = conditionsGenerator.generateCall(ktCondition, inResolvedCall, inOperator)
val irInCall = CallGenerator(statementGenerator).generateCall(ktCondition, inCall, inOperator)
return when (inOperator) {
IrOperator.IN -> irInCall
IrOperator.NOT_IN ->
@@ -129,6 +126,6 @@ class WhenExpressionGenerator(parentGenerator: StatementGenerator) : IrChildBody
IrBinaryOperatorImpl(
ktCondition.startOffset, ktCondition.endOffset,
IrOperator.EQEQ, context.irBuiltIns.eqeq,
irSubject.defaultLoad(), parentGenerator.generateExpression(ktCondition.expression!!)
irSubject.defaultLoad(), statementGenerator.generateExpression(ktCondition.expression!!)
)
}
@@ -1,168 +0,0 @@
/*
* 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.values
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.defaultLoad
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.psi2ir.generators.Scope
import org.jetbrains.kotlin.psi2ir.generators.StatementGenerator
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
class IndexedLValue(
val statementGenerator: StatementGenerator,
val ktArrayAccessExpression: KtArrayAccessExpression,
val irOperator: IrOperator?,
val irArray: IrExpression,
override val type: KotlinType?,
val indexValues: List<Pair<KtExpression, IrExpression>>,
val indexedGetCall: ResolvedCall<*>?,
val indexedSetCall: ResolvedCall<*>?
) : IrLValueWithAugmentedStore {
override fun load(): IrExpression {
if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}")
return generateGetOrSetCallAsDesugaredBlock(indexedGetCall)
}
override fun store(irExpression: IrExpression): IrExpression {
if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}")
return generateGetOrSetCallAsDesugaredBlock(indexedSetCall)
}
private fun generateGetOrSetCallAsDesugaredBlock(call: ResolvedCall<*>, irArgument: IrExpression? = null): IrExpression {
val callGenerator = CallGenerator(statementGenerator)
setupCallGeneratorContext(callGenerator.scope)
if (irArgument != null) {
callGenerator.scope.putValue(call.resultingDescriptor.valueParameters.last(), SingleExpressionValue(irArgument))
}
return callGenerator.generateCall(ktArrayAccessExpression, call, irOperator)
}
override fun prefixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression {
if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}")
if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}")
val callGenerator = CallGenerator(statementGenerator)
val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, true, irOperator)
val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver
val irGetCall = callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator)
callGenerator.scope.putValue(operatorCallReceiver!!, SingleExpressionValue(irGetCall))
val irOpCall = callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator)
val irTmp = callGenerator.scope.createTemporaryVariable(irOpCall)
irBlock.addStatement(irTmp)
callGenerator.scope.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), VariableLValue(callGenerator, irTmp))
irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator))
irBlock.addStatement(irTmp.defaultLoad())
return irBlock
}
override fun postfixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression {
if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}")
if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}")
val callGenerator = CallGenerator(statementGenerator)
val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, true, irOperator)
val irGetCall = callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator)
val irTmp = callGenerator.scope.createTemporaryVariable(irGetCall)
irBlock.addStatement(irTmp)
val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver
callGenerator.scope.putValue(operatorCallReceiver!!, VariableLValue(callGenerator, irTmp))
val irOpCall = callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator)
callGenerator.scope.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), SingleExpressionValue(irOpCall))
val irSetCall = callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator)
irBlock.addStatement(irSetCall)
irBlock.addStatement(irTmp.defaultLoad())
return irBlock
}
override fun augmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator, irOperatorArgument: IrExpression): IrExpression {
if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}")
if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}")
val callGenerator = CallGenerator(statementGenerator)
val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, false, irOperator)
callGenerator.scope.putValue(operatorCall.resultingDescriptor.valueParameters[0], SingleExpressionValue(irOperatorArgument))
val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver
callGenerator.scope.putValue(operatorCallReceiver!!,
SingleExpressionValue(callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator)))
callGenerator.scope.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(),
SingleExpressionValue(callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator)))
irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator))
return irBlock
}
private fun createDesugaredBlockWithTemporaries(
call: ResolvedCall<*>,
callGenerator: CallGenerator,
hasResult: Boolean,
operator: IrOperator?
): IrBlockImpl {
val irBlock = createDesugaredBlock(call, hasResult, operator)
defineTemporaryVariables(irBlock, callGenerator.scope)
return irBlock
}
private fun createDesugaredBlock(call: ResolvedCall<*>, hasResult: Boolean, operator: IrOperator?): IrBlockImpl {
return IrBlockImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset,
if (hasResult) type else call.resultingDescriptor.returnType,
hasResult, operator)
}
private fun defineTemporaryVariables(irBlock: IrBlockImpl, scope: Scope) {
irBlock.addIfNotNull(scope.introduceTemporary(ktArrayAccessExpression.arrayExpression!!, irArray, "array"))
var index = 0
for ((ktIndexExpression, irIndexValue) in indexValues) {
irBlock.addIfNotNull(scope.introduceTemporary(ktIndexExpression, irIndexValue, "index${index++}"))
}
}
private fun setupCallGeneratorContext(scope: Scope) {
scope.putValue(ktArrayAccessExpression.arrayExpression!!, SingleExpressionValue(irArray))
for ((ktIndexExpression, irIndexValue) in indexValues) {
scope.putValue(ktIndexExpression, SingleExpressionValue(irIndexValue))
}
}
}
@@ -1,57 +0,0 @@
/*
* 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.values
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.expressions.*
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.generators.IrBodyGenerator
import org.jetbrains.kotlin.psi2ir.generators.toExpectedType
import org.jetbrains.kotlin.types.KotlinType
class PropertyLValue(
val irBodyGenerator: IrBodyGenerator,
val ktElement: KtElement,
val irOperator: IrOperator?,
val descriptor: PropertyDescriptor,
val dispatchReceiver: IrExpression?,
val extensionReceiver: IrExpression?,
val isSafe: Boolean
) : IrLValue {
override val type: KotlinType?
get() = descriptor.type
override fun load(): IrExpression {
val getter = descriptor.getter!!
return IrGetterCallImpl(
ktElement.startOffset, ktElement.endOffset,
getter.returnType, getter, isSafe,
dispatchReceiver, extensionReceiver, IrOperator.GET_PROPERTY
)
}
override fun store(irExpression: IrExpression): IrExpression {
val setter = descriptor.setter!!
val irArgument = irBodyGenerator.toExpectedType(irExpression, descriptor.type)
val irCall = IrSetterCallImpl(ktElement.startOffset, ktElement.endOffset,
setter.returnType, setter, isSafe,
dispatchReceiver, extensionReceiver, irArgument, irOperator)
return irCall
}
}
@@ -1,57 +0,0 @@
/*
* 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.values
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.types.KotlinType
interface IrRematerializableValue : IrValue {
val irExpression: IrExpression
override val type: KotlinType?
get() = irExpression.type
}
fun createRematerializableValue(irExpression: IrExpression): IrRematerializableValue? =
when (irExpression) {
is IrConst<*> -> IrRematerializableLiteralValue(irExpression)
is IrGetVariable -> IrRematerializableVariableValue(irExpression)
is IrGetExtensionReceiver -> IrRematerializableExtensionReceiverValue(irExpression)
is IrThisReference -> IrRematerializableThisValue(irExpression)
else -> null
}
class IrRematerializableLiteralValue(override val irExpression: IrConst<*>): IrRematerializableValue {
override fun load(): IrExpression =
IrConstImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type,
irExpression.kind, irExpression.kind.valueOf(irExpression))
}
class IrRematerializableVariableValue(override val irExpression: IrGetVariable) : IrRematerializableValue {
override fun load(): IrExpression =
IrGetVariableImpl(irExpression.startOffset, irExpression.endOffset, irExpression.descriptor)
}
class IrRematerializableExtensionReceiverValue(override val irExpression: IrGetExtensionReceiver) : IrRematerializableValue {
override fun load(): IrExpression =
IrGetExtensionReceiverImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, irExpression.descriptor)
}
class IrRematerializableThisValue(override val irExpression: IrThisReference): IrRematerializableValue {
override fun load(): IrExpression =
IrThisReferenceImpl(irExpression.startOffset, irExpression.endOffset, irExpression.type, irExpression.classDescriptor)
}
@@ -1,57 +0,0 @@
/*
* 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.values
import org.jetbrains.kotlin.ir.assertDetached
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
interface IrValue {
fun load(): IrExpression
val type: KotlinType?
}
class SingleExpressionValue(val irExpression: IrExpression) : IrValue {
init {
irExpression.assertDetached()
}
private var instantiated = false
override fun load() =
if (!instantiated) {
instantiated = true
irExpression
}
else
throw AssertionError("Single exprssion value is already instantiated")
override val type: KotlinType? get() = irExpression.type
}
interface IrLValue : IrValue {
fun store(irExpression: IrExpression): IrExpression
}
interface IrLValueWithAugmentedStore : IrLValue {
fun postfixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression
fun prefixAugmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator): IrExpression
fun augmentedStore(operatorCall: ResolvedCall<*>, irOperator: IrOperator, irOperatorArgument: IrExpression): IrExpression
}
@@ -0,0 +1,63 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.types.KotlinType
import java.lang.AssertionError
class ArrayAccessAssignmentReceiver(
val irArray: IrExpression,
val irIndices: List<IrExpression>,
val indexedGetCall: PregeneratedCall?,
val indexedSetCall: PregeneratedCall?,
val callGenerator: CallGenerator,
val startOffset: Int,
val endOffset: Int,
val operator: IrOperator
) : AssignmentReceiver {
private val type: KotlinType = indexedGetCall?.let { it.descriptor.returnType!! } ?:
indexedSetCall?.let { it.descriptor.valueParameters.last().type } ?:
throw AssertionError("Array access should have either indexed-get call or indexed-set call")
override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression {
val hasResult = operator.isAssignmentOperatorWithResult()
val resultType = if (hasResult) type else callGenerator.context.builtIns.unitType
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, hasResult, operator)
val irArrayValue = createRematerializableOrTemporary(callGenerator.scope, irArray, irBlock, "array")
val irIndexValues = irIndices.mapIndexed { i, irIndex ->
createRematerializableOrTemporary(callGenerator.scope, irIndex, irBlock, "index$i")
}
indexedGetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues)
indexedSetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues)
val irLValue = LValueWithGetterAndSetterCalls(callGenerator, indexedGetCall, indexedSetCall, startOffset, endOffset, operator)
irBlock.inlineStatement(withLValue(irLValue))
return irBlock
}
private fun PregeneratedCall.fillArrayAndIndexArguments(arrayValue: IntermediateValue, indexValues: List<IntermediateValue>) {
setExplicitReceiverValue(arrayValue)
indexValues.forEachIndexed { i, irIndexValue ->
irValueArgumentsByIndex[i] = irIndexValue.load()
}
}
}
@@ -0,0 +1,59 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.psi2ir.intermediate.PregeneratedCall
import org.jetbrains.kotlin.psi2ir.intermediate.argumentsCount
import org.jetbrains.kotlin.types.KotlinType
class LValueWithGetterAndSetterCalls(
val callGenerator: CallGenerator,
val getterCall: PregeneratedCall?,
val setterCall: PregeneratedCall?,
val startOffset: Int,
val endOffset: Int,
val operator: IrOperator? = null
) : IntermediateReference {
private val descriptor: CallableDescriptor =
getterCall?.descriptor ?: setterCall?.descriptor ?:
throw AssertionError("Call-based LValue should have either a getter or a setter call")
private var getterInstantiated = false
private var setterInstantiated = false
override val type: KotlinType? get() = descriptor.returnType
override fun load(): IrExpression {
if (getterCall == null) throw AssertionError("No getter call for $descriptor")
if (getterInstantiated) throw AssertionError("Getter for $descriptor has already been instantiated")
getterInstantiated = true
return callGenerator.generateCall(startOffset, endOffset, getterCall, operator)
}
override fun store(irExpression: IrExpression): IrExpression {
if (setterCall == null) throw AssertionError("No setter call for $descriptor")
if (setterInstantiated) throw AssertionError("Setter for $descriptor has already been instantiated")
setterInstantiated = true
setterCall.irValueArgumentsByIndex[setterCall.argumentsCount - 1] = irExpression
return callGenerator.generateCall(startOffset, endOffset, setterCall, operator)
}
}
@@ -0,0 +1,42 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.psi2ir.generators.StatementGenerator
import org.jetbrains.kotlin.psi2ir.intermediate.PregeneratedCall
import org.jetbrains.kotlin.types.KotlinType
class OnceCallValue(
val statementGenerator: StatementGenerator,
val call: PregeneratedCall,
val startOffset: Int,
val endOffset: Int,
val operator: IrOperator? = null
): IntermediateValue {
private var instantiated = false
override fun load(): IrExpression {
if (instantiated) throw AssertionError("Value for call ${call.descriptor} has already been instantiated")
instantiated = true
return CallGenerator(statementGenerator).generateCall(startOffset, endOffset, call, operator)
}
override val type: KotlinType? get() = call.descriptor.returnType
}
@@ -0,0 +1,39 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.ir.assertDetached
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.types.KotlinType
class OnceExpressionValue(val irExpression: IrExpression) : IntermediateValue {
init {
irExpression.assertDetached()
}
private var instantiated = false
override fun load(): IrExpression {
if (instantiated) throw AssertionError("Single expression value for ${irExpression.render()} is already instantiated")
instantiated = true
return irExpression
}
override val type: KotlinType? get() = irExpression.type
}
@@ -0,0 +1,79 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.psi2ir.isValueArgumentReorderingRequired
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
class PregeneratedCall(val original: ResolvedCall<*>) {
val descriptor: CallableDescriptor = original.resultingDescriptor
var superQualifier: ClassDescriptor? = null
lateinit var callReceiver: CallReceiver
val irValueArgumentsByIndex = arrayOfNulls<IrExpression>(descriptor.valueParameters.size)
fun getValueArgument(valueParameterDescriptor: ValueParameterDescriptor) =
irValueArgumentsByIndex[valueParameterDescriptor.index]
}
val PregeneratedCall.argumentsCount: Int get() =
irValueArgumentsByIndex.size
fun PregeneratedCall.getValueArgumentsInParameterOrder(): List<IrExpression?> =
descriptor.valueParameters.map { irValueArgumentsByIndex[it.index] }
fun PregeneratedCall.isValueArgumentReorderingRequired() =
original.isValueArgumentReorderingRequired()
val PregeneratedCall.hasExtensionReceiver: Boolean get() =
descriptor.extensionReceiverParameter != null
val PregeneratedCall.hasDispatchReceiver: Boolean get() =
descriptor.dispatchReceiverParameter != null
val PregeneratedCall.extensionReceiverType: KotlinType? get() =
descriptor.extensionReceiverParameter?.type
val PregeneratedCall.dispatchReceiverType: KotlinType? get() =
descriptor.dispatchReceiverParameter?.type
val PregeneratedCall.explicitReceiverParameter: ReceiverParameterDescriptor? get() =
descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter
val PregeneratedCall.explicitReceiverType: KotlinType? get() =
explicitReceiverParameter?.type
fun PregeneratedCall.setExplicitReceiverValue(explicitReceiverValue: IntermediateValue) {
val previousCallReceiver = callReceiver
callReceiver = object : CallReceiver {
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
return previousCallReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
val newDispatchReceiverValue = if (hasExtensionReceiver) dispatchReceiverValue else explicitReceiverValue
val newExtensionReceiverValue = if (hasExtensionReceiver) explicitReceiverValue else null
withDispatchAndExtensionReceivers(newDispatchReceiverValue, newExtensionReceiverValue)
}
}
}
}
@@ -0,0 +1,53 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.generators.Scope
import org.jetbrains.kotlin.types.KotlinType
class RematerializableValue(val irExpression: IrExpressionWithCopy) : IntermediateValue {
override val type: KotlinType?
get() = irExpression.type
override fun load(): IrExpression = irExpression.copy()
}
fun createRematerializableValue(irExpression: IrExpression): IntermediateValue? =
(irExpression as? IrExpressionWithCopy)?.let { RematerializableValue(it) }
inline fun createRematerializableOrTemporary(
scope: Scope,
irExpression: IrExpression,
nameHint: String? = null,
addVariable: (IrVariable) -> Unit
): IntermediateValue {
val rematerializable = createRematerializableValue(irExpression)
if (rematerializable != null) {
return rematerializable
}
val temporaryVariable = scope.createTemporaryVariable(irExpression, nameHint)
addVariable(temporaryVariable)
return VariableLValue(temporaryVariable)
}
fun createRematerializableOrTemporary(scope: Scope, irExpression: IrExpression, block: IrBlockImpl, nameHint: String? = null): IntermediateValue =
createRematerializableOrTemporary(scope, irExpression, nameHint) {
block.addStatement(it)
}
@@ -0,0 +1,67 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.ir.expressions.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrIfThenElseImpl
import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.psi2ir.generators.BodyGenerator
import org.jetbrains.kotlin.psi2ir.generators.constNull
import org.jetbrains.kotlin.psi2ir.generators.equalsNull
import org.jetbrains.kotlin.types.typeUtil.makeNullable
class SafeCallReceiver(
val generator: BodyGenerator,
val startOffset: Int,
val endOffset: Int,
val explicitReceiver: IrExpression,
val implicitDispatchReceiverValue: IntermediateValue?
) : CallReceiver {
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
val irTmp = generator.scope.createTemporaryVariable(explicitReceiver, "safe_receiver")
val safeReceiverValue = VariableLValue(irTmp)
val dispatchReceiverValue: IntermediateValue
val extensionReceiverValue: IntermediateValue?
if (implicitDispatchReceiverValue != null) {
dispatchReceiverValue = implicitDispatchReceiverValue
extensionReceiverValue = safeReceiverValue
}
else {
dispatchReceiverValue = safeReceiverValue
extensionReceiverValue = null
}
val irResult = withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
val resultType = irResult.type?.makeNullable()
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, resultType != null, IrOperator.SAFE_CALL)
irBlock.addStatement(irTmp)
val irIfThenElse = IrIfThenElseImpl(startOffset, endOffset, resultType,
generator.equalsNull(startOffset, endOffset, safeReceiverValue.load()),
generator.constNull(startOffset, endOffset),
irResult,
IrOperator.SAFE_CALL)
irBlock.addStatement(irIfThenElse)
return irBlock
}
}
@@ -14,12 +14,15 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.psi2ir
package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiUtil
fun KtElement.deparenthesize(): KtElement =
if (this is KtExpression) KtPsiUtil.safeDeparenthesize(this) else this
import org.jetbrains.kotlin.ir.expressions.IrExpression
class SimpleCallReceiver(
val dispatchReceiverValue: IntermediateValue?,
val extensionReceiverValue: IntermediateValue?
) : CallReceiver {
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
return withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.generators.Scope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.SmartList
class SimplePropertyLValue(
val scope: Scope,
val startOffset: Int,
val endOffset: Int,
val irOperator: IrOperator?,
val descriptor: PropertyDescriptor,
val callReceiver: CallReceiver
) : IntermediateReference, AssignmentReceiver {
override val type: KotlinType?
get() = descriptor.type
override fun load(): IrExpression {
val getter = descriptor.getter!!
return callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
IrGetterCallImpl(startOffset, endOffset, getter,
dispatchReceiverValue?.load(),
extensionReceiverValue?.load(),
irOperator)
}
}
override fun store(irExpression: IrExpression): IrExpression {
val setter = descriptor.setter!!
return callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
IrSetterCallImpl(startOffset, endOffset, setter,
dispatchReceiverValue?.load(),
extensionReceiverValue?.load(),
irExpression, irOperator)
}
}
override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression {
return callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
val variablesForReceivers = SmartList<IrVariable>()
val tmpDispatchReceiverValue = dispatchReceiverValue?.load()?.let {
createRematerializableOrTemporary(scope, it, "this") {
irVar -> variablesForReceivers.add(irVar)
}
}
val tmpExtensionReceiverValue = extensionReceiverValue?.load()?.let {
createRematerializableOrTemporary(scope, it, "receiver") {
irVar -> variablesForReceivers.add(irVar)
}
}
val irResultExpression = withLValue(
SimplePropertyLValue(scope, startOffset, endOffset, irOperator, descriptor,
SimpleCallReceiver(tmpDispatchReceiverValue, tmpExtensionReceiverValue))
)
if (variablesForReceivers.isEmpty()) {
irResultExpression
}
else {
val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irResultExpression.type != null, irOperator)
irBlock.addAll(variablesForReceivers)
irBlock.addStatement(irResultExpression)
irBlock
}
}
}
}
@@ -0,0 +1,37 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.types.KotlinType
interface IntermediateValue {
fun load(): IrExpression
val type: KotlinType?
}
interface IntermediateReference : IntermediateValue {
fun store(irExpression: IrExpression): IrExpression
}
interface AssignmentReceiver {
fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression
}
interface CallReceiver {
fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.psi2ir.generators.values
package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.ir.declarations.IrVariable
@@ -22,22 +22,16 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetVariableImpl
import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.ir.expressions.IrSetVariableImpl
import org.jetbrains.kotlin.psi2ir.generators.IrBodyGenerator
import org.jetbrains.kotlin.psi2ir.generators.toExpectedType
import org.jetbrains.kotlin.types.KotlinType
class VariableLValue(
val irBodyGenerator: IrBodyGenerator,
val startOffset: Int,
val endOffset: Int,
val descriptor: VariableDescriptor,
val irOperator: IrOperator? = null
) : IrLValue {
constructor(
irBodyGenerator: IrBodyGenerator,
irVariable: IrVariable,
irOperator: IrOperator? = null
) : this(irBodyGenerator, irVariable.startOffset, irVariable.endOffset, irVariable.descriptor, irOperator)
) : IntermediateReference, AssignmentReceiver {
constructor(irVariable: IrVariable, irOperator: IrOperator? = null) :
this(irVariable.startOffset, irVariable.endOffset, irVariable.descriptor, irOperator)
override val type: KotlinType? get() = descriptor.type
@@ -45,6 +39,8 @@ class VariableLValue(
IrGetVariableImpl(startOffset, endOffset, descriptor, irOperator)
override fun store(irExpression: IrExpression): IrExpression =
IrSetVariableImpl(startOffset, endOffset, descriptor,
irBodyGenerator.toExpectedType(irExpression, descriptor.type), irOperator)
IrSetVariableImpl(startOffset, endOffset, descriptor, irExpression, irOperator)
override fun assign(withLValue: (IntermediateReference) -> IrExpression): IrExpression =
withLValue(this)
}
@@ -0,0 +1,118 @@
/*
* 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.builtins.KotlinBuiltIns
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.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isNullabilityFlexible
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.upperIfFlexible
import java.lang.AssertionError
fun insertImplicitCasts(builtIns: KotlinBuiltIns, element: IrElement) {
element.accept(InsertImplicitCasts(builtIns), null)
}
class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementVisitor<Unit, Nothing?> {
override fun visitElement(element: IrElement, data: Nothing?) {
element.acceptChildren(this, null)
}
override fun visitCall(expression: IrCall, data: Nothing?) {
expression.acceptChildren(this, null)
with(expression) {
dispatchReceiver?.replaceWithCast(descriptor.dispatchReceiverParameter?.type)
extensionReceiver?.replaceWithCast(descriptor.extensionReceiverParameter?.type)
for (index in descriptor.valueParameters.indices) {
val parameterType = descriptor.valueParameters[index].type
getArgument(index)?.replaceWithCast(parameterType)
}
}
}
override fun visitReturn(expression: IrReturn, data: Nothing?) {
expression.acceptChildren(this, null)
expression.value?.replaceWithCast(expression.returnTarget.returnType)
}
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?) {
expression.acceptChildren(this, null)
expression.value.replaceWithCast(expression.descriptor.type)
}
override fun visitVariable(declaration: IrVariable, data: Nothing?) {
declaration.acceptChildren(this, null)
declaration.initializer?.replaceWithCast(declaration.descriptor.type)
}
override fun visitWhen(expression: IrWhen, data: Nothing?) {
expression.acceptChildren(this, null)
for (branchIndex in expression.branchIndices) {
expression.getNthCondition(branchIndex)!!.replaceWithCast(builtIns.booleanType)
expression.getNthResult(branchIndex)!!.replaceWithCast(expression.type)
}
expression.elseBranch?.replaceWithCast(expression.type)
}
override fun visitLoop(loop: IrLoop, data: Nothing?) {
loop.acceptChildren(this, null)
loop.condition.replaceWithCast(builtIns.booleanType)
}
private fun IrExpression.replaceWithCast(expectedType: KotlinType?) {
replaceWith { it.wrapWithImplicitCast(expectedType) }
}
private fun IrExpression.wrapWithImplicitCast(expectedType: KotlinType?): IrExpression {
if (this is IrBlock && !this.hasResult) return this
if (expectedType == null) return this
if (expectedType.isError) return this
if (KotlinBuiltIns.isUnit(expectedType)) return this // TODO expose coercion to Unit in IR?
val valueType = this.type ?: throw AssertionError("expectedType != null, valueType == null: $this")
if (valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull()) {
val nonNullValueType = valueType.upperIfFlexible().makeNotNullable();
return IrTypeOperatorCallImpl(
this.startOffset, this.endOffset, nonNullValueType,
IrTypeOperator.IMPLICIT_NOTNULL, nonNullValueType, this.detach()
).wrapWithImplicitCast(expectedType)
}
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType.makeNotNullable(), expectedType)) {
return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, expectedType,
IrTypeOperator.IMPLICIT_CAST, expectedType, this.detach())
}
return this
}
}
@@ -55,6 +55,15 @@ fun IrElement.replaceWith(otherElement: IrElement) {
parent.replaceChild(slot, otherElement.detach())
}
fun <T : IrElement> T.replaceWith(transformation: (T) -> IrElement) {
val originalParent = this.parent ?: throw AssertionError("Can't replace a non-root element $this")
val originalSlot = this.slot
val transformed = transformation(this)
if (transformed != this) {
originalParent.replaceChild(originalSlot, transformed)
}
}
fun IrElement.assertChild(child: IrElement) {
assert(getChild(child.slot) == child) { "$this: Invalid child: $child" }
}
@@ -68,7 +68,7 @@ class IrVariableImpl(
}
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitLocalVariable(this, data)
return visitor.visitVariable(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
@@ -48,6 +48,15 @@ class IrBlockImpl(
statements.add(statement)
}
fun addAll(statements: List<IrStatement>) {
statements.forEach { it.assertDetached() }
val originalSize = statements.size
this.statements.addAll(statements)
statements.forEachIndexed { i, irStatement ->
irStatement.setTreeLocation(this, originalSize + i)
}
}
override fun getChild(slot: Int): IrElement? =
statements.getOrNull(slot)
@@ -66,4 +75,14 @@ class IrBlockImpl(
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
statements.forEach { it.accept(visitor, data) }
}
}
fun IrBlockImpl.inlineStatement(statement: IrStatement) {
if (statement is IrBlock) {
statement.statements.forEach { it.detach() }
addAll(statement.statements)
}
else {
addStatement(statement)
}
}
@@ -37,10 +37,9 @@ class IrCallImpl(
endOffset: Int,
type: KotlinType?,
override val descriptor: CallableDescriptor,
isSafe: Boolean,
override val operator: IrOperator? = null,
override val superQualifier: ClassDescriptor? = null
) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrCall {
) : IrMemberAccessExpressionBase(startOffset, endOffset, type), IrCall {
private val argumentsByParameterIndex =
arrayOfNulls<IrExpression>(descriptor.valueParameters.size)
@@ -87,12 +86,10 @@ class IrCallImpl(
abstract class IrPropertyAccessorCallBase(
startOffset: Int,
endOffset: Int,
type: KotlinType?,
override val descriptor: CallableDescriptor,
isSafe: Boolean,
override val operator: IrOperator? = null,
override val superQualifier: ClassDescriptor? = null
) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrCall {
) : IrMemberAccessExpressionBase(startOffset, endOffset, descriptor.returnType), IrCall {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitCall(this, data)
}
@@ -101,23 +98,19 @@ abstract class IrPropertyAccessorCallBase(
class IrGetterCallImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor,
isSafe: Boolean,
operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null
) : IrPropertyAccessorCallBase(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier), IrCall {
) : IrPropertyAccessorCallBase(startOffset, endOffset, descriptor, operator, superQualifier), IrCall {
constructor(
startOffset: Int,
endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor,
isSafe: Boolean,
dispatchReceiver: IrExpression?,
extensionReceiver: IrExpression?,
operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null
) : this(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier) {
) : this(startOffset, endOffset, descriptor, operator, superQualifier) {
this.dispatchReceiver = dispatchReceiver
this.extensionReceiver = extensionReceiver
}
@@ -136,24 +129,20 @@ class IrGetterCallImpl(
class IrSetterCallImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor,
isSafe: Boolean,
operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null
) : IrPropertyAccessorCallBase(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier), IrCall {
) : IrPropertyAccessorCallBase(startOffset, endOffset, descriptor, operator, superQualifier), IrCall {
constructor(
startOffset: Int,
endOffset: Int,
type: KotlinType?,
descriptor: CallableDescriptor,
isSafe: Boolean,
dispatchReceiver: IrExpression?,
extensionReceiver: IrExpression?,
argument: IrExpression,
operator: IrOperator? = null,
superQualifier: ClassDescriptor? = null
) : this(startOffset, endOffset, type, descriptor, isSafe, operator, superQualifier) {
) : this(startOffset, endOffset, descriptor, operator, superQualifier) {
this.dispatchReceiver = dispatchReceiver
this.extensionReceiver = extensionReceiver
putArgument(SETTER_ARGUMENT_INDEX, argument)
@@ -19,9 +19,11 @@ package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrConst<out T> : IrExpression {
interface IrConst<out T> : IrExpression, IrExpressionWithCopy {
val kind: IrConstKind<T>
val value: T
override fun copy(): IrConst<T>
}
sealed class IrConstKind<out T>(val asString: kotlin.String) {
@@ -52,6 +54,9 @@ class IrConstImpl<out T> (
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitConst(this, data)
override fun copy(): IrConst<T> =
IrConstImpl(startOffset, endOffset, type, kind, value)
companion object {
fun string(startOffset: Int, endOffset: Int, type: KotlinType, value: String): IrConstImpl<String> =
IrConstImpl(startOffset, endOffset, type, IrConstKind.String, value)
@@ -24,11 +24,14 @@ class IrDummyExpression(
endOffset: Int,
type: KotlinType?,
val description: String
) : IrTerminalExpressionBase(startOffset, endOffset, type) {
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrExpressionWithCopy {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitDummyExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
}
override fun copy(): IrDummyExpression =
IrDummyExpression(startOffset, endOffset, type, description)
}
@@ -27,6 +27,10 @@ interface IrExpression : IrStatement {
val type: KotlinType?
}
interface IrExpressionWithCopy : IrExpression {
fun copy(): IrExpression
}
abstract class IrExpressionBase(
startOffset: Int,
endOffset: Int,
@@ -20,8 +20,10 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrGetExtensionReceiver : IrDeclarationReference {
interface IrGetExtensionReceiver : IrDeclarationReference, IrExpressionWithCopy {
override val descriptor: ReceiverParameterDescriptor
override fun copy(): IrGetExtensionReceiver
}
class IrGetExtensionReceiverImpl(
@@ -32,4 +34,7 @@ class IrGetExtensionReceiverImpl(
) : IrTerminalDeclarationReferenceBase<ReceiverParameterDescriptor>(startOffset, endOffset, type, descriptor), IrGetExtensionReceiver {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitGetExtensionReceiver(this, data)
override fun copy(): IrGetExtensionReceiver =
IrGetExtensionReceiverImpl(startOffset, endOffset, type, descriptor)
}
@@ -21,22 +21,17 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
interface IrLoop : IrExpression {
var body: IrExpression
}
interface IrConditionalLoop : IrLoop {
var condition: IrExpression
}
interface IrWhileLoop : IrConditionalLoop
interface IrWhileLoop : IrLoop
interface IrDoWhileLoop : IrConditionalLoop
interface IrDoWhileLoop : IrLoop
abstract class IrLoopBase(
startOffset: Int,
endOffset: Int
) : IrExpressionBase(startOffset, endOffset, null), IrLoop {
private var bodyImpl: IrExpression? = null
override var body: IrExpression
get() = bodyImpl!!
@@ -63,7 +58,7 @@ abstract class IrLoopBase(
abstract class IrConditionalLoopBase(
startOffset: Int,
endOffset: Int
) : IrLoopBase(startOffset, endOffset), IrConditionalLoop {
) : IrLoopBase(startOffset, endOffset) {
private var conditionImpl: IrExpression? = null
override var condition: IrExpression
get() = conditionImpl!!
@@ -23,14 +23,12 @@ import org.jetbrains.kotlin.types.KotlinType
interface IrMemberAccessExpression : IrDeclarationReference {
var dispatchReceiver: IrExpression?
var extensionReceiver: IrExpression?
val isSafe: Boolean
}
abstract class IrMemberAccessExpressionBase(
startOffset: Int,
endOffset: Int,
type: KotlinType?,
override val isSafe: Boolean
type: KotlinType?
) : IrExpressionBase(startOffset, endOffset, type), IrMemberAccessExpression {
override var dispatchReceiver: IrExpression? = null
set(newReceiver) {
@@ -20,6 +20,8 @@ interface IrOperator {
abstract class IrOperatorImpl(val debugName: String): IrOperator {
override fun toString(): String = debugName
}
object SAFE_CALL : IrOperatorImpl("SAFE_CALL")
object UMINUS : IrOperatorImpl("UMINUS")
object UPLUS : IrOperatorImpl("UPLUS")
@@ -88,3 +90,12 @@ interface IrOperator {
}
}
fun IrOperator.isAssignmentOperatorWithResult() =
when (this) {
IrOperator.PREFIX_INCR, IrOperator.PREFIX_DECR,
IrOperator.POSTFIX_INCR, IrOperator.POSTFIX_DECR ->
true
else ->
false
}
@@ -33,8 +33,6 @@ abstract class IrOperatorCallBase(
get() = null
set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver")
override val isSafe: Boolean get() = false
override var extensionReceiver: IrExpression?
get() = null
set(value) = throw UnsupportedOperationException("Operator call expression can't have a receiver")
@@ -20,8 +20,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrThisReference : IrExpression {
interface IrThisReference : IrExpression, IrExpressionWithCopy {
val classDescriptor: ClassDescriptor
override fun copy(): IrThisReference
}
class IrThisReferenceImpl(
@@ -32,4 +33,8 @@ class IrThisReferenceImpl(
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrThisReference {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitThisReference(this, data)
override fun copy(): IrThisReference =
IrThisReferenceImpl(startOffset, endOffset, type, classDescriptor)
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.types.KotlinType
enum class IrTypeOperator {
CAST,
IMPLICIT_CAST,
IMPLICIT_NOTNULL,
SAFE_CAST,
INSTANCEOF,
NOT_INSTANCEOF;
@@ -26,11 +26,12 @@ interface IrVariableAccessExpression : IrDeclarationReference {
val operator: IrOperator?
}
interface IrGetVariable : IrVariableAccessExpression
interface IrGetVariable : IrVariableAccessExpression, IrExpressionWithCopy {
override fun copy(): IrGetVariable
}
interface IrSetVariable : IrVariableAccessExpression {
val value: IrExpression
var value: IrExpression
}
class IrGetVariableImpl(
@@ -41,6 +42,9 @@ class IrGetVariableImpl(
) : IrTerminalDeclarationReferenceBase<VariableDescriptor>(startOffset, endOffset, descriptor.type, descriptor), IrGetVariable {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitGetVariable(this, data)
override fun copy(): IrGetVariable =
IrGetVariableImpl(startOffset, endOffset, descriptor, operator)
}
class IrSetVariableImpl(
@@ -31,6 +31,8 @@ interface IrWhen : IrExpression {
var elseBranch: IrExpression?
}
val IrWhen.branchIndices: IntRange get() = 0 ..branchesCount - 1
class IrWhenImpl(
startOffset: Int,
endOffset: Int,
@@ -176,28 +178,4 @@ class IrIfThenElseImpl(
thenBranch.accept(visitor, data)
elseBranch?.accept(visitor, data)
}
companion object {
// a || b == if (a) true else b
fun oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
IrIfThenElseImpl(startOffset, endOffset, b.type!!,
a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type!!), b,
operator)
fun oror(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.OROR): IrWhen =
oror(b.startOffset, b.endOffset, a, b, operator)
fun whenComma(a: IrExpression, b: IrExpression): IrWhen =
oror(a, b, IrOperator.WHEN_COMMA)
// a && b == if (a) b else false
fun andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
IrIfThenElseImpl(startOffset, endOffset, b.type!!,
a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type!!),
operator)
fun andand(a: IrExpression, b: IrExpression, operator: IrOperator = IrOperator.ANDAND): IrWhen =
andand(b.startOffset, b.endOffset, a, b, operator)
}
}
@@ -51,7 +51,7 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitPropertySetter(declaration: IrPropertySetter, data: Nothing?): String =
"IrPropertySetter ${declaration.descriptor.render()} property=${declaration.property?.name()}"
override fun visitLocalVariable(declaration: IrVariable, data: Nothing?): String =
override fun visitVariable(declaration: IrVariable, data: Nothing?): String =
"VAR ${declaration.descriptor.render()}"
override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): String =
@@ -76,7 +76,7 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"THIS ${expression.classDescriptor.render()} type=${expression.renderType()}"
override fun visitCall(expression: IrCall, data: Nothing?): String =
"CALL ${if (expression.isSafe) "?." else "."}${expression.descriptor.name} " +
"CALL .${expression.descriptor.name} " +
"type=${expression.renderType()} operator=${expression.operator}"
override fun visitGetVariable(expression: IrGetVariable, data: Nothing?): String =
@@ -33,7 +33,7 @@ interface IrElementVisitor<out R, in D> {
fun visitProperty(declaration: IrProperty, data: D): R = visitDeclaration(declaration, data)
fun visitSimpleProperty(declaration: IrSimpleProperty, data: D): R = visitProperty(declaration, data)
fun visitDelegatedProperty(declaration: IrDelegatedProperty, data: D): R = visitProperty(declaration, data)
fun visitLocalVariable(declaration: IrVariable, data: D) = visitDeclaration(declaration, data)
fun visitVariable(declaration: IrVariable, data: D) = visitDeclaration(declaration, data)
fun visitBody(body: IrBody, data: D): R = visitElement(body, data)
fun visitExpressionBody(body: IrExpressionBody, data: D): R = visitBody(body, data)
+18 -10
View File
@@ -4,11 +4,13 @@ IrFile /arrayAssignment.kt
BLOCK type=<no-type> hasResult=false operator=null
VAR val x: kotlin.IntArray
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: DUMMY vararg type=kotlin.Int
CALL .set type=kotlin.Unit operator=EQ
$this: GET_VAR x type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='1'
value: CONST Int type=kotlin.Int value='0'
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
BLOCK type=kotlin.Unit hasResult=false operator=EQ
CALL .set type=kotlin.Unit operator=EQ
$this: GET_VAR x type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='1'
value: CONST Int type=kotlin.Int value='0'
IrFunction public fun foo(): kotlin.Int
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
@@ -17,8 +19,14 @@ IrFile /arrayAssignment.kt
IrFunction public fun test2(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
CALL .set type=kotlin.Unit operator=EQ
$this: CALL .intArrayOf type=kotlin.IntArray operator=null
elements: DUMMY vararg type=kotlin.Int
index: CALL .foo type=kotlin.Int operator=null
value: CONST Int type=kotlin.Int value='1'
BLOCK type=kotlin.Unit hasResult=false operator=EQ
VAR val tmp0_array: kotlin.IntArray
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
VAR val tmp1_index0: kotlin.Int
CALL .foo type=kotlin.Int operator=null
CALL .set type=kotlin.Unit operator=EQ
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null
index: GET_VAR tmp1_index0 type=kotlin.Int operator=null
value: CONST Int type=kotlin.Int value='1'
+10 -2
View File
@@ -1,9 +1,17 @@
fun foo(): IntArray = intArrayOf(1, 2, 3)
fun bar() = 42
class C(val x: IntArray)
fun testVariable() {
var x = foo()
x[0] += 1
foo()[0] *= 2
foo()[bar()] -= 1
}
fun testCall() {
foo()[bar()] *= 2
}
fun testMember(c: C) {
c.x[0]++
}
+27 -16
View File
@@ -4,12 +4,14 @@ IrFile /arrayAugmentedAssignment1.kt
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: DUMMY vararg type=kotlin.Int
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
IrFunction public fun bar(): kotlin.Int
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
CONST Int type=kotlin.Int value='42'
DUMMY C
IrFunction public fun testVariable(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
@@ -24,27 +26,36 @@ IrFile /arrayAugmentedAssignment1.kt
$this: GET_VAR x type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='0'
other: CONST Int type=kotlin.Int value='1'
IrFunction public fun testCall(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
BLOCK type=kotlin.Unit hasResult=false operator=MULTEQ
VAR val tmp0_array: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator=null
VAR val tmp1_index0: kotlin.Int
CALL .bar type=kotlin.Int operator=null
CALL .set type=kotlin.Unit operator=MULTEQ
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='0'
index: GET_VAR tmp1_index0 type=kotlin.Int operator=null
value: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .get type=kotlin.Int operator=MULTEQ
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='0'
index: GET_VAR tmp1_index0 type=kotlin.Int operator=null
other: CONST Int type=kotlin.Int value='2'
BLOCK type=kotlin.Unit hasResult=false operator=MINUSEQ
VAR val tmp1_array: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator=null
VAR val tmp2_index0: kotlin.Int
CALL .bar type=kotlin.Int operator=null
CALL .set type=kotlin.Unit operator=MINUSEQ
$this: GET_VAR tmp1_array type=kotlin.IntArray operator=null
index: GET_VAR tmp2_index0 type=kotlin.Int operator=null
value: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: CALL .get type=kotlin.Int operator=MINUSEQ
$this: GET_VAR tmp1_array type=kotlin.IntArray operator=null
index: GET_VAR tmp2_index0 type=kotlin.Int operator=null
other: CONST Int type=kotlin.Int value='1'
IrFunction public fun testMember(/*0*/ c: C): kotlin.Unit
IrExpressionBody
BLOCK type=kotlin.Int hasResult=true operator=null
BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR
VAR val tmp0_array: kotlin.IntArray
CALL .<get-x> type=kotlin.IntArray operator=GET_PROPERTY
$this: GET_VAR c type=C operator=null
VAR val tmp1: kotlin.Int
CALL .get type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='0'
CALL .set type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp0_array type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='0'
value: CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
+5 -5
View File
@@ -32,21 +32,21 @@ IrFile /augmentedAssignment1.kt
BLOCK type=kotlin.Int hasResult=true operator=null
CALL .<set-p> type=kotlin.Unit operator=PLUSEQ
<set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=PLUSEQ
other: CONST Int type=kotlin.Int value='1'
CALL .<set-p> type=kotlin.Unit operator=MINUSEQ
<set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=MINUSEQ
other: CONST Int type=kotlin.Int value='2'
CALL .<set-p> type=kotlin.Unit operator=MULTEQ
<set-?>: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=MULTEQ
other: CONST Int type=kotlin.Int value='3'
CALL .<set-p> type=kotlin.Unit operator=DIVEQ
<set-?>: CALL .div type=kotlin.Int operator=DIVEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=DIVEQ
other: CONST Int type=kotlin.Int value='4'
CALL .<set-p> type=kotlin.Unit operator=PERCEQ
<set-?>: CALL .mod type=kotlin.Int operator=PERCEQ
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=PERCEQ
other: CONST Int type=kotlin.Int value='5'
+5 -5
View File
@@ -42,17 +42,17 @@ IrFile /augmentedAssignment2.kt
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY
$receiver: CALL .<get-p> type=A operator=PLUSEQ
s: CONST String type=kotlin.String value='+='
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY
$receiver: CALL .<get-p> type=A operator=MINUSEQ
s: CONST String type=kotlin.String value='-='
CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY
$receiver: CALL .<get-p> type=A operator=MULTEQ
s: CONST String type=kotlin.String value='*='
CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY
$receiver: CALL .<get-p> type=A operator=DIVEQ
s: CONST String type=kotlin.String value='/='
CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: CALL .<get-p> type=A operator=GET_PROPERTY
$receiver: CALL .<get-p> type=A operator=PERCEQ
s: CONST String type=kotlin.String value='%='
+2 -2
View File
@@ -28,7 +28,7 @@ IrFile /callWithReorderedArguments.kt
CALL .foo type=kotlin.Unit operator=null
a: CALL .noReorder1 type=kotlin.Int operator=null
b: CALL .noReorder2 type=kotlin.Int operator=null
BLOCK type=kotlin.Unit hasResult=false operator=SYNTHETIC_BLOCK
BLOCK type=kotlin.Unit hasResult=true operator=SYNTHETIC_BLOCK
VAR val tmp0_b: kotlin.Int
CALL .reordered1 type=kotlin.Int operator=null
VAR val tmp1_a: kotlin.Int
@@ -36,7 +36,7 @@ IrFile /callWithReorderedArguments.kt
CALL .foo type=kotlin.Unit operator=null
a: GET_VAR tmp1_a type=kotlin.Int operator=null
b: GET_VAR tmp0_b type=kotlin.Int operator=null
BLOCK type=kotlin.Unit hasResult=false operator=SYNTHETIC_BLOCK
BLOCK type=kotlin.Unit hasResult=true operator=SYNTHETIC_BLOCK
VAR val tmp2_a: kotlin.Int
CALL .reordered2 type=kotlin.Int operator=null
CALL .foo type=kotlin.Unit operator=null
+3 -3
View File
@@ -2,13 +2,13 @@ IrFunction public fun B.test(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK
VAR val tmp0: A
VAR val tmp0_container: A
GET_OBJECT A type=A
VAR val x: kotlin.Int
CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1)
$this: $RECEIVER of: test type=B
$receiver: GET_VAR tmp0 type=A operator=null
$receiver: GET_VAR tmp0_container type=A operator=null
VAR val y: kotlin.Int
CALL .component2 type=kotlin.Int operator=COMPONENT_N(index=2)
$this: $RECEIVER of: test type=B
$receiver: GET_VAR tmp0 type=A operator=null
$receiver: GET_VAR tmp0_container type=A operator=null
+9 -2
View File
@@ -9,6 +9,13 @@ IrFile /dotQualified.kt
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
CALL ?.<get-length> type=kotlin.Int? operator=GET_PROPERTY
$this: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String
BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: kotlin.String?
GET_VAR s type=kotlin.String? operator=null
WHEN type=kotlin.Int? 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 .<get-length> type=kotlin.Int operator=GET_PROPERTY
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
+29 -29
View File
@@ -11,22 +11,24 @@ IrFile /elvis.kt
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
WHEN type=kotlin.Any operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS
arg0: GET_VAR a type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR b type=kotlin.Any operator=null
else: GET_VAR a type=kotlin.Any? operator=null
BLOCK type=kotlin.Any hasResult=true operator=ELVIS
WHEN type=kotlin.Any operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR a type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR b type=kotlin.Any operator=null
else: GET_VAR a type=kotlin.Any? operator=null
IrFunction public fun test2(/*0*/ a: kotlin.String?, /*1*/ b: kotlin.Any): kotlin.Any
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
WHEN type=kotlin.Any operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS
arg0: GET_VAR a type=kotlin.String? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR b type=kotlin.Any operator=null
else: GET_VAR a type=kotlin.String? operator=null
BLOCK type=kotlin.Any hasResult=true operator=ELVIS
WHEN type=kotlin.Any operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR a type=kotlin.String? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR b type=kotlin.Any operator=null
else: GET_VAR a type=kotlin.String? operator=null
IrFunction public fun test3(/*0*/ a: kotlin.Any?, /*1*/ b: kotlin.Any?): kotlin.String
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
@@ -42,39 +44,37 @@ IrFile /elvis.kt
CONST String type=kotlin.String value=''
RETURN type=<no-type>
BLOCK type=kotlin.String hasResult=true operator=ELVIS
VAR val tmp0: kotlin.String?
TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String?
GET_VAR a type=kotlin.Any? operator=null
WHEN type=kotlin.String operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS
arg0: GET_VAR tmp0 type=kotlin.String? operator=null
WHEN type=kotlin.String operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR a type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String
GET_VAR b type=kotlin.Any? operator=null
else: GET_VAR tmp0 type=kotlin.String? operator=null
else: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String
GET_VAR a type=kotlin.Any? operator=null
IrFunction public fun test4(/*0*/ x: kotlin.Any): kotlin.Any
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Any hasResult=true operator=ELVIS
VAR val tmp0: kotlin.Any?
VAR val tmp0_elvis_lhs: kotlin.Any?
CALL .<get-p> type=kotlin.Any? operator=GET_PROPERTY
WHEN type=kotlin.Any operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS
arg0: GET_VAR tmp0 type=kotlin.Any? operator=null
WHEN type=kotlin.Any operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR x type=kotlin.Any operator=null
else: GET_VAR tmp0 type=kotlin.Any? operator=null
else: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null
IrFunction public fun test5(/*0*/ x: kotlin.Any): kotlin.Any
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Any hasResult=true operator=ELVIS
VAR val tmp0: kotlin.Any?
VAR val tmp0_elvis_lhs: kotlin.Any?
CALL .foo type=kotlin.Any? operator=null
WHEN type=kotlin.Any operator=ELVIS
if: CALL .EQEQ type=kotlin.Boolean operator=ELVIS
arg0: GET_VAR tmp0 type=kotlin.Any? operator=null
WHEN type=kotlin.Any operator=null
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: GET_VAR x type=kotlin.Any operator=null
else: GET_VAR tmp0 type=kotlin.Any? operator=null
else: GET_VAR tmp0_elvis_lhs type=kotlin.Any? operator=null
+3 -10
View File
@@ -3,13 +3,6 @@ IrFile /implicitCastOnPlatformType.kt
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.String hasResult=true operator=IMPLICIT_NOTNULL
VAR val tmp0: kotlin.String!
CALL .getProperty type=kotlin.String! operator=null
p0: CONST String type=kotlin.String value='test'
WHEN type=kotlin.String operator=IMPLICIT_NOTNULL
if: CALL .EQEQ type=kotlin.Boolean operator=IMPLICIT_NOTNULL
arg0: GET_VAR tmp0 type=kotlin.String! operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: CALL .THROW_NPE type=kotlin.Nothing operator=IMPLICIT_NOTNULL
else: GET_VAR tmp0 type=kotlin.String! operator=null
TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=kotlin.String
CALL .getProperty type=kotlin.String! operator=null
p0: CONST String type=kotlin.String value='test'
+9 -8
View File
@@ -5,7 +5,8 @@ IrFile /incrementDecrement.kt
IrProperty public val arr: kotlin.IntArray getter=null setter=null
IrExpressionBody
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: DUMMY vararg type=kotlin.Int
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
IrFunction public fun testVarPrefix(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
@@ -38,7 +39,7 @@ IrFile /incrementDecrement.kt
GET_VAR x type=kotlin.Int operator=POSTFIX_INCR
SET_VAR x type=<no-type> operator=POSTFIX_INCR
CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0 type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null
VAR val x2: kotlin.Int
BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_DECR
@@ -46,7 +47,7 @@ IrFile /incrementDecrement.kt
GET_VAR x type=kotlin.Int operator=POSTFIX_DECR
SET_VAR x type=<no-type> operator=POSTFIX_DECR
CALL .dec type=kotlin.Int operator=POSTFIX_DECR
$this: GET_VAR tmp1 type=kotlin.Int operator=POSTFIX_DECR
$this: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
IrFunction public fun testPropPrefix(): kotlin.Unit
IrExpressionBody
@@ -55,7 +56,7 @@ IrFile /incrementDecrement.kt
BLOCK type=kotlin.Int hasResult=true operator=PREFIX_INCR
VAR val tmp0: kotlin.Int
CALL .inc type=kotlin.Int operator=PREFIX_INCR
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_INCR
<set-?>: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null
@@ -63,7 +64,7 @@ IrFile /incrementDecrement.kt
BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR
VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
@@ -73,16 +74,16 @@ IrFile /incrementDecrement.kt
VAR val p1: kotlin.Int
BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR
VAR val tmp0: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
<set-?>: CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0 type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null
VAR val p2: kotlin.Int
BLOCK type=kotlin.Int hasResult=true operator=PREFIX_DECR
VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=GET_PROPERTY
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
@@ -0,0 +1,20 @@
package test
class C
var C?.p: Int
get() = 42
set(value) {}
operator fun Int?.inc(): Int? = this?.inc()
operator fun Int?.get(index: Int): Int = 42
operator fun Int?.set(index: Int, value: Int) {}
fun testProperty(nc: C?) {
nc?.p++
}
fun testArrayAccess(nc: C?) {
nc?.p[0]++
}
@@ -0,0 +1,53 @@
IrFile /safeCallWithIncrementDecrement.kt
DUMMY C
IrProperty public var test.C?.p: kotlin.Int getter=<get-p> setter=<set-p>
IrPropertyGetter public fun test.C?.<get-p>(): kotlin.Int property=p
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
CONST Int type=kotlin.Int value='42'
IrPropertySetter public fun test.C?.<set-p>(/*0*/ value: kotlin.Int): kotlin.Unit property=p
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
IrFunction public operator fun kotlin.Int?.inc(): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: kotlin.Int?
$RECEIVER of: inc type=kotlin.Int?
WHEN type=kotlin.Int? operator=SAFE_CALL
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0_safe_receiver type=kotlin.Int? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: CONST Null type=kotlin.Nothing? value='null'
else: CALL .inc type=kotlin.Int operator=null
$this: GET_VAR tmp0_safe_receiver type=kotlin.Int? operator=null
IrFunction public operator fun kotlin.Int?.get(/*0*/ index: kotlin.Int): kotlin.Int
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
CONST Int type=kotlin.Int value='42'
IrFunction public operator fun kotlin.Int?.set(/*0*/ index: kotlin.Int, /*1*/ value: kotlin.Int): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
IrFunction public fun testProperty(/*0*/ nc: test.C?): kotlin.Unit
IrExpressionBody
BLOCK type=kotlin.Int? hasResult=true operator=null
BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: test.C?
GET_VAR nc type=test.C? operator=null
WHEN type=kotlin.Int? operator=SAFE_CALL
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0_safe_receiver type=test.C? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: CONST Null type=kotlin.Nothing? value='null'
else: BLOCK type=kotlin.Int hasResult=true operator=POSTFIX_INCR
VAR val tmp1: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null
value: CALL .inc type=kotlin.Int? operator=POSTFIX_INCR
$receiver: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
+15
View File
@@ -0,0 +1,15 @@
class Ref(var value: Int)
interface IHost {
fun String.extLength() = length
}
fun test1(x: String?) = x?.length
fun test2(x: String?) = x?.hashCode()
fun test3(x: String?, y: Any?) = x?.equals(y)
fun test4(x: Ref?) {
x?.value = 0
}
fun IHost.test5(s: String?) = s?.extLength()
+75
View File
@@ -0,0 +1,75 @@
IrFile /safeCalls.kt
DUMMY Ref
DUMMY IHost
IrFunction public fun test1(/*0*/ x: kotlin.String?): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: kotlin.String?
GET_VAR x type=kotlin.String? operator=null
WHEN type=kotlin.Int? 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 .<get-length> type=kotlin.Int operator=GET_PROPERTY
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
IrFunction public fun test2(/*0*/ x: kotlin.String?): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: kotlin.String?
GET_VAR x type=kotlin.String? operator=null
WHEN type=kotlin.Int? 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 .hashCode type=kotlin.Int operator=null
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
IrFunction public fun test3(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.Any?): kotlin.Boolean?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Boolean? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: kotlin.String?
GET_VAR x type=kotlin.String? operator=null
WHEN type=kotlin.Boolean? 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 .equals type=kotlin.Boolean operator=null
$this: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
other: GET_VAR y type=kotlin.Any? operator=null
IrFunction public fun test4(/*0*/ x: Ref?): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
BLOCK type=kotlin.Unit? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: Ref?
GET_VAR x type=Ref? operator=null
WHEN type=kotlin.Unit? operator=SAFE_CALL
if: CALL .EQEQ type=kotlin.Boolean operator=EQEQ
arg0: GET_VAR tmp0_safe_receiver type=Ref? operator=null
arg1: CONST Null type=kotlin.Nothing? value='null'
then: CONST Null type=kotlin.Nothing? value='null'
else: CALL .<set-value> type=kotlin.Unit operator=EQ
$this: GET_VAR tmp0_safe_receiver type=Ref? operator=null
<set-?>: CONST Int type=kotlin.Int value='0'
IrFunction public fun IHost.test5(/*0*/ s: kotlin.String?): kotlin.Int?
IrExpressionBody
BLOCK type=<no-type> hasResult=false operator=null
RETURN type=<no-type>
BLOCK type=kotlin.Int? hasResult=true operator=SAFE_CALL
VAR val tmp0_safe_receiver: kotlin.String?
GET_VAR s type=kotlin.String? operator=null
WHEN type=kotlin.Int? 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 .extLength type=kotlin.Int operator=null
$this: $RECEIVER of: test5 type=IHost
$receiver: GET_VAR tmp0_safe_receiver type=kotlin.String? operator=null
@@ -19,12 +19,10 @@ IrFile /smartCastsWithDestructuring.kt
GET_VAR x type=I1 operator=null
then: RETURN type=<no-type>
BLOCK type=<no-type> hasResult=false operator=SYNTHETIC_BLOCK
VAR val tmp0: I1
GET_VAR x type=I1 operator=null
VAR val c1: kotlin.Int
CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1)
$receiver: GET_VAR tmp0 type=I1 operator=null
$receiver: GET_VAR x type=I1 operator=null
VAR val c2: kotlin.String
CALL .component2 type=kotlin.String operator=COMPONENT_N(index=2)
$receiver: TYPE_OP operator=IMPLICIT_CAST typeOperand=I2
GET_VAR tmp0 type=I1 operator=null
GET_VAR x type=I1 operator=null
+2 -4
View File
@@ -21,8 +21,7 @@ IrFile /when.kt
then: CONST 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
element: GET_VAR tmp0_subject type=kotlin.Any? operator=null
then: CONST String type=kotlin.String value='nothingness?'
else: CONST String type=kotlin.String value='something'
IrFunction public fun test(/*0*/ x: kotlin.Any?): kotlin.String
@@ -43,8 +42,7 @@ IrFile /when.kt
then: CONST 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 x type=kotlin.Any? operator=null
element: GET_VAR x type=kotlin.Any? operator=null
then: CONST String type=kotlin.String value='nothingness?'
else: CONST String type=kotlin.String value='something'
IrFunction public fun testComma(/*0*/ x: kotlin.Int): kotlin.String
@@ -179,6 +179,18 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("safeCallWithIncrementDecrement.kt")
public void testSafeCallWithIncrementDecrement() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/safeCallWithIncrementDecrement.kt");
doTest(fileName);
}
@TestMetadata("safeCalls.kt")
public void testSafeCalls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/safeCalls.kt");
doTest(fileName);
}
@TestMetadata("simpleOperators.kt")
public void testSimpleOperators() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/simpleOperators.kt");