Inline: set correct containing declaration + some fixes

1. Containing declaration can be computed as parent from the IR.

2. Sometimes we need to substitute FunctionDescriptor which is defined
inside an inline function call but nevertheless leaks to the outside:

public inline fun <T, R> T.myLet(f: (T) -> R): R = f(this)

interface foo {
    fun bar(): String
}

fun box(): String {
    val baz = "OK".myLet {
        object : foo {
            override fun bar() = it
        }
    }
    return baz.bar()
}
This commit is contained in:
Igor Chevdar
2017-04-06 18:55:54 +03:00
committed by KonstantinAnisimov
parent 3c2b5a9762
commit db50381bc9
4 changed files with 217 additions and 114 deletions
@@ -23,20 +23,20 @@ import org.jetbrains.kotlin.backend.konan.ir.IrInlineFunctionBody
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallableReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.util.DeepCopyIrTree
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
@@ -44,7 +44,7 @@ import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.makeNullable
internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typeArgsMap: Map <TypeParameterDescriptor, KotlinType>?, val context: Context) {
internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr, typeArgsMap: Map <TypeParameterDescriptor, KotlinType>?, val context: Context) {
private val descriptorSubstituteMap: MutableMap<DeclarationDescriptor, DeclarationDescriptor> = mutableMapOf()
private var typeSubstitutor = createTypeSubstitutor(typeArgsMap)
@@ -58,20 +58,64 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
inlinedFunctionName = functionName
descriptorSubstituteMap.clear()
irElement.acceptChildrenVoid(DescriptorCollector())
// Transform calls to object that might be returned from inline function call.
targetScope.irElement.transformChildrenVoid(descriptorSubstitutorForExternalScope)
return irElement.accept(InlineCopyIr(), null)
}
//-------------------------------------------------------------------------//
inner class DescriptorCollector: IrElementVisitorVoid {
val descriptorSubstitutorForExternalScope = object : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass) {
override fun visitCall(expression: IrCall): IrExpression {
val oldExpression = super.visitCall(expression) as IrCall
return when (oldExpression) {
is IrCallImpl -> copyIrCallImpl(oldExpression)
else -> oldExpression
}
}
}
private fun copyIrCallImpl(oldExpression: IrCallImpl): IrCallImpl {
val oldDescriptor = oldExpression.descriptor
val newDescriptor = descriptorSubstituteMap.getOrDefault(oldDescriptor.original,
oldDescriptor) as FunctionDescriptor
val oldSuperQualifier = oldExpression.superQualifier
val newSuperQualifier = oldSuperQualifier?.let { (descriptorSubstituteMap[it] ?: it) as ClassDescriptor }
val newExpression = IrCallImpl(
oldExpression.startOffset,
oldExpression.endOffset,
substituteType(oldExpression.type)!!,
newDescriptor,
substituteTypeArguments(oldExpression.typeArguments),
oldExpression.origin,
newSuperQualifier
).apply {
oldExpression.descriptor.valueParameters.forEach {
val valueArgument = oldExpression.getValueArgument(it)
putValueArgument(it.index, valueArgument)
}
extensionReceiver = oldExpression.extensionReceiver
dispatchReceiver = oldExpression.dispatchReceiver
}
return newExpression
}
//-------------------------------------------------------------------------//
inner class DescriptorCollector: IrElementVisitorVoidWithContext() {
override fun visitClassNew(declaration: IrClass) {
val oldDescriptor = declaration.descriptor
val newDescriptor = copyClassDescriptor(oldDescriptor)
descriptorSubstituteMap[oldDescriptor] = newDescriptor
descriptorSubstituteMap[oldDescriptor.thisAsReceiverParameter] = newDescriptor.thisAsReceiverParameter
super.visitClass(declaration)
super.visitClassNew(declaration)
val constructors = oldDescriptor.constructors.map { oldConstructorDescriptor ->
descriptorSubstituteMap[oldConstructorDescriptor] as ClassConstructorDescriptor
@@ -110,30 +154,30 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
}
}
override fun visitProperty(declaration: IrProperty) {
override fun visitPropertyNew(declaration: IrProperty) {
copyPropertyOrField(declaration.descriptor)
super.visitProperty(declaration)
super.visitPropertyNew(declaration)
}
override fun visitField(declaration: IrField) {
override fun visitFieldNew(declaration: IrField) {
val oldDescriptor = declaration.descriptor
if (descriptorSubstituteMap[oldDescriptor] == null) {
// A field without a property or a field of a delegated property.
copyPropertyOrField(oldDescriptor)
}
super.visitField(declaration)
super.visitFieldNew(declaration)
}
//---------------------------------------------------------------------//
override fun visitFunction(declaration: IrFunction) {
override fun visitFunctionNew(declaration: IrFunction) {
val oldDescriptor = declaration.descriptor
if (oldDescriptor !is PropertyAccessorDescriptor) { // Property accessors are copied along with their property.
val newDescriptor = copyFunctionDescriptor(oldDescriptor)
descriptorSubstituteMap[oldDescriptor] = newDescriptor
}
super.visitFunction(declaration)
super.visitFunctionNew(declaration)
}
//---------------------------------------------------------------------//
@@ -143,7 +187,8 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
val descriptor = expression.descriptor as FunctionDescriptor
if (descriptor.isFunctionInvoke) {
val oldDescriptor = descriptor as SimpleFunctionDescriptor
val containingDeclaration = targetFunction.descriptor
// Containing declaration for value parameter is not that important - other lowerings should not rely on it.
val containingDeclaration = (targetScope.scope.scopeOwner as? CallableDescriptor) ?: oldDescriptor
val newReturnType = substituteType(oldDescriptor.returnType)!!
val newValueParameters = copyValueParameters(oldDescriptor.valueParameters, containingDeclaration)
val newDescriptor = oldDescriptor.newCopyBuilder().apply {
@@ -163,7 +208,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
val oldDescriptor = declaration.descriptor
val newDescriptor = IrTemporaryVariableDescriptorImpl(
targetFunction.descriptor,
targetScope.scope.scopeOwner,
generateName(oldDescriptor.name),
substituteType(oldDescriptor.type)!!,
oldDescriptor.isVar)
@@ -181,7 +226,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
private fun generateName(name: Name): Name {
val containingName = targetFunction.descriptor.name.toString() // Name of inline target (function we inline in)
val containingName = targetScope.scope.scopeOwner.name.toString() // Name of inline target (function we inline in)
val declarationName = name.toString() // Name of declaration
val indexStr = (nameIndex++).toString() // Unique for inline target index
return Name.identifier(containingName + "_" + inlinedFunctionName + "_" + declarationName + "_" + indexStr)
@@ -202,8 +247,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
private fun copySimpleFunctionDescriptor(oldDescriptor: SimpleFunctionDescriptor) : FunctionDescriptor {
val oldContainingDeclaration = oldDescriptor.containingDeclaration // TODO should target function to be containing declaration
val newContainingDeclaration = descriptorSubstituteMap[oldContainingDeclaration] ?: targetFunction.descriptor
val newContainingDeclaration = parentScope?.let { descriptorSubstituteMap[it.scope.scopeOwner] } ?: targetScope.scope.scopeOwner
val newDescriptor = SimpleFunctionDescriptorImpl.create(
newContainingDeclaration,
@@ -239,10 +283,9 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
private fun copyConstructorDescriptor(oldDescriptor: ConstructorDescriptor) : FunctionDescriptor {
val oldContainingDeclaration = oldDescriptor.containingDeclaration
val containingDeclaration = descriptorSubstituteMap[oldContainingDeclaration]
val containingDeclaration = parentScope?.let { descriptorSubstituteMap[it.scope.scopeOwner] } as ClassDescriptor
val newDescriptor = ClassConstructorDescriptorImpl.create(
containingDeclaration as ClassDescriptor,
containingDeclaration,
oldDescriptor.annotations,
oldDescriptor.isPrimary,
oldDescriptor.source
@@ -270,8 +313,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
//---------------------------------------------------------------------//
private fun copyPropertyDescriptor(oldDescriptor: PropertyDescriptor): PropertyDescriptor {
val oldContainingDeclaration = oldDescriptor.containingDeclaration
val memberOwner = descriptorSubstituteMap[oldContainingDeclaration] as ClassDescriptor
val memberOwner = currentClass?.let { descriptorSubstituteMap[it.scope.scopeOwner] } as ClassDescriptor
val newDescriptor = PropertyDescriptorImpl.create(
memberOwner,
oldDescriptor.annotations,
@@ -349,9 +391,14 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
val oldSuperClass = oldDescriptor.getSuperClassOrAny()
val newSuperClass = descriptorSubstituteMap.getOrDefault(oldSuperClass, oldSuperClass) as ClassDescriptor
val newContainingDeclaration = parentScope?.let { descriptorSubstituteMap[it.scope.scopeOwner] } ?: targetScope.scope.scopeOwner
val newName = if (DescriptorUtils.isAnonymousObject(oldDescriptor)) // Anonymous objects are identified by their name.
oldDescriptor.name // We need to preserve it for LocalDeclarationsLowering.
else
generateName(oldDescriptor.name)
return ClassDescriptorImpl(
targetFunction.descriptor,
generateName(oldDescriptor.name),
newContainingDeclaration,
newName,
oldDescriptor.modality,
oldDescriptor.kind,
listOf(newSuperClass.defaultType),
@@ -401,35 +448,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetFunction: IrFunction, typ
val oldExpression = super.visitCall(expression) as IrCall
if (oldExpression !is IrCallImpl) return oldExpression // TODO what other kinds of call can we meet?
val oldDescriptor = oldExpression.descriptor
val newDescriptor = descriptorSubstituteMap.getOrDefault(oldDescriptor,
oldDescriptor) as FunctionDescriptor
val oldSuperQualifier = oldExpression.superQualifier
var newSuperQualifier: ClassDescriptor? = oldSuperQualifier
if (newSuperQualifier != null) {
newSuperQualifier = descriptorSubstituteMap.getOrDefault(newSuperQualifier,
newSuperQualifier) as ClassDescriptor
}
val newExpression = IrCallImpl(
oldExpression.startOffset,
oldExpression.endOffset,
substituteType(oldExpression.type)!!,
newDescriptor,
substituteTypeArguments(oldExpression.typeArguments),
oldExpression.origin,
newSuperQualifier
).apply {
oldExpression.descriptor.valueParameters.forEach {
val valueArgument = oldExpression.getValueArgument(it)
putValueArgument(it.index, valueArgument)
}
extensionReceiver = oldExpression.extensionReceiver
dispatchReceiver = oldExpression.dispatchReceiver
}
return newExpression
return copyIrCallImpl(oldExpression)
}
//---------------------------------------------------------------------//
@@ -16,49 +16,157 @@
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.descriptors.*
abstract class IrElementTransformerVoidWithContext(): IrElementTransformerVoid() {
private fun <E> MutableList<E>.push(element: E) = this.add(element)
private fun <E> MutableList<E>.push(element: E) = this.add(element)
private fun <E> MutableList<E>.pop() = this.removeAt(size - 1)
private fun <E> MutableList<E>.pop() = this.removeAt(size - 1)
private fun <E> MutableList<E>.peek(): E? = if (size == 0) null else this[size - 1]
private fun <E> MutableList<E>.peek(): E? = if (size == 0) null else this[size - 1]
private val functionsStack = mutableListOf<FunctionDescriptor>()
private val functionDeclarationsStack = mutableListOf<IrFunction>()
private val classesStack = mutableListOf<ClassDescriptor>()
internal class ScopeWithIr(val scope: Scope, val irElement: IrElement)
override final fun visitFunction(declaration: IrFunction): IrStatement {
functionsStack.push(declaration.descriptor)
functionDeclarationsStack.push(declaration)
val result = visitFunctionNew(declaration)
functionDeclarationsStack.pop()
functionsStack.pop()
abstract internal class IrElementTransformerVoidWithContext : IrElementTransformerVoid() {
private val scopeStack = mutableListOf<ScopeWithIr>()
override final fun visitFile(declaration: IrFile): IrFile {
scopeStack.push(ScopeWithIr(Scope(declaration.packageFragmentDescriptor), declaration))
val result = visitFileNew(declaration)
scopeStack.pop()
return result
}
override final fun visitClass(declaration: IrClass): IrStatement {
classesStack.push(declaration.descriptor)
scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
val result = visitClassNew(declaration)
classesStack.pop()
scopeStack.pop()
return result
}
protected val currentFunction get() = functionsStack.peek()
protected val currentFunctionDeclaration get() = functionDeclarationsStack.peek()
protected val currentClass get() = classesStack.peek()
override final fun visitProperty(declaration: IrProperty): IrStatement {
scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
val result = visitPropertyNew(declaration)
scopeStack.pop()
return result
}
open fun visitFunctionNew(declaration: IrFunction) : IrStatement {
override final fun visitField(declaration: IrField): IrStatement {
val isDelegated = declaration.descriptor.isDelegated
if (isDelegated) scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
val result = visitFieldNew(declaration)
if (isDelegated) scopeStack.pop()
return result
}
override final fun visitFunction(declaration: IrFunction): IrStatement {
scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
val result = visitFunctionNew(declaration)
scopeStack.pop()
return result
}
protected val currentFile get() = scopeStack.lastOrNull { it.scope.scopeOwner is PackageFragmentDescriptor }
protected val currentClass get() = scopeStack.lastOrNull { it.scope.scopeOwner is ClassDescriptor }
protected val currentFunction get() = scopeStack.lastOrNull { it.scope.scopeOwner is FunctionDescriptor }
protected val currentProperty get() = scopeStack.lastOrNull { it.scope.scopeOwner is PropertyDescriptor }
protected val currentScope get() = scopeStack.peek()
protected val parentScope get() = if (scopeStack.size < 2) null else scopeStack[scopeStack.size - 2]
fun printScopeStack() {
scopeStack.forEach { println(it.scope.scopeOwner) }
}
open fun visitFileNew(declaration: IrFile): IrFile {
return super.visitFile(declaration)
}
open fun visitClassNew(declaration: IrClass): IrStatement {
return super.visitClass(declaration)
}
open fun visitFunctionNew(declaration: IrFunction): IrStatement {
return super.visitFunction(declaration)
}
open fun visitClassNew(declaration: IrClass) : IrStatement {
return super.visitClass(declaration)
open fun visitPropertyNew(declaration: IrProperty): IrStatement {
return super.visitProperty(declaration)
}
open fun visitFieldNew(declaration: IrField): IrStatement {
return super.visitField(declaration)
}
}
abstract internal class IrElementVisitorVoidWithContext : IrElementVisitorVoid {
private val scopeStack = mutableListOf<ScopeWithIr>()
override final fun visitFile(declaration: IrFile) {
scopeStack.push(ScopeWithIr(Scope(declaration.packageFragmentDescriptor), declaration))
visitFileNew(declaration)
scopeStack.pop()
}
override final fun visitClass(declaration: IrClass) {
scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
visitClassNew(declaration)
scopeStack.pop()
}
override final fun visitProperty(declaration: IrProperty) {
scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
visitPropertyNew(declaration)
scopeStack.pop()
}
override final fun visitField(declaration: IrField) {
val isDelegated = declaration.descriptor.isDelegated
if (isDelegated) scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
visitFieldNew(declaration)
if (isDelegated) scopeStack.pop()
}
override final fun visitFunction(declaration: IrFunction) {
scopeStack.push(ScopeWithIr(Scope(declaration.descriptor), declaration))
visitFunctionNew(declaration)
scopeStack.pop()
}
protected val currentFile get() = scopeStack.lastOrNull { it.scope.scopeOwner is PackageFragmentDescriptor }
protected val currentClass get() = scopeStack.lastOrNull { it.scope.scopeOwner is ClassDescriptor }
protected val currentFunction get() = scopeStack.lastOrNull { it.scope.scopeOwner is FunctionDescriptor }
protected val currentProperty get() = scopeStack.lastOrNull { it.scope.scopeOwner is PropertyDescriptor }
protected val currentScope get() = scopeStack.peek()
protected val parentScope get() = if (scopeStack.size < 2) null else scopeStack[scopeStack.size - 2]
fun printScopeStack() {
scopeStack.forEach { println(it.scope.scopeOwner) }
}
open fun visitFileNew(declaration: IrFile) {
super.visitFile(declaration)
}
open fun visitClassNew(declaration: IrClass) {
super.visitClass(declaration)
}
open fun visitFunctionNew(declaration: IrFunction) {
super.visitFunction(declaration)
}
open fun visitPropertyNew(declaration: IrProperty) {
super.visitProperty(declaration)
}
open fun visitFieldNew(declaration: IrField) {
super.visitField(declaration)
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.DeepCopyIrTreeWithDescriptors
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.isFunctionInvoke
import org.jetbrains.kotlin.backend.konan.descriptors.needsInlining
@@ -43,11 +44,8 @@ import org.jetbrains.kotlin.types.KotlinType
//-----------------------------------------------------------------------------//
internal class FunctionInlining(val context: Context): IrElementTransformerVoid() {
internal class FunctionInlining(val context: Context): IrElementTransformerVoidWithContext() {
private var currentFile : IrFile? = null
private var currentFunction : IrFunction? = null
private var currentScope : Scope? = null
private var copyWithDescriptors: DeepCopyIrTreeWithDescriptors? = null
private val deserializer = DeserializerDriver(context)
@@ -61,28 +59,6 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
//-------------------------------------------------------------------------//
override fun visitFile(declaration: IrFile): IrFile {
currentFile = declaration
return super.visitFile(declaration)
}
//-------------------------------------------------------------------------//
override fun visitProperty(declaration: IrProperty): IrStatement {
currentScope = Scope(declaration.descriptor)
return super.visitProperty(declaration)
}
//-------------------------------------------------------------------------//
override fun visitFunction(declaration: IrFunction): IrStatement {
currentFunction = declaration
currentScope = Scope(declaration.descriptor)
return super.visitFunction(declaration)
}
//-------------------------------------------------------------------------//
override fun visitCall(expression: IrCall): IrExpression {
val irCall = super.visitCall(expression) as IrCall
val functionDescriptor = irCall.descriptor as FunctionDescriptor
@@ -94,12 +70,12 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
//-------------------------------------------------------------------------//
private fun createInlineFunctionBody(functionDeclaration: IrFunction, typeArgsMap: Map <TypeParameterDescriptor, KotlinType>?): IrInlineFunctionBody? {
functionDeclaration.transformChildrenVoid(this) // TODO recursive inline is already processed in visitCall. Check
functionDeclaration.transformChildrenVoid(this) // Process recursive inline.
val originBlockBody = functionDeclaration.body
if (originBlockBody == null) return null // TODO workaround
copyWithDescriptors = DeepCopyIrTreeWithDescriptors(currentFunction!!, typeArgsMap, context)
copyWithDescriptors = DeepCopyIrTreeWithDescriptors(currentScope!!, typeArgsMap, context)
val functionName = functionDeclaration.descriptor.name.toString()
val copyBlockBody = copyWithDescriptors!!.copy(originBlockBody, functionName) as IrBlockBody // Create copy of original function body.
@@ -321,8 +297,8 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
return@forEach
}
val varName = currentScope!!.scopeOwner.name.toString() + "_inline"
val newVar = currentScope!!.createTemporaryVariable(argument, varName, false) // Create new variable and init it with the parameter expression.
val varName = currentScope!!.scope.scopeOwner.name.toString() + "_inline"
val newVar = currentScope!!.scope.createTemporaryVariable(argument, varName, false) // Create new variable and init it with the parameter expression.
statements.add(newVar) // Add initialization of the new variable in statement list.
val getVal = IrGetValueImpl(0, 0, newVar.descriptor) // Create new IR element representing access the new variable.
@@ -106,7 +106,7 @@ internal class InnerClassLowering(val context: Context) : ClassLoweringPass {
if (implicitThisClass == classDescriptor) return expression
val constructorDescriptor = currentFunction!! as? ConstructorDescriptor
val constructorDescriptor = currentFunction!!.scope.scopeOwner as? ConstructorDescriptor
val startOffset = expression.startOffset
val endOffset = expression.endOffset