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:
committed by
TeamCityServer
parent
3c0c8d97cc
commit
4c75576414
+81
-33
@@ -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
|
||||
|
||||
|
||||
+3
-11
@@ -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'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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) }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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 }"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user