[K/JS] fix(KT-46643): prevent removal of setter for overridden external field.
This commit is contained in:
@@ -77,7 +77,8 @@ private fun IrDeclaration.addRootsTo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this is IrSimpleFunction -> {
|
this is IrSimpleFunction -> {
|
||||||
if (correspondingPropertySymbol?.owner?.isExported(context) == true) {
|
val correspondingProperty = correspondingPropertySymbol?.owner ?: return
|
||||||
|
if (correspondingProperty.isExported(context)) {
|
||||||
acceptVoid(nestedVisitor)
|
acceptVoid(nestedVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -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.
|
// A hack to enforce property lowering.
|
||||||
// Until a getter is accessed it doesn't get moved to the declaration list.
|
// Until a getter is accessed it doesn't get moved to the declaration list.
|
||||||
if (declaration is IrProperty) {
|
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
|
protected open fun handleAssociatedObjects(): Unit = Unit
|
||||||
|
|
||||||
fun collectDeclarations(rootDeclarations: Iterable<IrDeclaration>): Set<IrDeclaration> {
|
fun collectDeclarations(rootDeclarations: Iterable<IrDeclaration>): Set<IrDeclaration> {
|
||||||
|
|||||||
+7
-3
@@ -416,7 +416,7 @@ fun IrSimpleFunction?.shouldExportAccessor(context: JsIrBackendContext): Boolean
|
|||||||
|
|
||||||
if (parentAsClass.isExported(context)) return true
|
if (parentAsClass.isExported(context)) return true
|
||||||
|
|
||||||
return overriddenStableProperty(context)
|
return isAccessorOfOverriddenStableProperty(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrSimpleFunction.overriddenStableProperty(context: JsIrBackendContext): Boolean {
|
fun IrSimpleFunction.overriddenStableProperty(context: JsIrBackendContext): Boolean {
|
||||||
@@ -429,10 +429,14 @@ fun IrSimpleFunction.overriddenStableProperty(context: JsIrBackendContext): Bool
|
|||||||
return overridesExternal() || property.getJsName() != null
|
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
|
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
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("inheritanceFromNativeClass.kt")
|
@TestMetadata("inheritanceFromNativeClass.kt")
|
||||||
public void testInheritanceFromNativeClass() throws Exception {
|
public void testInheritanceFromNativeClass() throws Exception {
|
||||||
|
|||||||
+6
@@ -7970,6 +7970,12 @@ public class FirJsTestGenerated extends AbstractFirJsTest {
|
|||||||
runTest("js/js.translator/testData/box/native/externalNestedEnum.kt");
|
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
|
@Test
|
||||||
@TestMetadata("inheritanceFromNativeClass.kt")
|
@TestMetadata("inheritanceFromNativeClass.kt")
|
||||||
public void testInheritanceFromNativeClass() throws Exception {
|
public void testInheritanceFromNativeClass() throws Exception {
|
||||||
|
|||||||
+6
@@ -7970,6 +7970,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/native/externalNestedEnum.kt");
|
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
|
@Test
|
||||||
@TestMetadata("inheritanceFromNativeClass.kt")
|
@TestMetadata("inheritanceFromNativeClass.kt")
|
||||||
public void testInheritanceFromNativeClass() throws Exception {
|
public void testInheritanceFromNativeClass() throws Exception {
|
||||||
|
|||||||
+5
@@ -125,6 +125,11 @@ public class JsTranslatorWasmTestGenerated extends AbstractJsTranslatorWasmTest
|
|||||||
runTest("js/js.translator/testData/box/native/classObject.kt");
|
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")
|
@TestMetadata("kt2209.kt")
|
||||||
public void testKt2209() throws Exception {
|
public void testKt2209() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/native/kt2209.kt");
|
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"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user