Rewrite wrap function to take into account function name

This is needed to understand whenever arrays must be unwrapped or not.
For now, in case of kotlin.Array.set and kotlin.Pair.<init> calls it is
considered to remain arrays as Primitive class to save their irType
info.
This commit is contained in:
Ivan Kylchik
2021-04-08 13:50:22 +03:00
committed by TeamCityServer
parent 04b36ff19e
commit 6411d09579
6 changed files with 55 additions and 33 deletions
@@ -54,7 +54,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
return interpreter.withNewCallStack(irFunction) {
this@withNewCallStack.environment.callStack.addInstruction(CompoundInstruction(irFunction))
valueArguments.forEach { this@withNewCallStack.environment.callStack.addVariable(it) }
}.wrap(this@DefaultCallInterceptor, expectedResultClass)
}.wrap(this@DefaultCallInterceptor, remainArraysAsIs = true, extendFrom = expectedResultClass)
}
override fun interceptCall(call: IrCall, irFunction: IrFunction, receiver: State?, args: List<State>, defaultAction: () -> Unit) {
@@ -150,7 +150,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
val receiverType = irFunction.dispatchReceiverParameter?.type
val argsType = listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }
val argsValues = args.map { it.wrap(this, calledFromBuiltIns = methodName !in setOf("plus", IrBuiltIns.OperatorNames.EQEQ)) }
val argsValues = args.wrap(this, irFunction)
fun IrType.getOnlyName(): String {
return when {
@@ -282,9 +282,7 @@ internal fun IrFunction?.checkCast(environment: IrInterpreterEnvironment): Boole
internal fun IrFunction.getArgsForMethodInvocation(
callInterceptor: CallInterceptor, methodType: MethodType, args: List<State>
): List<Any?> {
val argsValues = args
.mapIndexed { index, state -> state.wrap(callInterceptor, methodType.parameterType(index)) }
.toMutableList()
val argsValues = args.wrap(callInterceptor, this, methodType).toMutableList()
// TODO if vararg isn't last parameter
// must convert vararg array into separated elements for correct invoke
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.interpreter.proxy
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
@@ -13,22 +14,28 @@ import org.jetbrains.kotlin.ir.interpreter.state.Common
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
/**
* calledFromBuiltIns - used to avoid cyclic calls. For example:
* override fun toString(): String {
* return super.toString()
* }
*/
internal class CommonProxy private constructor(
override val state: Common, override val callInterceptor: CallInterceptor, private val calledFromBuiltIns: Boolean = false
) : Proxy {
internal class CommonProxy private constructor(override val state: Common, override val callInterceptor: CallInterceptor) : Proxy {
private fun defaultEquals(other: Proxy): Boolean = this.state === other.state
private fun defaultHashCode(): Int = System.identityHashCode(state)
private fun defaultToString(): String = "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
/**
* This check used to avoid cyclic calls. For example:
* override fun toString(): String = super.toString()
*/
private fun IrFunction.wasAlreadyCalled(): Boolean {
val anyParameter = this.getLastOverridden().dispatchReceiverParameter!!.symbol
if (callInterceptor.environment.callStack.containsVariable(anyParameter)) return true
return this == callInterceptor.environment.callStack.getCurrentFrameOwner()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Proxy) return false
val valueArguments = mutableListOf<Variable>()
val equalsFun = state.getEqualsFunction()
if (equalsFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return this.state === other.state
if (equalsFun.isFakeOverriddenFromAny() || equalsFun.wasAlreadyCalled()) return defaultEquals(other)
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state))
@@ -39,7 +46,7 @@ internal class CommonProxy private constructor(
override fun hashCode(): Int {
val valueArguments = mutableListOf<Variable>()
val hashCodeFun = state.getHashCodeFunction()
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
if (hashCodeFun.isFakeOverriddenFromAny() || hashCodeFun.wasAlreadyCalled()) return defaultHashCode()
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return callInterceptor.interceptProxy(hashCodeFun, valueArguments) as Int
@@ -48,24 +55,20 @@ internal class CommonProxy private constructor(
override fun toString(): String {
val valueArguments = mutableListOf<Variable>()
val toStringFun = state.getToStringFunction()
if (toStringFun.isFakeOverriddenFromAny() || calledFromBuiltIns) {
return "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
}
if (toStringFun.isFakeOverriddenFromAny() || toStringFun.wasAlreadyCalled()) return defaultToString()
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return callInterceptor.interceptProxy(toStringFun, valueArguments) as String
}
companion object {
internal fun Common.asProxy(
callInterceptor: CallInterceptor, extendFrom: Class<*>? = null, calledFromBuiltIns: Boolean = false
): Any {
val commonProxy = CommonProxy(this, callInterceptor, calledFromBuiltIns)
internal fun Common.asProxy(callInterceptor: CallInterceptor, extendFrom: Class<*>? = null): Any {
val commonProxy = CommonProxy(this, callInterceptor)
val interfaces = when (extendFrom) {
null, Object::class.java -> arrayOf(Proxy::class.java)
else -> arrayOf(extendFrom, Proxy::class.java)
}
return java.lang.reflect.Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), interfaces)
{ /*proxy*/_, method, args ->
when {
@@ -5,12 +5,16 @@
package org.jetbrains.kotlin.ir.interpreter.proxy
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray
import org.jetbrains.kotlin.ir.interpreter.proxy.CommonProxy.Companion.asProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.ReflectionProxy.Companion.asProxy
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.interpreter.state.reflection.ReflectionState
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import java.lang.invoke.MethodType
internal interface Proxy {
val state: State
@@ -21,20 +25,32 @@ internal interface Proxy {
override fun toString(): String
}
/**
* Prepare state object to be passed in outer world
*/
internal fun State.wrap(callInterceptor: CallInterceptor, extendFrom: Class<*>? = null, calledFromBuiltIns: Boolean = false): Any? {
internal fun State.wrap(callInterceptor: CallInterceptor, remainArraysAsIs: Boolean, extendFrom: Class<*>? = null): Any? {
return when (this) {
is ExceptionState -> this
is Wrapper -> this.value
is Primitive<*> -> this.value
is Common -> this.asProxy(callInterceptor, extendFrom, calledFromBuiltIns)
is Primitive<*> -> when {
this.type.isArray() || this.type.isPrimitiveArray() -> if (remainArraysAsIs) this else this.value
else -> this.value
}
is Common -> this.asProxy(callInterceptor, extendFrom)
is ReflectionState -> this.asProxy(callInterceptor)
else -> throw AssertionError("${this::class} is unsupported as argument for wrap function")
}
}
/**
* Prepare state object to be passed in outer world
*/
internal fun List<State>.wrap(callInterceptor: CallInterceptor, irFunction: IrFunction, methodType: MethodType? = null): List<Any?> {
val name = irFunction.fqNameWhenAvailable?.asString()
return this.mapIndexed { index, state ->
// don't get arrays from Primitive in case of "set" and "Pair.<init>"; information about type will be lost
val unwrapArrays = name?.let { (it == "kotlin.Array.set" && index != 0) || it == "kotlin.Pair.<init>" } ?: false
state.wrap(callInterceptor, unwrapArrays, methodType?.parameterType(index))
}
}
internal fun Class<*>.isObject(): Boolean {
return this.name == "java.lang.Object"
return this == java.lang.Object::class.java
}
@@ -175,6 +175,7 @@ internal class CallStack {
}
fun getVariable(symbol: IrSymbol): Variable = getCurrentFrame().getVariable(symbol)
fun containsVariable(symbol: IrSymbol): Boolean = getCurrentFrame().containsVariable(symbol)
fun storeUpValues(state: StateWithClosure) {
// TODO save only necessary declarations
@@ -66,10 +66,14 @@ internal class Frame(subFrame: SubFrame, val irFile: IrFile? = null) {
}
fun getVariable(symbol: IrSymbol): Variable {
return innerStack.reversed().firstNotNullResult { it.getVariable(symbol) }
return tryToGetVariable(symbol)
?: throw InterpreterError("$symbol not found") // TODO better message
}
fun containsVariable(symbol: IrSymbol): Boolean = tryToGetVariable(symbol) != null
private fun tryToGetVariable(symbol: IrSymbol): Variable? = innerStack.reversed().firstNotNullResult { it.getVariable(symbol) }
fun getAll(): List<Variable> = innerStack.flatMap { it.getAll() }
private fun getLineNumberForCurrentInstruction(): String {