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:
committed by
TeamCityServer
parent
04b36ff19e
commit
6411d09579
+2
-2
@@ -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
|
||||
|
||||
+22
-19
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user