Fix equals check in interpreter in case when object is wrapped as Proxy
This commit is contained in:
committed by
TeamCityServer
parent
b9decc3b30
commit
668bb4fd71
+4
-3
@@ -11,11 +11,12 @@ import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
|
|||||||
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
|
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
|
||||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||||
import org.jetbrains.kotlin.ir.interpreter.state.Common
|
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.interpreter.toState
|
||||||
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
|
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
|
||||||
|
|
||||||
internal class CommonProxy private constructor(override val state: Common, override val callInterceptor: CallInterceptor) : 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 defaultEquals(other: Any?): Boolean = if (other is Proxy) this.state === other.state else false
|
||||||
private fun defaultHashCode(): Int = System.identityHashCode(state)
|
private fun defaultHashCode(): Int = System.identityHashCode(state)
|
||||||
private fun defaultToString(): String = "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is Proxy) return false
|
if (other == null) return false
|
||||||
|
|
||||||
val valueArguments = mutableListOf<Variable>()
|
val valueArguments = mutableListOf<Variable>()
|
||||||
val equalsFun = state.getEqualsFunction()
|
val equalsFun = state.getEqualsFunction()
|
||||||
if (equalsFun.isFakeOverriddenFromAny() || equalsFun.wasAlreadyCalled()) return defaultEquals(other)
|
if (equalsFun.isFakeOverriddenFromAny() || equalsFun.wasAlreadyCalled()) return defaultEquals(other)
|
||||||
|
|
||||||
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
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
|
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?> {
|
internal fun List<State>.wrap(callInterceptor: CallInterceptor, irFunction: IrFunction, methodType: MethodType? = null): List<Any?> {
|
||||||
val name = irFunction.fqNameWhenAvailable?.asString() ?: ""
|
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 ->
|
return this.mapIndexed { index, state ->
|
||||||
// don't get arrays from Primitive in case of "set" and "Pair.<init>"; information about type will be lost
|
// 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"
|
val unwrapArrays = (name == "kotlin.Array.set" && index != 0) || name == "kotlin.Pair.<init>" || name == "kotlin.internal.ir.CHECK_NOT_NULL"
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
import kotlin.collections.*
|
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
|
@CompileTimeCalculation
|
||||||
class B(val b: Int) {
|
class B(val b: Int) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user