- IR expressions for calls & references

- first calls "parsed"
This commit is contained in:
Dmitry Petrov
2016-08-10 19:41:07 +03:00
committed by Dmitry Petrov
parent c09ba962c4
commit c93666f6f1
24 changed files with 768 additions and 64 deletions
@@ -16,37 +16,61 @@
package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.constants.IntValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.scopes.receivers.*
class IrExpressionGenerator(
override val context: IrGeneratorContext,
val declarationFactory: IrDeclarationFactory
) : KtVisitor<IrExpressionBase, Nothing?>(), IrGenerator {
) : KtVisitor<IrExpression, Nothing?>(), IrGenerator {
fun generateExpression(ktExpression: KtExpression) = ktExpression.generate()
private fun KtElement.generate(): IrExpressionBase = accept(this@IrExpressionGenerator, null)
private fun KtExpression.type() = getType(this) ?: TODO("no type for expression")
private fun KtElement.generate(): IrExpression =
accept(this@IrExpressionGenerator, null).applySmartCast(this)
override fun visitExpression(expression: KtExpression, data: Nothing?): IrExpressionBase =
IrDummyExpression(expression.startOffset, expression.endOffset, expression.type())
private fun KtExpression.type() =
getType(this) ?: TODO("no type for expression")
override fun visitBlockExpression(expression: KtBlockExpression, data: Nothing?): IrExpressionBase {
val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, expression.type())
expression.statements.forEach { irBlock.childExpressions.add(it.generate()) }
private fun IrExpression.applySmartCast(ktElement: KtElement): IrExpression {
if (ktElement is KtExpression) {
val smartCastType = get(BindingContext.SMARTCAST, ktElement)
if (smartCastType != null) {
return IrTypeOperatorExpressionImpl(
ktElement.startOffset, ktElement.endOffset, smartCastType,
IrTypeOperator.SMART_AS, smartCastType
).apply { childExpression = this@applySmartCast }
}
}
return this
}
override fun visitExpression(expression: KtExpression, data: Nothing?): IrExpression =
IrDummyExpression(expression.startOffset, expression.endOffset, expression.type(), expression.javaClass.simpleName)
override fun visitBlockExpression(expression: KtBlockExpression, data: Nothing?): IrExpression {
val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, expression.type(), false)
expression.statements.forEach { irBlock.addChildExpression(it.generate()) }
return irBlock
}
override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrExpressionBase =
override fun visitReturnExpression(expression: KtReturnExpression, data: Nothing?): IrExpression =
IrReturnExpressionImpl(expression.startOffset, expression.endOffset, expression.type())
.apply { this.childExpression = expression.returnedExpression?.generate() }
override fun visitConstantExpression(expression: KtConstantExpression, data: Nothing?): IrExpressionBase {
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(expression.type())
@@ -62,7 +86,7 @@ class IrExpressionGenerator(
}
}
override fun visitStringTemplateExpression(expression: KtStringTemplateExpression, data: Nothing?): IrExpressionBase {
override fun visitStringTemplateExpression(expression: KtStringTemplateExpression, data: Nothing?): IrExpression {
if (expression.entries.size == 1 && expression.entries[0] is KtLiteralStringTemplateEntry) {
return expression.entries[0].generate()
}
@@ -72,6 +96,110 @@ class IrExpressionGenerator(
return irStringTemplate
}
override fun visitLiteralStringTemplateEntry(entry: KtLiteralStringTemplateEntry, data: Nothing?): IrExpressionBase =
override fun visitLiteralStringTemplateEntry(entry: KtLiteralStringTemplateEntry, data: Nothing?): IrExpression =
IrLiteralExpressionImpl.string(entry.startOffset, entry.endOffset, context.builtIns.stringType, entry.text)
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Nothing?): IrExpression {
val resolvedCall = getResolvedCall(expression)
if (resolvedCall is VariableAsFunctionResolvedCall) {
TODO("Unexpected VariableAsFunctionResolvedCall")
}
val descriptor = resolvedCall?.resultingDescriptor ?: get(BindingContext.REFERENCE_TARGET, expression)
return when (descriptor) {
is ClassDescriptor ->
IrSingletonReferenceImpl(expression.startOffset, expression.endOffset, expression.type(),
descriptor.singletonDescriptor() ?: TODO("Unsupported class as value: ${descriptor.name}"))
is PropertyDescriptor -> {
generateCall(
expression,
resolvedCall ?: TODO("Property, no resolved call: ${descriptor.name}"),
IrCallableOperator.PROPERTY_GET
)
}
is VariableDescriptor ->
IrVariableReferenceImpl(expression.startOffset, expression.endOffset, expression.type(), descriptor)
else ->
IrDummyExpression(expression.startOffset, expression.endOffset, expression.type(),
expression.getReferencedName() +
": ${descriptor?.name} ${descriptor?.javaClass?.simpleName}")
}
}
override fun visitCallExpression(expression: KtCallExpression, data: Nothing?): IrExpression {
val resolvedCall = getResolvedCall(expression) ?: TODO("No resolved call for call expression")
if (resolvedCall is VariableAsFunctionResolvedCall) {
TODO("VariableAsFunctionResolvedCall = variable call + invoke call")
}
return generateCall(expression, resolvedCall, null, null)
}
private fun generateCall(
ktExpression: KtExpression,
resolvedCall: ResolvedCall<out CallableDescriptor>,
operator: IrCallableOperator?,
superQualifier: ClassDescriptor? = null
): IrExpression =
IrCallExpressionImpl(
ktExpression.startOffset, ktExpression.endOffset, ktExpression.type(),
resolvedCall.resultingDescriptor, resolvedCall.call.isSafeCall(), operator, superQualifier
).apply {
dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver)
extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver)
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
val valueArguments = resolvedCall.valueArgumentsByIndex ?: TODO("null for value arguments: ${ktExpression.text}")
for (index in valueArguments.indices) {
val valueArgument = valueArguments[index]
val valueParameter = valueParameters[index]
putValueArgument(valueParameter, generateValueArgument(valueParameter, valueArgument))
}
}
private fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? =
when (receiver) {
is ImplicitClassReceiver ->
IrThisExpressionImpl(ktExpression.startOffset, ktExpression.startOffset, receiver.type, receiver.classDescriptor)
is ThisClassReceiver ->
(receiver as? ExpressionReceiver)?.expression?.let { receiverExpression ->
IrThisExpressionImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor)
} ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver")
is ExpressionReceiver ->
receiver.expression.generate()
is ClassValueReceiver ->
IrSingletonReferenceImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type,
receiver.classQualifier.descriptor)
is ExtensionReceiver ->
IrExtensionReceiverReferenceImpl(ktExpression.startOffset, ktExpression.startOffset, receiver.type,
receiver.declarationDescriptor.extensionReceiverParameter!!)
null ->
null
else ->
IrDummyExpression(ktExpression.startOffset, ktExpression.endOffset, receiver.type,
"Receiver: ${receiver.javaClass.simpleName}")
}
private fun generateValueArgument(valueParameter: ValueParameterDescriptor, valueArgument: ResolvedValueArgument): IrExpression? {
if (valueParameter.varargElementType == null) {
assert(valueArgument.arguments.size == 1) { "Single value argument expected for a non-vararg parameter" }
return valueArgument.arguments.single().getArgumentExpression()!!.generate()
}
else {
TODO("vararg")
}
}
companion object {
fun ClassDescriptor.singletonDescriptor(): ClassDescriptor? =
if (DescriptorUtils.isObject(this) || DescriptorUtils.isEnumEntry(this))
this
else
companionObjectDescriptor
}
}
@@ -16,7 +16,10 @@
package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
@@ -34,4 +37,7 @@ inline fun <K, V : Any> IrGenerator.getOrFail(slice: ReadOnlySlice<K, V>, key: K
context.bindingContext[slice, key] ?: throw RuntimeException(message(key))
inline fun <K, V : Any> IrGenerator.getOrElse(slice: ReadOnlySlice<K, V>, key: K, otherwise: (K) -> V): V =
context.bindingContext[slice, key] ?: otherwise(key)
context.bindingContext[slice, key] ?: otherwise(key)
fun IrGenerator.getResolvedCall(key: KtExpression): ResolvedCall<out CallableDescriptor>? =
key.getResolvedCall(context.bindingContext)
@@ -20,12 +20,18 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrBlockExpression : IrCompoundExpressionN
interface IrBlockExpression : IrCompoundExpressionN {
val hasResult: Boolean
}
fun IrBlockExpression.getResultExpression() =
if (hasResult) childExpressions.lastOrNull() else null
class IrBlockExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType
type: KotlinType,
override val hasResult: Boolean
) : IrCompoundExpressionNBase(startOffset, endOffset, type), IrBlockExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitBlockExpression(this, data)
@@ -0,0 +1,142 @@
/*
* 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.ir.expressions
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrCallExpression : IrCompoundExpression {
val superQualifier: ClassDescriptor?
val operator: IrCallableOperator?
val isSafe: Boolean
val callee: CallableDescriptor
var dispatchReceiver: IrExpression?
var extensionReceiver: IrExpression?
fun getValueArgument(valueParameterDescriptor: ValueParameterDescriptor): IrExpression?
fun putValueArgument(valueParameterDescriptor: ValueParameterDescriptor, valueArgument: IrExpression?)
fun removeValueArgument(valueParameterDescriptor: ValueParameterDescriptor)
fun <D> acceptValueArguments(visitor: IrElementVisitor<Unit, D>, data: D)
}
fun IrCallExpression.getMappedValueArguments(): List<IrExpression?> =
callee.valueParameters.mapNotNull { getValueArgument(it) }
abstract class IrCallExpressionBase(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override final val callee: CallableDescriptor,
override val isSafe: Boolean,
override val operator: IrCallableOperator?,
override val superQualifier: ClassDescriptor?
) : IrExpressionBase(startOffset, endOffset, type), IrCallExpression {
private val argumentsByParameterIndex =
kotlin.arrayOfNulls<IrExpression>(callee.valueParameters.size)
override var dispatchReceiver: IrExpression? = null
set(newReceiver) {
field?.detach()
field = newReceiver
newReceiver?.setTreeLocation(this, DISPATCH_RECEIVER_INDEX)
}
override var extensionReceiver: IrExpression? = null
set(newReceiver) {
field?.detach()
field = newReceiver
newReceiver?.setTreeLocation(this, EXTENSION_RECEIVER_INDEX)
}
override fun getValueArgument(valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
argumentsByParameterIndex[valueParameterDescriptor.index]
override fun putValueArgument(valueParameterDescriptor: ValueParameterDescriptor, valueArgument: IrExpression?) {
putValueArgument(valueParameterDescriptor.index, valueArgument)
}
private fun putValueArgument(index: Int, valueArgument: IrExpression?) {
argumentsByParameterIndex[index]?.detach()
argumentsByParameterIndex[index] = valueArgument
valueArgument?.setTreeLocation(this, index)
}
override fun removeValueArgument(valueParameterDescriptor: ValueParameterDescriptor) {
argumentsByParameterIndex[valueParameterDescriptor.index]?.detach()
argumentsByParameterIndex[valueParameterDescriptor.index] = null
}
override fun getChildExpression(index: Int): IrExpression? =
when (index) {
DISPATCH_RECEIVER_INDEX ->
dispatchReceiver
EXTENSION_RECEIVER_INDEX ->
extensionReceiver
else ->
argumentsByParameterIndex.getOrNull(index)
}
override fun replaceChildExpression(oldChild: IrExpression, newChild: IrExpression) {
validateChild(oldChild)
when (oldChild.index) {
DISPATCH_RECEIVER_INDEX ->
dispatchReceiver = newChild
EXTENSION_RECEIVER_INDEX ->
extensionReceiver = newChild
else ->
putValueArgument(oldChild.index, newChild)
}
}
override fun <D> acceptValueArguments(visitor: IrElementVisitor<Unit, D>, data: D) {
for (valueArgument in argumentsByParameterIndex) {
valueArgument?.let { it.accept(visitor, data) }
}
}
override fun <D> acceptChildExpressions(visitor: IrElementVisitor<Unit, D>, data: D) {
dispatchReceiver?.accept(visitor, data)
extensionReceiver?.accept(visitor, data)
acceptValueArguments(visitor, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
acceptChildExpressions(visitor, data)
}
companion object {
const val DISPATCH_RECEIVER_INDEX = -1
const val EXTENSION_RECEIVER_INDEX = -2
}
}
class IrCallExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
callee: CallableDescriptor,
isSafe: Boolean,
operator: IrCallableOperator? = null,
superQualifier: ClassDescriptor? = null
) : IrCallExpressionBase(startOffset, endOffset, type, callee, isSafe, operator, superQualifier), IrCallExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitCallExpression(this, data)
}
@@ -0,0 +1,69 @@
/*
* 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.ir.expressions
enum class IrCallableOperator {
PROPERTY_GET,
PROPERTY_SET,
INVOKE,
INVOKE_EXTENSION,
PLUSPLUS,
MINUSMINUS,
UMINUS,
EXCL,
LT,
GT,
LTEQ,
GTEQ,
EQEQ,
EQEQEQ,
EXCLEQ,
EXCLEQEQ,
IN,
NOT_IN,
ANDAND,
OROR,
RANGE,
PLUS,
MINUS,
MUL,
DIV,
MOD,
PLUSEQ,
MINUSEQ,
MULEQ,
DIVEQ,
MODEQ;
}
fun IrCallableOperator.isCompoundAssignment(): Boolean =
this >= IrCallableOperator.PLUSEQ && this <= IrCallableOperator.MODEQ
fun IrCallableOperator.hasCompoundAssignmentDual(): Boolean =
this >= IrCallableOperator.PLUS && this <= IrCallableOperator.MOD
fun IrCallableOperator.toDualOperator(): IrCallableOperator =
when {
isCompoundAssignment() ->
IrCallableOperator.values()[this.ordinal - IrCallableOperator.PLUSEQ.ordinal + IrCallableOperator.PLUS.ordinal]
hasCompoundAssignmentDual() ->
IrCallableOperator.values()[this.ordinal - IrCallableOperator.PLUS.ordinal + IrCallableOperator.PLUSEQ.ordinal]
else ->
throw UnsupportedOperationException("Operator $this is not a compound assignment")
}
@@ -0,0 +1,77 @@
/*
* 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.ir.expressions
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrDeclarationReference : IrExpression {
val descriptor: DeclarationDescriptor
}
interface IrVariableReference : IrDeclarationReference {
override val descriptor: VariableDescriptor
}
interface IrSingletonReference : IrDeclarationReference {
override val descriptor: ClassDescriptor
}
interface IrExtensionReceiverReference : IrDeclarationReference {
override val descriptor: CallableDescriptor
}
abstract class IrDeclarationReferenceBase<out D : DeclarationDescriptor>(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val descriptor: D
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrDeclarationReference
class IrVariableReferenceImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
descriptor: VariableDescriptor
) : IrDeclarationReferenceBase<VariableDescriptor>(startOffset, endOffset, type, descriptor), IrVariableReference {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitVariableReference(this, data)
}
class IrSingletonReferenceImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
descriptor: ClassDescriptor
) : IrDeclarationReferenceBase<ClassDescriptor>(startOffset, endOffset, type, descriptor), IrSingletonReference {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitSingletonReference(this, data)
}
class IrExtensionReceiverReferenceImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val descriptor: CallableDescriptor
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrExtensionReceiverReference {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitExtensionReceiverReference(this, data)
}
}
@@ -22,10 +22,11 @@ import org.jetbrains.kotlin.types.KotlinType
class IrDummyExpression(
startOffset: Int,
endOffset: Int,
type: KotlinType
type: KotlinType,
val description: String
) : IrExpressionBase(startOffset, endOffset, type) {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitElement(this, data)
visitor.visitDummyExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrElementBase
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
@@ -53,4 +54,14 @@ abstract class IrExpressionBase(
this.parent = parent
this.index = index
}
}
abstract class IrTerminalExpressionBase(
startOffset: Int,
endOffset: Int,
type: KotlinType
) : IrExpressionBase(startOffset, endOffset, type) {
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
}
}
@@ -33,16 +33,6 @@ interface IrExpressionOwner1 : IrExpressionOwner {
}
}
interface IrExpressionOwner2 : IrExpressionOwner {
var childExpression1: IrExpression?
var childExpression2: IrExpression?
companion object {
const val EXPRESSION1_INDEX = 1
const val EXPRESSION2_INDEX = 2
}
}
interface IrExpressionOwnerN : IrExpressionOwner {
val childExpressions: List<IrExpression>
fun addChildExpression(child: IrExpression)
@@ -48,14 +48,10 @@ class IrLiteralExpressionImpl<out T> (
type: KotlinType,
override val kind: IrLiteralKind<T>,
override val value: T
) : IrExpressionBase(startOffset, endOffset, type), IrLiteralExpression<T> {
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrLiteralExpression<T> {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitLiteral(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
}
companion object {
fun string(startOffset: Int, endOffset: Int, type: KotlinType, value: String): IrLiteralExpressionImpl<String> =
IrLiteralExpressionImpl(startOffset, endOffset, type, IrLiteralKind.String, value)
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrThisExpression : IrExpression {
val classDescriptor: ClassDescriptor
}
class IrThisExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val classDescriptor: ClassDescriptor
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrThisExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitThisExpression(this, data)
}
}
@@ -0,0 +1,44 @@
/*
* 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.ir.expressions
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
enum class IrTypeOperator {
SMART_AS,
AS,
AS_SAFE,
IS;
}
interface IrTypeOperatorExpression : IrExpression, IrCompoundExpression1 {
val operator: IrTypeOperator
val typeOperand: KotlinType
}
class IrTypeOperatorExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val operator: IrTypeOperator,
override val typeOperand: KotlinType
) : IrCompoundExpression1Base(startOffset, endOffset, type), IrTypeOperatorExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitTypeOperatorExpression(this, data)
}
}
@@ -18,23 +18,52 @@ package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrCallExpression
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.utils.Printer
fun IrDeclaration.dump(): String {
val sb = StringBuilder()
accept(DumpIrTreeVisitor(sb), null)
accept(DumpIrTreeVisitor(sb), "")
return sb.toString()
}
class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, Nothing?> {
class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
val printer = Printer(out, " ")
val elementRenderer = RenderIrElementVisitor()
override fun visitElement(element: IrElement, data: Nothing?) {
printer.println(element.accept(elementRenderer, null))
override fun visitElement(element: IrElement, data: String) {
element.dumpLabeledSubTree(data)
}
override fun visitCallExpression(expression: IrCallExpression, data: String) {
expression.dumpLabeledElementWith(data) {
expression.dispatchReceiver?.accept(this, "\$this")
expression.extensionReceiver?.accept(this, "\$receiver")
for (valueParameter in expression.callee.valueParameters) {
expression.getValueArgument(valueParameter)?.accept(this, valueParameter.name.asString())
}
}
}
private inline fun IrElement.dumpLabeledElementWith(label: String, body: () -> Unit) {
printer.println(accept(elementRenderer, null).withLabel(label))
indented(body)
}
private fun IrElement.dumpLabeledSubTree(label: String) {
printer.println(accept(elementRenderer, null).withLabel(label))
indented {
acceptChildren(this@DumpIrTreeVisitor, "")
}
}
private inline fun indented(body: () -> Unit) {
printer.pushIndent()
element.acceptChildren(this, null)
body()
printer.popIndent()
}
private fun String.withLabel(label: String) =
if (label.isEmpty()) this else "$label: $this"
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
@@ -27,40 +28,58 @@ import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitElement(element: IrElement, data: Nothing?): String =
"??? ${element.javaClass.simpleName}"
"? ${element.javaClass.simpleName}"
override fun visitDeclaration(declaration: IrDeclaration, data: Nothing?): String =
"??? ${declaration.javaClass.simpleName} ${declaration.descriptor?.name}"
"? ${declaration.javaClass.simpleName} ${declaration.descriptor?.name}"
override fun visitFile(declaration: IrFile, data: Nothing?): String =
"IrFile ${declaration.name}"
override fun visitFunction(declaration: IrFunction, data: Nothing?): String =
"IrFunction ${declaration.renderDescriptor()}"
"IrFunction ${declaration.descriptor.render()}"
override fun visitProperty(declaration: IrProperty, data: Nothing?): String =
"IrProperty ${declaration.renderDescriptor()} getter=${declaration.getter?.name()} setter=${declaration.setter?.name()}"
"IrProperty ${declaration.descriptor.render()} getter=${declaration.getter?.name()} setter=${declaration.setter?.name()}"
override fun visitPropertyGetter(declaration: IrPropertyGetter, data: Nothing?): String =
"IrPropertyGetter ${declaration.renderDescriptor()} property=${declaration.property?.name()}"
"IrPropertyGetter ${declaration.descriptor.render()} property=${declaration.property?.name()}"
override fun visitPropertySetter(declaration: IrPropertySetter, data: Nothing?): String =
"IrPropertySetter ${declaration.renderDescriptor()} property=${declaration.property?.name()}"
"IrPropertySetter ${declaration.descriptor.render()} property=${declaration.property?.name()}"
override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): String =
"IrExpressionBody"
override fun visitExpression(expression: IrExpression, data: Nothing?): String =
"??? ${expression.javaClass.simpleName} type=${expression.renderType()}"
"? ${expression.javaClass.simpleName} type=${expression.renderType()}"
override fun <T> visitLiteral(expression: IrLiteralExpression<T>, data: Nothing?): String =
"IrLiteral ${expression.kind} type=${expression.renderType()} value='${expression.value}'"
"LITERAL ${expression.kind} type=${expression.renderType()} value='${expression.value}'"
override fun visitBlockExpression(expression: IrBlockExpression, data: Nothing?): String =
"IrBlockExpression type=${expression.renderType()}"
"BLOCK type=${expression.renderType()}"
override fun visitReturnExpression(expression: IrReturnExpression, data: Nothing?): String =
"IrReturnExpression type=${expression.renderType()}"
"RETURN type=${expression.renderType()}"
override fun visitVariableReference(expression: IrVariableReference, data: Nothing?): String =
"VAR ${expression.descriptor.name}"
override fun visitSingletonReference(expression: IrSingletonReference, data: Nothing?): String =
"SINGLETON ${expression.descriptor.name}"
override fun visitExtensionReceiverReference(expression: IrExtensionReceiverReference, data: Nothing?): String =
"\$RECEIVER of: ${expression.descriptor.containingDeclaration.name}"
override fun visitThisExpression(expression: IrThisExpression, data: Nothing?): String =
"THIS ${expression.classDescriptor.render()}"
override fun visitCallExpression(expression: IrCallExpression, data: Nothing?): String =
"CALL ${expression.callee.name} ${expression.operator ?: ""}"
override fun visitDummyExpression(expression: IrDummyExpression, data: Nothing?): String =
"DUMMY ${expression.description}"
companion object {
private val DESCRIPTOR_RENDERER = DescriptorRenderer.withOptions {
@@ -72,8 +91,13 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
modifiers = DescriptorRendererModifier.ALL
}
private fun IrDeclaration.name(): String = descriptor?.let { it.name.toString() } ?: "<none>"
private fun IrDeclaration.renderDescriptor(): String = descriptor?.let { DESCRIPTOR_RENDERER.render(it) } ?: "<none>"
private fun IrExpression.renderType(): String = DESCRIPTOR_RENDERER.renderType(type)
internal fun IrDeclaration.name(): String =
descriptor?.let { it.name.toString() } ?: "<none>"
internal fun DeclarationDescriptor.render(): String =
DESCRIPTOR_RENDERER.render(this)
internal fun IrExpression.renderType(): String =
DESCRIPTOR_RENDERER.renderType(type)
}
}
@@ -43,4 +43,18 @@ interface IrElementVisitor<out R, in D> {
fun visitReturnExpression(expression: IrReturnExpression, data: D): R = visitExpression(expression, data)
fun visitBlockExpression(expression: IrBlockExpression, data: D): R = visitExpression(expression, data)
fun visitStringTemplate(expression: IrStringConcatenationExpression, data: D) = visitExpression(expression, data)
fun visitThisExpression(expression: IrThisExpression, data: D) = visitExpression(expression, data)
fun visitDeclarationReference(expression: IrDeclarationReference, data: D) = visitExpression(expression, data)
fun visitVariableReference(expression: IrVariableReference, data: D) = visitDeclarationReference(expression, data)
fun visitSingletonReference(expression: IrSingletonReference, data: D) = visitDeclarationReference(expression, data)
fun visitExtensionReceiverReference(expression: IrExtensionReceiverReference, data: D) = visitDeclarationReference(expression, data)
fun visitCallExpression(expression: IrCallExpression, data: D) = visitExpression(expression, data)
fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: D) = visitExpression(expression, data)
// NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered
fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data)
}
+2 -2
View File
@@ -1,5 +1,5 @@
IrFile /boxOk.kt
IrFunction public fun box(): kotlin.String
IrExpressionBody
IrReturnExpression type=kotlin.String
IrLiteral String type=kotlin.String value='OK'
RETURN type=kotlin.String
LITERAL String type=kotlin.String value='OK'
+8
View File
@@ -0,0 +1,8 @@
fun foo(x: Int, y: Int) = x
fun bar(x: Int) = foo(x, 1)
fun qux(x: Int) = foo(foo(x, x), x)
fun Int.ext1() = this
fun Int.ext2(x: Int) = foo(this, x)
// IR_FILE_TXT calls.txt
+29
View File
@@ -0,0 +1,29 @@
IrFile /calls.kt
IrFunction public fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
VAR x
IrFunction public fun bar(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
CALL foo
x: VAR x
y: LITERAL Int type=kotlin.Int value='1'
IrFunction public fun qux(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
CALL foo
x: CALL foo
x: VAR x
y: VAR x
y: VAR x
IrFunction public fun kotlin.Int.ext1(): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
DUMMY KtThisExpression
IrFunction public fun kotlin.Int.ext2(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
CALL foo
x: DUMMY KtThisExpression
y: VAR x
@@ -0,0 +1,4 @@
val String.okext: String get() = "OK"
fun String.test5() = okext
// IR_FILE_TXT extensionPropertyGetterCall.txt
@@ -0,0 +1,11 @@
IrFile /extensionPropertyGetterCall.kt
IrProperty public val kotlin.String.okext: kotlin.String getter=<get-okext> setter=null
IrPropertyGetter public fun kotlin.String.<get-okext>(): kotlin.String property=okext
IrExpressionBody
RETURN type=kotlin.String
LITERAL String type=kotlin.String value='OK'
IrFunction public fun kotlin.String.test5(): kotlin.String
IrExpressionBody
RETURN type=kotlin.String
CALL okext PROPERTY_GET
$receiver: $RECEIVER of: test5
+19
View File
@@ -0,0 +1,19 @@
val ok = "OK"
val ok2 = ok
val ok3: String get() = "OK"
fun test1() = ok
fun test2(x: String) = x
fun test3(): String {
val x = "OK"
return x
}
fun test4() = ok3
val String.okext: String get() = "OK"
fun String.test5() = okext
// IR_FILE_TXT references.txt
+42
View File
@@ -0,0 +1,42 @@
IrFile /references.kt
IrProperty public val ok: kotlin.String = "OK" getter=null setter=null
IrExpressionBody
RETURN type=kotlin.String
LITERAL String type=kotlin.String value='OK'
IrProperty public val ok2: kotlin.String = "OK" getter=null setter=null
IrExpressionBody
RETURN type=kotlin.String
CALL ok PROPERTY_GET
IrProperty public val ok3: kotlin.String getter=<get-ok3> setter=null
IrPropertyGetter public fun <get-ok3>(): kotlin.String property=ok3
IrExpressionBody
RETURN type=kotlin.String
LITERAL String type=kotlin.String value='OK'
IrFunction public fun test1(): kotlin.String
IrExpressionBody
RETURN type=kotlin.String
CALL ok PROPERTY_GET
IrFunction public fun test2(/*0*/ x: kotlin.String): kotlin.String
IrExpressionBody
RETURN type=kotlin.String
VAR x
IrFunction public fun test3(): kotlin.String
IrExpressionBody
BLOCK type=kotlin.Nothing
DUMMY KtProperty
RETURN type=kotlin.Nothing
VAR x
IrFunction public fun test4(): kotlin.String
IrExpressionBody
RETURN type=kotlin.String
CALL ok3 PROPERTY_GET
IrProperty public val kotlin.String.okext: kotlin.String getter=<get-okext> setter=null
IrPropertyGetter public fun kotlin.String.<get-okext>(): kotlin.String property=okext
IrExpressionBody
RETURN type=kotlin.String
LITERAL String type=kotlin.String value='OK'
IrFunction public fun kotlin.String.test5(): kotlin.String
IrExpressionBody
RETURN type=kotlin.String
CALL okext PROPERTY_GET
$receiver: $RECEIVER of: test5
+12 -12
View File
@@ -1,27 +1,27 @@
IrFile /smoke.kt
IrFunction public fun testFun(): kotlin.String
IrExpressionBody
IrBlockExpression type=kotlin.Nothing
IrReturnExpression type=kotlin.Nothing
IrLiteral String type=kotlin.String value='OK'
BLOCK type=kotlin.Nothing
RETURN type=kotlin.Nothing
LITERAL String type=kotlin.String value='OK'
IrProperty public val testSimpleVal: kotlin.Int = 1 getter=null setter=null
IrExpressionBody
IrReturnExpression type=kotlin.Int
IrLiteral Int type=kotlin.Int value='1'
RETURN type=kotlin.Int
LITERAL Int type=kotlin.Int value='1'
IrProperty public val testValWithGetter: kotlin.Int getter=<get-testValWithGetter> setter=null
IrPropertyGetter public fun <get-testValWithGetter>(): kotlin.Int property=testValWithGetter
IrExpressionBody
IrReturnExpression type=kotlin.Int
IrLiteral Int type=kotlin.Int value='42'
RETURN type=kotlin.Int
LITERAL Int type=kotlin.Int value='42'
IrProperty public var testSimpleVar: kotlin.Int getter=null setter=null
IrExpressionBody
IrReturnExpression type=kotlin.Int
IrLiteral Int type=kotlin.Int value='2'
RETURN type=kotlin.Int
LITERAL Int type=kotlin.Int value='2'
IrProperty public var testVarWithAccessors: kotlin.Int getter=<get-testVarWithAccessors> setter=<set-testVarWithAccessors>
IrPropertyGetter public fun <get-testVarWithAccessors>(): kotlin.Int property=testVarWithAccessors
IrExpressionBody
IrReturnExpression type=kotlin.Int
IrLiteral Int type=kotlin.Int value='42'
RETURN type=kotlin.Int
LITERAL Int type=kotlin.Int value='42'
IrPropertySetter public fun <set-testVarWithAccessors>(/*0*/ v: kotlin.Int): kotlin.Unit property=testVarWithAccessors
IrExpressionBody
IrBlockExpression type=kotlin.Unit
BLOCK type=kotlin.Unit
@@ -41,6 +41,24 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("calls.kt")
public void testCalls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/calls.kt");
doTest(fileName);
}
@TestMetadata("extensionPropertyGetterCall.kt")
public void testExtensionPropertyGetterCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/extensionPropertyGetterCall.kt");
doTest(fileName);
}
@TestMetadata("references.kt")
public void testReferences() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/references.kt");
doTest(fileName);
}
@TestMetadata("smoke.kt")
public void testSmoke() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smoke.kt");