Creating IrLineNumberTestGenerated, adding line numbers for few common expressions
This PR enables LineNumberTestGenerated test on IR backend. The testing of hardcoded sequence of line numbers is replaced with mere checks for set-like checks for expected line numbers.
This commit is contained in:
committed by
Mikhael Bogdanov
parent
36936d252c
commit
ddf92ef187
+5
-9
@@ -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!!
|
||||
}
|
||||
|
||||
+80
-11
@@ -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 <T> visitConst(expression: IrConst<T>, 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<IrBranch>): 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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+96
-4
@@ -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<out String>?
|
||||
): 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<out String>
|
||||
) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-2
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user