diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt index d09a0d38945..c3cc2fc2146 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt @@ -23,12 +23,12 @@ import java.util.* //TODO comment class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) { - val sortedRanges = createLineNumberSequence(node, classSMAP).map { it.mapper }.distinct().toList().sortedWith(RangeMapping.Comparator) + val sortedRanges = createLineNumberSequence(node, classSMAP).distinct().toList().sortedWith(RangeMapping.Comparator) fun copyWithNewNode(newMethodNode: MethodNode) = SMAPAndMethodNode(newMethodNode, classSMAP) } -private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence { +private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence { return InsnSequence(node.instructions.first, null).filterIsInstance().map { lineNumber -> val index = classSMAP.intervals.binarySearch(RangeMapping(lineNumber.line, lineNumber.line, 1), Comparator { value, key -> @@ -37,8 +37,6 @@ private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequenc if (index < 0) { error("Unmapped line number in inlined function ${node.name}:${lineNumber.line}") } - LabelAndMapping(lineNumber, classSMAP.intervals[index]) + classSMAP.intervals[index] } } - -class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 4b80164e982..45f7c361b12 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -39,7 +39,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import java.lang.RuntimeException -class ClassCodegen private constructor( +open class ClassCodegen protected constructor( internal val irClass: IrClass, val context: JvmBackendContext, private val parentClassCodegen: ClassCodegen? = null @@ -66,7 +66,9 @@ class ClassCodegen private constructor( val psiElement = irClass.descriptor.psiElement!! - val visitor: ClassBuilder = state.factory.newVisitor(OtherOrigin(psiElement, descriptor), type, psiElement.containingFile) + val visitor: ClassBuilder = createClassBuilder() + + open fun createClassBuilder() = state.factory.newVisitor(OtherOrigin(psiElement, descriptor), type, psiElement.containingFile) private var sourceMapper: DefaultSourceMapper? = null @@ -210,13 +212,7 @@ class ClassCodegen private constructor( fun getOrCreateSourceMapper(): DefaultSourceMapper { if (sourceMapper == null) { - sourceMapper = DefaultSourceMapper( - SourceInfo.createInfoForIr( - fileEntry.getSourceRangeInfo(irClass.startOffset, irClass.endOffset).endLineNumber + 1, - this.visitor.thisName, - this.psiElement.containingFile.name - ) - ) + sourceMapper = context.getSourceMapper(irClass) } return sourceMapper!! } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index e6bc2ffd4b8..ead6a3bc7d9 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicFunction import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.lower.CrIrType @@ -118,12 +119,9 @@ class ExpressionCodegen( fun generate() { mv.visitCode() val startLabel = markNewLabel() - irFunction.markLineNumber(true) val info = BlockInfo.create() val result = irFunction.body!!.accept(this, info) - - irFunction.markLineNumber(false) - + markFunctionLineNumber() val returnType = typeMapper.mapReturnType(irFunction.descriptor) if (irFunction.body is IrExpressionBody) { mv.areturn(returnType) @@ -141,6 +139,28 @@ class ExpressionCodegen( mv.visitEnd() } + private fun markFunctionLineNumber() { + if (irFunction.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER) { + return + } + if (irFunction is IrConstructor && irFunction.isPrimary) { + irFunction.markLineNumber(startOffset = true) + return + } + val lastElement = irFunction.body!!.getLastElement() + if (lastElement !is IrReturn) { + irFunction.markLineNumber(startOffset = false) + } + } + + private fun IrElement.getLastElement(): IrElement { + return when (this) { + is IrStatementContainer -> if (this.statements.isEmpty()) this else this.statements[this.statements.size - 1].getLastElement() + is IrExpressionBody -> this.expression.getLastElement() + else -> this + } + } + private fun writeParameterInLocalVariableTable(startLabel: Label) { if (!irFunction.isStatic) { mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, markNewLabel(), 0) @@ -175,7 +195,9 @@ class ExpressionCodegen( override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue { return body.statements.fold(none()) { _, exp -> - exp.accept(this, data) + exp.accept(this, data).also { + (exp as? IrExpression)?.markEndOfStatementIfNeeded() + } } } @@ -223,10 +245,12 @@ class ExpressionCodegen( } override fun visitMemberAccess(expression: IrMemberAccessExpression, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) return generateCall(expression, null, data) } override fun visitCall(expression: IrCall, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) if (expression.descriptor is ConstructorDescriptor) { return generateNewCall(expression, data) } @@ -367,8 +391,11 @@ class ExpressionCodegen( val varType = typeMapper.mapType(declaration.descriptor) val index = frame.enter(declaration.symbol, varType) + declaration.markLineNumber(startOffset = true) + declaration.initializer?.apply { StackValue.local(index, varType).store(gen(this, varType, data), mv) + this.markLineNumber(startOffset = true) } val info = VariableInfo( @@ -392,6 +419,7 @@ class ExpressionCodegen( } override fun visitGetValue(expression: IrGetValue, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) return generateLocal(expression.symbol, expression.asmType) } @@ -410,12 +438,14 @@ class ExpressionCodegen( } override fun visitGetField(expression: IrGetField, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) val value = generateFieldValue(expression, data) value.put(value.type, mv) return onStack(value.type) } override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) val fieldValue = generateFieldValue(expression, data) fieldValue.store(expression.value.accept(this, data), mv) return none() @@ -450,12 +480,15 @@ class ExpressionCodegen( } override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) + expression.value.markLineNumber(startOffset = true) val value = expression.value.accept(this, data) StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv) return none() } override fun visitConst(expression: IrConst, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) val value = expression.value val type = expression.asmType StackValue.constant(value, type).put(type, mv) @@ -480,6 +513,7 @@ class ExpressionCodegen( } override fun visitVararg(expression: IrVararg, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) val outType = expression.type val type = expression.asmType assert(type.sort == Type.ARRAY) @@ -588,7 +622,7 @@ class ExpressionCodegen( val afterReturnLabel = Label() generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data) - expression.markLineNumber(false) + expression.markLineNumber(startOffset = true) mv.areturn(returnType) mv.mark(afterReturnLabel) mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/ @@ -596,8 +630,10 @@ class ExpressionCodegen( } - override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue = - genIfWithBranches(expression.branches[0], data, expression.type.toKotlinType(), expression.branches.drop(1)) + override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) + return genIfWithBranches(expression.branches[0], data, expression.type.toKotlinType(), expression.branches.drop(1)) + } private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List): StackValue { @@ -636,6 +672,7 @@ class ExpressionCodegen( when (expression.operator) { IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { val result = expression.argument.accept(this, data) + expression.argument.markEndOfStatementIfNeeded() coerce(result.type, Type.VOID_TYPE, mv) return none() } @@ -686,7 +723,21 @@ class ExpressionCodegen( return expression.onStack } + private fun IrExpression.markEndOfStatementIfNeeded() { + when (this) { + is IrWhen -> if (this.branches.size > 1) { + this.markLineNumber(false) + } + is IrTry -> this.markLineNumber(false) + is IrContainerExpression -> when (this.origin) { + IrStatementOrigin.WHEN, IrStatementOrigin.IF -> + this.markLineNumber(false) + } + } + } + override fun visitStringConcatenation(expression: IrStringConcatenation, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) AsmUtil.genStringBuilderConstructor(mv) expression.arguments.forEach { val stackValue = gen(it, data) @@ -712,6 +763,7 @@ class ExpressionCodegen( // GOTO L0 // L1 //TODO: write elimination lower + condition.markLineNumber(startOffset = true) if (!(condition is IrConst<*> && condition.value == true)) { gen(condition, data) BranchedValue.condJump(StackValue.onStack(condition.asmType), endLabel, true, mv) @@ -733,6 +785,7 @@ class ExpressionCodegen( } override fun visitBreakContinue(jump: IrBreakContinue, data: BlockInfo): StackValue { + jump.markLineNumber(startOffset = true) generateBreakOrContinueExpression(jump, Label(), data) return none() } @@ -788,6 +841,7 @@ class ExpressionCodegen( mv.visitLabel(continueLabel) val condition = loop.condition + condition.markLineNumber(startOffset = true) gen(condition, data) BranchedValue.condJump(StackValue.onStack(condition.asmType), entry, false, mv) mv.mark(endLabel) @@ -796,6 +850,7 @@ class ExpressionCodegen( } override fun visitTry(aTry: IrTry, data: BlockInfo): StackValue { + aTry.markLineNumber(startOffset = true) val finallyExpression = aTry.finallyExpression val tryInfo = if (finallyExpression != null) TryInfo(aTry) else null if (tryInfo != null) { @@ -821,6 +876,7 @@ class ExpressionCodegen( mv.store(index, descriptorType) val catchBody = clause.result + catchBody.markLineNumber(true) gen(catchBody, catchBody.asmType, data) frame.leave(clause.catchParameter) @@ -919,6 +975,9 @@ class ExpressionCodegen( } if (tryCatchBlockEnd != null) { + if (tryInfo != null) { + tryInfo.tryBlock.finallyExpression!!.markLineNumber(startOffset = false) + } mv.goTo(tryCatchBlockEnd) } @@ -967,6 +1026,7 @@ class ExpressionCodegen( } override fun visitThrow(expression: IrThrow, data: BlockInfo): StackValue { + expression.markLineNumber(startOffset = true) gen(expression.value, JAVA_THROWABLE_TYPE, data) mv.athrow() return none() @@ -1124,8 +1184,7 @@ class ExpressionCodegen( get() = mv override val inlineNameGenerator: NameGenerator = NameGenerator("${classCodegen.type.internalName}\$todo") - override val lastLineNumber: Int - get() = -1 //TODO + override var lastLineNumber: Int = -1 override fun consumeReifiedOperationMarker(typeParameterDescriptor: TypeParameterDescriptor) { //TODO @@ -1151,7 +1210,17 @@ class ExpressionCodegen( private fun markNewLabel() = Label().apply { mv.visitLabel(this) } private fun IrElement.markLineNumber(startOffset: Boolean) { - mv.visitLineNumber(fileEntry.getLineNumber(if (startOffset) this.startOffset else endOffset) + 1, markNewLabel()) + val offset = if (startOffset) this.startOffset else endOffset + if (offset < 0) { + return + } + val lineNumber = fileEntry.getLineNumber(offset) + 1 + assert(lineNumber > 0) + if (lastLineNumber == lineNumber) { + return + } + lastLineNumber = lineNumber + mv.visitLineNumber(lineNumber, markNewLabel()) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt index 560d0ea1465..2e3380c5821 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt @@ -16,10 +16,12 @@ package org.jetbrains.kotlin.backend.jvm.codegen +import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.OwnerKind -import org.jetbrains.kotlin.codegen.SourceInfo import org.jetbrains.kotlin.codegen.inline.* +import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -31,9 +33,12 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature -import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.AnnotationVisitor +import org.jetbrains.org.objectweb.asm.ClassVisitor +import org.jetbrains.org.objectweb.asm.FieldVisitor import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.MethodNode @@ -109,7 +114,8 @@ class IrSourceCompilerForInline( //ExpressionCodegen() var node: MethodNode? = null var maxCalcAdapter: MethodVisitor? = null - val functionCodegen = object : FunctionCodegen(irFunction, codegen.classCodegen) { + val fakeClassCodegen = FakeClassCodegen(irFunction, codegen.classCodegen) + val functionCodegen = object : FunctionCodegen(irFunction, fakeClassCodegen) { override fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor { node = MethodNode( API, @@ -121,11 +127,15 @@ class IrSourceCompilerForInline( return maxCalcAdapter!! } } + + assert(codegen.lastLineNumber >= 0) + lazySourceMapper.callSiteMarker = CallSiteMarker(codegen.lastLineNumber) functionCodegen.generate() + lazySourceMapper.callSiteMarker = null maxCalcAdapter!!.visitMaxs(-1, -1) maxCalcAdapter!!.visitEnd() - return SMAPAndMethodNode(node!!, SMAP(/*TODO*/listOf(FileMapping.SKIP))) + return SMAPAndMethodNode(node!!, SMAP(fakeClassCodegen.getOrCreateSourceMapper().resultMappings)) } override fun generateAndInsertFinallyBlocks( @@ -160,4 +170,86 @@ class IrSourceCompilerForInline( override fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) { //TODO } + + private class FakeClassCodegen(irFunction: IrFunction, codegen: ClassCodegen) : + ClassCodegen(irFunction.parent as IrClass, codegen.context) { + + override fun createClassBuilder(): ClassBuilder { + return FakeBuilder + } + + companion object { + val FakeBuilder = object : ClassBuilder { + override fun newField( + origin: JvmDeclarationOrigin, + access: Int, + name: String, + desc: String, + signature: String?, + value: Any? + ): FieldVisitor { + TODO("not implemented") + } + + override fun newMethod( + origin: JvmDeclarationOrigin, + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + TODO("not implemented") + } + + override fun getSerializationBindings(): JvmSerializationBindings { + TODO("not implemented") + } + + override fun newAnnotation(desc: String, visible: Boolean): AnnotationVisitor { + TODO("not implemented") + } + + override fun done() { + TODO("not implemented") + } + + override fun getVisitor(): ClassVisitor { + TODO("not implemented") + } + + override fun defineClass( + origin: PsiElement?, + version: Int, + access: Int, + name: String, + signature: String?, + superName: String, + interfaces: Array + ) { + TODO("not implemented") + } + + override fun visitSource(name: String, debug: String?) { + TODO("not implemented") + } + + override fun visitOuterClass(owner: String, name: String?, desc: String?) { + TODO("not implemented") + } + + override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) { + TODO("not implemented") + } + + override fun getThisName(): String { + TODO("not implemented") + } + + override fun addSMAP(mapping: FileMapping?) { + TODO("not implemented") + } + } + } + } } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index 1f101b91e24..65e3236f23a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt @@ -6,12 +6,15 @@ package org.jetbrains.kotlin.backend.jvm.codegen import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.codegen.FrameMapBase +import org.jetbrains.kotlin.codegen.SourceInfo +import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.ir.util.isAnnotationClass +import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.org.objectweb.asm.Type @@ -41,3 +44,19 @@ internal val IrDeclaration.fileParent: IrFile internal val DeclarationDescriptorWithSource.psiElement: PsiElement? get() = (source as? PsiSourceElement)?.psi + +fun JvmBackendContext.getSourceMapper(declaration: IrClass): DefaultSourceMapper { + val sourceManager = this.psiSourceManager + val fileEntry = sourceManager.getFileEntry(declaration.fileParent) + // NOTE: apparently inliner requires the source range to cover the + // whole file the class is declared in rather than the class only. + // TODO: revise + val endLineNumber = fileEntry.getSourceRangeInfo(0, fileEntry.maxOffset).endLineNumber + return DefaultSourceMapper( + SourceInfo.createInfoForIr( + endLineNumber + 1, + this.state.typeMapper.mapType(declaration.descriptor).internalName, + declaration.descriptor.psiElement!!.containingFile.name + ) + ) +} diff --git a/compiler/testData/codegen/boxInline/complexStack/asCheck.kt b/compiler/testData/codegen/boxInline/complexStack/asCheck.kt index fb841e9a0d9..7612ca6abd5 100644 --- a/compiler/testData/codegen/boxInline/complexStack/asCheck.kt +++ b/compiler/testData/codegen/boxInline/complexStack/asCheck.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // FILE: 1.kt // WITH_RUNTIME package test diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt index d289f00b39f..a62515523e3 100644 --- a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // MODULE: lib // FILE: lib.kt diff --git a/compiler/testData/lineNumber/custom/functionCallWithDefault.kt b/compiler/testData/lineNumber/custom/functionCallWithDefault.kt index e7e155b002a..f1a99034a0c 100644 --- a/compiler/testData/lineNumber/custom/functionCallWithDefault.kt +++ b/compiler/testData/lineNumber/custom/functionCallWithDefault.kt @@ -8,5 +8,5 @@ fun foo(i: Int = 1) { inline fun bar(i: Int = 1) { } - +// IGNORE_BACKEND: JVM_IR // 2 3 13 14 4 7 6 10 9 15 \ No newline at end of file diff --git a/compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt b/compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt index 429c2b0484b..1aece9b9bc3 100644 --- a/compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt +++ b/compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt @@ -15,5 +15,5 @@ inline fun baz() { } fun nop() {} - +// IGNORE_BACKEND: JVM_IR // 2 20 21 3 4 25 26 5 27 6 9 10 11 14 15 17 \ No newline at end of file diff --git a/compiler/testData/lineNumber/if.kt b/compiler/testData/lineNumber/if.kt index ffa49a338fd..67105f6fb7f 100644 --- a/compiler/testData/lineNumber/if.kt +++ b/compiler/testData/lineNumber/if.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JVM_IR fun foo() { if (test.lineNumber() > 0) { test.lineNumber() diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLineNumberTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLineNumberTest.kt index b46ffd3bfca..eef08db4788 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLineNumberTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLineNumberTest.kt @@ -20,11 +20,11 @@ import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase -import org.jetbrains.kotlin.utils.rethrow import org.jetbrains.org.objectweb.asm.* import java.io.File import java.util.* import java.util.regex.Pattern +import kotlin.collections.ArrayList abstract class AbstractLineNumberTest : CodegenTestCase() { @@ -41,10 +41,7 @@ abstract class AbstractLineNumberTest : CodegenTestCase() { try { if (isCustomTest) { - val actualLineNumbers = extractActualLineNumbersFromBytecode(classFileFactory, false) - val text = psiFile.text - val newFileText = text.substringBefore("// ") + getActualLineNumbersAsString(actualLineNumbers) - KotlinTestUtils.assertEqualsToFile(wholeFile, newFileText) + compareCustom(psiFile, wholeFile) } else { val expectedLineNumbers = extractSelectedLineNumbersFromSource(psiFile) val actualLineNumbers = extractActualLineNumbersFromBytecode(classFileFactory, true) @@ -57,8 +54,108 @@ abstract class AbstractLineNumberTest : CodegenTestCase() { } } + protected open fun compareCustom(psiFile: KtFile, wholeFile: File) { + val actualLineNumbers = extractActualLineNumbersFromBytecode(classFileFactory, false) + val text = psiFile.text + val newFileText = text.substring(0 until Regex("// \\d+").find(text)!!.range.first) + + getActualLineNumbersAsString(actualLineNumbers) + KotlinTestUtils.assertEqualsToFile(wholeFile, newFileText) + } + + protected fun extractActualLineNumbersFromBytecode(factory: ClassFileFactory, testFunInvoke: Boolean) = + factory.getClassFiles().flatMap { outputFile -> + val cr = ClassReader(outputFile.asByteArray()) + if (testFunInvoke) readTestFunLineNumbers(cr) else readAllLineNumbers(cr) + } + + protected open fun readTestFunLineNumbers(cr: ClassReader): List { + val labels = arrayListOf