Skip to content

Commit

Permalink
[Wasm] Fix equality checks for non-capturing property references
Browse files Browse the repository at this point in the history
#KT-71474 Fixed
  • Loading branch information
bashor authored and qodana-bot committed Sep 17, 2024
1 parent 2eea913 commit d0c7346
Show file tree
Hide file tree
Showing 26 changed files with 704 additions and 41 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -254,20 +254,18 @@ class WasmSymbols(
(0..2).map { getInternalFunction("startCoroutineUninterceptedOrReturnIntrinsic$it") }

// KProperty implementations
val kLocalDelegatedPropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedPropertyImpl")
val kLocalDelegatedMutablePropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedMutablePropertyImpl")
val kProperty0Impl: IrClassSymbol = this.getInternalClass("KProperty0Impl")
val kProperty1Impl: IrClassSymbol = this.getInternalClass("KProperty1Impl")
val kProperty2Impl: IrClassSymbol = this.getInternalClass("KProperty2Impl")
val kMutableProperty0Impl: IrClassSymbol = this.getInternalClass("KMutableProperty0Impl")
val kMutableProperty1Impl: IrClassSymbol = this.getInternalClass("KMutableProperty1Impl")
val kMutableProperty2Impl: IrClassSymbol = this.getInternalClass("KMutableProperty2Impl")
val kLocalDelegatedPropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedPropertyImpl2")
val kLocalDelegatedMutablePropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedMutablePropertyImpl2")
val kProperty0Impl: IrClassSymbol = this.getInternalClass("KProperty0Impl2")
val kProperty1Impl: IrClassSymbol = this.getInternalClass("KProperty1Impl2")
val kProperty2Impl: IrClassSymbol = this.getInternalClass("KProperty2Impl2")
val kMutableProperty0Impl: IrClassSymbol = this.getInternalClass("KMutableProperty0Impl2")
val kMutableProperty1Impl: IrClassSymbol = this.getInternalClass("KMutableProperty1Impl2")
val kMutableProperty2Impl: IrClassSymbol = this.getInternalClass("KMutableProperty2Impl2")
val kMutableProperty0: IrClassSymbol = getIrClass(FqName("kotlin.reflect.KMutableProperty0"))
val kMutableProperty1: IrClassSymbol = getIrClass(FqName("kotlin.reflect.KMutableProperty1"))
val kMutableProperty2: IrClassSymbol = getIrClass(FqName("kotlin.reflect.KMutableProperty2"))

val kTypeStub = getInternalFunction("kTypeStub")

val arraysCopyInto = findFunctions(collectionsPackage.memberScope, Name.identifier("copyInto"))
.map { symbolTable.descriptorExtension.referenceSimpleFunction(it) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.getKFunctionType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name

Expand All @@ -38,10 +35,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
private var tempIndex = 0
val symbols = context.wasmSymbols

fun createKTypeGenerator(): KTypeGeneratorInterface {
return KTypeGeneratorInterface { this.irCall(symbols.kTypeStub) }
}

private fun getKPropertyImplConstructor(
receiverTypes: List<IrType>,
returnType: IrType,
Expand Down Expand Up @@ -80,10 +73,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
return classSymbol.constructors.single() to arguments
}

fun interface KTypeGeneratorInterface {
fun IrBuilderWithScope.irKType(type: IrType): IrExpression
}

override fun lower(irFile: IrFile) {
// Somehow there is no reasonable common ancestor for IrProperty and IrLocalDelegatedProperty,
// so index by IrDeclaration.
Expand Down Expand Up @@ -119,7 +108,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
expression.transformChildrenVoid(this)

val kTypeGenerator = createKTypeGenerator()
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
Expand All @@ -128,7 +116,7 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
return when (receiversCount) {
0 -> { // Cache KProperties with no arguments.
val field = kProperties.getOrPut(expression.symbol.owner) {
createKProperty(expression, kTypeGenerator, this) to kProperties.size
createKProperty(expression, this) to kProperties.size
}

irCall(arrayItemGetter).apply {
Expand All @@ -137,7 +125,7 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
}
}

1 -> createKProperty(expression, kTypeGenerator, this) // Has receiver.
1 -> createKProperty(expression, this) // Has receiver.

else -> error("Callable reference to properties with two receivers is not allowed: ${expression.symbol.owner.name}")
}
Expand All @@ -160,7 +148,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
createLocalKProperty(
expression.symbol.owner.name.asString(),
expression.getter.owner.returnType,
createKTypeGenerator(),
this
) to kProperties.size
}
Expand All @@ -187,7 +174,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :

private fun createKProperty(
expression: IrPropertyReference,
kTypeGenerator: KTypeGeneratorInterface,
irBuilder: IrBuilderWithScope
): IrExpression {
val startOffset = expression.startOffset
Expand Down Expand Up @@ -260,14 +246,17 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
isMutable = setterCallableReference != null
)

val capturedReceiver = expression.dispatchReceiver != null || expression.dispatchReceiver != null

val initializerType = symbol.owner.returnType.classifierOrFail.typeWith(constructorTypeArguments)
val initializer = irCall(symbol, initializerType, constructorTypeArguments).apply {
putValueArgument(0, irString(expression.symbol.owner.name.asString()))
putValueArgument(1, with(kTypeGenerator) { irKType(returnType) })
putValueArgument(1, irString(expression.symbol.owner.parent.kotlinFqName.asString()))
putValueArgument(2, irBoolean(capturedReceiver))
if (getterCallableReference != null)
putValueArgument(2, getterCallableReference)
putValueArgument(3, getterCallableReference)
if (setterCallableReference != null)
putValueArgument(3, setterCallableReference)
putValueArgument(4, setterCallableReference)
}
+initializer
}
Expand All @@ -276,7 +265,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
private fun createLocalKProperty(
propertyName: String,
propertyType: IrType,
kTypeGenerator: KTypeGeneratorInterface,
irBuilder: IrBuilderWithScope
): IrExpression {
irBuilder.run {
Expand All @@ -289,7 +277,6 @@ internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) :
val initializerType = symbol.owner.returnType.classifierOrFail.typeWith(constructorTypeArguments)
val initializer = irCall(symbol, initializerType, constructorTypeArguments).apply {
putValueArgument(0, irString(propertyName))
putValueArgument(1, with(kTypeGenerator) { irKType(propertyType) })
}
return initializer
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// IGNORE_BACKEND: JS, JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB
// FILE: test.kt

import kotlin.test.assertEquals

val topLevelVal = ""
var topLevelVar = ""

val String.bar get() = "top"
val Foo.baz get() = "top"

class Foo {
val memberVal = ""
var memberVar = ""

val bar = "member"
private val baz = "member"

companion object {
fun referenceToMemberBaz() = Foo::baz
}
}

fun box(): String {
checkEqual(::topLevelVal, ::topLevelVal)
checkEqual(::topLevelVar, ::topLevelVar)
checkEqual(::topLevelVal, referenceTopLevelValFromOtherFile())
checkEqual(::topLevelVar, referenceTopLevelVarFromOtherFile())

checkEqual(Foo::memberVal, Foo::memberVal)
checkEqual(Foo::memberVar, Foo::memberVar)
checkEqual(Foo::memberVal, referenceMemberValFromOtherFile())
checkEqual(Foo::memberVar, referenceMemberVarFromOtherFile())

checkNotEqual(String::bar, Foo::bar)
assertEquals("top", String::bar.get(""))
assertEquals("member", Foo::bar.get(Foo()))

checkNotEqual(Foo::baz, Foo.referenceToMemberBaz())
assertEquals("top", Foo::baz.get(Foo()))
assertEquals("member", Foo.referenceToMemberBaz().get(Foo()))

return "OK"
}

fun checkEqual(x: Any, y: Any) {
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
}

fun checkNotEqual(x: Any, y: Any) {
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
}

// FILE: otherFile.kt

fun referenceTopLevelValFromOtherFile() = ::topLevelVal
fun referenceTopLevelVarFromOtherFile() = ::topLevelVar
fun referenceMemberValFromOtherFile() = Foo::memberVal
fun referenceMemberVarFromOtherFile() = Foo::memberVar

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: FAILS_IN_JS_IR
// IGNORE_BACKEND: JS, JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// FILE: test.kt

val topLevelVal = ""
var topLevelVar = ""

class Foo {
val memberVal = ""
var memberVar = ""
}

fun box(): String {
val foo0 = Foo()
val foo1 = Foo()

checkEqual(foo0::memberVal, foo0::memberVal)
checkEqual(foo0::memberVal, referenceMemberValFromOtherFile(foo0))
checkEqual(foo0::memberVar, foo0::memberVar)
checkEqual(foo0::memberVar, referenceMemberVarFromOtherFile(foo0))

return "OK"
}

fun checkEqual(x: Any, y: Any) {
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
}

fun checkNotEqual(x: Any, y: Any) {
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
}

// FILE: otherFile.kt

fun referenceMemberValFromOtherFile(foo: Foo) = foo::memberVal
fun referenceMemberVarFromOtherFile(foo: Foo) = foo::memberVar

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// IGNORE_BACKEND: JS
// FILE: test.kt

val topLevelVal = ""
var topLevelVar = ""

class Foo {
val memberVal = ""
var memberVar = ""
}

fun box(): String {
val foo0 = Foo()
val foo1 = Foo()

checkNotEqual(foo0::memberVal, Foo::memberVal)
checkNotEqual(foo0::memberVal, foo1::memberVal)
checkNotEqual(foo0::memberVar, Foo::memberVar)
checkNotEqual(foo0::memberVar, foo1::memberVar)

return "OK"
}

fun checkEqual(x: Any, y: Any) {
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
}

fun checkNotEqual(x: Any, y: Any) {
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
}
Loading

0 comments on commit d0c7346

Please sign in to comment.