Implement basic proxy for Common state

For now proxy works only for Any class methods and only for Common
state.
This commit is contained in:
Ivan Kylchik
2020-08-26 16:36:53 +03:00
committed by TeamCityServer
parent 3c0c8d97cc
commit 4c75576414
8 changed files with 121 additions and 108 deletions
@@ -89,6 +89,84 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
}
}
internal class Proxy(val state: Common, val interpreter: IrInterpreter) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Proxy
val valueArguments = mutableListOf<Variable>()
val equalsFun = state.getEqualsFunction()
if (equalsFun.isFakeOverriddenFromAny()) return this.state.getOriginal() === other.state.getOriginal()
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state))
return with(interpreter) {
stack.newFrame(initPool = valueArguments) {
equalsFun.interpret()
}
(stack.popReturnValue() as Primitive<*>).value as Boolean
} // TODO check
}
override fun hashCode(): Int {
val valueArguments = mutableListOf<Variable>()
val hashCodeFun = state.getHashCodeFunction()
if (hashCodeFun.isFakeOverriddenFromAny()) return System.identityHashCode(state.getOriginal())
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return with(interpreter) {
stack.newFrame(initPool = valueArguments) {
hashCodeFun.interpret()
}
(stack.popReturnValue() as Primitive<*>).value as Int
}// TODO check
}
override fun toString(): String {
val valueArguments = mutableListOf<Variable>()
val toStringFun = state.getToStringFunction()
if (toStringFun.isFakeOverriddenFromAny()) {
return "${state.getOriginal().irClass.internalName()}@" + System.identityHashCode(state).toString(16).padStart(8, '0')
}
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return with(interpreter) {
stack.newFrame(initPool = valueArguments) {
toStringFun.interpret()
}
(stack.popReturnValue() as Primitive<*>).value as String
}// TODO check
}
}
internal fun State.wrapAsProxyIfNeeded(): Any? {
return when (this) {
is ExceptionState -> this.getThisAsCauseForException()
is Wrapper -> this.value
is Primitive<*> -> this.value
is Common -> Proxy(this, this@IrInterpreter)
is Lambda -> this // TODO as Proxy
else -> throw AssertionError("${this::class} is unsupported as argument for wrap function")
}
}
internal fun IrFunction.getArgsForMethodInvocation(args: List<Variable>): List<Any?> {
val argsValues = args.map { it.state.wrapAsProxyIfNeeded() }.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
}
private fun IrElement.interpret(): ExecutionResult {
try {
incrementAndCheckCommands()
@@ -172,19 +250,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
val receiverType = irFunction.dispatchReceiverParameter?.type
val argsType = listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }
val argsValues = args.map {
when (it) {
is Wrapper -> it.value // wrapper can be used in built in calculation, for example, in null or equality checks
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 also can be used, for example, in null check or toString
else -> TODO("unsupported type of argument for builtins calculations: ${it::class.java}")
}
}
val argsValues = args.map { it.wrapAsProxyIfNeeded() }
fun IrType.getOnlyName(): String {
return when {
@@ -209,7 +275,6 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
?: throw InterpreterMethodNotFoundError("For given function $signature there is no entry in binary map")
when (methodName) {
"rangeTo" -> return calculateRangeTo(irFunction.returnType)
"EQEQ" -> return calculateEquals(argsValues[0], argsValues[1])
else -> function.invoke(argsValues[0], argsValues[1])
}
}
@@ -241,24 +306,6 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
}
}
private fun calculateEquals(first: Any?, second: Any?): ExecutionResult {
if (first == null || second == null || first !is Common || second !is Common) {
return Next.apply { stack.pushReturnValue((first == second).toState(irBuiltIns.booleanType)) }
}
val valueArguments = mutableListOf<Variable>()
val equalsFun = first.getEqualsFunction()
if (equalsFun.isFakeOverriddenFromAny()) {
return Next.apply { stack.pushReturnValue((first == second).toState(irBuiltIns.booleanType)) }
}
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, first)) }
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, second))
return stack.newFrame(initPool = valueArguments) {
equalsFun.interpret()
}.check { return it }
}
private fun interpretValueParameters(
expression: IrFunctionAccessExpression, irFunction: IrFunction, pool: MutableList<Variable>
): ExecutionResult {
@@ -722,7 +769,8 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
is Primitive<*> -> arrayToList(result.value)
is Common -> when {
result.irClass.defaultType.isUnsignedArray() -> arrayToList((result.fields.single().state as Primitive<*>).value)
else -> listOf(result)
result.irClass.defaultType.isUnsigned() -> listOf(result)
else -> listOf(Proxy(result, this))
}
else -> listOf(result)
}
@@ -57,6 +57,7 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression {
internal fun Any?.toState(irType: IrType): State {
return when (this) {
is IrInterpreter.Proxy -> this.state
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)
@@ -130,27 +131,6 @@ internal fun getPrimitiveClass(irType: IrType, asObject: Boolean = false): Class
}
}
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
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.ir.interpreter.builtins
import org.jetbrains.kotlin.ir.interpreter.internalName
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.state.*
/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */
@@ -139,7 +139,7 @@ val unaryFunctions = mapOf<CompileTimeFunction, Function1<Any?, Any?>>(
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>("toString", "Any") { a -> a.toString() },
unaryOperation<Any?>("CHECK_NOT_NULL", "T0?") { a -> a!! },
unaryOperation<ExceptionState>("message", "Throwable") { a -> a.getMessage() },
unaryOperation<ExceptionState>("cause", "Throwable") { a -> a.getCause() }
@@ -450,7 +450,7 @@ val binaryFunctions = mapOf<CompileTimeFunction, Function2<Any?, Any?, Any?>>(
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<Any?, Any?>("EQEQEQ", "Any?", "Any?") { a, b -> if (a is IrInterpreter.Proxy && b is IrInterpreter.Proxy) a.state === b.state else 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 },
@@ -470,11 +470,3 @@ val ternaryFunctions = mapOf<CompileTimeFunction, Function3<Any?, Any?, Any?, An
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.internalName()}@" + System.identityHashCode(this).toString(16).padStart(8, '0')
else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0'))
}
}
@@ -44,7 +44,7 @@ internal object ArrayOf : IntrinsicBase() {
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val elementsVariable = irFunction.valueParameters.single().symbol
val array = irFunction.getArgsForMethodInvocation(listOf(stack.getVariable(elementsVariable))).toTypedArray()
val array = (stack.getVariable(elementsVariable).state as Primitive<*>).value as Array<out Any?>
val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) }
stack.pushReturnValue(array.toState(irFunction.returnType).apply { addTypeArguments(typeArguments) })
return Next
@@ -5,15 +5,11 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.interpreter.isInterface
import org.jetbrains.kotlin.ir.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.types.isNullableAny
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.name.Name
internal class Common private constructor(
override val irClass: IrClass, override val fields: MutableList<Variable>
@@ -43,23 +39,6 @@ internal class Common private constructor(
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) }
}
fun getEqualsFunction(): IrFunction {
val equalsFun = irClass.declarations
.filterIsInstance<IrSimpleFunction>()
.single {
it.name == Name.identifier("equals") && it.dispatchReceiverParameter != null
&& it.valueParameters.size == 1 && it.valueParameters[0].type.isNullableAny()
}
return getOverridden(equalsFun, this)
}
override fun toString(): String {
return "Common(obj='${irClass.fqNameForIrSerialization}', super=$superClass, values=$fields)"
}
@@ -14,9 +14,11 @@ 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.types.isNullableAny
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.overrides
import org.jetbrains.kotlin.name.Name
internal abstract class Complex(override val irClass: IrClass, override val fields: MutableList<Variable>) : State {
var superClass: Complex? = null
@@ -90,4 +92,28 @@ internal abstract class Complex(override val irClass: IrClass, override val fiel
else -> irFunction
}
}
fun getEqualsFunction(): IrSimpleFunction {
val equalsFun = irClass.declarations
.filterIsInstance<IrSimpleFunction>()
.single {
it.name == Name.identifier("equals") && it.dispatchReceiverParameter != null
&& it.valueParameters.size == 1 && it.valueParameters[0].type.isNullableAny()
}
return getOverridden(equalsFun, this)
}
fun getHashCodeFunction(): IrSimpleFunction {
return irClass.declarations.filterIsInstance<IrSimpleFunction>()
.filter { it.name.asString() == "hashCode" }
.first { it.valueParameters.isEmpty() }
.let { getOverridden(it, this) }
}
fun getToStringFunction(): IrSimpleFunction {
return irClass.declarations.filterIsInstance<IrSimpleFunction>()
.filter { it.name.asString() == "toString" }
.first { it.valueParameters.isEmpty() }
.let { getOverridden(it, this) }
}
}
@@ -19,7 +19,7 @@ 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 {
internal class Primitive<T>(val 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
@@ -39,7 +39,7 @@ fun generateMap(): String {
p.println(File("license/COPYRIGHT.txt").readText())
p.println("package org.jetbrains.kotlin.ir.interpreter.builtins")
p.println()
p.println("import org.jetbrains.kotlin.ir.interpreter.internalName")
p.println("import org.jetbrains.kotlin.ir.interpreter.IrInterpreter")
p.println("import org.jetbrains.kotlin.ir.interpreter.state.*")
p.println()
p.println("/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */")
@@ -68,19 +68,6 @@ fun generateMap(): String {
p.println(")")
p.println()
p.println(
"""
private fun Any.defaultToString(): String {
return when (this) {
is Lambda -> this.toString()
is State -> "${'$'}{this.irClass.internalName()}@" + System.identityHashCode(this).toString(16).padStart(8, '0')
else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0'))
}
}
""".trimIndent()
)
p.println()
return sb.toString()
}
@@ -150,10 +137,7 @@ private fun generateUnaryBody(unaryOperationsMap: Map<CallableDescriptor, Pair<S
return unaryOperationsMap.entries.joinToString(separator = ",\n", postfix = ",\n") { (function, parameters) ->
val methodName = "${function.name}"
val parentheses = if (function is FunctionDescriptor) "()" else ""
val body =
if (methodName == "toString" && parameters.first == "<Any>") "a.defaultToString()"
else "a.$methodName$parentheses"
" unaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a -> $body }"
" unaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a -> a.$methodName$parentheses }"
} +
" unaryOperation<Any?>(\"${irNullCheck.name}\", \"${irNullCheck.valueParameters.first().type.originalKotlinType}\") { a -> a!! },\n" +
" unaryOperation<ExceptionState>(\"message\", \"Throwable\") { a -> a.getMessage() },\n" +
@@ -169,7 +153,11 @@ private fun generateBinaryBody(
} + binaryIrOperationsMap.entries.joinToString(separator = ",\n") { (function, parameters) ->
val methodName = "${function.name}"
val methodSymbol = getIrMethodSymbolByName(methodName)
" binaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a, b -> a $methodSymbol b }"
val body = when (methodName) {
IrBuiltIns.OperatorNames.EQEQEQ -> "if (a is IrInterpreter.Proxy && b is IrInterpreter.Proxy) a.state === b.state else a === b"
else -> "a $methodSymbol b"
}
" binaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a, b -> $body }"
}
}