[K/JS] fix(KT-46643): prevent removal of setter for overridden external field.

This commit is contained in:
Artem Kobzar
2022-07-29 12:41:17 +00:00
committed by Space
parent 1c39042e7a
commit b9189be728
9 changed files with 138 additions and 4 deletions
@@ -77,7 +77,8 @@ private fun IrDeclaration.addRootsTo(
}
}
this is IrSimpleFunction -> {
if (correspondingPropertySymbol?.owner?.isExported(context) == true) {
val correspondingProperty = correspondingPropertySymbol?.owner ?: return
if (correspondingProperty.isExported(context)) {
acceptVoid(nestedVisitor)
}
}
@@ -185,6 +185,10 @@ abstract class UsefulDeclarationProcessor(
}
}
if (declaration is IrSimpleFunction && declaration.isAccessorForOverriddenExternalField()) {
declaration.enqueue(declaration.correspondingPropertySymbol!!.owner, "overrides external property")
}
// A hack to enforce property lowering.
// Until a getter is accessed it doesn't get moved to the declaration list.
if (declaration is IrProperty) {
@@ -197,6 +201,14 @@ abstract class UsefulDeclarationProcessor(
}
}
private fun IrSimpleFunction.isAccessorForOverriddenExternalField(): Boolean {
return correspondingPropertySymbol?.owner?.isExternalOrOverriddenExternal() ?: false
}
private fun IrProperty.isExternalOrOverriddenExternal(): Boolean {
return isEffectivelyExternal() || overriddenSymbols.any { it.owner.isExternalOrOverriddenExternal() }
}
protected open fun handleAssociatedObjects(): Unit = Unit
fun collectDeclarations(rootDeclarations: Iterable<IrDeclaration>): Set<IrDeclaration> {
@@ -416,7 +416,7 @@ fun IrSimpleFunction?.shouldExportAccessor(context: JsIrBackendContext): Boolean
if (parentAsClass.isExported(context)) return true
return overriddenStableProperty(context)
return isAccessorOfOverriddenStableProperty(context)
}
fun IrSimpleFunction.overriddenStableProperty(context: JsIrBackendContext): Boolean {
@@ -429,10 +429,14 @@ fun IrSimpleFunction.overriddenStableProperty(context: JsIrBackendContext): Bool
return overridesExternal() || property.getJsName() != null
}
private fun IrSimpleFunction.overridesExternal(): Boolean {
fun IrSimpleFunction.isAccessorOfOverriddenStableProperty(context: JsIrBackendContext): Boolean {
return overriddenStableProperty(context) || correspondingPropertySymbol!!.owner.overridesExternal()
}
private fun IrOverridableDeclaration<*>.overridesExternal(): Boolean {
if (this.isEffectivelyExternal()) return true
return this.overriddenSymbols.any { it.owner.overridesExternal() }
return overriddenSymbols.any { (it.owner as IrOverridableDeclaration<*>).overridesExternal() }
}
private val IrClassifierSymbol.isInterface get() = (owner as? IrClass)?.isInterface == true
@@ -7498,6 +7498,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
runTest("js/js.translator/testData/box/native/externalNestedEnum.kt");
}
@Test
@TestMetadata("externalValWithOverridenVar.kt")
public void testExternalValWithOverridenVar() throws Exception {
runTest("js/js.translator/testData/box/native/externalValWithOverridenVar.kt");
}
@Test
@TestMetadata("inheritanceFromNativeClass.kt")
public void testInheritanceFromNativeClass() throws Exception {
@@ -7970,6 +7970,12 @@ public class FirJsTestGenerated extends AbstractFirJsTest {
runTest("js/js.translator/testData/box/native/externalNestedEnum.kt");
}
@Test
@TestMetadata("externalValWithOverridenVar.kt")
public void testExternalValWithOverridenVar() throws Exception {
runTest("js/js.translator/testData/box/native/externalValWithOverridenVar.kt");
}
@Test
@TestMetadata("inheritanceFromNativeClass.kt")
public void testInheritanceFromNativeClass() throws Exception {
@@ -7970,6 +7970,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/native/externalNestedEnum.kt");
}
@Test
@TestMetadata("externalValWithOverridenVar.kt")
public void testExternalValWithOverridenVar() throws Exception {
runTest("js/js.translator/testData/box/native/externalValWithOverridenVar.kt");
}
@Test
@TestMetadata("inheritanceFromNativeClass.kt")
public void testInheritanceFromNativeClass() throws Exception {
@@ -125,6 +125,11 @@ public class JsTranslatorWasmTestGenerated extends AbstractJsTranslatorWasmTest
runTest("js/js.translator/testData/box/native/classObject.kt");
}
@TestMetadata("externalValWithOverridenVar.kt")
public void testExternalValWithOverridenVar() throws Exception {
runTest("js/js.translator/testData/box/native/externalValWithOverridenVar.kt");
}
@TestMetadata("kt2209.kt")
public void testKt2209() throws Exception {
runTest("js/js.translator/testData/box/native/kt2209.kt");
@@ -0,0 +1,3 @@
function Base() {
this.foo = "K"
}
@@ -0,0 +1,91 @@
// KT-46643
// IGNORE_BACKEND: WASM
// WITH_STDLIB
import kotlin.reflect.KProperty
external interface IBase {
val foo: String
}
external abstract class Base : IBase
open class A : Base() {
override var foo: String = "Error: A setter was not called."
set(k) { result = "O$k"}
lateinit var result: String
}
open class B : Base() {
override val foo: String = "OK"
open val result: String get() = foo
}
class C : B() {
override var foo: String = "Error: C setter was not called."
set(k) { result = "O$k"}
override lateinit var result: String
}
open class D : B() {
override val foo: String = "OK"
}
open class E : D() {
override var foo: String = "Error: E setter was not called."
set(k) { result = "O$k"}
override lateinit var result: String
}
open class F: B() {
override var foo: String by CustomDelegator
private object CustomDelegator {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "Error: F setter was not called."
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
result = "O$value"
}
lateinit var result: String
}
override val result: String get() = CustomDelegator.result
}
class G(val b: B): IBase by b {
val result: String get() = b.result
}
fun box(): String {
val a = A()
if (a.result != "OK") return a.foo
val b = B()
if (b.result != "OK") return b.foo
val c = C()
if (c.result != "OK") return c.foo
val d = D()
if (d.result != "OK") return d.foo
val e = E()
if (e.result != "OK") return e.foo
try {
val f = F()
return "Failed: it should not work for now, because of delegating objects initialization order"
} catch (e: Throwable) {}
val g = G(e)
if (g.result != "OK") return g.foo
return "OK"
}