rra/ilgonmic/export-call-site

[JS IR] Add test with exported overridden property from interface

[JS IR] Accessors should not be exported when overridden from non-exported interface

Merge-request: KT-MR-6166
Merged-by: Ilya Goncharov <Ilya.Goncharov@jetbrains.com>

^KT-52144 fixed
This commit is contained in:
Ilya Goncharov
2022-04-29 12:13:09 +00:00
committed by Space
parent cdb5845693
commit 1fc7fbed79
4 changed files with 77 additions and 34 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isAllowedFakeOverriddenDeclaration
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.export.isOverriddenExported
@@ -176,7 +177,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
// });
val getterForwarder = property.getter
.takeIf { it.shouldExportAccessor() }
.takeIf { it.shouldExportAccessor(context.staticContext.backendContext) }
.getOrGenerateIfFinal {
propertyAccessorForwarder("getter forwarder") {
JsReturn(JsInvocation(it))
@@ -184,7 +185,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
}
val setterForwarder = property.setter
.takeIf { it.shouldExportAccessor() }
.takeIf { it.shouldExportAccessor(context.staticContext.backendContext) }
.getOrGenerateIfFinal {
val setterArgName = JsName("value", false)
propertyAccessorForwarder("setter forwarder") {
@@ -219,20 +220,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
overriddenSymbols.any { it.owner.isDefinedInsideExportedInterface() }
}
private fun IrSimpleFunction?.shouldExportAccessor(): Boolean {
if (this == null) return false
if (parentAsClass.isExported(context.staticContext.backendContext)) return true
val property = correspondingPropertySymbol!!.owner
if (property.isOverriddenExported(context.staticContext.backendContext)) {
return isOverriddenExported(context.staticContext.backendContext)
}
return overridesExternal() || property.getJsName() != null
}
private fun IrSimpleFunction.accessorRef(): JsNameRef? =
when (visibility) {
DescriptorVisibilities.PRIVATE -> null
@@ -260,12 +247,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return jsElementAccess(name.asString(), classPrototypeRef)
}
private fun IrSimpleFunction.overridesExternal(): Boolean {
if (this.isEffectivelyExternal()) return true
return this.overriddenSymbols.any { it.owner.overridesExternal() }
}
private fun IrClass.shouldCopyFrom(): Boolean {
return isInterface && !isEffectivelyExternal()
}
@@ -430,6 +411,30 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
}
}
fun IrSimpleFunction?.shouldExportAccessor(context: JsIrBackendContext): Boolean {
if (this == null) return false
if (parentAsClass.isExported(context)) return true
return overriddenStableProperty(context)
}
fun IrSimpleFunction.overriddenStableProperty(context: JsIrBackendContext): Boolean {
val property = correspondingPropertySymbol!!.owner
if (property.isOverriddenExported(context)) {
return isOverriddenExported(context)
}
return overridesExternal() || property.getJsName() != null
}
private fun IrSimpleFunction.overridesExternal(): Boolean {
if (this.isEffectivelyExternal()) return true
return this.overriddenSymbols.any { it.owner.overridesExternal() }
}
private val IrClassifierSymbol.isInterface get() = (owner as? IrClass)?.isInterface == true
private val IrClassifierSymbol.isEffectivelyExternal get() = (owner as? IrDeclaration)?.isEffectivelyExternal() == true
@@ -126,18 +126,20 @@ fun translateCall(
property != null &&
(property.isEffectivelyExternal() || property.isExportedMember(context.staticContext.backendContext))
) {
val propertyName = context.getNameForProperty(property)
val nameRef = when (jsDispatchReceiver) {
null -> JsNameRef(propertyName)
else -> jsElementAccess(propertyName.ident, jsDispatchReceiver)
}
return when (function) {
property.getter -> nameRef
property.setter -> jsAssignment(nameRef, arguments.single())
else -> compilationException(
"Function must be an accessor of corresponding property",
function
)
if (function.overriddenSymbols.isEmpty() || function.overriddenStableProperty(context.staticContext.backendContext)) {
val propertyName = context.getNameForProperty(property)
val nameRef = when (jsDispatchReceiver) {
null -> JsNameRef(propertyName)
else -> jsElementAccess(propertyName.ident, jsDispatchReceiver)
}
return when (function) {
property.getter -> nameRef
property.setter -> jsAssignment(nameRef, arguments.single())
else -> compilationException(
"Function must be an accessor of corresponding property",
function
)
}
}
}
}
@@ -2568,6 +2568,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/export/overriddenExternalMethodWithSameStableNameMethod.kt");
}
@Test
@TestMetadata("overriddenPropertyFromInterface.kt")
public void testOverriddenPropertyFromInterface() throws Exception {
runTest("js/js.translator/testData/box/export/overriddenPropertyFromInterface.kt");
}
@Test
@TestMetadata("overridenMethod.kt")
public void testOverridenMethod() throws Exception {
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JS_IR
interface Foo {
val foo: String
val foo2: String
var foo3: String
}
@JsExport
class Bar : Foo {
override val foo: String
get() = "foo"
override val foo2: String = "foo2"
override var foo3: String = "foo3"
}
fun box(): String {
val bar = Bar()
if (bar.foo != "foo") return "fail 1"
if (bar.foo2 != "foo2") return "fail 2"
if (bar.foo3 != "foo3") return "fail 3"
bar.foo3 = "foo4"
if (bar.foo3 != "foo4") return "fail 4"
return "OK"
}