From 70a7b613094e0405f4321e2efc4da999f067b1ad Mon Sep 17 00:00:00 2001 From: Anastasiya Shadrina Date: Sun, 24 Nov 2019 01:32:00 +0700 Subject: [PATCH] [Coverage] Support conditional expressions --- .../llvm/coverage/CoverageRegionCollector.kt | 189 ++++-------------- backend.native/tests/build.gradle | 15 ++ .../tests/coverage/basic/controlflow/main.kt | 126 ++++++++++++ 3 files changed, 182 insertions(+), 148 deletions(-) create mode 100644 backend.native/tests/coverage/basic/controlflow/main.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt index 85b263125f2..b599f11e032 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.util.nameForIrSerialization import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid -import org.jetbrains.kotlin.ir.visitors.acceptVoid /** * Collect all regions in the module. @@ -84,169 +83,63 @@ private class IrFunctionRegionsCollector( override fun visitFunction(declaration: IrFunction) { declaration.body?.let { recordRegion(it) - visitBody(it) + it.acceptChildrenVoid(this) } } - override fun visitExpression(expression: IrExpression) { - collectRegions(expression) - } + override fun visitWhen(expression: IrWhen) { + val branches = expression.branches + branches.forEach { + val condition = it.condition + val result = it.result - override fun visitVariable(declaration: IrVariable) { - recordRegion(declaration) - declaration.initializer?.let { collectRegions(it) } - } - - override fun visitBody(body: IrBody) = when (body) { - is IrExpressionBody -> body.acceptChildrenVoid(this) - is IrBlockBody -> body.acceptChildrenVoid(this) - else -> error("Unexpected function body type: $body") - } - - fun collectRegions(value: IrExpression): Unit = when (value) { - is IrTypeOperatorCall -> collectTypeOperator(value) - is IrCall -> collectCall(value) - is IrConstructorCall -> collectCall(value) - is IrDelegatingConstructorCall -> collectCall(value) - is IrInstanceInitializerCall -> collectInstanceInitializerCall(value) - is IrGetValue -> collectGetValue(value) - is IrSetVariable -> collectSetVariable(value) - is IrGetField -> collectGetField(value) - is IrSetField -> collectSetField(value) - is IrConst<*> -> collectConst(value) - is IrReturn -> collectReturn(value) - is IrWhen -> collectWhen(value) - is IrThrow -> collectThrow(value) - is IrTry -> collectTry(value) - is IrReturnableBlock -> collectReturnableBlock(value) - is IrContainerExpression -> collectContainerExpression(value) - is IrWhileLoop -> collectWhileLoop(value) - is IrDoWhileLoop -> collectDoWhileLoop(value) - is IrVararg -> collectVararg(value) - is IrBreak -> collectBreak(value) - is IrContinue -> collectContinue(value) - is IrGetObjectValue -> collectGetObjectValue(value) - is IrFunctionReference -> collectFunctionReference(value) - is IrSuspendableExpression -> collectSuspendableExpression(value) - is IrSuspensionPoint -> collectSuspensionPoint(value) - else -> { - } - } - - private fun collectInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall) { - - } - - private fun collectGetValue(getValue: IrGetValue) { - recordRegion(getValue) - } - - private fun collectSetVariable(setVariable: IrSetVariable) { - recordRegion(setVariable) - setVariable.value.acceptVoid(this) - } - - private fun collectGetField(getField: IrGetField) { - getField.receiver?.let { collectRegions(it) } - } - - private fun collectSetField(setField: IrSetField) { - collectRegions(setField.value) - setField.receiver?.let { collectRegions(it) } - } - - private fun collectConst(const: IrConst<*>) { - recordRegion(const) - } - - private fun collectReturn(irReturn: IrReturn) { - collectRegions(irReturn.value) - } - - private fun collectWhen(irWhen: IrWhen) { - irWhen.branches.forEach { branch -> - // Do not record location for else branch since it doesn't look correct. - if (branch.condition !is IrConst<*>) { - collectRegions(branch.condition) + if (it is IrElseBranch) { + recordRegion(result) + } else { + recordRegion(condition) + recordRegion(result, condition.endOffset, result.endOffset) + condition.acceptChildrenVoid(this) } - collectRegions(branch.result) + result.acceptChildrenVoid(this) } } - private fun collectThrow(irThrow: IrThrow) { - collectRegions(irThrow.value) - recordRegion(irThrow) - } + override fun visitLoop(loop: IrLoop) { + val condition = loop.condition + recordRegion(condition) + condition.acceptChildrenVoid(this) - private fun collectTry(irTry: IrTry) { - } - - private fun collectReturnableBlock(returnableBlock: IrReturnableBlock) { - val file = (returnableBlock.sourceFileSymbol?.owner) - if (file != null && file != currentFile && fileFilter(file)) { - recordRegion(returnableBlock) - irFileStack.push(file) - returnableBlock.acceptChildrenVoid(this) - irFileStack.pop() + val body = loop.body ?: return + when (loop) { + is IrWhileLoop -> recordRegion(body, condition.endOffset, body.endOffset) + is IrDoWhileLoop -> recordRegion(body, body.startOffset, condition.startOffset) } + body.acceptChildrenVoid(this) } - private fun collectContainerExpression(containerExpression: IrContainerExpression) { - containerExpression.acceptChildrenVoid(this) - } - - private fun collectWhileLoop(whileLoop: IrWhileLoop) { - collectRegions(whileLoop.condition) - whileLoop.body?.let { collectRegions(it) } - } - - private fun collectDoWhileLoop(doWhileLoop: IrDoWhileLoop) { - collectRegions(doWhileLoop.condition) - doWhileLoop.body?.let { collectRegions(it) } - } - - private fun collectVararg(vararg: IrVararg) { - vararg.elements.forEach { it.acceptVoid(this) } - } - - private fun collectBreak(irBreak: IrBreak) { - recordRegion(irBreak) - } - - private fun collectContinue(irContinue: IrContinue) { - recordRegion(irContinue) - } - - private fun collectGetObjectValue(getObjectValue: IrGetObjectValue) { - - } - - private fun collectFunctionReference(functionReference: IrFunctionReference) { - - } - - - private fun collectSuspendableExpression(suspendableExpression: IrSuspendableExpression) { - - } - - private fun collectSuspensionPoint(suspensionPoint: IrSuspensionPoint) { - - } - - private fun collectTypeOperator(typeOperatorCall: IrTypeOperatorCall) { - - } - - private fun collectCall(call: IrFunctionAccessExpression) { - recordRegion(call, RegionKind.Code) - call.acceptChildrenVoid(this) + override fun visitBlock(expression: IrBlock) { + when (expression) { + is IrReturnableBlock -> { + val file = (expression.sourceFileSymbol?.owner) + if (file != null && file != currentFile && fileFilter(file)) { + recordRegion(expression) + irFileStack.push(file) + expression.acceptChildrenVoid(this) + irFileStack.pop() + } + } + else -> expression.acceptChildrenVoid(this) + } } private fun recordRegion(irElement: IrElement, kind: RegionKind = RegionKind.Code) { - if (irElement.startOffset == UNDEFINED_OFFSET || irElement.endOffset == UNDEFINED_OFFSET) { + recordRegion(irElement, irElement.startOffset, irElement.endOffset, kind) + } + + private fun recordRegion(irElement: IrElement, startOffset: Int, endOffset: Int, kind: RegionKind = RegionKind.Code) { + if (startOffset == UNDEFINED_OFFSET || endOffset == UNDEFINED_OFFSET) { return } - regions[irElement] = Region.fromIr(irElement, currentFile, kind) + regions[irElement] = Region.fromOffset(startOffset, endOffset, currentFile, kind) } } \ No newline at end of file diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index c58ad145a57..ab76c698134 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3936,6 +3936,21 @@ if (UtilsKt.testTargetSupportsCodeCoverage(project)) { } } } + + task coverage_controlflow(type: CoverageTest) { + binaryName = "CoverageControlFlow" + numberOfCoveredFunctions = 1 + numberOfCoveredLines = 102 + + konanArtifacts { + program(binaryName, targets: [ target ]) { + srcDir 'coverage/basic/controlflow' + baseDir "$testOutputCoverage/$binaryName" + entryPoint "coverage.basic.controlflow.main" + extraOpts "-Xcoverage", "-Xcoverage-file=$profrawFile" + } + } + } } /** diff --git a/backend.native/tests/coverage/basic/controlflow/main.kt b/backend.native/tests/coverage/basic/controlflow/main.kt new file mode 100644 index 00000000000..41788ed83eb --- /dev/null +++ b/backend.native/tests/coverage/basic/controlflow/main.kt @@ -0,0 +1,126 @@ +package coverage.basic.controlflow + +fun main() { + + // If Expression + + var a = 1 + var b = 2 + if (a < b) println("a < b") + + if (a > b) { + println("a > b") + } else if (a == b) { + println("a == b") + } else { + println("a < b") + } + + if (a < b) { + println("a < b") + } + else + { + println("a >= b") + } + + var max = if (a > b) a else b + + max = if (a > b) { + println("Choose a") + a + } else { + println("Choose b") + b + } + + if (a < b) + + println("a < b") + else + + println("a >= b") + + if (a > b) + + println("a > b") + + else + + println("a <= b") + + // When Expression + + when { + a < b -> { + println("a < b") + } + + a == b -> + + println("a == b") + a > b -> { + println("a > b") + } + + else -> { + + + + } + } + + var x = 1 + when (x) { + 1 -> print("x == 1") + 2 -> print("x == 2") + else -> { // Note the block + print("x is neither 1 nor 2") + } + } + x = 2 + when (x) { + 1 -> print("x == 1") + 2 -> print("x == 2") + else -> { // Note the block + print("x is neither 1 nor 2") + } + } + x = 3 + when (x) { + 1 -> print("x == 1") + 2 -> print("x == 2") + else -> { // Note the block + print("x is neither 1 nor 2") + } + } + + when (x) { + 0, 1 + -> + print("x == 0 or x == 1") + else -> + print("otherwise") + } + + when { + else -> println("=)") + } + + // While Loops + + do { + b++ + } while (b < 5) + + while (a < 10) { + a++ + if (a > 7) { + println(a) + } + } + + while (a > 0) + + a-- +} \ No newline at end of file