Fix equals check in interpreter in case when object is wrapped as Proxy

This commit is contained in:
Ivan Kylchik
2021-06-23 14:23:39 +03:00
committed by TeamCityServer
parent b9decc3b30
commit 668bb4fd71
3 changed files with 25 additions and 3 deletions
@@ -11,11 +11,12 @@ import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.Common
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
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 defaultEquals(other: Any?): Boolean = if (other is Proxy) this.state === other.state else false
private fun defaultHashCode(): Int = System.identityHashCode(state)
private fun defaultToString(): String = "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
@@ -32,14 +33,14 @@ internal class CommonProxy private constructor(override val state: Common, overr
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Proxy) return false
if (other == null) return false
val valueArguments = mutableListOf<Variable>()
val equalsFun = state.getEqualsFunction()
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))
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, if (other is Proxy) other.state else other as State))
return callInterceptor.interceptProxy(equalsFun, valueArguments) as Boolean
}
@@ -45,6 +45,14 @@ internal fun State.wrap(callInterceptor: CallInterceptor, remainArraysAsIs: Bool
*/
internal fun List<State>.wrap(callInterceptor: CallInterceptor, irFunction: IrFunction, methodType: MethodType? = null): List<Any?> {
val name = irFunction.fqNameWhenAvailable?.asString() ?: ""
if (name == "kotlin.internal.ir.EQEQ" && this.any { it is Common }) {
// in case of custom `equals` it is important not to lose information obout type
// so all states remain as is, only common will be converted to Proxy
return mapIndexed { index, state ->
if (state !is Common) return@mapIndexed state
state.wrap(callInterceptor, remainArraysAsIs = true, methodType?.parameterType(index))
}
}
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 == "kotlin.Array.set" && index != 0) || name == "kotlin.Pair.<init>" || name == "kotlin.internal.ir.CHECK_NOT_NULL"
+13
View File
@@ -1,5 +1,18 @@
import kotlin.collections.*
@CompileTimeCalculation
class A(val a: Int) {
override fun equals(other: Any?): Boolean {
return other is Int && other == a
}
}
const val customEquals1 = <!EVALUATED: `true`!>A(1) == 1<!>
const val customEquals2 = <!EVALUATED: `false`!>A(1) == 123<!>
const val customEquals3 = <!EVALUATED: `false`!>1 == A(1)<!>
const val customEquals4 = <!EVALUATED: `false`!>123 == A(1)<!>
const val customEquals5 = <!EVALUATED: `false`!>null == A(1)<!>
const val customEquals6 = <!EVALUATED: `false`!>A(1) == null<!>
@CompileTimeCalculation
class B(val b: Int) {
override fun equals(other: Any?): Boolean {