Signature based fake override validation
This commit is contained in:
+80
@@ -0,0 +1,80 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.common.override
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||||
|
|
||||||
|
class FakeOverrideChecker(
|
||||||
|
private val irMangler: KotlinMangler.IrMangler,
|
||||||
|
private val descriptorMangler: KotlinMangler.DescriptorMangler
|
||||||
|
) {
|
||||||
|
|
||||||
|
private fun checkOverriddenSymbols(fake: IrOverridableMember) {
|
||||||
|
if (fake !is IrSimpleFunction) return // TODO: we need overridden symbols on IrProperty.
|
||||||
|
fake.overriddenSymbols.forEach { symbol ->
|
||||||
|
assert((symbol.owner.parent as IrClass).declarations.contains(symbol.owner)) {
|
||||||
|
"CHECK overridden symbols: ${fake.render()} refers to ${symbol.owner.render()} which is not a member of ${symbol.owner.parent.render()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateFakeOverrides(clazz: IrClass) {
|
||||||
|
val classId = clazz.classId ?: return
|
||||||
|
val classDescriptor = clazz.module.module.findClassAcrossModuleDependencies(classId) ?: return
|
||||||
|
// All enum entry overrides look like fake overrides in descriptor enum entries
|
||||||
|
if (classDescriptor.kind == ClassKind.ENUM_ENTRY) return
|
||||||
|
|
||||||
|
val descriptorFakeOverrides = classDescriptor.unsubstitutedMemberScope
|
||||||
|
.getDescriptorsFiltered(DescriptorKindFilter.CALLABLES)
|
||||||
|
.filterIsInstance<CallableMemberDescriptor>()
|
||||||
|
.filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
|
||||||
|
.filterNot { it.visibility == Visibilities.PRIVATE || it.visibility == Visibilities.INVISIBLE_FAKE }
|
||||||
|
|
||||||
|
val descriptorSignatures = descriptorFakeOverrides
|
||||||
|
.map { with(descriptorMangler) { it.signatureString }}
|
||||||
|
.sorted()
|
||||||
|
|
||||||
|
val irFakeOverrides = clazz.declarations
|
||||||
|
.filterIsInstance<IrOverridableMember>()
|
||||||
|
.filter { it.isFakeOverride }
|
||||||
|
|
||||||
|
irFakeOverrides.forEach {
|
||||||
|
checkOverriddenSymbols(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val irSignatures = irFakeOverrides
|
||||||
|
.map { with(irMangler) { it.signatureString }}
|
||||||
|
.sorted()
|
||||||
|
|
||||||
|
assert(descriptorSignatures == irSignatures) {
|
||||||
|
"[IR VALIDATION] Fake override mismatch for ${clazz.fqNameWhenAvailable!!}\n" +
|
||||||
|
"\tDescriptor based: $descriptorSignatures\n" +
|
||||||
|
"\tIR based : $irSignatures"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun check(module: IrModuleFragment) {
|
||||||
|
module.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
validateFakeOverrides(declaration)
|
||||||
|
super.visitClass(declaration)
|
||||||
|
}
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
// Don't go for function local classes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
|||||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy
|
import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.FakeOverrideChecker
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
|
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.knownBuiltins
|
import org.jetbrains.kotlin.backend.common.serialization.knownBuiltins
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
|
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
|
||||||
@@ -162,6 +163,8 @@ fun generateKLib(
|
|||||||
val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, files, irLinker, expectDescriptorToSymbol)
|
val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, files, irLinker, expectDescriptorToSymbol)
|
||||||
|
|
||||||
moduleFragment.acceptVoid(ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc)))
|
moduleFragment.acceptVoid(ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc)))
|
||||||
|
val fakeOverrideChecker = FakeOverrideChecker(JsManglerIr, JsManglerDesc)
|
||||||
|
irLinker.modules.forEach{ fakeOverrideChecker.check(it) }
|
||||||
|
|
||||||
val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||||
|
|
||||||
@@ -230,6 +233,10 @@ fun loadIr(
|
|||||||
// TODO: not sure whether this check should be enabled by default. Add configuration key for it.
|
// TODO: not sure whether this check should be enabled by default. Add configuration key for it.
|
||||||
val mangleChecker = ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc))
|
val mangleChecker = ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc))
|
||||||
moduleFragment.acceptVoid(mangleChecker)
|
moduleFragment.acceptVoid(mangleChecker)
|
||||||
|
|
||||||
|
val fakeOverrideChecker = FakeOverrideChecker(JsManglerIr, JsManglerDesc)
|
||||||
|
irLinker.modules.forEach{ fakeOverrideChecker.check(it) }
|
||||||
|
|
||||||
irBuiltIns.knownBuiltins.forEach { it.acceptVoid(mangleChecker) }
|
irBuiltIns.knownBuiltins.forEach { it.acceptVoid(mangleChecker) }
|
||||||
|
|
||||||
return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, irLinker)
|
return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, irLinker)
|
||||||
|
|||||||
Reference in New Issue
Block a user