Move interpreter files to separate module

This commit is contained in:
Ivan Kylchik
2020-06-19 00:00:54 +03:00
parent 64aa0ec5c8
commit f028d6c898
25 changed files with 101 additions and 91 deletions
+1 -3
View File
@@ -8,10 +8,8 @@ dependencies {
compile(project(":compiler:frontend"))
compile(project(":compiler:backend-common"))
compile(project(":compiler:ir.tree"))
compile(project(":compiler:ir.interpreter"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { // primary used in ir interpreter
isTransitive = false
}
}
sourceSets {
@@ -1,268 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter
import org.jetbrains.kotlin.backend.common.interpreter.builtins.compileTimeAnnotation
import org.jetbrains.kotlin.backend.common.interpreter.builtins.contractsDslAnnotation
import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.FqName
enum class EvaluationMode {
FULL, ONLY_BUILTINS
}
class IrCompileTimeChecker(
containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.FULL
) : IrElementVisitor<Boolean, Nothing?> {
private val visitedStack = mutableListOf<IrElement>().apply { if (containingDeclaration != null) add(containingDeclaration) }
private val compileTimeTypeAliases = setOf(
"java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
)
private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation)
private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation)
private fun IrDeclaration.isMarkedAsCompileTime(): Boolean {
if (mode == EvaluationMode.FULL)
return isMarkedWith(compileTimeAnnotation) ||
(this is IrSimpleFunction && this.isFakeOverride && this.overriddenSymbols.any { it.owner.isMarkedAsCompileTime() }) ||
this.parentClassOrNull?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases
val parent = this.parentClassOrNull
val parentType = parent?.defaultType
return when {
parentType?.isPrimitiveType() == true -> (this as IrFunction).name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode")
parentType?.isString() == true -> (this as IrDeclarationWithName).name.asString() !in setOf("subSequence", "hashCode")
parent?.isCompanion == true -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
else -> false
}
}
private fun IrDeclaration.isMarkedWith(annotation: FqName): Boolean {
if (this is IrClass && this.isCompanion) return false
if (this.hasAnnotation(annotation)) return true
return (this.parent as? IrClass)?.isMarkedWith(annotation) ?: false
}
private fun IrProperty?.isCompileTime(): Boolean {
if (this == null) return false
if (this.isConst) return true
if (this.isMarkedAsCompileTime()) return true
val backingField = this.backingField
val backingFieldExpression = backingField?.initializer?.expression as? IrGetValue
return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
}
private fun IrElement.asVisited(block: () -> Boolean): Boolean {
visitedStack += this
val result = block()
visitedStack.removeAt(visitedStack.lastIndex)
return result
}
override fun visitElement(element: IrElement, data: Nothing?) = false
private fun visitStatements(statements: List<IrStatement>, data: Nothing?): Boolean {
if (mode == EvaluationMode.ONLY_BUILTINS) return false
return statements.all { it.accept(this, data) }
}
private fun visitConstructor(expression: IrFunctionAccessExpression): Boolean {
return when {
expression.symbol.owner.isMarkedAsEvaluateIntrinsic() -> true
!visitValueParameters(expression, null) -> false
else -> expression.symbol.owner.isMarkedAsCompileTime()
}
}
override fun visitCall(expression: IrCall, data: Nothing?): Boolean {
if (expression.symbol.owner.isContract()) return false
val property = (expression.symbol.owner as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
if (expression.symbol.owner.isMarkedAsCompileTime() || property.isCompileTime()) {
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
if (!visitValueParameters(expression, null)) return false
val bodyComputable = if (expression.symbol.owner.isLocal) expression.symbol.owner.body?.accept(this, null) ?: true else true
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
}
return false
}
override fun visitVariable(declaration: IrVariable, data: Nothing?): Boolean {
return declaration.initializer?.accept(this, data) ?: true
}
private fun visitValueParameters(expression: IrFunctionAccessExpression, data: Nothing?): Boolean {
return (0 until expression.valueArgumentsCount)
.map { expression.getValueArgument(it) }
.none { it?.accept(this, data) == false }
}
override fun visitBody(body: IrBody, data: Nothing?): Boolean {
return visitStatements(body.statements, data)
}
override fun visitBlock(expression: IrBlock, data: Nothing?): Boolean {
return visitStatements(expression.statements, data)
}
override fun visitSyntheticBody(body: IrSyntheticBody, data: Nothing?): Boolean {
return body.kind == IrSyntheticBodyKind.ENUM_VALUES || body.kind == IrSyntheticBodyKind.ENUM_VALUEOF
}
override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): Boolean = true
override fun visitVararg(expression: IrVararg, data: Nothing?): Boolean {
return expression.elements.any { it.accept(this, data) }
}
override fun visitSpreadElement(spread: IrSpreadElement, data: Nothing?): Boolean {
return spread.expression.accept(this, data)
}
override fun visitComposite(expression: IrComposite, data: Nothing?): Boolean {
if (expression.origin == IrStatementOrigin.DESTRUCTURING_DECLARATION) {
return visitStatements(expression.statements, data)
}
return false
}
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): Boolean {
return expression.arguments.all { it.accept(this, data) }
}
override fun visitGetObjectValue(expression: IrGetObjectValue, data: Nothing?): Boolean {
// to get object value we need nothing but it will contain only fields with compile time annotation
return true
}
override fun visitGetEnumValue(expression: IrGetEnumValue, data: Nothing?): Boolean {
return expression.symbol.owner.initializerExpression?.accept(this, data) == true
}
override fun visitGetValue(expression: IrGetValue, data: Nothing?): Boolean {
val parent = expression.symbol.owner.parent as IrSymbolOwner
val isObject = (parent as? IrClass)?.isObject == true //used to evaluate constants inside object
return visitedStack.contains(parent) || isObject
}
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?): Boolean {
return expression.value.accept(this, data)
}
override fun visitGetField(expression: IrGetField, data: Nothing?): Boolean {
val owner = expression.symbol.owner
val parent = owner.parent as IrSymbolOwner
val isJavaPrimitiveStatic = owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic &&
owner.parentAsClass.fqNameWhenAvailable.isJavaPrimitive()
return visitedStack.contains(parent) || isJavaPrimitiveStatic
}
// TODO find similar method in utils
private fun FqName?.isJavaPrimitive(): Boolean {
this ?: return false
return this.toString() in setOf(
"java.lang.Byte", "java.lang.Short", "java.lang.Integer", "java.lang.Long",
"java.lang.Float", "java.lang.Double", "java.lang.Boolean", "java.lang.Character"
)
}
override fun visitSetField(expression: IrSetField, data: Nothing?): Boolean {
//todo check receiver?
val parent = expression.symbol.owner.parent as IrSymbolOwner
return visitedStack.contains(parent) && expression.value.accept(this, data)
}
override fun visitConstructorCall(expression: IrConstructorCall, data: Nothing?): Boolean {
return visitConstructor(expression)
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall, data: Nothing?): Boolean {
return visitConstructor(expression)
}
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
return expression.asVisited {
expression.symbol.owner.isMarkedAsCompileTime() && expression.symbol.owner.body?.accept(this, data) == true
}
}
override fun visitFunctionExpression(expression: IrFunctionExpression, data: Nothing?): Boolean {
val isLambda = expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION
val isCompileTime = expression.function.isMarkedAsCompileTime()
return expression.function.asVisited {
if (isLambda || isCompileTime) expression.function.body?.accept(this, data) == true else false
}
}
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Nothing?): Boolean {
return when (expression.operator) {
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF,
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.SAFE_CAST,
IrTypeOperator.IMPLICIT_NOTNULL -> {
val operand = expression.typeOperand.classifierOrNull?.owner
if (operand is IrTypeParameter && !visitedStack.contains(operand.parent)) return false
expression.argument.accept(this, data)
}
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> false
else -> false
}
}
override fun visitWhen(expression: IrWhen, data: Nothing?): Boolean {
return expression.branches.all { it.accept(this, data) }
}
override fun visitBranch(branch: IrBranch, data: Nothing?): Boolean {
return branch.condition.accept(this, data) && branch.result.accept(this, data)
}
override fun visitWhileLoop(loop: IrWhileLoop, data: Nothing?): Boolean {
return loop.asVisited {
loop.condition.accept(this, data) && (loop.body?.accept(this, data) ?: true)
}
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): Boolean {
return loop.asVisited {
loop.condition.accept(this, data) && (loop.body?.accept(this, data) ?: true)
}
}
override fun visitTry(aTry: IrTry, data: Nothing?): Boolean {
if (mode == EvaluationMode.ONLY_BUILTINS) return false
if (!aTry.tryResult.accept(this, data)) return false
if (aTry.finallyExpression != null && aTry.finallyExpression?.accept(this, data) == false) return false
return aTry.catches.all { it.result.accept(this, data) }
}
override fun visitBreak(jump: IrBreak, data: Nothing?): Boolean = visitedStack.contains(jump.loop)
override fun visitContinue(jump: IrContinue, data: Nothing?): Boolean = visitedStack.contains(jump.loop)
override fun visitReturn(expression: IrReturn, data: Nothing?): Boolean {
if (!visitedStack.contains(expression.returnTargetSymbol.owner)) return false
return expression.value.accept(this, data)
}
override fun visitThrow(expression: IrThrow, data: Nothing?): Boolean {
return expression.value.accept(this, data)
}
}
@@ -1,791 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import org.jetbrains.kotlin.backend.common.interpreter.builtins.*
import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterException
import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterMethodNotFoundException
import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterTimeOutException
import org.jetbrains.kotlin.backend.common.interpreter.intrinsics.IntrinsicEvaluator
import org.jetbrains.kotlin.backend.common.interpreter.stack.StackImpl
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.backend.common.interpreter.state.*
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.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import java.lang.invoke.MethodHandle
private const val MAX_STACK_SIZE = 10_000
private const val MAX_COMMANDS = 500_000
class IrInterpreter(irModule: IrModuleFragment, private val bodyMap: Map<IdSignature, IrBody> = emptyMap()) {
private val irBuiltIns = irModule.irBuiltins
private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance<IrClass>()
.filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) }
private val stack = StackImpl()
private var commandCount = 0
private val mapOfEnums = mutableMapOf<IrSymbol, Complex>()
private val mapOfObjects = mutableMapOf<IrSymbol, Complex>()
private fun Any?.getType(defaultType: IrType): IrType {
return when (this) {
is Boolean -> irBuiltIns.booleanType
is Char -> irBuiltIns.charType
is Byte -> irBuiltIns.byteType
is Short -> irBuiltIns.shortType
is Int -> irBuiltIns.intType
is Long -> irBuiltIns.longType
is String -> irBuiltIns.stringType
is Float -> irBuiltIns.floatType
is Double -> irBuiltIns.doubleType
null -> irBuiltIns.nothingNType
else -> when (defaultType.classifierOrNull?.owner) {
is IrTypeParameter -> stack.getVariable(defaultType.classifierOrFail).state.irClass.defaultType
else -> defaultType
}
}
}
private fun incrementAndCheckCommands() {
commandCount++
if (commandCount >= MAX_COMMANDS) throw InterpreterTimeOutException()
}
fun interpret(expression: IrExpression): IrExpression {
stack.clean()
return try {
runBlocking {
return@runBlocking when (val returnLabel = withContext(this.coroutineContext) { expression.interpret().returnLabel }) {
ReturnLabel.REGULAR -> stack.popReturnValue().toIrExpression(expression)
ReturnLabel.EXCEPTION -> {
val message = (stack.popReturnValue() as ExceptionState).getFullDescription()
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message)
}
else -> TODO("$returnLabel not supported as result of interpretation")
}
}
} catch (e: InterpreterException) {
// TODO don't handle, throw to lowering
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + e.message)
}
}
private suspend fun IrElement.interpret(): ExecutionResult {
try {
incrementAndCheckCommands()
val executionResult = when (this) {
is IrFunctionImpl -> interpretFunction(this)
is IrLazyFunction -> interpretFunction(this as IrSimpleFunction)
is IrCall -> interpretCall(this)
is IrConstructorCall -> interpretConstructorCall(this)
is IrEnumConstructorCall -> interpretEnumConstructorCall(this)
is IrDelegatingConstructorCall -> interpretDelegatedConstructorCall(this)
is IrInstanceInitializerCall -> interpretInstanceInitializerCall(this)
is IrBody -> interpretBody(this)
is IrBlock -> interpretBlock(this)
is IrReturn -> interpretReturn(this)
is IrSetField -> interpretSetField(this)
is IrGetField -> interpretGetField(this)
is IrGetValue -> interpretGetValue(this)
is IrGetObjectValue -> interpretGetObjectValue(this)
is IrGetEnumValue -> interpretGetEnumValue(this)
is IrEnumEntry -> interpretEnumEntry(this)
is IrConst<*> -> interpretConst(this)
is IrVariable -> interpretVariable(this)
is IrSetVariable -> interpretSetVariable(this)
is IrTypeOperatorCall -> interpretTypeOperatorCall(this)
is IrBranch -> interpretBranch(this)
is IrWhileLoop -> interpretWhile(this)
is IrDoWhileLoop -> interpretDoWhile(this)
is IrWhen -> interpretWhen(this)
is IrBreak -> interpretBreak(this)
is IrContinue -> interpretContinue(this)
is IrVararg -> interpretVararg(this)
is IrSpreadElement -> interpretSpreadElement(this)
is IrTry -> interpretTry(this)
is IrCatch -> interpretCatch(this)
is IrThrow -> interpretThrow(this)
is IrStringConcatenation -> interpretStringConcatenation(this)
is IrFunctionExpression -> interpretFunctionExpression(this)
is IrFunctionReference -> interpretFunctionReference(this)
is IrComposite -> interpretComposite(this)
else -> TODO("${this.javaClass} not supported")
}
return executionResult.getNextLabel(this) { this@getNextLabel.interpret() }
} catch (e: InterpreterException) {
throw e
} catch (e: Throwable) {
// catch exception from JVM such as: ArithmeticException, StackOverflowError and others
val exceptionName = e::class.java.simpleName
val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == exceptionName } ?: irBuiltIns.throwableClass.owner
stack.pushReturnValue(ExceptionState(e, irExceptionClass, stack.getStackTrace()))
return Exception
}
}
// this method is used to get stack trace after exception
private suspend fun interpretFunction(irFunction: IrSimpleFunction): ExecutionResult {
yield()
if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction)
if (stack.getStackTrace().size == MAX_STACK_SIZE) throw StackOverflowError("")
if (irFunction.body is IrSyntheticBody) return handleIntrinsicMethods(irFunction)
return irFunction.body?.interpret() ?: throw InterpreterException("Ir function must be with body")
}
private suspend fun MethodHandle?.invokeMethod(irFunction: IrFunction): ExecutionResult {
this ?: return handleIntrinsicMethods(irFunction)
val result = this.invokeWithArguments(irFunction.getArgsForMethodInvocation(stack.getAll()))
stack.pushReturnValue(result.toState(result.getType(irFunction.returnType)))
return Next
}
private suspend fun handleIntrinsicMethods(irFunction: IrFunction): ExecutionResult {
return IntrinsicEvaluator().evaluate(irFunction, stack) { this.interpret() }
}
private suspend fun calculateBuiltIns(irFunction: IrFunction): ExecutionResult {
val methodName = when (val property = (irFunction as? IrSimpleFunction)?.correspondingPropertySymbol) {
null -> irFunction.name.asString()
else -> property.owner.name.asString()
}
val args = stack.getAll().map { it.state }
val receiverType = irFunction.dispatchReceiverParameter?.type
val argsType = listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }
val argsValues = args.map {
when (it) {
is Complex -> when (irFunction.fqNameWhenAvailable?.asString()) {
// must explicitly convert Common to String in String plus method or else will be taken default toString from Common
"kotlin.String.plus" -> stack.apply { interpretToString(it) }.popReturnValue().asString()
else -> it.getOriginal()
}
is Primitive<*> -> it.value
is Lambda -> it // lambda can be used in built in calculation, for example, in null check or toString
else -> TODO("unsupported type of argument for builtins calculations: ${it::class.java}")
}
}
fun IrType.getOnlyName(): String {
return when {
this.originalKotlinType != null -> this.originalKotlinType.toString()
this is IrSimpleType -> (this.classifierOrFail.owner as IrDeclarationWithName).name.asString() + (if (this.hasQuestionMark) "?" else "")
else -> this.render()
}
}
val signature = CompileTimeFunction(methodName, argsType.map { it.getOnlyName() })
// TODO replace unary, binary, ternary functions with vararg
val result = when (argsType.size) {
1 -> {
val function = unaryFunctions[signature]
?: throw InterpreterMethodNotFoundException("For given function $signature there is no entry in unary map")
function.invoke(argsValues.first())
}
2 -> {
val function = binaryFunctions[signature]
?: throw InterpreterMethodNotFoundException("For given function $signature there is no entry in binary map")
when (methodName) {
"rangeTo" -> return calculateRangeTo(irFunction.returnType)
else -> function.invoke(argsValues[0], argsValues[1])
}
}
3 -> {
val function = ternaryFunctions[signature]
?: throw InterpreterMethodNotFoundException("For given function $signature there is no entry in ternary map")
function.invoke(argsValues[0], argsValues[1], argsValues[2])
}
else -> throw InterpreterException("Unsupported number of arguments")
}
stack.pushReturnValue(result.toState(result.getType(irFunction.returnType)))
return Next
}
private suspend fun calculateRangeTo(type: IrType): ExecutionResult {
val constructor = type.classOrNull!!.owner.constructors.first()
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
val primitiveValueParameters = stack.getAll().map { it.state as Primitive<*> }
primitiveValueParameters.forEachIndexed { index, primitive ->
constructorCall.putValueArgument(index, primitive.value.toIrConst(primitive.type))
}
val constructorValueParameters = constructor.valueParameters.map { it.symbol }.zip(primitiveValueParameters)
return stack.newFrame(initPool = constructorValueParameters.map { Variable(it.first, it.second) }) {
constructorCall.interpret()
}
}
private suspend fun interpretValueParameters(
expression: IrFunctionAccessExpression, irFunction: IrFunction, pool: MutableList<Variable>
): ExecutionResult {
// if irFunction is lambda and it has receiver, then first descriptor must be taken from extension receiver
val receiverAsFirstArgument = when (expression.dispatchReceiver?.type?.isFunction()) {
true -> listOfNotNull(irFunction.getExtensionReceiver())
else -> listOf()
}
val valueParametersSymbols = receiverAsFirstArgument + irFunction.valueParameters.map { it.symbol }
val valueArguments = (0 until expression.valueArgumentsCount).map { expression.getValueArgument(it) }
val defaultValues = expression.symbol.owner.valueParameters.map { it.defaultValue?.expression }
return stack.newFrame(asSubFrame = true, initPool = pool) {
for (i in valueArguments.indices) {
(valueArguments[i] ?: defaultValues[i])?.interpret()?.check { return@newFrame it }
?: stack.pushReturnValue(listOf<Any?>().toPrimitiveStateArray(expression.getVarargType(i)!!)) // if vararg is empty
stack.peekReturnValue().checkNullability(valueParametersSymbols[i].owner.type) {
val method = irFunction.getCapitalizedFileName() + "." + irFunction.fqNameWhenAvailable
val parameter = valueParametersSymbols[i].owner.name
throw IllegalArgumentException("Parameter specified as non-null is null: method $method, parameter $parameter")
}
with(Variable(valueParametersSymbols[i], stack.popReturnValue())) {
stack.addVar(this) //must add value argument in current stack because it can be used later as default argument
pool.add(this)
}
}
Next
}
}
private suspend fun interpretCall(expression: IrCall): ExecutionResult {
val valueArguments = mutableListOf<Variable>()
// dispatch receiver processing
val rawDispatchReceiver = expression.dispatchReceiver
rawDispatchReceiver?.interpret()?.check { return it }
val dispatchReceiver = rawDispatchReceiver?.let { stack.popReturnValue() }?.checkNullability(expression.dispatchReceiver?.type)
// extension receiver processing
val rawExtensionReceiver = expression.extensionReceiver
rawExtensionReceiver?.interpret()?.check { return it }
val extensionReceiver = rawExtensionReceiver?.let { stack.popReturnValue() }?.checkNullability(expression.extensionReceiver?.type)
// get correct ir function
val irFunction = dispatchReceiver?.getIrFunctionByIrCall(expression) ?: expression.symbol.owner
val functionReceiver = dispatchReceiver.getCorrectReceiverByFunction(irFunction)
// it is important firstly to add receiver, then arguments; this order is used in builtin method call
irFunction.getDispatchReceiver()?.let { functionReceiver?.let { receiver -> valueArguments.add(Variable(it, receiver)) } }
irFunction.getExtensionReceiver()?.let { extensionReceiver?.let { receiver -> valueArguments.add(Variable(it, receiver)) } }
interpretValueParameters(expression, irFunction, valueArguments).check { return it }
valueArguments.addAll(getTypeArguments(irFunction, expression) { stack.getVariable(it).state })
if (dispatchReceiver is Complex) valueArguments.addAll(dispatchReceiver.typeArguments)
if (extensionReceiver is Complex) valueArguments.addAll(extensionReceiver.typeArguments)
val isLocal = (dispatchReceiver as? Complex)?.getOriginal()?.irClass?.isLocal ?: irFunction.isLocal
if (isLocal) valueArguments.addAll(dispatchReceiver.extractNonLocalDeclarations())
if (functionReceiver is Complex && irFunction.parentClassOrNull?.isInner == true) {
generateSequence(functionReceiver.outerClass) { (it.state as? Complex)?.outerClass }.forEach { valueArguments.add(it) }
}
return stack.newFrame(asSubFrame = irFunction.isInline || irFunction.isLocal, initPool = valueArguments) {
// inline only methods are not presented in lookup table, so must be interpreted instead of execution
val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly"))
return@newFrame when {
dispatchReceiver is Wrapper && !isInlineOnly -> dispatchReceiver.getMethod(irFunction).invokeMethod(irFunction)
irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction)
dispatchReceiver is Primitive<*> -> calculateBuiltIns(irFunction) // 'is Primitive' check for js char and js long
irFunction.body == null ->
irFunction.trySubstituteFunctionBody() ?: irFunction.tryCalculateLazyConst() ?: calculateBuiltIns(irFunction)
else -> irFunction.interpret()
}
}.check { return it }.implicitCastIfNeeded(expression.type, irFunction.returnType, stack)
}
private suspend fun IrFunction.trySubstituteFunctionBody(): ExecutionResult? {
if (!this.symbol.isPublicApi) return null
val body = bodyMap[this.symbol.signature]
return body?.let {
try {
this.body = it
this.interpret()
} finally {
this.body = null
}
}
}
// TODO fix in FIR2IR; const val getter must have body with IrGetField node
private suspend fun IrFunction.tryCalculateLazyConst(): ExecutionResult? {
if (this !is IrSimpleFunction) return null
return this.correspondingPropertySymbol?.owner?.backingField?.initializer?.interpret()
}
private suspend fun interpretInstanceInitializerCall(call: IrInstanceInitializerCall): ExecutionResult {
val irClass = call.classSymbol.owner
// properties processing
val classProperties = irClass.declarations.filterIsInstance<IrProperty>()
classProperties.forEach { property ->
property.backingField?.initializer?.expression?.interpret()?.check { return it }
val receiver = irClass.thisReceiver!!.symbol
if (property.backingField?.initializer != null) {
val receiverState = stack.getVariable(receiver).state
val propertyVar = Variable(property.symbol, stack.popReturnValue())
receiverState.setField(propertyVar)
}
}
// init blocks processing
val anonymousInitializer = irClass.declarations.filterIsInstance<IrAnonymousInitializer>().filter { !it.isStatic }
anonymousInitializer.forEach { init -> init.body.interpret().check { return it } }
return Next
}
private suspend fun interpretConstructor(constructorCall: IrFunctionAccessExpression): ExecutionResult {
val owner = constructorCall.symbol.owner
val valueArguments = mutableListOf<Variable>()
interpretValueParameters(constructorCall, owner, valueArguments).check { return it }
val irClass = owner.parent as IrClass
val typeArguments = getTypeArguments(irClass, constructorCall) { stack.getVariable(it).state }
if (irClass.hasAnnotation(evaluateIntrinsicAnnotation) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java"))) {
return stack.newFrame(initPool = valueArguments) { Wrapper.getConstructorMethod(owner).invokeMethod(owner) }
.apply { stack.peekReturnValue().addTypeArguments(typeArguments) }
}
if (irClass.defaultType.isArray() || irClass.defaultType.isPrimitiveArray()) {
// array constructor doesn't have body so must be treated separately
return stack.newFrame(initPool = valueArguments) { handleIntrinsicMethods(owner) }
.apply { stack.peekReturnValue().addTypeArguments(typeArguments) }
}
val state = Common(irClass).apply { this.addTypeArguments(typeArguments) }
if (irClass.isLocal) state.fields.addAll(stack.getAll()) // TODO save only necessary declarations
if (irClass.isInner) {
constructorCall.dispatchReceiver!!.interpret().check { return it }
state.outerClass = Variable(irClass.parentAsClass.thisReceiver!!.symbol, stack.popReturnValue())
}
valueArguments.add(Variable(irClass.thisReceiver!!.symbol, state)) //used to set up fields in body
return stack.newFrame(initPool = valueArguments + state.typeArguments) {
val statements = constructorCall.getBody()!!.statements
// enum entry use IrTypeOperatorCall with IMPLICIT_COERCION_TO_UNIT as delegation call, but we need the value
((statements[0] as? IrTypeOperatorCall)?.argument ?: statements[0]).interpret().check { return@newFrame it }
val returnedState = stack.popReturnValue() as Complex
for (i in 1 until statements.size) statements[i].interpret().check { return@newFrame it }
stack.pushReturnValue(state.apply { this.setSuperClassInstance(returnedState) })
Next
}
}
private suspend fun interpretConstructorCall(constructorCall: IrConstructorCall): ExecutionResult {
return interpretConstructor(constructorCall)
}
private suspend fun interpretEnumConstructorCall(enumConstructorCall: IrEnumConstructorCall): ExecutionResult {
return interpretConstructor(enumConstructorCall)
}
private suspend fun interpretDelegatedConstructorCall(delegatingConstructorCall: IrDelegatingConstructorCall): ExecutionResult {
if (delegatingConstructorCall.symbol.owner.parent == irBuiltIns.anyClass.owner) {
val anyAsStateObject = Common(irBuiltIns.anyClass.owner)
stack.pushReturnValue(anyAsStateObject)
return Next
}
return interpretConstructor(delegatingConstructorCall)
}
private suspend fun interpretConst(expression: IrConst<*>): ExecutionResult {
fun getSignedType(unsignedType: IrType): IrType {
return when {
unsignedType.isUByte() -> irBuiltIns.byteType
unsignedType.isUShort() -> irBuiltIns.shortType
unsignedType.isUInt() -> irBuiltIns.intType
unsignedType.isULong() -> irBuiltIns.longType
else -> throw InterpreterException("Unsupported unsigned class ${unsignedType.render()}")
}
}
return if (expression.type.isUnsigned()) {
val unsignedClass = expression.type.classOrNull!!
val constructor = unsignedClass.constructors.single().owner
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
constructorCall.putValueArgument(0, expression.value.toIrConst(getSignedType(expression.type)))
constructorCall.interpret()
} else {
stack.pushReturnValue(expression.toPrimitive())
Next
}
}
private suspend fun interpretStatements(statements: List<IrStatement>): ExecutionResult {
var executionResult: ExecutionResult = Next
for (statement in statements) {
when (statement) {
is IrClass -> if (statement.isLocal) Next else TODO("Only local classes are supported")
is IrFunction -> if (statement.isLocal) Next else TODO("Only local functions are supported")
else -> executionResult = statement.interpret().check { return it }
}
}
return executionResult
}
private suspend fun interpretBlock(block: IrBlock): ExecutionResult {
return stack.newFrame(asSubFrame = true) { interpretStatements(block.statements) }
}
private suspend fun interpretBody(body: IrBody): ExecutionResult {
return stack.newFrame(asSubFrame = true) { interpretStatements(body.statements) }
}
private suspend fun interpretReturn(expression: IrReturn): ExecutionResult {
expression.value.interpret().check { return it }
return Return.addOwnerInfo(expression.returnTargetSymbol.owner)
}
private suspend fun interpretWhile(expression: IrWhileLoop): ExecutionResult {
while (true) {
expression.condition.interpret().check { return it }
if (stack.popReturnValue().asBooleanOrNull() != true) break
expression.body?.interpret()?.check { return it }
}
return Next
}
private suspend fun interpretDoWhile(expression: IrDoWhileLoop): ExecutionResult {
do {
// pool from body must be seen to condition expression, so must create temp frame here
stack.newFrame(asSubFrame = true) {
expression.body?.interpret()?.check { return@newFrame it }
expression.condition.interpret().check { return@newFrame it }
Next
}.check { return it }
if (stack.popReturnValue().asBooleanOrNull() != true) break
} while (true)
return Next
}
private suspend fun interpretWhen(expression: IrWhen): ExecutionResult {
var executionResult: ExecutionResult = Next
for (branch in expression.branches) {
executionResult = branch.interpret().check { return it }
}
return executionResult
}
private suspend fun interpretBranch(expression: IrBranch): ExecutionResult {
val executionResult = expression.condition.interpret().check { return it }
if (stack.popReturnValue().asBooleanOrNull() == true) {
expression.result.interpret().check { return it }
return BreakWhen
}
return executionResult
}
private fun interpretBreak(breakStatement: IrBreak): ExecutionResult {
return BreakLoop.addOwnerInfo(breakStatement.loop)
}
private fun interpretContinue(continueStatement: IrContinue): ExecutionResult {
return Continue.addOwnerInfo(continueStatement.loop)
}
private suspend fun interpretSetField(expression: IrSetField): ExecutionResult {
expression.value.interpret().check { return it }
// receiver is null only for top level var, but it cannot be used in constexpr; corresponding check is on frontend
val receiver = (expression.receiver as IrDeclarationReference).symbol
val propertySymbol = expression.symbol.owner.correspondingPropertySymbol!!
stack.getVariable(receiver).apply { this.state.setField(Variable(propertySymbol, stack.popReturnValue())) }
return Next
}
private suspend fun interpretGetField(expression: IrGetField): ExecutionResult {
val receiver = (expression.receiver as? IrDeclarationReference)?.symbol
val field = expression.symbol.owner
// for java static variables
if (field.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && field.isStatic) {
stack.pushReturnValue(Wrapper.getStaticGetter(field)!!.invokeWithArguments().toState(field.type))
return Next
}
// receiver is null, for example, for top level fields
val result = receiver?.let { stack.getVariable(receiver).state.getState(field.correspondingPropertySymbol!!) }
?: return (expression.symbol.owner.initializer?.expression?.interpret() ?: Next)
stack.pushReturnValue(result)
return Next
}
private fun interpretGetValue(expression: IrGetValue): ExecutionResult {
val owner = expression.type.classOrNull?.owner
// used to evaluate constants inside object
if (owner != null && owner.isObject) return getOrCreateObjectValue(owner) // TODO is this correct behaviour?
stack.pushReturnValue(stack.getVariable(expression.symbol).state)
return Next
}
private suspend fun interpretVariable(expression: IrVariable): ExecutionResult {
expression.initializer?.interpret()?.check { return it } ?: return Next
stack.addVar(Variable(expression.symbol, stack.popReturnValue()))
return Next
}
private suspend fun interpretSetVariable(expression: IrSetVariable): ExecutionResult {
expression.value.interpret().check { return it }
if (stack.contains(expression.symbol)) {
stack.getVariable(expression.symbol).apply { this.state = stack.popReturnValue() }
} else {
stack.addVar(Variable(expression.symbol, stack.popReturnValue()))
}
return Next
}
private fun interpretGetObjectValue(expression: IrGetObjectValue): ExecutionResult {
return getOrCreateObjectValue(expression.symbol.owner)
}
private fun getOrCreateObjectValue(objectClass: IrClass): ExecutionResult {
mapOfObjects[objectClass.symbol]?.let { return Next.apply { stack.pushReturnValue(it) } }
val objectState = when {
objectClass.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getCompanionObject(objectClass)
else -> Common(objectClass).apply { setSuperClassRecursive() } // TODO test type arguments
}
mapOfObjects[objectClass.symbol] = objectState
stack.pushReturnValue(objectState)
return Next
}
private suspend fun interpretGetEnumValue(expression: IrGetEnumValue): ExecutionResult {
mapOfEnums[expression.symbol]?.let { return Next.apply { stack.pushReturnValue(it) } }
val enumEntry = expression.symbol.owner
val enumClass = enumEntry.symbol.owner.parentAsClass
val valueOfFun = enumClass.declarations.single { it.nameForIrSerialization.asString() == "valueOf" } as IrFunction
enumClass.declarations.filterIsInstance<IrEnumEntry>().forEach {
val executionResult = when {
enumClass.hasAnnotation(evaluateIntrinsicAnnotation) -> {
val enumEntryName = it.name.asString().toState(irBuiltIns.stringType)
val enumNameAsVariable = Variable(valueOfFun.valueParameters.first().symbol, enumEntryName)
stack.newFrame(initPool = listOf(enumNameAsVariable)) { Wrapper.getEnumEntry(enumClass)!!.invokeMethod(valueOfFun) }
}
else -> interpretEnumEntry(it)
}
executionResult.check { result -> return result }
mapOfEnums[it.symbol] = stack.popReturnValue() as Complex
}
stack.pushReturnValue(mapOfEnums[expression.symbol]!!)
return Next
}
private suspend fun interpretEnumEntry(enumEntry: IrEnumEntry): ExecutionResult {
val enumClass = enumEntry.symbol.owner.parentAsClass
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
val enumSuperCall = (enumClass.primaryConstructor?.body?.statements?.firstOrNull() as? IrEnumConstructorCall)
if (enumEntries.isNotEmpty() && enumSuperCall != null) {
val valueArguments = listOf(
enumEntry.name.asString().toIrConst(irBuiltIns.stringType), enumEntries.indexOf(enumEntry).toIrConst(irBuiltIns.intType)
)
valueArguments.forEachIndexed { index, irConst -> enumSuperCall.putValueArgument(index, irConst) }
}
val executionResult = enumEntry.initializerExpression?.interpret()?.check { return it }
enumSuperCall?.apply { (0 until this.valueArgumentsCount).forEach { putValueArgument(it, null) } } // restore to null
return executionResult ?: throw InterpreterException("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
}
private suspend fun interpretTypeOperatorCall(expression: IrTypeOperatorCall): ExecutionResult {
val executionResult = expression.argument.interpret().check { return it }
val typeClassifier = expression.typeOperand.classifierOrFail
val isReified = (typeClassifier.owner as? IrTypeParameter)?.isReified == true
val isErased = typeClassifier.owner is IrTypeParameter && !isReified
val typeOperand = if (isReified) stack.getVariable(typeClassifier).state.irClass.defaultType else expression.typeOperand
when (expression.operator) {
// coercion to unit means that return value isn't used
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue()
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
if (!isErased && !stack.peekReturnValue().isSubtypeOf(typeOperand)) {
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
throw ClassCastException("$convertibleClassName cannot be cast to ${typeOperand.render()}")
}
}
IrTypeOperator.SAFE_CAST -> {
if (!isErased && !stack.peekReturnValue().isSubtypeOf(typeOperand)) {
stack.popReturnValue()
stack.pushReturnValue(null.toState(irBuiltIns.nothingNType))
}
}
IrTypeOperator.INSTANCEOF -> {
val isInstance = isErased || stack.peekReturnValue().isSubtypeOf(typeOperand)
stack.pushReturnValue(isInstance.toState(irBuiltIns.nothingType))
}
IrTypeOperator.NOT_INSTANCEOF -> {
val isInstance = isErased || stack.peekReturnValue().isSubtypeOf(typeOperand)
stack.pushReturnValue((!isInstance).toState(irBuiltIns.nothingType))
}
IrTypeOperator.IMPLICIT_NOTNULL -> {
}
else -> TODO("${expression.operator} not implemented")
}
return executionResult
}
private suspend fun interpretVararg(expression: IrVararg): ExecutionResult {
val args = expression.elements.flatMap {
it.interpret().check { executionResult -> return executionResult }
return@flatMap when (val result = stack.popReturnValue()) {
is Wrapper -> listOf(result.value)
is Primitive<*> ->
when (val value = result.value) {
is ByteArray -> value.toList()
is CharArray -> value.toList()
is ShortArray -> value.toList()
is IntArray -> value.toList()
is LongArray -> value.toList()
is FloatArray -> value.toList()
is DoubleArray -> value.toList()
is BooleanArray -> value.toList()
is Array<*> -> value.toList()
else -> listOf(value)
}
else -> listOf(result)
}
}
val array = when ((expression.type.classifierOrFail.owner as? IrDeclaration)?.nameForIrSerialization?.asString()) {
"UByteArray", "UShortArray", "UIntArray", "ULongArray" -> {
val owner = expression.type.classOrNull!!.owner
val storageProperty = owner.declarations.filterIsInstance<IrProperty>().first { it.name.asString() == "storage" }
val primitiveArray = args.map { ((it as Common).fields.single().state as Primitive<*>).value }
val unsignedArray = primitiveArray.toPrimitiveStateArray(storageProperty.backingField!!.type)
Common(owner).apply {
setSuperClassRecursive()
fields.add(Variable(storageProperty.symbol, unsignedArray))
}
}
else -> args.toPrimitiveStateArray(expression.type)
}
stack.pushReturnValue(array)
return Next
}
private suspend fun interpretSpreadElement(spreadElement: IrSpreadElement): ExecutionResult {
return spreadElement.expression.interpret().check { return it }
}
private suspend fun interpretTry(expression: IrTry): ExecutionResult {
try {
expression.tryResult.interpret().check(ReturnLabel.EXCEPTION) { return it } // if not exception -> return
val exception = stack.peekReturnValue() as ExceptionState
for (catchBlock in expression.catches) {
if (exception.isSubtypeOf(catchBlock.catchParameter.type.classOrNull!!.owner)) {
catchBlock.interpret().check { return it }
break
}
}
} finally {
expression.finallyExpression?.interpret()?.check { return it }
}
return Next
}
private suspend fun interpretCatch(expression: IrCatch): ExecutionResult {
val catchParameter = Variable(expression.catchParameter.symbol, stack.popReturnValue())
return stack.newFrame(asSubFrame = true, initPool = listOf(catchParameter)) {
expression.result.interpret()
}
}
private suspend fun interpretThrow(expression: IrThrow): ExecutionResult {
expression.value.interpret().check { return it }
when (val exception = stack.popReturnValue()) {
is Common -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
is Wrapper -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
is ExceptionState -> stack.pushReturnValue(exception)
else -> throw InterpreterException("${exception::class} cannot be used as exception state")
}
return Exception
}
private suspend fun interpretStringConcatenation(expression: IrStringConcatenation): ExecutionResult {
val result = StringBuilder()
expression.arguments.forEach {
it.interpret().check { executionResult -> return executionResult }
interpretToString(stack.popReturnValue()).check { executionResult -> return executionResult }
result.append(stack.popReturnValue().asString())
}
stack.pushReturnValue(result.toString().toState(expression.type))
return Next
}
private suspend fun interpretToString(state: State): ExecutionResult {
val result = when (state) {
is Primitive<*> -> state.value.toString()
is Wrapper -> state.value.toString()
is Common -> {
val toStringFun = state.getToStringFunction()
return stack.newFrame(initPool = mutableListOf(Variable(toStringFun.getReceiver()!!, state))) {
toStringFun.body?.let { toStringFun.interpret() } ?: calculateBuiltIns(toStringFun)
}
}
is Lambda -> state.toString()
else -> throw InterpreterException("${state::class.java} cannot be used in StringConcatenation expression")
}
stack.pushReturnValue(result.toState(irBuiltIns.stringType))
return Next
}
private fun interpretFunctionExpression(expression: IrFunctionExpression): ExecutionResult {
val lambda = Lambda(expression.function, expression.type.classOrNull!!.owner)
if (expression.function.isLocal) lambda.fields.addAll(stack.getAll()) // TODO save only necessary declarations
stack.pushReturnValue(lambda)
return Next
}
private fun interpretFunctionReference(reference: IrFunctionReference): ExecutionResult {
stack.pushReturnValue(Lambda(reference.symbol.owner, reference.type.classOrNull!!.owner))
return Next
}
private suspend fun interpretComposite(expression: IrComposite): ExecutionResult {
return when (expression.origin) {
IrStatementOrigin.DESTRUCTURING_DECLARATION -> interpretStatements(expression.statements)
null -> interpretStatements(expression.statements) // is null for body of do while loop
else -> TODO("${expression.origin} not implemented")
}
}
}
@@ -1,86 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter
import org.jetbrains.kotlin.backend.common.interpreter.stack.Stack
import org.jetbrains.kotlin.backend.common.interpreter.state.Primitive
import org.jetbrains.kotlin.backend.common.interpreter.state.isSubtypeOf
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.ir.expressions.IrWhileLoop
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.render
enum class ReturnLabel {
REGULAR, RETURN, BREAK_LOOP, BREAK_WHEN, CONTINUE, EXCEPTION
}
open class ExecutionResult(val returnLabel: ReturnLabel, private val owner: IrElement? = null) {
suspend fun getNextLabel(irElement: IrElement, interpret: suspend IrElement.() -> ExecutionResult): ExecutionResult {
return when (returnLabel) {
ReturnLabel.RETURN -> when (irElement) {
is IrCall, is IrReturnableBlock, is IrFunctionImpl, is IrLazyFunction -> if (owner == irElement) Next else this
else -> this
}
ReturnLabel.BREAK_WHEN -> when (irElement) {
is IrWhen -> Next
else -> this
}
ReturnLabel.BREAK_LOOP -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) Next else this
else -> this
}
ReturnLabel.CONTINUE -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) irElement.interpret() else this
else -> this
}
ReturnLabel.EXCEPTION -> Exception
ReturnLabel.REGULAR -> Next
}
}
fun addOwnerInfo(owner: IrElement): ExecutionResult {
return ExecutionResult(returnLabel, owner)
}
}
inline fun ExecutionResult.check(toCheckLabel: ReturnLabel = ReturnLabel.REGULAR, returnBlock: (ExecutionResult) -> Unit): ExecutionResult {
if (this.returnLabel != toCheckLabel) returnBlock(this)
return this
}
/**
* This method is analog of `checkcast` jvm bytecode operation. Throw exception whenever actual type is not a subtype of expected.
*/
internal fun ExecutionResult.implicitCastIfNeeded(expectedType: IrType, actualType: IrType, stack: Stack): ExecutionResult {
if (actualType.classifierOrNull !is IrTypeParameterSymbol) return this
if (expectedType.classifierOrFail is IrTypeParameterSymbol) return this
val actualState = stack.peekReturnValue()
if (actualState is Primitive<*> && actualState.value == null) return this // this is handled as NullPointerException
if (!actualState.isSubtypeOf(expectedType)) {
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
throw ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}")
}
return this
}
object Next : ExecutionResult(ReturnLabel.REGULAR)
object Return : ExecutionResult(ReturnLabel.RETURN)
object BreakLoop : ExecutionResult(ReturnLabel.BREAK_LOOP)
object BreakWhen : ExecutionResult(ReturnLabel.BREAK_WHEN)
object Continue : ExecutionResult(ReturnLabel.CONTINUE)
object Exception : ExecutionResult(ReturnLabel.EXCEPTION)
@@ -1,207 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter
import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.backend.common.interpreter.state.*
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal fun IrFunction.getDispatchReceiver(): IrValueParameterSymbol? = this.dispatchReceiverParameter?.symbol
internal fun IrFunction.getExtensionReceiver(): IrValueParameterSymbol? = this.extensionReceiverParameter?.symbol
internal fun IrFunction.getReceiver(): IrSymbol? = this.getDispatchReceiver() ?: this.getExtensionReceiver()
internal fun IrFunctionAccessExpression.getBody(): IrBody? = this.symbol.owner.body
internal fun State.toIrExpression(expression: IrExpression): IrExpression {
val start = expression.startOffset
val end = expression.endOffset
val type = expression.type.makeNotNull()
return when (this) {
is Primitive<*> ->
when {
this.value == null -> this.value.toIrConst(type, start, end)
type.isPrimitiveType() || type.isString() -> this.value.toIrConst(type, start, end)
else -> expression // TODO support for arrays
}
is Complex -> {
val stateType = this.irClass.defaultType
when {
stateType.isUnsigned() -> (this.fields.single().state as Primitive<*>).value.toIrConst(type, start, end)
else -> expression
}
}
else -> expression // TODO support
}
}
internal fun Any?.toState(irType: IrType): State {
return when (this) {
is State -> this
is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double, is Array<*>, is ByteArray,
is CharArray, is ShortArray, is IntArray, is LongArray, is FloatArray, is DoubleArray, is BooleanArray -> Primitive(this, irType)
null -> Primitive(this, irType)
else -> Wrapper(this, irType.classOrNull!!.owner)
}
}
fun Any?.toIrConst(irType: IrType, startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET): IrConst<*> {
val constType = irType.makeNotNull()
return when {
this == null -> IrConstImpl.constNull(startOffset, endOffset, irType)
constType.isBoolean() -> IrConstImpl.boolean(startOffset, endOffset, constType, this as Boolean)
constType.isChar() -> IrConstImpl.char(startOffset, endOffset, constType, this as Char)
constType.isByte() -> IrConstImpl.byte(startOffset, endOffset, constType, (this as Number).toByte())
constType.isShort() -> IrConstImpl.short(startOffset, endOffset, constType, (this as Number).toShort())
constType.isInt() -> IrConstImpl.int(startOffset, endOffset, constType, (this as Number).toInt())
constType.isLong() -> IrConstImpl.long(startOffset, endOffset, constType, (this as Number).toLong())
constType.isString() -> IrConstImpl.string(startOffset, endOffset, constType, this as String)
constType.isFloat() -> IrConstImpl.float(startOffset, endOffset, constType, (this as Number).toFloat())
constType.isDouble() -> IrConstImpl.double(startOffset, endOffset, constType, (this as Number).toDouble())
constType.isUByte() -> IrConstImpl.byte(startOffset, endOffset, constType, (this as Number).toByte())
constType.isUShort() -> IrConstImpl.short(startOffset, endOffset, constType, (this as Number).toShort())
constType.isUInt() -> IrConstImpl.int(startOffset, endOffset, constType, (this as Number).toInt())
constType.isULong() -> IrConstImpl.long(startOffset, endOffset, constType, (this as Number).toLong())
else -> throw UnsupportedOperationException("Unsupported const element type ${constType.render()}")
}
}
internal fun <T> IrConst<T>.toPrimitive(): Primitive<T> {
return Primitive(this.value, this.type)
}
fun IrAnnotationContainer?.hasAnnotation(annotation: FqName): Boolean {
this ?: return false
if (this.annotations.isNotEmpty()) {
return this.annotations.any { it.symbol.owner.parentAsClass.fqNameWhenAvailable == annotation }
}
return false
}
fun IrAnnotationContainer.getAnnotation(annotation: FqName): IrConstructorCall {
return this.annotations.firstOrNull { it.symbol.owner.parentAsClass.fqNameWhenAvailable == annotation }
?: ((this as IrFunction).parent as IrClass).annotations.first { it.symbol.owner.parentAsClass.fqNameWhenAvailable == annotation }
}
internal fun IrAnnotationContainer.getEvaluateIntrinsicValue(): String? {
if (this is IrClass && this.fqNameWhenAvailable?.startsWith(Name.identifier("java")) == true) return this.fqNameWhenAvailable?.asString()
if (!this.hasAnnotation(evaluateIntrinsicAnnotation)) return null
return (this.getAnnotation(evaluateIntrinsicAnnotation).getValueArgument(0) as IrConst<*>).value.toString()
}
internal fun getPrimitiveClass(irType: IrType, asObject: Boolean = false): Class<*>? {
return when {
irType.isBoolean() -> if (asObject) Boolean::class.javaObjectType else Boolean::class.java
irType.isChar() -> if (asObject) Char::class.javaObjectType else Char::class.java
irType.isByte() -> if (asObject) Byte::class.javaObjectType else Byte::class.java
irType.isShort() -> if (asObject) Short::class.javaObjectType else Short::class.java
irType.isInt() -> if (asObject) Int::class.javaObjectType else Int::class.java
irType.isLong() -> if (asObject) Long::class.javaObjectType else Long::class.java
irType.isString() -> if (asObject) String::class.javaObjectType else String::class.java
irType.isFloat() -> if (asObject) Float::class.javaObjectType else Float::class.java
irType.isDouble() -> if (asObject) Double::class.javaObjectType else Double::class.java
else -> null
}
}
internal fun IrFunction.getArgsForMethodInvocation(args: List<Variable>): List<Any?> {
val argsValues = args.map {
when (val state = it.state) {
is ExceptionState -> state.getThisAsCauseForException()
is Wrapper -> state.value
is Primitive<*> -> state.value
else -> throw AssertionError("${state::class} is unsupported as argument for wrapper method invocation")
}
}.toMutableList()
// TODO if vararg isn't last parameter
// must convert vararg array into separated elements for correct invoke
if (this.valueParameters.lastOrNull()?.varargElementType != null) {
val varargValue = argsValues.last()
argsValues.removeAt(argsValues.size - 1)
argsValues.addAll(varargValue as Array<out Any?>)
}
return argsValues
}
fun IrFunction.getLastOverridden(): IrFunction {
if (this !is IrSimpleFunction) return this
return generateSequence(listOf(this)) { it.firstOrNull()?.overriddenSymbols?.map { it.owner } }.flatten().last()
}
internal fun List<Any?>.toPrimitiveStateArray(type: IrType): Primitive<*> {
return when {
type.isByteArray() -> Primitive(ByteArray(size) { i -> (this[i] as Number).toByte() }, type)
type.isCharArray() -> Primitive(CharArray(size) { i -> this[i] as Char }, type)
type.isShortArray() -> Primitive(ShortArray(size) { i -> (this[i] as Number).toShort() }, type)
type.isIntArray() -> Primitive(IntArray(size) { i -> (this[i] as Number).toInt() }, type)
type.isLongArray() -> Primitive(LongArray(size) { i -> (this[i] as Number).toLong() }, type)
type.isFloatArray() -> Primitive(FloatArray(size) { i -> (this[i] as Number).toFloat() }, type)
type.isDoubleArray() -> Primitive(DoubleArray(size) { i -> (this[i] as Number).toDouble() }, type)
type.isBooleanArray() -> Primitive(BooleanArray(size) { i -> this[i].toString().toBoolean() }, type)
else -> Primitive<Array<*>>(this.toTypedArray(), type)
}
}
fun IrFunctionAccessExpression.getVarargType(index: Int): IrType? {
val varargType = this.symbol.owner.valueParameters[index].varargElementType ?: return null
varargType.classOrNull?.let { return this.symbol.owner.valueParameters[index].type }
val typeParameter = varargType.classifierOrFail.owner as IrTypeParameter
return this.getTypeArgument(typeParameter.index)
}
internal fun getTypeArguments(
container: IrTypeParametersContainer, expression: IrFunctionAccessExpression, mapper: (IrTypeParameterSymbol) -> State
): List<Variable> {
fun IrType.getState(): State {
return this.classOrNull?.owner?.let { Common(it) } ?: mapper(this.classifierOrFail as IrTypeParameterSymbol)
}
val typeArguments = container.typeParameters.mapIndexed { index, typeParameter ->
val typeArgument = expression.getTypeArgument(index)!!
Variable(typeParameter.symbol, typeArgument.getState())
}.toMutableList()
if (container is IrSimpleFunction) {
container.returnType.classifierOrFail.owner.safeAs<IrTypeParameter>()
?.let { typeArguments.add(Variable(it.symbol, expression.type.getState())) }
}
return typeArguments
}
internal fun State?.extractNonLocalDeclarations(): List<Variable> {
this ?: return listOf()
val state = this.takeIf { it !is Complex } ?: (this as Complex).getOriginal()
return state.fields.filter { it.symbol !is IrFieldSymbol }
}
internal fun State?.getCorrectReceiverByFunction(irFunction: IrFunction): State? {
if (this !is Complex) return this
val original: Complex? = this.getOriginal()
val other = irFunction.parentClassOrNull?.thisReceiver ?: return this
return generateSequence(original) { it.superClass }.firstOrNull { it.irClass.thisReceiver == other } ?: this
}
internal fun IrFunction.getCapitalizedFileName() = this.file.name.replace(".kt", "Kt").capitalize()
@@ -1,37 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.builtins
import org.jetbrains.kotlin.name.FqName
val compileTimeAnnotation = FqName("kotlin.CompileTimeCalculation")
val evaluateIntrinsicAnnotation = FqName("kotlin.EvaluateIntrinsic")
val contractsDslAnnotation = FqName("kotlin.internal.ContractsDsl")
data class CompileTimeFunction(val methodName: String, val args: List<String>)
@Suppress("UNCHECKED_CAST")
fun <T> unaryOperation(
methodName: String, receiverType: String, function: (T) -> Any?
): Pair<CompileTimeFunction, Function1<Any?, Any?>> {
return CompileTimeFunction(methodName, listOf(receiverType)) to function as Function1<Any?, Any?>
}
@Suppress("UNCHECKED_CAST")
fun <T, E> binaryOperation(
methodName: String, receiverType: String, parameterType: String, function: (T, E) -> Any?
): Pair<CompileTimeFunction, Function2<Any?, Any?, Any?>> {
return CompileTimeFunction(methodName, listOfNotNull(receiverType, parameterType)) to function as Function2<Any?, Any?, Any?>
}
@Suppress("UNCHECKED_CAST")
fun <T, E, R> ternaryOperation(
methodName: String, receiverType: String, firstParameterType: String, secondParameterType: String, function: (T, E, R) -> Any?
): Pair<CompileTimeFunction, Function3<Any?, Any?, Any?, Any?>> {
return CompileTimeFunction(
methodName, listOfNotNull(receiverType, firstParameterType, secondParameterType)
) to function as Function3<Any?, Any?, Any?, Any?>
}
@@ -1,478 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
*
* 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.backend.common.interpreter.builtins
import org.jetbrains.kotlin.backend.common.interpreter.state.*
/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */
val unaryFunctions = mapOf<CompileTimeFunction, Function1<Any?, Any?>>(
unaryOperation<Boolean>("hashCode", "Boolean") { a -> a.hashCode() },
unaryOperation<Boolean>("not", "Boolean") { a -> a.not() },
unaryOperation<Boolean>("toString", "Boolean") { a -> a.toString() },
unaryOperation<Char>("dec", "Char") { a -> a.dec() },
unaryOperation<Char>("hashCode", "Char") { a -> a.hashCode() },
unaryOperation<Char>("inc", "Char") { a -> a.inc() },
unaryOperation<Char>("toByte", "Char") { a -> a.toByte() },
unaryOperation<Char>("toChar", "Char") { a -> a.toChar() },
unaryOperation<Char>("toDouble", "Char") { a -> a.toDouble() },
unaryOperation<Char>("toFloat", "Char") { a -> a.toFloat() },
unaryOperation<Char>("toInt", "Char") { a -> a.toInt() },
unaryOperation<Char>("toLong", "Char") { a -> a.toLong() },
unaryOperation<Char>("toShort", "Char") { a -> a.toShort() },
unaryOperation<Char>("toString", "Char") { a -> a.toString() },
unaryOperation<Byte>("dec", "Byte") { a -> a.dec() },
unaryOperation<Byte>("hashCode", "Byte") { a -> a.hashCode() },
unaryOperation<Byte>("inc", "Byte") { a -> a.inc() },
unaryOperation<Byte>("toByte", "Byte") { a -> a.toByte() },
unaryOperation<Byte>("toChar", "Byte") { a -> a.toChar() },
unaryOperation<Byte>("toDouble", "Byte") { a -> a.toDouble() },
unaryOperation<Byte>("toFloat", "Byte") { a -> a.toFloat() },
unaryOperation<Byte>("toInt", "Byte") { a -> a.toInt() },
unaryOperation<Byte>("toLong", "Byte") { a -> a.toLong() },
unaryOperation<Byte>("toShort", "Byte") { a -> a.toShort() },
unaryOperation<Byte>("toString", "Byte") { a -> a.toString() },
unaryOperation<Byte>("unaryMinus", "Byte") { a -> a.unaryMinus() },
unaryOperation<Byte>("unaryPlus", "Byte") { a -> a.unaryPlus() },
unaryOperation<Short>("dec", "Short") { a -> a.dec() },
unaryOperation<Short>("hashCode", "Short") { a -> a.hashCode() },
unaryOperation<Short>("inc", "Short") { a -> a.inc() },
unaryOperation<Short>("toByte", "Short") { a -> a.toByte() },
unaryOperation<Short>("toChar", "Short") { a -> a.toChar() },
unaryOperation<Short>("toDouble", "Short") { a -> a.toDouble() },
unaryOperation<Short>("toFloat", "Short") { a -> a.toFloat() },
unaryOperation<Short>("toInt", "Short") { a -> a.toInt() },
unaryOperation<Short>("toLong", "Short") { a -> a.toLong() },
unaryOperation<Short>("toShort", "Short") { a -> a.toShort() },
unaryOperation<Short>("toString", "Short") { a -> a.toString() },
unaryOperation<Short>("unaryMinus", "Short") { a -> a.unaryMinus() },
unaryOperation<Short>("unaryPlus", "Short") { a -> a.unaryPlus() },
unaryOperation<Int>("dec", "Int") { a -> a.dec() },
unaryOperation<Int>("hashCode", "Int") { a -> a.hashCode() },
unaryOperation<Int>("inc", "Int") { a -> a.inc() },
unaryOperation<Int>("inv", "Int") { a -> a.inv() },
unaryOperation<Int>("toByte", "Int") { a -> a.toByte() },
unaryOperation<Int>("toChar", "Int") { a -> a.toChar() },
unaryOperation<Int>("toDouble", "Int") { a -> a.toDouble() },
unaryOperation<Int>("toFloat", "Int") { a -> a.toFloat() },
unaryOperation<Int>("toInt", "Int") { a -> a.toInt() },
unaryOperation<Int>("toLong", "Int") { a -> a.toLong() },
unaryOperation<Int>("toShort", "Int") { a -> a.toShort() },
unaryOperation<Int>("toString", "Int") { a -> a.toString() },
unaryOperation<Int>("unaryMinus", "Int") { a -> a.unaryMinus() },
unaryOperation<Int>("unaryPlus", "Int") { a -> a.unaryPlus() },
unaryOperation<Float>("dec", "Float") { a -> a.dec() },
unaryOperation<Float>("hashCode", "Float") { a -> a.hashCode() },
unaryOperation<Float>("inc", "Float") { a -> a.inc() },
unaryOperation<Float>("toByte", "Float") { a -> a.toByte() },
unaryOperation<Float>("toChar", "Float") { a -> a.toChar() },
unaryOperation<Float>("toDouble", "Float") { a -> a.toDouble() },
unaryOperation<Float>("toFloat", "Float") { a -> a.toFloat() },
unaryOperation<Float>("toInt", "Float") { a -> a.toInt() },
unaryOperation<Float>("toLong", "Float") { a -> a.toLong() },
unaryOperation<Float>("toShort", "Float") { a -> a.toShort() },
unaryOperation<Float>("toString", "Float") { a -> a.toString() },
unaryOperation<Float>("unaryMinus", "Float") { a -> a.unaryMinus() },
unaryOperation<Float>("unaryPlus", "Float") { a -> a.unaryPlus() },
unaryOperation<Long>("dec", "Long") { a -> a.dec() },
unaryOperation<Long>("hashCode", "Long") { a -> a.hashCode() },
unaryOperation<Long>("inc", "Long") { a -> a.inc() },
unaryOperation<Long>("inv", "Long") { a -> a.inv() },
unaryOperation<Long>("toByte", "Long") { a -> a.toByte() },
unaryOperation<Long>("toChar", "Long") { a -> a.toChar() },
unaryOperation<Long>("toDouble", "Long") { a -> a.toDouble() },
unaryOperation<Long>("toFloat", "Long") { a -> a.toFloat() },
unaryOperation<Long>("toInt", "Long") { a -> a.toInt() },
unaryOperation<Long>("toLong", "Long") { a -> a.toLong() },
unaryOperation<Long>("toShort", "Long") { a -> a.toShort() },
unaryOperation<Long>("toString", "Long") { a -> a.toString() },
unaryOperation<Long>("unaryMinus", "Long") { a -> a.unaryMinus() },
unaryOperation<Long>("unaryPlus", "Long") { a -> a.unaryPlus() },
unaryOperation<Double>("dec", "Double") { a -> a.dec() },
unaryOperation<Double>("hashCode", "Double") { a -> a.hashCode() },
unaryOperation<Double>("inc", "Double") { a -> a.inc() },
unaryOperation<Double>("toByte", "Double") { a -> a.toByte() },
unaryOperation<Double>("toChar", "Double") { a -> a.toChar() },
unaryOperation<Double>("toDouble", "Double") { a -> a.toDouble() },
unaryOperation<Double>("toFloat", "Double") { a -> a.toFloat() },
unaryOperation<Double>("toInt", "Double") { a -> a.toInt() },
unaryOperation<Double>("toLong", "Double") { a -> a.toLong() },
unaryOperation<Double>("toShort", "Double") { a -> a.toShort() },
unaryOperation<Double>("toString", "Double") { a -> a.toString() },
unaryOperation<Double>("unaryMinus", "Double") { a -> a.unaryMinus() },
unaryOperation<Double>("unaryPlus", "Double") { a -> a.unaryPlus() },
unaryOperation<String>("length", "String") { a -> a.length },
unaryOperation<String>("hashCode", "String") { a -> a.hashCode() },
unaryOperation<String>("toString", "String") { a -> a.toString() },
unaryOperation<BooleanArray>("size", "BooleanArray") { a -> a.size },
unaryOperation<BooleanArray>("iterator", "BooleanArray") { a -> a.iterator() },
unaryOperation<CharArray>("size", "CharArray") { a -> a.size },
unaryOperation<CharArray>("iterator", "CharArray") { a -> a.iterator() },
unaryOperation<ByteArray>("size", "ByteArray") { a -> a.size },
unaryOperation<ByteArray>("iterator", "ByteArray") { a -> a.iterator() },
unaryOperation<ShortArray>("size", "ShortArray") { a -> a.size },
unaryOperation<ShortArray>("iterator", "ShortArray") { a -> a.iterator() },
unaryOperation<IntArray>("size", "IntArray") { a -> a.size },
unaryOperation<IntArray>("iterator", "IntArray") { a -> a.iterator() },
unaryOperation<FloatArray>("size", "FloatArray") { a -> a.size },
unaryOperation<FloatArray>("iterator", "FloatArray") { a -> a.iterator() },
unaryOperation<LongArray>("size", "LongArray") { a -> a.size },
unaryOperation<LongArray>("iterator", "LongArray") { a -> a.iterator() },
unaryOperation<DoubleArray>("size", "DoubleArray") { a -> a.size },
unaryOperation<DoubleArray>("iterator", "DoubleArray") { a -> a.iterator() },
unaryOperation<Array<Any?>>("size", "Array") { a -> a.size },
unaryOperation<Array<Any?>>("iterator", "Array") { a -> a.iterator() },
unaryOperation<Any>("hashCode", "Any") { a -> a.hashCode() },
unaryOperation<Any>("toString", "Any") { a -> a.defaultToString() },
unaryOperation<Any?>("CHECK_NOT_NULL", "T0?") { a -> a!! },
unaryOperation<ExceptionState>("message", "Throwable") { a -> a.getMessage() },
unaryOperation<ExceptionState>("cause", "Throwable") { a -> a.getCause() }
)
val binaryFunctions = mapOf<CompileTimeFunction, Function2<Any?, Any?, Any?>>(
binaryOperation<Boolean, Boolean>("and", "Boolean", "Boolean") { a, b -> a.and(b) },
binaryOperation<Boolean, Boolean>("compareTo", "Boolean", "Boolean") { a, b -> a.compareTo(b) },
binaryOperation<Boolean, Any?>("equals", "Boolean", "Any?") { a, b -> a.equals(b) },
binaryOperation<Boolean, Boolean>("or", "Boolean", "Boolean") { a, b -> a.or(b) },
binaryOperation<Boolean, Boolean>("xor", "Boolean", "Boolean") { a, b -> a.xor(b) },
binaryOperation<Char, Char>("compareTo", "Char", "Char") { a, b -> a.compareTo(b) },
binaryOperation<Char, Any?>("equals", "Char", "Any?") { a, b -> a.equals(b) },
binaryOperation<Char, Char>("minus", "Char", "Char") { a, b -> a.minus(b) },
binaryOperation<Char, Int>("minus", "Char", "Int") { a, b -> a.minus(b) },
binaryOperation<Char, Int>("plus", "Char", "Int") { a, b -> a.plus(b) },
binaryOperation<Char, Char>("rangeTo", "Char", "Char") { a, b -> a.rangeTo(b) },
binaryOperation<Byte, Byte>("compareTo", "Byte", "Byte") { a, b -> a.compareTo(b) },
binaryOperation<Byte, Double>("compareTo", "Byte", "Double") { a, b -> a.compareTo(b) },
binaryOperation<Byte, Float>("compareTo", "Byte", "Float") { a, b -> a.compareTo(b) },
binaryOperation<Byte, Int>("compareTo", "Byte", "Int") { a, b -> a.compareTo(b) },
binaryOperation<Byte, Long>("compareTo", "Byte", "Long") { a, b -> a.compareTo(b) },
binaryOperation<Byte, Short>("compareTo", "Byte", "Short") { a, b -> a.compareTo(b) },
binaryOperation<Byte, Byte>("div", "Byte", "Byte") { a, b -> a.div(b) },
binaryOperation<Byte, Double>("div", "Byte", "Double") { a, b -> a.div(b) },
binaryOperation<Byte, Float>("div", "Byte", "Float") { a, b -> a.div(b) },
binaryOperation<Byte, Int>("div", "Byte", "Int") { a, b -> a.div(b) },
binaryOperation<Byte, Long>("div", "Byte", "Long") { a, b -> a.div(b) },
binaryOperation<Byte, Short>("div", "Byte", "Short") { a, b -> a.div(b) },
binaryOperation<Byte, Any?>("equals", "Byte", "Any?") { a, b -> a.equals(b) },
binaryOperation<Byte, Byte>("minus", "Byte", "Byte") { a, b -> a.minus(b) },
binaryOperation<Byte, Double>("minus", "Byte", "Double") { a, b -> a.minus(b) },
binaryOperation<Byte, Float>("minus", "Byte", "Float") { a, b -> a.minus(b) },
binaryOperation<Byte, Int>("minus", "Byte", "Int") { a, b -> a.minus(b) },
binaryOperation<Byte, Long>("minus", "Byte", "Long") { a, b -> a.minus(b) },
binaryOperation<Byte, Short>("minus", "Byte", "Short") { a, b -> a.minus(b) },
binaryOperation<Byte, Byte>("plus", "Byte", "Byte") { a, b -> a.plus(b) },
binaryOperation<Byte, Double>("plus", "Byte", "Double") { a, b -> a.plus(b) },
binaryOperation<Byte, Float>("plus", "Byte", "Float") { a, b -> a.plus(b) },
binaryOperation<Byte, Int>("plus", "Byte", "Int") { a, b -> a.plus(b) },
binaryOperation<Byte, Long>("plus", "Byte", "Long") { a, b -> a.plus(b) },
binaryOperation<Byte, Short>("plus", "Byte", "Short") { a, b -> a.plus(b) },
binaryOperation<Byte, Byte>("rangeTo", "Byte", "Byte") { a, b -> a.rangeTo(b) },
binaryOperation<Byte, Int>("rangeTo", "Byte", "Int") { a, b -> a.rangeTo(b) },
binaryOperation<Byte, Long>("rangeTo", "Byte", "Long") { a, b -> a.rangeTo(b) },
binaryOperation<Byte, Short>("rangeTo", "Byte", "Short") { a, b -> a.rangeTo(b) },
binaryOperation<Byte, Byte>("rem", "Byte", "Byte") { a, b -> a.rem(b) },
binaryOperation<Byte, Double>("rem", "Byte", "Double") { a, b -> a.rem(b) },
binaryOperation<Byte, Float>("rem", "Byte", "Float") { a, b -> a.rem(b) },
binaryOperation<Byte, Int>("rem", "Byte", "Int") { a, b -> a.rem(b) },
binaryOperation<Byte, Long>("rem", "Byte", "Long") { a, b -> a.rem(b) },
binaryOperation<Byte, Short>("rem", "Byte", "Short") { a, b -> a.rem(b) },
binaryOperation<Byte, Byte>("times", "Byte", "Byte") { a, b -> a.times(b) },
binaryOperation<Byte, Double>("times", "Byte", "Double") { a, b -> a.times(b) },
binaryOperation<Byte, Float>("times", "Byte", "Float") { a, b -> a.times(b) },
binaryOperation<Byte, Int>("times", "Byte", "Int") { a, b -> a.times(b) },
binaryOperation<Byte, Long>("times", "Byte", "Long") { a, b -> a.times(b) },
binaryOperation<Byte, Short>("times", "Byte", "Short") { a, b -> a.times(b) },
binaryOperation<Short, Byte>("compareTo", "Short", "Byte") { a, b -> a.compareTo(b) },
binaryOperation<Short, Double>("compareTo", "Short", "Double") { a, b -> a.compareTo(b) },
binaryOperation<Short, Float>("compareTo", "Short", "Float") { a, b -> a.compareTo(b) },
binaryOperation<Short, Int>("compareTo", "Short", "Int") { a, b -> a.compareTo(b) },
binaryOperation<Short, Long>("compareTo", "Short", "Long") { a, b -> a.compareTo(b) },
binaryOperation<Short, Short>("compareTo", "Short", "Short") { a, b -> a.compareTo(b) },
binaryOperation<Short, Byte>("div", "Short", "Byte") { a, b -> a.div(b) },
binaryOperation<Short, Double>("div", "Short", "Double") { a, b -> a.div(b) },
binaryOperation<Short, Float>("div", "Short", "Float") { a, b -> a.div(b) },
binaryOperation<Short, Int>("div", "Short", "Int") { a, b -> a.div(b) },
binaryOperation<Short, Long>("div", "Short", "Long") { a, b -> a.div(b) },
binaryOperation<Short, Short>("div", "Short", "Short") { a, b -> a.div(b) },
binaryOperation<Short, Any?>("equals", "Short", "Any?") { a, b -> a.equals(b) },
binaryOperation<Short, Byte>("minus", "Short", "Byte") { a, b -> a.minus(b) },
binaryOperation<Short, Double>("minus", "Short", "Double") { a, b -> a.minus(b) },
binaryOperation<Short, Float>("minus", "Short", "Float") { a, b -> a.minus(b) },
binaryOperation<Short, Int>("minus", "Short", "Int") { a, b -> a.minus(b) },
binaryOperation<Short, Long>("minus", "Short", "Long") { a, b -> a.minus(b) },
binaryOperation<Short, Short>("minus", "Short", "Short") { a, b -> a.minus(b) },
binaryOperation<Short, Byte>("plus", "Short", "Byte") { a, b -> a.plus(b) },
binaryOperation<Short, Double>("plus", "Short", "Double") { a, b -> a.plus(b) },
binaryOperation<Short, Float>("plus", "Short", "Float") { a, b -> a.plus(b) },
binaryOperation<Short, Int>("plus", "Short", "Int") { a, b -> a.plus(b) },
binaryOperation<Short, Long>("plus", "Short", "Long") { a, b -> a.plus(b) },
binaryOperation<Short, Short>("plus", "Short", "Short") { a, b -> a.plus(b) },
binaryOperation<Short, Byte>("rangeTo", "Short", "Byte") { a, b -> a.rangeTo(b) },
binaryOperation<Short, Int>("rangeTo", "Short", "Int") { a, b -> a.rangeTo(b) },
binaryOperation<Short, Long>("rangeTo", "Short", "Long") { a, b -> a.rangeTo(b) },
binaryOperation<Short, Short>("rangeTo", "Short", "Short") { a, b -> a.rangeTo(b) },
binaryOperation<Short, Byte>("rem", "Short", "Byte") { a, b -> a.rem(b) },
binaryOperation<Short, Double>("rem", "Short", "Double") { a, b -> a.rem(b) },
binaryOperation<Short, Float>("rem", "Short", "Float") { a, b -> a.rem(b) },
binaryOperation<Short, Int>("rem", "Short", "Int") { a, b -> a.rem(b) },
binaryOperation<Short, Long>("rem", "Short", "Long") { a, b -> a.rem(b) },
binaryOperation<Short, Short>("rem", "Short", "Short") { a, b -> a.rem(b) },
binaryOperation<Short, Byte>("times", "Short", "Byte") { a, b -> a.times(b) },
binaryOperation<Short, Double>("times", "Short", "Double") { a, b -> a.times(b) },
binaryOperation<Short, Float>("times", "Short", "Float") { a, b -> a.times(b) },
binaryOperation<Short, Int>("times", "Short", "Int") { a, b -> a.times(b) },
binaryOperation<Short, Long>("times", "Short", "Long") { a, b -> a.times(b) },
binaryOperation<Short, Short>("times", "Short", "Short") { a, b -> a.times(b) },
binaryOperation<Int, Int>("and", "Int", "Int") { a, b -> a.and(b) },
binaryOperation<Int, Byte>("compareTo", "Int", "Byte") { a, b -> a.compareTo(b) },
binaryOperation<Int, Double>("compareTo", "Int", "Double") { a, b -> a.compareTo(b) },
binaryOperation<Int, Float>("compareTo", "Int", "Float") { a, b -> a.compareTo(b) },
binaryOperation<Int, Int>("compareTo", "Int", "Int") { a, b -> a.compareTo(b) },
binaryOperation<Int, Long>("compareTo", "Int", "Long") { a, b -> a.compareTo(b) },
binaryOperation<Int, Short>("compareTo", "Int", "Short") { a, b -> a.compareTo(b) },
binaryOperation<Int, Byte>("div", "Int", "Byte") { a, b -> a.div(b) },
binaryOperation<Int, Double>("div", "Int", "Double") { a, b -> a.div(b) },
binaryOperation<Int, Float>("div", "Int", "Float") { a, b -> a.div(b) },
binaryOperation<Int, Int>("div", "Int", "Int") { a, b -> a.div(b) },
binaryOperation<Int, Long>("div", "Int", "Long") { a, b -> a.div(b) },
binaryOperation<Int, Short>("div", "Int", "Short") { a, b -> a.div(b) },
binaryOperation<Int, Any?>("equals", "Int", "Any?") { a, b -> a.equals(b) },
binaryOperation<Int, Byte>("minus", "Int", "Byte") { a, b -> a.minus(b) },
binaryOperation<Int, Double>("minus", "Int", "Double") { a, b -> a.minus(b) },
binaryOperation<Int, Float>("minus", "Int", "Float") { a, b -> a.minus(b) },
binaryOperation<Int, Int>("minus", "Int", "Int") { a, b -> a.minus(b) },
binaryOperation<Int, Long>("minus", "Int", "Long") { a, b -> a.minus(b) },
binaryOperation<Int, Short>("minus", "Int", "Short") { a, b -> a.minus(b) },
binaryOperation<Int, Int>("or", "Int", "Int") { a, b -> a.or(b) },
binaryOperation<Int, Byte>("plus", "Int", "Byte") { a, b -> a.plus(b) },
binaryOperation<Int, Double>("plus", "Int", "Double") { a, b -> a.plus(b) },
binaryOperation<Int, Float>("plus", "Int", "Float") { a, b -> a.plus(b) },
binaryOperation<Int, Int>("plus", "Int", "Int") { a, b -> a.plus(b) },
binaryOperation<Int, Long>("plus", "Int", "Long") { a, b -> a.plus(b) },
binaryOperation<Int, Short>("plus", "Int", "Short") { a, b -> a.plus(b) },
binaryOperation<Int, Byte>("rangeTo", "Int", "Byte") { a, b -> a.rangeTo(b) },
binaryOperation<Int, Int>("rangeTo", "Int", "Int") { a, b -> a.rangeTo(b) },
binaryOperation<Int, Long>("rangeTo", "Int", "Long") { a, b -> a.rangeTo(b) },
binaryOperation<Int, Short>("rangeTo", "Int", "Short") { a, b -> a.rangeTo(b) },
binaryOperation<Int, Byte>("rem", "Int", "Byte") { a, b -> a.rem(b) },
binaryOperation<Int, Double>("rem", "Int", "Double") { a, b -> a.rem(b) },
binaryOperation<Int, Float>("rem", "Int", "Float") { a, b -> a.rem(b) },
binaryOperation<Int, Int>("rem", "Int", "Int") { a, b -> a.rem(b) },
binaryOperation<Int, Long>("rem", "Int", "Long") { a, b -> a.rem(b) },
binaryOperation<Int, Short>("rem", "Int", "Short") { a, b -> a.rem(b) },
binaryOperation<Int, Int>("shl", "Int", "Int") { a, b -> a.shl(b) },
binaryOperation<Int, Int>("shr", "Int", "Int") { a, b -> a.shr(b) },
binaryOperation<Int, Byte>("times", "Int", "Byte") { a, b -> a.times(b) },
binaryOperation<Int, Double>("times", "Int", "Double") { a, b -> a.times(b) },
binaryOperation<Int, Float>("times", "Int", "Float") { a, b -> a.times(b) },
binaryOperation<Int, Int>("times", "Int", "Int") { a, b -> a.times(b) },
binaryOperation<Int, Long>("times", "Int", "Long") { a, b -> a.times(b) },
binaryOperation<Int, Short>("times", "Int", "Short") { a, b -> a.times(b) },
binaryOperation<Int, Int>("ushr", "Int", "Int") { a, b -> a.ushr(b) },
binaryOperation<Int, Int>("xor", "Int", "Int") { a, b -> a.xor(b) },
binaryOperation<Float, Byte>("compareTo", "Float", "Byte") { a, b -> a.compareTo(b) },
binaryOperation<Float, Double>("compareTo", "Float", "Double") { a, b -> a.compareTo(b) },
binaryOperation<Float, Float>("compareTo", "Float", "Float") { a, b -> a.compareTo(b) },
binaryOperation<Float, Int>("compareTo", "Float", "Int") { a, b -> a.compareTo(b) },
binaryOperation<Float, Long>("compareTo", "Float", "Long") { a, b -> a.compareTo(b) },
binaryOperation<Float, Short>("compareTo", "Float", "Short") { a, b -> a.compareTo(b) },
binaryOperation<Float, Byte>("div", "Float", "Byte") { a, b -> a.div(b) },
binaryOperation<Float, Double>("div", "Float", "Double") { a, b -> a.div(b) },
binaryOperation<Float, Float>("div", "Float", "Float") { a, b -> a.div(b) },
binaryOperation<Float, Int>("div", "Float", "Int") { a, b -> a.div(b) },
binaryOperation<Float, Long>("div", "Float", "Long") { a, b -> a.div(b) },
binaryOperation<Float, Short>("div", "Float", "Short") { a, b -> a.div(b) },
binaryOperation<Float, Any?>("equals", "Float", "Any?") { a, b -> a.equals(b) },
binaryOperation<Float, Byte>("minus", "Float", "Byte") { a, b -> a.minus(b) },
binaryOperation<Float, Double>("minus", "Float", "Double") { a, b -> a.minus(b) },
binaryOperation<Float, Float>("minus", "Float", "Float") { a, b -> a.minus(b) },
binaryOperation<Float, Int>("minus", "Float", "Int") { a, b -> a.minus(b) },
binaryOperation<Float, Long>("minus", "Float", "Long") { a, b -> a.minus(b) },
binaryOperation<Float, Short>("minus", "Float", "Short") { a, b -> a.minus(b) },
binaryOperation<Float, Byte>("plus", "Float", "Byte") { a, b -> a.plus(b) },
binaryOperation<Float, Double>("plus", "Float", "Double") { a, b -> a.plus(b) },
binaryOperation<Float, Float>("plus", "Float", "Float") { a, b -> a.plus(b) },
binaryOperation<Float, Int>("plus", "Float", "Int") { a, b -> a.plus(b) },
binaryOperation<Float, Long>("plus", "Float", "Long") { a, b -> a.plus(b) },
binaryOperation<Float, Short>("plus", "Float", "Short") { a, b -> a.plus(b) },
binaryOperation<Float, Byte>("rem", "Float", "Byte") { a, b -> a.rem(b) },
binaryOperation<Float, Double>("rem", "Float", "Double") { a, b -> a.rem(b) },
binaryOperation<Float, Float>("rem", "Float", "Float") { a, b -> a.rem(b) },
binaryOperation<Float, Int>("rem", "Float", "Int") { a, b -> a.rem(b) },
binaryOperation<Float, Long>("rem", "Float", "Long") { a, b -> a.rem(b) },
binaryOperation<Float, Short>("rem", "Float", "Short") { a, b -> a.rem(b) },
binaryOperation<Float, Byte>("times", "Float", "Byte") { a, b -> a.times(b) },
binaryOperation<Float, Double>("times", "Float", "Double") { a, b -> a.times(b) },
binaryOperation<Float, Float>("times", "Float", "Float") { a, b -> a.times(b) },
binaryOperation<Float, Int>("times", "Float", "Int") { a, b -> a.times(b) },
binaryOperation<Float, Long>("times", "Float", "Long") { a, b -> a.times(b) },
binaryOperation<Float, Short>("times", "Float", "Short") { a, b -> a.times(b) },
binaryOperation<Long, Long>("and", "Long", "Long") { a, b -> a.and(b) },
binaryOperation<Long, Byte>("compareTo", "Long", "Byte") { a, b -> a.compareTo(b) },
binaryOperation<Long, Double>("compareTo", "Long", "Double") { a, b -> a.compareTo(b) },
binaryOperation<Long, Float>("compareTo", "Long", "Float") { a, b -> a.compareTo(b) },
binaryOperation<Long, Int>("compareTo", "Long", "Int") { a, b -> a.compareTo(b) },
binaryOperation<Long, Long>("compareTo", "Long", "Long") { a, b -> a.compareTo(b) },
binaryOperation<Long, Short>("compareTo", "Long", "Short") { a, b -> a.compareTo(b) },
binaryOperation<Long, Byte>("div", "Long", "Byte") { a, b -> a.div(b) },
binaryOperation<Long, Double>("div", "Long", "Double") { a, b -> a.div(b) },
binaryOperation<Long, Float>("div", "Long", "Float") { a, b -> a.div(b) },
binaryOperation<Long, Int>("div", "Long", "Int") { a, b -> a.div(b) },
binaryOperation<Long, Long>("div", "Long", "Long") { a, b -> a.div(b) },
binaryOperation<Long, Short>("div", "Long", "Short") { a, b -> a.div(b) },
binaryOperation<Long, Any?>("equals", "Long", "Any?") { a, b -> a.equals(b) },
binaryOperation<Long, Byte>("minus", "Long", "Byte") { a, b -> a.minus(b) },
binaryOperation<Long, Double>("minus", "Long", "Double") { a, b -> a.minus(b) },
binaryOperation<Long, Float>("minus", "Long", "Float") { a, b -> a.minus(b) },
binaryOperation<Long, Int>("minus", "Long", "Int") { a, b -> a.minus(b) },
binaryOperation<Long, Long>("minus", "Long", "Long") { a, b -> a.minus(b) },
binaryOperation<Long, Short>("minus", "Long", "Short") { a, b -> a.minus(b) },
binaryOperation<Long, Long>("or", "Long", "Long") { a, b -> a.or(b) },
binaryOperation<Long, Byte>("plus", "Long", "Byte") { a, b -> a.plus(b) },
binaryOperation<Long, Double>("plus", "Long", "Double") { a, b -> a.plus(b) },
binaryOperation<Long, Float>("plus", "Long", "Float") { a, b -> a.plus(b) },
binaryOperation<Long, Int>("plus", "Long", "Int") { a, b -> a.plus(b) },
binaryOperation<Long, Long>("plus", "Long", "Long") { a, b -> a.plus(b) },
binaryOperation<Long, Short>("plus", "Long", "Short") { a, b -> a.plus(b) },
binaryOperation<Long, Byte>("rangeTo", "Long", "Byte") { a, b -> a.rangeTo(b) },
binaryOperation<Long, Int>("rangeTo", "Long", "Int") { a, b -> a.rangeTo(b) },
binaryOperation<Long, Long>("rangeTo", "Long", "Long") { a, b -> a.rangeTo(b) },
binaryOperation<Long, Short>("rangeTo", "Long", "Short") { a, b -> a.rangeTo(b) },
binaryOperation<Long, Byte>("rem", "Long", "Byte") { a, b -> a.rem(b) },
binaryOperation<Long, Double>("rem", "Long", "Double") { a, b -> a.rem(b) },
binaryOperation<Long, Float>("rem", "Long", "Float") { a, b -> a.rem(b) },
binaryOperation<Long, Int>("rem", "Long", "Int") { a, b -> a.rem(b) },
binaryOperation<Long, Long>("rem", "Long", "Long") { a, b -> a.rem(b) },
binaryOperation<Long, Short>("rem", "Long", "Short") { a, b -> a.rem(b) },
binaryOperation<Long, Int>("shl", "Long", "Int") { a, b -> a.shl(b) },
binaryOperation<Long, Int>("shr", "Long", "Int") { a, b -> a.shr(b) },
binaryOperation<Long, Byte>("times", "Long", "Byte") { a, b -> a.times(b) },
binaryOperation<Long, Double>("times", "Long", "Double") { a, b -> a.times(b) },
binaryOperation<Long, Float>("times", "Long", "Float") { a, b -> a.times(b) },
binaryOperation<Long, Int>("times", "Long", "Int") { a, b -> a.times(b) },
binaryOperation<Long, Long>("times", "Long", "Long") { a, b -> a.times(b) },
binaryOperation<Long, Short>("times", "Long", "Short") { a, b -> a.times(b) },
binaryOperation<Long, Int>("ushr", "Long", "Int") { a, b -> a.ushr(b) },
binaryOperation<Long, Long>("xor", "Long", "Long") { a, b -> a.xor(b) },
binaryOperation<Double, Byte>("compareTo", "Double", "Byte") { a, b -> a.compareTo(b) },
binaryOperation<Double, Double>("compareTo", "Double", "Double") { a, b -> a.compareTo(b) },
binaryOperation<Double, Float>("compareTo", "Double", "Float") { a, b -> a.compareTo(b) },
binaryOperation<Double, Int>("compareTo", "Double", "Int") { a, b -> a.compareTo(b) },
binaryOperation<Double, Long>("compareTo", "Double", "Long") { a, b -> a.compareTo(b) },
binaryOperation<Double, Short>("compareTo", "Double", "Short") { a, b -> a.compareTo(b) },
binaryOperation<Double, Byte>("div", "Double", "Byte") { a, b -> a.div(b) },
binaryOperation<Double, Double>("div", "Double", "Double") { a, b -> a.div(b) },
binaryOperation<Double, Float>("div", "Double", "Float") { a, b -> a.div(b) },
binaryOperation<Double, Int>("div", "Double", "Int") { a, b -> a.div(b) },
binaryOperation<Double, Long>("div", "Double", "Long") { a, b -> a.div(b) },
binaryOperation<Double, Short>("div", "Double", "Short") { a, b -> a.div(b) },
binaryOperation<Double, Any?>("equals", "Double", "Any?") { a, b -> a.equals(b) },
binaryOperation<Double, Byte>("minus", "Double", "Byte") { a, b -> a.minus(b) },
binaryOperation<Double, Double>("minus", "Double", "Double") { a, b -> a.minus(b) },
binaryOperation<Double, Float>("minus", "Double", "Float") { a, b -> a.minus(b) },
binaryOperation<Double, Int>("minus", "Double", "Int") { a, b -> a.minus(b) },
binaryOperation<Double, Long>("minus", "Double", "Long") { a, b -> a.minus(b) },
binaryOperation<Double, Short>("minus", "Double", "Short") { a, b -> a.minus(b) },
binaryOperation<Double, Byte>("plus", "Double", "Byte") { a, b -> a.plus(b) },
binaryOperation<Double, Double>("plus", "Double", "Double") { a, b -> a.plus(b) },
binaryOperation<Double, Float>("plus", "Double", "Float") { a, b -> a.plus(b) },
binaryOperation<Double, Int>("plus", "Double", "Int") { a, b -> a.plus(b) },
binaryOperation<Double, Long>("plus", "Double", "Long") { a, b -> a.plus(b) },
binaryOperation<Double, Short>("plus", "Double", "Short") { a, b -> a.plus(b) },
binaryOperation<Double, Byte>("rem", "Double", "Byte") { a, b -> a.rem(b) },
binaryOperation<Double, Double>("rem", "Double", "Double") { a, b -> a.rem(b) },
binaryOperation<Double, Float>("rem", "Double", "Float") { a, b -> a.rem(b) },
binaryOperation<Double, Int>("rem", "Double", "Int") { a, b -> a.rem(b) },
binaryOperation<Double, Long>("rem", "Double", "Long") { a, b -> a.rem(b) },
binaryOperation<Double, Short>("rem", "Double", "Short") { a, b -> a.rem(b) },
binaryOperation<Double, Byte>("times", "Double", "Byte") { a, b -> a.times(b) },
binaryOperation<Double, Double>("times", "Double", "Double") { a, b -> a.times(b) },
binaryOperation<Double, Float>("times", "Double", "Float") { a, b -> a.times(b) },
binaryOperation<Double, Int>("times", "Double", "Int") { a, b -> a.times(b) },
binaryOperation<Double, Long>("times", "Double", "Long") { a, b -> a.times(b) },
binaryOperation<Double, Short>("times", "Double", "Short") { a, b -> a.times(b) },
binaryOperation<String, String>("compareTo", "String", "String") { a, b -> a.compareTo(b) },
binaryOperation<String, Any?>("equals", "String", "Any?") { a, b -> a.equals(b) },
binaryOperation<String, Int>("get", "String", "Int") { a, b -> a.get(b) },
binaryOperation<String, Any?>("plus", "String", "Any?") { a, b -> a.plus(b) },
binaryOperation<BooleanArray, Int>("get", "BooleanArray", "Int") { a, b -> a.get(b) },
binaryOperation<CharArray, Int>("get", "CharArray", "Int") { a, b -> a.get(b) },
binaryOperation<ByteArray, Int>("get", "ByteArray", "Int") { a, b -> a.get(b) },
binaryOperation<ShortArray, Int>("get", "ShortArray", "Int") { a, b -> a.get(b) },
binaryOperation<IntArray, Int>("get", "IntArray", "Int") { a, b -> a.get(b) },
binaryOperation<FloatArray, Int>("get", "FloatArray", "Int") { a, b -> a.get(b) },
binaryOperation<LongArray, Int>("get", "LongArray", "Int") { a, b -> a.get(b) },
binaryOperation<DoubleArray, Int>("get", "DoubleArray", "Int") { a, b -> a.get(b) },
binaryOperation<Array<Any?>, Int>("get", "Array", "Int") { a, b -> a.get(b) },
binaryOperation<Any, Any?>("equals", "Any", "Any?") { a, b -> a.equals(b) },
binaryOperation<Char, Char>("less", "Char", "Char") { a, b -> a < b },
binaryOperation<Byte, Byte>("less", "Byte", "Byte") { a, b -> a < b },
binaryOperation<Short, Short>("less", "Short", "Short") { a, b -> a < b },
binaryOperation<Int, Int>("less", "Int", "Int") { a, b -> a < b },
binaryOperation<Float, Float>("less", "Float", "Float") { a, b -> a < b },
binaryOperation<Long, Long>("less", "Long", "Long") { a, b -> a < b },
binaryOperation<Double, Double>("less", "Double", "Double") { a, b -> a < b },
binaryOperation<Char, Char>("lessOrEqual", "Char", "Char") { a, b -> a <= b },
binaryOperation<Byte, Byte>("lessOrEqual", "Byte", "Byte") { a, b -> a <= b },
binaryOperation<Short, Short>("lessOrEqual", "Short", "Short") { a, b -> a <= b },
binaryOperation<Int, Int>("lessOrEqual", "Int", "Int") { a, b -> a <= b },
binaryOperation<Float, Float>("lessOrEqual", "Float", "Float") { a, b -> a <= b },
binaryOperation<Long, Long>("lessOrEqual", "Long", "Long") { a, b -> a <= b },
binaryOperation<Double, Double>("lessOrEqual", "Double", "Double") { a, b -> a <= b },
binaryOperation<Char, Char>("greater", "Char", "Char") { a, b -> a > b },
binaryOperation<Byte, Byte>("greater", "Byte", "Byte") { a, b -> a > b },
binaryOperation<Short, Short>("greater", "Short", "Short") { a, b -> a > b },
binaryOperation<Int, Int>("greater", "Int", "Int") { a, b -> a > b },
binaryOperation<Float, Float>("greater", "Float", "Float") { a, b -> a > b },
binaryOperation<Long, Long>("greater", "Long", "Long") { a, b -> a > b },
binaryOperation<Double, Double>("greater", "Double", "Double") { a, b -> a > b },
binaryOperation<Char, Char>("greaterOrEqual", "Char", "Char") { a, b -> a >= b },
binaryOperation<Byte, Byte>("greaterOrEqual", "Byte", "Byte") { a, b -> a >= b },
binaryOperation<Short, Short>("greaterOrEqual", "Short", "Short") { a, b -> a >= b },
binaryOperation<Int, Int>("greaterOrEqual", "Int", "Int") { a, b -> a >= b },
binaryOperation<Float, Float>("greaterOrEqual", "Float", "Float") { a, b -> a >= b },
binaryOperation<Long, Long>("greaterOrEqual", "Long", "Long") { a, b -> a >= b },
binaryOperation<Double, Double>("greaterOrEqual", "Double", "Double") { a, b -> a >= b },
binaryOperation<Any?, Any?>("EQEQ", "Any?", "Any?") { a, b -> a == b },
binaryOperation<Any?, Any?>("EQEQEQ", "Any?", "Any?") { a, b -> a === b },
binaryOperation<Float?, Float?>("ieee754equals", "Float?", "Float?") { a, b -> a == b },
binaryOperation<Double?, Double?>("ieee754equals", "Double?", "Double?") { a, b -> a == b },
binaryOperation<Boolean, Boolean>("ANDAND", "Boolean", "Boolean") { a, b -> a && b },
binaryOperation<Boolean, Boolean>("OROR", "Boolean", "Boolean") { a, b -> a || b }
)
val ternaryFunctions = mapOf<CompileTimeFunction, Function3<Any?, Any?, Any?, Any?>>(
ternaryOperation<String, Int, Int>("subSequence", "String", "Int", "Int") { a, b, c -> a.subSequence(b, c) },
ternaryOperation<BooleanArray, Int, Boolean>("set", "BooleanArray", "Int", "Boolean") { a, b, c -> a.set(b, c) },
ternaryOperation<CharArray, Int, Char>("set", "CharArray", "Int", "Char") { a, b, c -> a.set(b, c) },
ternaryOperation<ByteArray, Int, Byte>("set", "ByteArray", "Int", "Byte") { a, b, c -> a.set(b, c) },
ternaryOperation<ShortArray, Int, Short>("set", "ShortArray", "Int", "Short") { a, b, c -> a.set(b, c) },
ternaryOperation<IntArray, Int, Int>("set", "IntArray", "Int", "Int") { a, b, c -> a.set(b, c) },
ternaryOperation<FloatArray, Int, Float>("set", "FloatArray", "Int", "Float") { a, b, c -> a.set(b, c) },
ternaryOperation<LongArray, Int, Long>("set", "LongArray", "Int", "Long") { a, b, c -> a.set(b, c) },
ternaryOperation<DoubleArray, Int, Double>("set", "DoubleArray", "Int", "Double") { a, b, c -> a.set(b, c) },
ternaryOperation<Array<Any?>, Int, Any?>("set", "Array", "Int", "T") { a, b, c -> a.set(b, c) }
)
private fun Any.defaultToString(): String {
return when (this) {
is Lambda -> this.toString()
is State -> "${this.irClass.name}@" + System.identityHashCode(this).toString(16).padStart(8, '0')
else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0'))
}
}
@@ -1,9 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.exceptions
open class InterpreterException(override val message: String) : Exception(message) {
}
@@ -1,9 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.exceptions
class InterpreterMethodNotFoundException(override val message: String): InterpreterException(message) {
}
@@ -1,10 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.exceptions
class InterpreterTimeOutException : InterpreterException("Exceeded execution limit of constexpr expression") {
}
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.intrinsics
import org.jetbrains.kotlin.backend.common.interpreter.ExecutionResult
import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterMethodNotFoundException
import org.jetbrains.kotlin.backend.common.interpreter.stack.Stack
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
internal class IntrinsicEvaluator {
suspend fun evaluate(irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult): ExecutionResult {
return when {
EmptyArray.equalTo(irFunction) -> EmptyArray.evaluate(irFunction, stack, interpret)
ArrayOf.equalTo(irFunction) -> ArrayOf.evaluate(irFunction, stack, interpret)
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.evaluate(irFunction, stack, interpret)
EnumValues.equalTo(irFunction) -> EnumValues.evaluate(irFunction, stack, interpret)
EnumValueOf.equalTo(irFunction) -> EnumValueOf.evaluate(irFunction, stack, interpret)
RegexReplace.equalTo(irFunction) -> RegexReplace.evaluate(irFunction, stack, interpret)
EnumHashCode.equalTo(irFunction) -> EnumHashCode.evaluate(irFunction, stack, interpret)
JsPrimitives.equalTo(irFunction) -> JsPrimitives.evaluate(irFunction, stack, interpret)
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.evaluate(irFunction, stack, interpret)
else -> throw InterpreterMethodNotFoundException("Method ${irFunction.name} hasn't implemented")
}
}
}
@@ -1,211 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.intrinsics
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.backend.common.interpreter.*
import org.jetbrains.kotlin.backend.common.interpreter.stack.Stack
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.backend.common.interpreter.state.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.*
internal sealed class IntrinsicBase {
abstract fun equalTo(irFunction: IrFunction): Boolean
abstract suspend fun evaluate(irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult): ExecutionResult
}
internal object EmptyArray : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(emptyArray<Any?>().toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
}
}
internal object ArrayOf : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.arrayOf"
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val array = irFunction.getArgsForMethodInvocation(stack.getAll()).toTypedArray()
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(array.toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
}
}
internal object ArrayOfNulls : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.arrayOfNulls"
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val size = stack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
val array = arrayOfNulls<Any?>(size)
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(array.toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
}
}
internal object EnumValues : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return (fqName == "kotlin.enumValues" || fqName.endsWith(".values")) && irFunction.valueParameters.isEmpty()
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValues" -> stack.getVariable(irFunction.typeParameters.first().symbol).state.irClass
else -> irFunction.parent as IrClass
}
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
.map { entry -> entry.interpret().check { return it }.let { stack.popReturnValue() as Common } }
stack.pushReturnValue(enumEntries.toTypedArray().toState(irFunction.returnType))
return Next
}
}
internal object EnumValueOf : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return (fqName == "kotlin.enumValueOf" || fqName.endsWith(".valueOf")) && irFunction.valueParameters.size == 1
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValueOf" -> stack.getVariable(irFunction.typeParameters.first().symbol).state.irClass
else -> irFunction.parent as IrClass
}
val enumEntryName = stack.getVariable(irFunction.valueParameters.first().symbol).state.asString()
val enumEntry = enumClass.declarations.filterIsInstance<IrEnumEntry>().singleOrNull { it.name.asString() == enumEntryName }
enumEntry?.interpret()?.check { return it }
?: throw IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName")
return Next
}
}
internal object RegexReplace : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.text.Regex.replace" && irFunction.valueParameters.size == 2
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val states = stack.getAll().map { it.state }
val regex = states.filterIsInstance<Wrapper>().single().value as Regex
val input = states.filterIsInstance<Primitive<*>>().single().asString()
val transform = states.filterIsInstance<Lambda>().single().irFunction
val matchResultParameter = transform.valueParameters.single()
val result = regex.replace(input) {
val itAsState = Variable(matchResultParameter.symbol, Wrapper(it, matchResultParameter.type.classOrNull!!.owner))
runBlocking { stack.newFrame(initPool = listOf(itAsState)) { transform.interpret() } }//.check { return it }
stack.popReturnValue().asString()
}
stack.pushReturnValue(result.toState(irFunction.returnType))
return Next
}
}
internal object EnumHashCode : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.Enum.hashCode"
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val hashCode = stack.getAll().single().state.hashCode()
stack.pushReturnValue(hashCode.toState(irFunction.returnType))
return Next
}
}
internal object JsPrimitives : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.Long.<init>" || fqName == "kotlin.Char.<init>"
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.Long.<init>" -> {
val low = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
val high = stack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
stack.pushReturnValue((high.toLong().shl(32) + low).toState(irFunction.returnType))
}
"kotlin.Char.<init>" -> {
val value = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
stack.pushReturnValue(value.toChar().toState(irFunction.returnType))
}
}
return Next
}
}
internal object ArrayConstructor : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName.matches("kotlin\\.(Byte|Char|Short|Int|Long|Float|Double|Boolean|)Array\\.<init>".toRegex())
}
override suspend fun evaluate(
irFunction: IrFunction, stack: Stack, interpret: suspend IrElement.() -> ExecutionResult
): ExecutionResult {
val sizeDescriptor = irFunction.valueParameters[0].symbol
val size = stack.getVariable(sizeDescriptor).state.asInt()
val arrayValue = MutableList<Any>(size) { 0 }
if (irFunction.valueParameters.size == 2) {
val initDescriptor = irFunction.valueParameters[1].symbol
val initLambda = stack.getVariable(initDescriptor).state as Lambda
val index = initLambda.irFunction.valueParameters.single()
val nonLocalDeclarations = initLambda.extractNonLocalDeclarations()
for (i in 0 until size) {
val indexVar = listOf(Variable(index.symbol, i.toState(index.type)))
// TODO throw exception if label != RETURN
stack.newFrame(
asSubFrame = initLambda.irFunction.isLocal || initLambda.irFunction.isInline,
initPool = nonLocalDeclarations + indexVar
) { initLambda.irFunction.body!!.interpret() }.check(ReturnLabel.RETURN) { return it }
arrayValue[i] = stack.popReturnValue().let { (it as? Wrapper)?.value ?: (it as? Primitive<*>)?.value ?: it }
}
}
stack.pushReturnValue(arrayValue.toPrimitiveStateArray(irFunction.parentAsClass.defaultType))
return Next
}
}
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.stack
import org.jetbrains.kotlin.backend.common.interpreter.state.State
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
internal interface Frame {
fun addVar(variable: Variable)
fun addAll(variables: List<Variable>)
fun getVariable(symbol: IrSymbol): Variable?
fun getAll(): List<Variable>
fun contains(symbol: IrSymbol): Boolean
fun pushReturnValue(state: State)
fun pushReturnValue(frame: Frame) // TODO rename to getReturnValueFrom
fun peekReturnValue(): State
fun popReturnValue(): State
fun hasReturnValue(): Boolean
}
// TODO replace exceptions with InterpreterException
internal class InterpreterFrame(
private val pool: MutableList<Variable> = mutableListOf(),
private val typeArguments: List<Variable> = listOf()
) : Frame {
private val returnStack: MutableList<State> = mutableListOf()
override fun addVar(variable: Variable) {
pool.add(variable)
}
override fun addAll(variables: List<Variable>) {
pool.addAll(variables)
}
override fun getVariable(symbol: IrSymbol): Variable? {
return (if (symbol is IrTypeParameterSymbol) typeArguments else pool).firstOrNull { it.symbol == symbol }
}
override fun getAll(): List<Variable> {
return pool
}
override fun contains(symbol: IrSymbol): Boolean {
return (typeArguments + pool).any { it.symbol == symbol }
}
override fun pushReturnValue(state: State) {
returnStack += state
}
override fun pushReturnValue(frame: Frame) {
if (frame.hasReturnValue()) this.pushReturnValue(frame.popReturnValue())
}
override fun hasReturnValue(): Boolean {
return returnStack.isNotEmpty()
}
override fun peekReturnValue(): State {
if (returnStack.isNotEmpty()) {
return returnStack.last()
}
throw NoSuchElementException("Return values stack is empty")
}
override fun popReturnValue(): State {
if (returnStack.isNotEmpty()) {
val item = returnStack.last()
returnStack.removeAt(returnStack.size - 1)
return item
}
throw NoSuchElementException("Return values stack is empty")
}
}
@@ -1,149 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.stack
import org.jetbrains.kotlin.backend.common.interpreter.ExecutionResult
import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterException
import org.jetbrains.kotlin.backend.common.interpreter.getCapitalizedFileName
import org.jetbrains.kotlin.backend.common.interpreter.state.State
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.name
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.fileEntry
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal interface Stack {
suspend fun newFrame(
asSubFrame: Boolean = false, initPool: List<Variable> = listOf(), block: suspend () -> ExecutionResult
): ExecutionResult
fun setCurrentFrameName(irFunction: IrFunction)
fun getStackTrace(): List<String>
fun clean()
fun addVar(variable: Variable)
fun addAll(variables: List<Variable>)
fun getVariable(symbol: IrSymbol): Variable
fun getAll(): List<Variable>
fun contains(symbol: IrSymbol): Boolean
fun hasReturnValue(): Boolean
fun pushReturnValue(state: State)
fun popReturnValue(): State
fun peekReturnValue(): State
}
internal class StackImpl : Stack {
private val frameList = mutableListOf(FrameContainer()) // first frame is default, it is easier to work when last() is not null
private fun getCurrentFrame() = frameList.last()
override suspend fun newFrame(asSubFrame: Boolean, initPool: List<Variable>, block: suspend () -> ExecutionResult): ExecutionResult {
val typeArgumentsPool = initPool.filter { it.symbol is IrTypeParameterSymbol }
val valueArguments = initPool.filter { it.symbol !is IrTypeParameterSymbol }
val newFrame = InterpreterFrame(valueArguments.toMutableList(), typeArgumentsPool)
if (asSubFrame) getCurrentFrame().addSubFrame(newFrame) else frameList.add(FrameContainer(newFrame))
return try {
block()
} finally {
if (asSubFrame) getCurrentFrame().removeSubFrame() else removeLastFrame()
}
}
private fun removeLastFrame() {
if (frameList.size > 1 && getCurrentFrame().hasReturnValue()) frameList[frameList.lastIndex - 1].pushReturnValue(getCurrentFrame())
frameList.removeAt(frameList.lastIndex)
}
override fun setCurrentFrameName(irFunction: IrFunction) {
val fileName = irFunction.file.name
val fileNameCapitalized = irFunction.getCapitalizedFileName()
val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1
if (getCurrentFrame().frameEntryPoint == null)
getCurrentFrame().frameEntryPoint = "at $fileNameCapitalized.${irFunction.fqNameWhenAvailable}($fileName:$lineNum)"
}
override fun getStackTrace(): List<String> {
// TODO implement some sort of cache
return frameList.mapNotNull { it.frameEntryPoint }
}
override fun clean() {
frameList.clear()
frameList.add(FrameContainer())
}
override fun addVar(variable: Variable) {
getCurrentFrame().addVar(variable)
}
override fun addAll(variables: List<Variable>) {
getCurrentFrame().addAll(variables)
}
override fun getVariable(symbol: IrSymbol): Variable {
return getCurrentFrame().getVariable(symbol)
}
override fun getAll(): List<Variable> {
return getCurrentFrame().getAll()
}
override fun contains(symbol: IrSymbol): Boolean {
return getCurrentFrame().contains(symbol)
}
override fun hasReturnValue(): Boolean {
return getCurrentFrame().hasReturnValue()
}
override fun pushReturnValue(state: State) {
getCurrentFrame().pushReturnValue(state)
}
override fun popReturnValue(): State {
return getCurrentFrame().popReturnValue()
}
override fun peekReturnValue(): State {
return getCurrentFrame().peekReturnValue()
}
}
private class FrameContainer(current: Frame = InterpreterFrame()) {
var frameEntryPoint: String? = null
private val innerStack = mutableListOf(current)
private fun getTopFrame() = innerStack.first()
fun addSubFrame(frame: Frame) {
innerStack.add(0, frame)
}
fun removeSubFrame() {
if (getTopFrame().hasReturnValue() && innerStack.size > 1) innerStack[1].pushReturnValue(getTopFrame())
innerStack.removeAt(0)
}
fun addVar(variable: Variable) = getTopFrame().addVar(variable)
fun addAll(variables: List<Variable>) = getTopFrame().addAll(variables)
fun getAll() = innerStack.flatMap { it.getAll() }
fun getVariable(symbol: IrSymbol): Variable {
return innerStack.firstNotNullResult { it.getVariable(symbol) }
?: throw InterpreterException("$symbol not found") // TODO better message
}
fun contains(symbol: IrSymbol) = innerStack.any { it.contains(symbol) }
fun hasReturnValue() = getTopFrame().hasReturnValue()
fun pushReturnValue(container: FrameContainer) = getTopFrame().pushReturnValue(container.getTopFrame())
fun pushReturnValue(state: State) = getTopFrame().pushReturnValue(state)
fun popReturnValue() = getTopFrame().popReturnValue()
fun peekReturnValue() = getTopFrame().peekReturnValue()
override fun toString() = frameEntryPoint ?: "Not defined"
}
@@ -1,12 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.stack
import org.jetbrains.kotlin.backend.common.interpreter.state.State
import org.jetbrains.kotlin.ir.symbols.IrSymbol
// TODO maybe switch to typealias and use map instead of list
internal data class Variable(val symbol: IrSymbol, var state: State)
@@ -1,54 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isInterface
internal class Common private constructor(
override val irClass: IrClass, override val fields: MutableList<Variable>
) : Complex(irClass, fields) {
constructor(irClass: IrClass) : this(irClass, mutableListOf())
fun setSuperClassRecursive() {
var thisClass: Common? = this
while (thisClass != null) {
val superClass = thisClass.irClass.superTypes.filterNot { it.isInterface() }.singleOrNull()
val superClassOwner = superClass?.classOrNull?.owner
val superClassState = superClassOwner?.let { Common(it) }
superClassState?.let { thisClass!!.setSuperClassInstance(it) }
if (superClass == null && thisClass.irClass.superTypes.isNotEmpty()) {
// cover the case when super type implement an interface and so doesn't have explicit any as super class
thisClass.setSuperClassInstance(Common(getAnyClassRecursive()))
}
thisClass = superClassState
}
}
private fun getAnyClassRecursive(): IrClass {
var owner = irClass.superTypes.first().classOrNull!!.owner
while (owner.superTypes.isNotEmpty()) owner = owner.superTypes.first().classOrNull!!.owner
return owner
}
fun getToStringFunction(): IrFunction {
return irClass.declarations.filterIsInstance<IrFunction>()
.filter { it.name.asString() == "toString" }
.first { it.valueParameters.isEmpty() }
.let { getOverridden(it as IrSimpleFunction, this) }
}
override fun toString(): String {
return "Common(obj='${irClass.fqNameForIrSerialization}', super=$superClass, values=$fields)"
}
}
@@ -1,92 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.getCorrectReceiverByFunction
import org.jetbrains.kotlin.backend.common.interpreter.getLastOverridden
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.overrides
internal abstract class Complex(override val irClass: IrClass, override val fields: MutableList<Variable>) : State {
var superClass: Complex? = null
var subClass: Complex? = null
val interfaces: MutableList<Complex> = mutableListOf() // filled lazily, as needed
override val typeArguments: MutableList<Variable> = mutableListOf()
var outerClass: Variable? = null
fun setSuperClassInstance(superClass: Complex) {
if (this.irClass == superClass.irClass) {
// if superClass is just secondary constructor instance, then copy properties that isn't already present in instance
superClass.fields.forEach { if (!this.contains(it)) fields.add(it) }
this.superClass = superClass.superClass
superClass.superClass?.subClass = this
} else {
this.superClass = superClass
superClass.subClass = this
}
}
fun getOriginal(): Complex {
return subClass?.getOriginal() ?: this
}
fun irClassFqName(): String {
return irClass.fqNameForIrSerialization.toString()
}
private fun contains(variable: Variable) = fields.any { it.symbol == variable.symbol }
private fun getIrFunction(symbol: IrFunctionSymbol): IrFunction? {
val propertyGetters = irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.getter }
val functions = irClass.declarations.filterIsInstance<IrFunction>()
return (propertyGetters + functions).firstOrNull {
if (it is IrSimpleFunction) it.overrides(symbol.owner as IrSimpleFunction) else it == symbol.owner
}
}
private fun getThisOrSuperReceiver(superIrClass: IrClass?): Complex? {
return when {
superIrClass == null -> this.getOriginal()
superIrClass.isInterface -> Common(superIrClass).apply {
interfaces.add(this)
this.subClass = this@Complex
}
else -> this.superClass
}
}
protected fun getOverridden(owner: IrSimpleFunction, qualifier: State?): IrSimpleFunction {
if (!owner.isFakeOverride) return owner
if (qualifier == null || qualifier is ExceptionState || (qualifier as? Complex)?.superClass == null) {
return owner.getLastOverridden() as IrSimpleFunction
}
val overriddenOwner = owner.overriddenSymbols.single().owner
return when {
overriddenOwner.body != null -> overriddenOwner
else -> getOverridden(overriddenOwner, qualifier.superClass!!)
}
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
val receiver = getThisOrSuperReceiver(expression.superQualifierSymbol?.owner) ?: return null
val irFunction = receiver.getIrFunction(expression.symbol) ?: return null
return when (irFunction.body) {
null -> getOverridden(irFunction as IrSimpleFunction, this.getCorrectReceiverByFunction(irFunction))
else -> irFunction
}
}
}
@@ -1,152 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.getLastOverridden
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.backend.common.interpreter.toState
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.util.isSubclassOf
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
import kotlin.math.min
internal class ExceptionState private constructor(
override val irClass: IrClass, override val fields: MutableList<Variable>, stackTrace: List<String>
) : Complex(irClass, fields) {
private lateinit var exceptionFqName: String
private val exceptionHierarchy = mutableListOf<String>()
private val messageProperty = irClass.getPropertyByName("message")
private val causeProperty = irClass.getPropertyByName("cause")
private val stackTrace: List<String> = stackTrace.reversed()
init {
if (!this::exceptionFqName.isInitialized) this.exceptionFqName = irClassFqName()
if (fields.none { it.symbol == messageProperty.symbol }) {
setMessage()
}
}
constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace) {
var wrapperSuperType: Complex? = common
while (wrapperSuperType != null && wrapperSuperType !is Wrapper) wrapperSuperType = (wrapperSuperType as Common).superClass
setUpCauseIfNeeded(wrapperSuperType as? Wrapper)
}
constructor(wrapper: Wrapper, stackTrace: List<String>) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace) {
setUpCauseIfNeeded(wrapper)
}
constructor(
exception: Throwable, irClass: IrClass, stackTrace: List<String>
) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) {
if (irClass.name.asString() != exception::class.java.simpleName) {
// ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy
this.exceptionFqName = exception::class.java.name
exceptionHierarchy += this.exceptionFqName
generateSequence(exception::class.java.superclass) { it.superclass }.forEach { exceptionHierarchy += it.name }
exceptionHierarchy.removeAt(exceptionHierarchy.lastIndex) // remove unnecessary java.lang.Object
}
}
data class ExceptionData(val state: ExceptionState) : Throwable() {
override val message: String? = state.getMessage()
override fun fillInStackTrace() = this
override fun toString(): String = state.getMessageWithName()
}
private fun setUpCauseIfNeeded(wrapper: Wrapper?) {
val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionData
setCause(cause?.state)
if (getMessage() == null && cause != null) {
val causeMessage = cause.state.exceptionFqName + (cause.state.getMessage()?.let { ": $it" } ?: "")
setMessage(causeMessage)
}
}
fun isSubtypeOf(ancestor: IrClass): Boolean {
if (exceptionHierarchy.isNotEmpty()) {
return exceptionHierarchy.any { it.contains(ancestor.name.asString()) }
}
return irClass.isSubclassOf(ancestor)
}
private fun setMessage(messageValue: String? = null) {
setField(Variable(messageProperty.symbol, Primitive(messageValue, messageProperty.getter!!.returnType)))
}
private fun setCause(causeValue: State?) {
setField(Variable(causeProperty.symbol, causeValue ?: Primitive<Throwable?>(null, causeProperty.getter!!.returnType)))
}
fun getMessage(): String? = (getState(messageProperty.symbol) as Primitive<*>).value as String?
private fun getMessageWithName(): String = getMessage()?.let { "$exceptionFqName: $it" } ?: exceptionFqName
fun getCause(): ExceptionState? = getState(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
fun getFullDescription(): String {
// TODO remainder of the stack trace with "..."
val message = getMessage().let { if (it?.isNotEmpty() == true) ": $it" else "" }
val prefix = if (stackTrace.isNotEmpty()) "\n\t" else ""
val postfix = if (stackTrace.size > 10) "\n\t..." else ""
val causeMessage = getCause()?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: ""
return "Exception $exceptionFqName$message" +
stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) +
causeMessage
}
fun getThisAsCauseForException() = ExceptionData(this)
companion object {
private fun IrClass.getPropertyByName(name: String): IrProperty {
val property = this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty
return (property.getter!!.getLastOverridden() as IrSimpleFunction).correspondingPropertySymbol!!.owner
}
private fun evaluateFields(exception: Throwable, irClass: IrClass, stackTrace: List<String>): MutableList<Variable> {
val messageProperty = irClass.getPropertyByName("message")
val causeProperty = irClass.getPropertyByName("cause")
val messageVar = Variable(messageProperty.symbol, exception.message.toState(messageProperty.getter!!.returnType))
val causeVar = exception.cause?.let {
Variable(causeProperty.symbol, ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" }))
}
return listOfNotNull(messageVar, causeVar).toMutableList()
}
private fun evaluateAdditionalStackTrace(e: Throwable): List<String> {
// TODO do we really need this?... It will point to JVM stdlib
val additionalStack = mutableListOf<String>()
if (e.stackTrace.any { it.className == "java.lang.invoke.MethodHandle" }) {
for ((index, stackTraceElement) in e.stackTrace.withIndex()) {
if (stackTraceElement.methodName == "invokeWithArguments") {
additionalStack.addAll(e.stackTrace.slice(0 until index).reversed().map { "at $it" })
break
}
}
var cause = e.cause
val lastNeededValue = e.stackTrace.first().let { it.className + "." + it.methodName }
while (cause != null) {
for ((causeStackIndex, causeStackTraceElement) in cause.stackTrace.withIndex()) {
val currentStackTraceValue = causeStackTraceElement.let { it.className + "." + it.methodName }
if (currentStackTraceValue == lastNeededValue) {
cause.stackTrace = cause.stackTrace.sliceArray(0 until causeStackIndex).reversedArray()
break
}
}
cause = cause.cause
}
}
return additionalStack
}
}
}
@@ -1,37 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.getLastOverridden
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.utils.addToStdlib.cast
internal class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val typeArguments: MutableList<Variable> = mutableListOf()
private val invokeSymbol = irClass.declarations
.single { it.nameForIrSerialization.asString() == "invoke" }
.cast<IrSimpleFunction>()
.getLastOverridden().symbol
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
return if (invokeSymbol == expression.symbol) irFunction else null
}
override fun toString(): String {
val receiver = (irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type)?.render()
val arguments = irFunction.valueParameters.joinToString(prefix = "(", postfix = ")") { it.type.render() }
val returnType = irFunction.returnType.render()
return ("$arguments -> $returnType").let { if (receiver != null) "$receiver.$it" else it }
}
}
@@ -1,63 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.getLastOverridden
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.overrides
internal class Primitive<T>(var value: T, val type: IrType) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val typeArguments: MutableList<Variable> = mutableListOf()
override val irClass: IrClass = type.classOrNull!!.owner
override fun getState(symbol: IrSymbol): State {
return super.getState(symbol) ?: this
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
val owner = expression.symbol.owner
// must add property's getter to declaration's list because they are not present in ir class for primitives
val declarations = irClass.declarations.map { if (it is IrProperty) it.getter else it }
return declarations.filterIsInstance<IrFunction>()
.firstOrNull { it.symbol == owner.symbol || (it is IrSimpleFunction && owner is IrSimpleFunction && it.overrides(owner)) }
?.let { if (it.isFakeOverride) it.getLastOverridden() else it }
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Primitive<*>
if (value != other.value) return false
if (type != other.type) return false
if (fields != other.fields) return false
return true
}
override fun hashCode(): Int {
var result = value?.hashCode() ?: 0
result = 31 * result + type.hashCode()
result = 31 * result + fields.hashCode()
return result
}
override fun toString(): String {
return "Primitive(value=$value, type=${irClass.defaultType})"
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
internal interface State {
val fields: MutableList<Variable>
val irClass: IrClass
val typeArguments: MutableList<Variable>
fun getState(symbol: IrSymbol): State? {
return fields.firstOrNull { it.symbol == symbol }?.state
}
fun setField(newVar: Variable) {
when (val oldState = fields.firstOrNull { it.symbol == newVar.symbol }) {
null -> fields.add(newVar) // newVar isn't present in value list
else -> fields[fields.indexOf(oldState)].state = newVar.state // newVar already present
}
}
fun addTypeArguments(typeArguments: List<Variable>) {
this.typeArguments.addAll(typeArguments)
}
fun getIrFunctionByIrCall(expression: IrCall): IrFunction?
}
internal fun State.isNull() = this is Primitive<*> && this.value == null
internal fun State.asInt() = (this as Primitive<*>).value as Int
internal fun State.asBoolean() = (this as Primitive<*>).value as Boolean
internal fun State.asString() = (this as Primitive<*>).value.toString()
internal fun State.asBooleanOrNull() = (this as? Primitive<*>)?.value as? Boolean
internal fun State.isSubtypeOf(other: IrType): Boolean {
if (this is Primitive<*> && this.value == null) return other.isNullable()
if (this is Primitive<*> && this.type.isArray() && other.isArray()) {
val thisClass = this.typeArguments.single().state.irClass.symbol
val otherArgument = (other as IrSimpleType).arguments.single()
if (otherArgument is IrStarProjection) return true
return thisClass.isSubtypeOfClass(otherArgument.typeOrNull!!.classOrNull!!)
}
return this.irClass.defaultType.isSubtypeOfClass(other.classOrNull!!)
}
/**
* This method used to check if for not null parameter there was passed null argument.
*/
internal fun State.checkNullability(irType: IrType?, throwException: () -> Nothing = { throw NullPointerException() }): State {
if (irType !is IrSimpleType) return this
if (this.isNull() && !irType.isNullable()) {
throwException()
}
return this
}
@@ -1,203 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.interpreter.state
import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.backend.common.interpreter.getEvaluateIntrinsicValue
import org.jetbrains.kotlin.backend.common.interpreter.getLastOverridden
import org.jetbrains.kotlin.backend.common.interpreter.getPrimitiveClass
import org.jetbrains.kotlin.backend.common.interpreter.hasAnnotation
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(irClass, mutableListOf()) {
private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe()
private val receiverClass = irClass.defaultType.getClass(true)
fun getMethod(irFunction: IrFunction): MethodHandle? {
if (irFunction.getEvaluateIntrinsicValue()?.isEmpty() == true) return null // this method will handle IntrinsicEvaluator
// if function is actually a getter, then use "get${property.name.capitalize()}" as method name
val propertyName = (irFunction as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.name?.asString()
val propertyCall = listOfNotNull(propertyName, "get${propertyName?.capitalize()}")
.firstOrNull { receiverClass.methods.any { method -> method.name == it } }
val intrinsicName = getJavaOriginalName(irFunction)
val methodName = intrinsicName ?: propertyCall ?: irFunction.name.toString()
val methodType = irFunction.getMethodType()
return MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType)
}
// This method is used to get correct java method name
private fun getJavaOriginalName(irFunction: IrFunction): String? {
return when (irFunction.getLastOverridden().fqNameWhenAvailable?.asString()) {
"kotlin.collections.Map.<get-entries>" -> "entrySet"
"kotlin.collections.Map.<get-keys>" -> "keySet"
"kotlin.CharSequence.get" -> "charAt"
"kotlin.collections.MutableList.removeAt" -> "remove"
else -> null
}
}
companion object {
private val companionObjectValue = mapOf<String, Any>("kotlin.text.Regex\$Companion" to Regex.Companion)
fun getCompanionObject(irClass: IrClass): Wrapper {
val objectName = irClass.getEvaluateIntrinsicValue()!!
val objectValue = companionObjectValue[objectName] ?: throw AssertionError("Companion object $objectName cannot be interpreted")
return Wrapper(objectValue, irClass)
}
fun getConstructorMethod(irConstructor: IrFunction): MethodHandle? {
val intrinsicValue = irConstructor.parentAsClass.getEvaluateIntrinsicValue()
if (intrinsicValue == "kotlin.Char" || intrinsicValue == "kotlin.Long") return null
val methodType = irConstructor.getMethodType()
return MethodHandles.lookup().findConstructor(irConstructor.returnType.getClass(true), methodType)
}
fun getStaticMethod(irFunction: IrFunction): MethodHandle? {
val intrinsicName = irFunction.getEvaluateIntrinsicValue()
if (intrinsicName?.isEmpty() == true) return null
val jvmClassName = Class.forName(intrinsicName!!)
val methodType = irFunction.getMethodType()
return MethodHandles.lookup().findStatic(jvmClassName, irFunction.name.asString(), methodType)
}
fun getStaticGetter(field: IrField): MethodHandle? {
val jvmClass = field.parentAsClass.defaultType.getClass(true)
val returnType = field.type.getClass(false)
return MethodHandles.lookup().findStaticGetter(jvmClass, field.name.asString(), returnType)
}
fun getEnumEntry(enumClass: IrClass): MethodHandle? {
val intrinsicName = enumClass.getEvaluateIntrinsicValue()
if (intrinsicName?.isEmpty() == true) return null
val enumClassName = Class.forName(intrinsicName!!)
val methodType = MethodType.methodType(enumClassName, String::class.java)
return MethodHandles.lookup().findStatic(enumClassName, "valueOf", methodType)
}
private fun IrFunction.getMethodType(): MethodType {
val argsClasses = this.valueParameters.map { it.type.getClass(this.isValueParameterPrimitiveAsObject(it.index)) }
return if (this is IrSimpleFunction) {
// for regular methods and functions
val returnClass = this.returnType.getClass(this.isReturnTypePrimitiveAsObject())
val extensionClass = this.extensionReceiverParameter?.type?.getClass(this.isExtensionReceiverPrimitive())
MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses)
} else {
// for constructors
MethodType.methodType(Void::class.javaPrimitiveType, argsClasses)
}
}
private fun IrType.getClass(asObject: Boolean): Class<out Any> {
val owner = this.classOrNull?.owner
val fqName = owner?.fqNameWhenAvailable?.asString()
val notNullType = this.makeNotNull()
//TODO check if primitive array is possible here
return when {
notNullType.isPrimitiveType() || notNullType.isString() -> getPrimitiveClass(notNullType, asObject)!!
notNullType.isArray() -> if (asObject) Array<Any?>::class.javaObjectType else Array<Any?>::class.java
notNullType.isNothing() -> Nothing::class.java
notNullType.isAny() -> Any::class.java
notNullType.isNumber() -> Number::class.java
notNullType.isCharSequence() -> CharSequence::class.java
notNullType.isComparable() -> Comparable::class.java
notNullType.isThrowable() -> Throwable::class.java
notNullType.isIterable() -> Iterable::class.java
// TODO implement function mapping; all complexity is to map big arity to FunctionN
//notNullType.isKFunction() -> Class.forName("kotlin.reflect.KFunction")
//notNullType.isFunction() -> Class.forName("kotlin.jvm.functions.Function_TODO")
//notNullType.isSuspendFunction() || notNullType.isKSuspendFunction() -> throw AssertionError()
fqName == "kotlin.Enum" -> Enum::class.java
fqName == "kotlin.collections.Collection" || fqName == "kotlin.collections.MutableCollection" -> Collection::class.java
fqName == "kotlin.collections.List" || fqName == "kotlin.collections.MutableList" -> List::class.java
fqName == "kotlin.collections.Set" || fqName == "kotlin.collections.MutableSet" -> Set::class.java
fqName == "kotlin.collections.Map" || fqName == "kotlin.collections.MutableMap" -> Map::class.java
fqName == "kotlin.collections.ListIterator" || fqName == "kotlin.collections.MutableListIterator" -> ListIterator::class.java
fqName == "kotlin.collections.Iterator" || fqName == "kotlin.collections.MutableIterator" -> Iterator::class.java
fqName == "kotlin.collections.Map.Entry" || fqName == "kotlin.collections.MutableMap.MutableEntry" -> Map.Entry::class.java
fqName == "kotlin.collections.ListIterator" || fqName == "kotlin.collections.MutableListIterator" -> ListIterator::class.java
owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Class.forName(owner!!.getEvaluateIntrinsicValue())
fqName == null -> Any::class.java // null if this.isTypeParameter()
else -> Class.forName(fqName.replaceDotWithDollarForInnerClasses())
}
}
private fun String.replaceDotWithDollarForInnerClasses(): String? {
// TODO come up with something better
val names = this.split(".")
val result = StringBuilder()
for (i in 0 until (names.size - 1)) {
result.append(names[i])
if (names[i][0].isUpperCase() && names[i + 1][0].isUpperCase()) result.append("$") else result.append(".")
}
result.append(names.last())
return result.toString()
}
private fun IrFunction.getOriginalOverriddenSymbols(): MutableList<IrFunctionSymbol> {
val overriddenSymbols = mutableListOf<IrFunctionSymbol>()
if (this is IrFunctionImpl) {
val pool = this.overriddenSymbols.toMutableList()
val iterator = pool.listIterator()
for (symbol in iterator) {
if (symbol.owner.overriddenSymbols.isEmpty()) {
overriddenSymbols += symbol
iterator.remove()
} else {
symbol.owner.overriddenSymbols.forEach { iterator.add(it) }
}
}
}
if (overriddenSymbols.isEmpty()) overriddenSymbols.add(this.symbol)
return overriddenSymbols
}
private fun IrFunction.isExtensionReceiverPrimitive(): Boolean {
return this.extensionReceiverParameter?.type?.isPrimitiveType() == false
}
private fun IrFunction.isReturnTypePrimitiveAsObject(): Boolean {
for (symbol in getOriginalOverriddenSymbols()) {
if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) {
return false
}
}
return true
}
private fun IrFunction.isValueParameterPrimitiveAsObject(index: Int): Boolean {
for (symbol in getOriginalOverriddenSymbols()) {
if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) {
return false
}
}
return true
}
}
override fun toString(): String {
return "Wrapper(obj='$typeFqName', value=$value)"
}
}