Signature based fake override validation

This commit is contained in:
Alexander Gorshenev
2020-05-29 23:29:17 +03:00
parent 024385cbbd
commit 06abbe468b
2 changed files with 87 additions and 0 deletions
@@ -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.IrPluginContextImpl
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.knownBuiltins
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
@@ -162,6 +163,8 @@ fun generateKLib(
val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, files, irLinker, expectDescriptorToSymbol)
moduleFragment.acceptVoid(ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc)))
val fakeOverrideChecker = FakeOverrideChecker(JsManglerIr, JsManglerDesc)
irLinker.modules.forEach{ fakeOverrideChecker.check(it) }
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.
val mangleChecker = ManglerChecker(JsManglerIr, Ir2DescriptorManglerAdapter(JsManglerDesc))
moduleFragment.acceptVoid(mangleChecker)
val fakeOverrideChecker = FakeOverrideChecker(JsManglerIr, JsManglerDesc)
irLinker.modules.forEach{ fakeOverrideChecker.check(it) }
irBuiltIns.knownBuiltins.forEach { it.acceptVoid(mangleChecker) }
return IrModuleInfo(moduleFragment, deserializedModuleFragments, irBuiltIns, symbolTable, irLinker)