blob: 1d865488167a5819aa3af0c070f2e0a025f8eaa9 [file] [log] [blame]
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
fun validateIrFile(context: CommonBackendContext, irFile: IrFile) {
val visitor = IrValidator(context, IrValidatorConfig(abortOnError = false, ensureAllNodesAreDifferent = false))
irFile.acceptVoid(visitor)
}
fun validateIrModule(context: CommonBackendContext, irModule: IrModuleFragment) {
val visitor = IrValidator(
context,
IrValidatorConfig(abortOnError = false, ensureAllNodesAreDifferent = true)
) // TODO: consider taking the boolean from settings.
irModule.acceptVoid(visitor)
// TODO: also check that all referenced symbol targets are reachable.
}
private fun CommonBackendContext.reportIrValidationError(message: String, irFile: IrFile?, irElement: IrElement) {
try {
this.reportWarning("[IR VALIDATION] $message", irFile, irElement)
} catch (e: Throwable) {
println("an error trying to print a warning message: $e")
e.printStackTrace()
}
// TODO: throw an exception after fixing bugs leading to invalid IR.
}
data class IrValidatorConfig(
val abortOnError: Boolean,
val ensureAllNodesAreDifferent: Boolean,
val checkTypes: Boolean = true,
val checkDescriptors: Boolean = true
)
class IrValidator(val context: CommonBackendContext, val config: IrValidatorConfig) : IrElementVisitorVoid {
val builtIns = context.builtIns
var currentFile: IrFile? = null
override fun visitFile(declaration: IrFile) {
currentFile = declaration
super.visitFile(declaration)
}
private fun error(element: IrElement, message: String) {
// TODO: render all element's parents.
context.reportIrValidationError(
"$message\n" + element.render(),
currentFile,
element
)
if (config.abortOnError) {
error("Validation failed")
}
}
private val elementChecker = CheckIrElementVisitor(builtIns, this::error, config)
override fun visitElement(element: IrElement) {
element.acceptVoid(elementChecker)
element.acceptChildrenVoid(this)
}
}
fun IrModuleFragment.checkDeclarationParents() {
this.accept(CheckDeclarationParentsVisitor, null)
}
object CheckDeclarationParentsVisitor : IrElementVisitor<Unit, IrDeclarationParent?> {
override fun visitElement(element: IrElement, data: IrDeclarationParent?) {
element.acceptChildren(this, element as? IrDeclarationParent ?: data)
}
override fun visitDeclaration(declaration: IrDeclaration, data: IrDeclarationParent?) {
checkParent(declaration, data)
super.visitDeclaration(declaration, data)
}
private fun checkParent(declaration: IrDeclaration, expectedParent: IrDeclarationParent?) {
val parent = try {
declaration.parent
} catch (e: Throwable) {
error("$declaration for ${declaration.descriptor} has no parent")
}
if (parent != expectedParent) {
error("$declaration for ${declaration.descriptor} has unexpected parent $parent")
}
}
}