diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FakeOverrideChecker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FakeOverrideChecker.kt new file mode 100644 index 00000000000..3f8bdb4e86e --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FakeOverrideChecker.kt @@ -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() + .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() + .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 + } + }) + } +} diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index 6a3cec379fa..7a47731d226 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -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)