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
@@ -23,12 +23,12 @@ import java.util.*
|
|||||||
|
|
||||||
//TODO comment
|
//TODO comment
|
||||||
class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) {
|
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)
|
fun copyWithNewNode(newMethodNode: MethodNode) = SMAPAndMethodNode(newMethodNode, classSMAP)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence<LabelAndMapping> {
|
private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence<RangeMapping> {
|
||||||
return InsnSequence(node.instructions.first, null).filterIsInstance<LineNumberNode>().map { lineNumber ->
|
return InsnSequence(node.instructions.first, null).filterIsInstance<LineNumberNode>().map { lineNumber ->
|
||||||
val index = classSMAP.intervals.binarySearch(RangeMapping(lineNumber.line, lineNumber.line, 1), Comparator {
|
val index = classSMAP.intervals.binarySearch(RangeMapping(lineNumber.line, lineNumber.line, 1), Comparator {
|
||||||
value, key ->
|
value, key ->
|
||||||
@@ -37,8 +37,6 @@ private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequenc
|
|||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
error("Unmapped line number in inlined function ${node.name}:${lineNumber.line}")
|
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)
|
|
||||||
|
|||||||
+5
-9
@@ -39,7 +39,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
|||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import java.lang.RuntimeException
|
import java.lang.RuntimeException
|
||||||
|
|
||||||
class ClassCodegen private constructor(
|
open class ClassCodegen protected constructor(
|
||||||
internal val irClass: IrClass,
|
internal val irClass: IrClass,
|
||||||
val context: JvmBackendContext,
|
val context: JvmBackendContext,
|
||||||
private val parentClassCodegen: ClassCodegen? = null
|
private val parentClassCodegen: ClassCodegen? = null
|
||||||
@@ -66,7 +66,9 @@ class ClassCodegen private constructor(
|
|||||||
|
|
||||||
val psiElement = irClass.descriptor.psiElement!!
|
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
|
private var sourceMapper: DefaultSourceMapper? = null
|
||||||
|
|
||||||
@@ -210,13 +212,7 @@ class ClassCodegen private constructor(
|
|||||||
|
|
||||||
fun getOrCreateSourceMapper(): DefaultSourceMapper {
|
fun getOrCreateSourceMapper(): DefaultSourceMapper {
|
||||||
if (sourceMapper == null) {
|
if (sourceMapper == null) {
|
||||||
sourceMapper = DefaultSourceMapper(
|
sourceMapper = context.getSourceMapper(irClass)
|
||||||
SourceInfo.createInfoForIr(
|
|
||||||
fileEntry.getSourceRangeInfo(irClass.startOffset, irClass.endOffset).endLineNumber + 1,
|
|
||||||
this.visitor.thisName,
|
|
||||||
this.psiElement.containingFile.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return sourceMapper!!
|
return sourceMapper!!
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-11
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
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.IrIntrinsicFunction
|
||||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
||||||
@@ -118,12 +119,9 @@ class ExpressionCodegen(
|
|||||||
fun generate() {
|
fun generate() {
|
||||||
mv.visitCode()
|
mv.visitCode()
|
||||||
val startLabel = markNewLabel()
|
val startLabel = markNewLabel()
|
||||||
irFunction.markLineNumber(true)
|
|
||||||
val info = BlockInfo.create()
|
val info = BlockInfo.create()
|
||||||
val result = irFunction.body!!.accept(this, info)
|
val result = irFunction.body!!.accept(this, info)
|
||||||
|
markFunctionLineNumber()
|
||||||
irFunction.markLineNumber(false)
|
|
||||||
|
|
||||||
val returnType = typeMapper.mapReturnType(irFunction.descriptor)
|
val returnType = typeMapper.mapReturnType(irFunction.descriptor)
|
||||||
if (irFunction.body is IrExpressionBody) {
|
if (irFunction.body is IrExpressionBody) {
|
||||||
mv.areturn(returnType)
|
mv.areturn(returnType)
|
||||||
@@ -141,6 +139,28 @@ class ExpressionCodegen(
|
|||||||
mv.visitEnd()
|
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) {
|
private fun writeParameterInLocalVariableTable(startLabel: Label) {
|
||||||
if (!irFunction.isStatic) {
|
if (!irFunction.isStatic) {
|
||||||
mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, markNewLabel(), 0)
|
mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, markNewLabel(), 0)
|
||||||
@@ -175,7 +195,9 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue {
|
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue {
|
||||||
return body.statements.fold(none()) { _, exp ->
|
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 {
|
override fun visitMemberAccess(expression: IrMemberAccessExpression, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
return generateCall(expression, null, data)
|
return generateCall(expression, null, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: BlockInfo): StackValue {
|
override fun visitCall(expression: IrCall, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
if (expression.descriptor is ConstructorDescriptor) {
|
if (expression.descriptor is ConstructorDescriptor) {
|
||||||
return generateNewCall(expression, data)
|
return generateNewCall(expression, data)
|
||||||
}
|
}
|
||||||
@@ -367,8 +391,11 @@ class ExpressionCodegen(
|
|||||||
val varType = typeMapper.mapType(declaration.descriptor)
|
val varType = typeMapper.mapType(declaration.descriptor)
|
||||||
val index = frame.enter(declaration.symbol, varType)
|
val index = frame.enter(declaration.symbol, varType)
|
||||||
|
|
||||||
|
declaration.markLineNumber(startOffset = true)
|
||||||
|
|
||||||
declaration.initializer?.apply {
|
declaration.initializer?.apply {
|
||||||
StackValue.local(index, varType).store(gen(this, varType, data), mv)
|
StackValue.local(index, varType).store(gen(this, varType, data), mv)
|
||||||
|
this.markLineNumber(startOffset = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val info = VariableInfo(
|
val info = VariableInfo(
|
||||||
@@ -392,6 +419,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue, data: BlockInfo): StackValue {
|
override fun visitGetValue(expression: IrGetValue, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
return generateLocal(expression.symbol, expression.asmType)
|
return generateLocal(expression.symbol, expression.asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,12 +438,14 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetField(expression: IrGetField, data: BlockInfo): StackValue {
|
override fun visitGetField(expression: IrGetField, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
val value = generateFieldValue(expression, data)
|
val value = generateFieldValue(expression, data)
|
||||||
value.put(value.type, mv)
|
value.put(value.type, mv)
|
||||||
return onStack(value.type)
|
return onStack(value.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue {
|
override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
val fieldValue = generateFieldValue(expression, data)
|
val fieldValue = generateFieldValue(expression, data)
|
||||||
fieldValue.store(expression.value.accept(this, data), mv)
|
fieldValue.store(expression.value.accept(this, data), mv)
|
||||||
return none()
|
return none()
|
||||||
@@ -450,12 +480,15 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): StackValue {
|
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
|
expression.value.markLineNumber(startOffset = true)
|
||||||
val value = expression.value.accept(this, data)
|
val value = expression.value.accept(this, data)
|
||||||
StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv)
|
StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv)
|
||||||
return none()
|
return none()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): StackValue {
|
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
val value = expression.value
|
val value = expression.value
|
||||||
val type = expression.asmType
|
val type = expression.asmType
|
||||||
StackValue.constant(value, type).put(type, mv)
|
StackValue.constant(value, type).put(type, mv)
|
||||||
@@ -480,6 +513,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitVararg(expression: IrVararg, data: BlockInfo): StackValue {
|
override fun visitVararg(expression: IrVararg, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
val outType = expression.type
|
val outType = expression.type
|
||||||
val type = expression.asmType
|
val type = expression.asmType
|
||||||
assert(type.sort == Type.ARRAY)
|
assert(type.sort == Type.ARRAY)
|
||||||
@@ -588,7 +622,7 @@ class ExpressionCodegen(
|
|||||||
val afterReturnLabel = Label()
|
val afterReturnLabel = Label()
|
||||||
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data)
|
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data)
|
||||||
|
|
||||||
expression.markLineNumber(false)
|
expression.markLineNumber(startOffset = true)
|
||||||
mv.areturn(returnType)
|
mv.areturn(returnType)
|
||||||
mv.mark(afterReturnLabel)
|
mv.mark(afterReturnLabel)
|
||||||
mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/
|
mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/
|
||||||
@@ -596,8 +630,10 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue =
|
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
||||||
genIfWithBranches(expression.branches[0], data, expression.type.toKotlinType(), expression.branches.drop(1))
|
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 {
|
private fun genIfWithBranches(branch: IrBranch, data: BlockInfo, type: KotlinType, otherBranches: List<IrBranch>): StackValue {
|
||||||
@@ -636,6 +672,7 @@ class ExpressionCodegen(
|
|||||||
when (expression.operator) {
|
when (expression.operator) {
|
||||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||||
val result = expression.argument.accept(this, data)
|
val result = expression.argument.accept(this, data)
|
||||||
|
expression.argument.markEndOfStatementIfNeeded()
|
||||||
coerce(result.type, Type.VOID_TYPE, mv)
|
coerce(result.type, Type.VOID_TYPE, mv)
|
||||||
return none()
|
return none()
|
||||||
}
|
}
|
||||||
@@ -686,7 +723,21 @@ class ExpressionCodegen(
|
|||||||
return expression.onStack
|
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 {
|
override fun visitStringConcatenation(expression: IrStringConcatenation, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
AsmUtil.genStringBuilderConstructor(mv)
|
AsmUtil.genStringBuilderConstructor(mv)
|
||||||
expression.arguments.forEach {
|
expression.arguments.forEach {
|
||||||
val stackValue = gen(it, data)
|
val stackValue = gen(it, data)
|
||||||
@@ -712,6 +763,7 @@ class ExpressionCodegen(
|
|||||||
// GOTO L0
|
// GOTO L0
|
||||||
// L1
|
// L1
|
||||||
//TODO: write elimination lower
|
//TODO: write elimination lower
|
||||||
|
condition.markLineNumber(startOffset = true)
|
||||||
if (!(condition is IrConst<*> && condition.value == true)) {
|
if (!(condition is IrConst<*> && condition.value == true)) {
|
||||||
gen(condition, data)
|
gen(condition, data)
|
||||||
BranchedValue.condJump(StackValue.onStack(condition.asmType), endLabel, true, mv)
|
BranchedValue.condJump(StackValue.onStack(condition.asmType), endLabel, true, mv)
|
||||||
@@ -733,6 +785,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitBreakContinue(jump: IrBreakContinue, data: BlockInfo): StackValue {
|
override fun visitBreakContinue(jump: IrBreakContinue, data: BlockInfo): StackValue {
|
||||||
|
jump.markLineNumber(startOffset = true)
|
||||||
generateBreakOrContinueExpression(jump, Label(), data)
|
generateBreakOrContinueExpression(jump, Label(), data)
|
||||||
return none()
|
return none()
|
||||||
}
|
}
|
||||||
@@ -788,6 +841,7 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
mv.visitLabel(continueLabel)
|
mv.visitLabel(continueLabel)
|
||||||
val condition = loop.condition
|
val condition = loop.condition
|
||||||
|
condition.markLineNumber(startOffset = true)
|
||||||
gen(condition, data)
|
gen(condition, data)
|
||||||
BranchedValue.condJump(StackValue.onStack(condition.asmType), entry, false, mv)
|
BranchedValue.condJump(StackValue.onStack(condition.asmType), entry, false, mv)
|
||||||
mv.mark(endLabel)
|
mv.mark(endLabel)
|
||||||
@@ -796,6 +850,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitTry(aTry: IrTry, data: BlockInfo): StackValue {
|
override fun visitTry(aTry: IrTry, data: BlockInfo): StackValue {
|
||||||
|
aTry.markLineNumber(startOffset = true)
|
||||||
val finallyExpression = aTry.finallyExpression
|
val finallyExpression = aTry.finallyExpression
|
||||||
val tryInfo = if (finallyExpression != null) TryInfo(aTry) else null
|
val tryInfo = if (finallyExpression != null) TryInfo(aTry) else null
|
||||||
if (tryInfo != null) {
|
if (tryInfo != null) {
|
||||||
@@ -821,6 +876,7 @@ class ExpressionCodegen(
|
|||||||
mv.store(index, descriptorType)
|
mv.store(index, descriptorType)
|
||||||
|
|
||||||
val catchBody = clause.result
|
val catchBody = clause.result
|
||||||
|
catchBody.markLineNumber(true)
|
||||||
gen(catchBody, catchBody.asmType, data)
|
gen(catchBody, catchBody.asmType, data)
|
||||||
|
|
||||||
frame.leave(clause.catchParameter)
|
frame.leave(clause.catchParameter)
|
||||||
@@ -919,6 +975,9 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tryCatchBlockEnd != null) {
|
if (tryCatchBlockEnd != null) {
|
||||||
|
if (tryInfo != null) {
|
||||||
|
tryInfo.tryBlock.finallyExpression!!.markLineNumber(startOffset = false)
|
||||||
|
}
|
||||||
mv.goTo(tryCatchBlockEnd)
|
mv.goTo(tryCatchBlockEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -967,6 +1026,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitThrow(expression: IrThrow, data: BlockInfo): StackValue {
|
override fun visitThrow(expression: IrThrow, data: BlockInfo): StackValue {
|
||||||
|
expression.markLineNumber(startOffset = true)
|
||||||
gen(expression.value, JAVA_THROWABLE_TYPE, data)
|
gen(expression.value, JAVA_THROWABLE_TYPE, data)
|
||||||
mv.athrow()
|
mv.athrow()
|
||||||
return none()
|
return none()
|
||||||
@@ -1124,8 +1184,7 @@ class ExpressionCodegen(
|
|||||||
get() = mv
|
get() = mv
|
||||||
override val inlineNameGenerator: NameGenerator = NameGenerator("${classCodegen.type.internalName}\$todo")
|
override val inlineNameGenerator: NameGenerator = NameGenerator("${classCodegen.type.internalName}\$todo")
|
||||||
|
|
||||||
override val lastLineNumber: Int
|
override var lastLineNumber: Int = -1
|
||||||
get() = -1 //TODO
|
|
||||||
|
|
||||||
override fun consumeReifiedOperationMarker(typeParameterDescriptor: TypeParameterDescriptor) {
|
override fun consumeReifiedOperationMarker(typeParameterDescriptor: TypeParameterDescriptor) {
|
||||||
//TODO
|
//TODO
|
||||||
@@ -1151,7 +1210,17 @@ class ExpressionCodegen(
|
|||||||
private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
|
private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
|
||||||
|
|
||||||
private fun IrElement.markLineNumber(startOffset: Boolean) {
|
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
|
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||||
import org.jetbrains.kotlin.codegen.SourceInfo
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.*
|
import org.jetbrains.kotlin.codegen.inline.*
|
||||||
|
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
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.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
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.JvmMethodGenericSignature
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
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.MethodVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
@@ -109,7 +114,8 @@ class IrSourceCompilerForInline(
|
|||||||
//ExpressionCodegen()
|
//ExpressionCodegen()
|
||||||
var node: MethodNode? = null
|
var node: MethodNode? = null
|
||||||
var maxCalcAdapter: MethodVisitor? = 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 {
|
override fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor {
|
||||||
node = MethodNode(
|
node = MethodNode(
|
||||||
API,
|
API,
|
||||||
@@ -121,11 +127,15 @@ class IrSourceCompilerForInline(
|
|||||||
return maxCalcAdapter!!
|
return maxCalcAdapter!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(codegen.lastLineNumber >= 0)
|
||||||
|
lazySourceMapper.callSiteMarker = CallSiteMarker(codegen.lastLineNumber)
|
||||||
functionCodegen.generate()
|
functionCodegen.generate()
|
||||||
|
lazySourceMapper.callSiteMarker = null
|
||||||
maxCalcAdapter!!.visitMaxs(-1, -1)
|
maxCalcAdapter!!.visitMaxs(-1, -1)
|
||||||
maxCalcAdapter!!.visitEnd()
|
maxCalcAdapter!!.visitEnd()
|
||||||
|
|
||||||
return SMAPAndMethodNode(node!!, SMAP(/*TODO*/listOf(FileMapping.SKIP)))
|
return SMAPAndMethodNode(node!!, SMAP(fakeClassCodegen.getOrCreateSourceMapper().resultMappings))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateAndInsertFinallyBlocks(
|
override fun generateAndInsertFinallyBlocks(
|
||||||
@@ -160,4 +170,86 @@ class IrSourceCompilerForInline(
|
|||||||
override fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) {
|
override fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) {
|
||||||
//TODO
|
//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
|
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.codegen.FrameMapBase
|
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.descriptors.DeclarationDescriptorWithSource
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||||
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.ir.util.isInterface
|
||||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
@@ -41,3 +44,19 @@ internal val IrDeclaration.fileParent: IrFile
|
|||||||
|
|
||||||
internal val DeclarationDescriptorWithSource.psiElement: PsiElement?
|
internal val DeclarationDescriptorWithSource.psiElement: PsiElement?
|
||||||
get() = (source as? PsiSourceElement)?.psi
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
package test
|
package test
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND: JS_IR
|
// IGNORE_BACKEND: JS_IR
|
||||||
// MODULE: lib
|
// MODULE: lib
|
||||||
// FILE: lib.kt
|
// FILE: lib.kt
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ fun foo(i: Int = 1) {
|
|||||||
|
|
||||||
inline fun bar(i: Int = 1) {
|
inline fun bar(i: Int = 1) {
|
||||||
}
|
}
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// 2 3 13 14 4 7 6 10 9 15
|
// 2 3 13 14 4 7 6 10 9 15
|
||||||
+1
-1
@@ -15,5 +15,5 @@ inline fun baz() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun nop() {}
|
fun nop() {}
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// 2 20 21 3 4 25 26 5 27 6 9 10 11 14 15 17
|
// 2 20 21 3 4 25 26 5 27 6 9 10 11 14 15 17
|
||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
fun foo() {
|
fun foo() {
|
||||||
if (test.lineNumber() > 0) {
|
if (test.lineNumber() > 0) {
|
||||||
test.lineNumber()
|
test.lineNumber()
|
||||||
|
|||||||
+103
-91
@@ -20,11 +20,11 @@ import com.intellij.openapi.util.text.StringUtil
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||||
import org.jetbrains.kotlin.utils.rethrow
|
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
abstract class AbstractLineNumberTest : CodegenTestCase() {
|
abstract class AbstractLineNumberTest : CodegenTestCase() {
|
||||||
|
|
||||||
@@ -41,10 +41,7 @@ abstract class AbstractLineNumberTest : CodegenTestCase() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCustomTest) {
|
if (isCustomTest) {
|
||||||
val actualLineNumbers = extractActualLineNumbersFromBytecode(classFileFactory, false)
|
compareCustom(psiFile, wholeFile)
|
||||||
val text = psiFile.text
|
|
||||||
val newFileText = text.substringBefore("// ") + getActualLineNumbersAsString(actualLineNumbers)
|
|
||||||
KotlinTestUtils.assertEqualsToFile(wholeFile, newFileText)
|
|
||||||
} else {
|
} else {
|
||||||
val expectedLineNumbers = extractSelectedLineNumbersFromSource(psiFile)
|
val expectedLineNumbers = extractSelectedLineNumbersFromSource(psiFile)
|
||||||
val actualLineNumbers = extractActualLineNumbersFromBytecode(classFileFactory, true)
|
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<String> {
|
||||||
|
val labels = arrayListOf<Label>()
|
||||||
|
val labels2LineNumbers = HashMap<Label, String>()
|
||||||
|
|
||||||
|
val visitor = object : ClassVisitor(Opcodes.ASM5) {
|
||||||
|
override fun visitMethod(
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
desc: String,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<String>?
|
||||||
|
): MethodVisitor {
|
||||||
|
return getTestFunLineNumbersMethodVisitor(labels, labels2LineNumbers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.accept(visitor, ClassReader.SKIP_FRAMES)
|
||||||
|
|
||||||
|
return labels.map { label ->
|
||||||
|
labels2LineNumbers[label] ?: error("No line number found for a label")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun getTestFunLineNumbersMethodVisitor(
|
||||||
|
labels: ArrayList<Label>,
|
||||||
|
labels2LineNumbers: HashMap<Label, String>
|
||||||
|
): MethodVisitor {
|
||||||
|
return object : MethodVisitor(Opcodes.ASM5) {
|
||||||
|
private var lastLabel: Label? = null
|
||||||
|
|
||||||
|
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
|
||||||
|
if (LINE_NUMBER_FUN == name) {
|
||||||
|
labels.add(lastLabel ?: error("A function call with no preceding label"))
|
||||||
|
}
|
||||||
|
lastLabel = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLabel(label: Label) {
|
||||||
|
lastLabel = label
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLineNumber(line: Int, start: Label) {
|
||||||
|
labels2LineNumbers[start] = Integer.toString(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun readAllLineNumbers(reader: ClassReader): List<String> {
|
||||||
|
val result = ArrayList<String>()
|
||||||
|
val visitedLabels = HashSet<String>()
|
||||||
|
|
||||||
|
reader.accept(object : ClassVisitor(Opcodes.ASM5) {
|
||||||
|
override fun visitMethod(
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
desc: String,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<String>?
|
||||||
|
): MethodVisitor {
|
||||||
|
return object : MethodVisitor(Opcodes.ASM5) {
|
||||||
|
override fun visitLineNumber(line: Int, label: Label) {
|
||||||
|
val overrides = !visitedLabels.add(label.toString())
|
||||||
|
|
||||||
|
result.add((if (overrides) "+" else "") + line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, ClassReader.SKIP_FRAMES)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun extractSelectedLineNumbersFromSource(file: KtFile): List<String> {
|
||||||
|
val fileContent = file.text
|
||||||
|
val lineNumbers = arrayListOf<String>()
|
||||||
|
val lines = StringUtil.convertLineSeparators(fileContent).split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
|
||||||
|
for (i in lines.indices) {
|
||||||
|
val matcher = TEST_LINE_NUMBER_PATTERN.matcher(lines[i])
|
||||||
|
if (matcher.matches()) {
|
||||||
|
lineNumbers.add(Integer.toString(i + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lineNumbers
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val LINE_NUMBER_FUN = "lineNumber"
|
const val LINE_NUMBER_FUN = "lineNumber"
|
||||||
private val TEST_LINE_NUMBER_PATTERN = Pattern.compile("^.*test.$LINE_NUMBER_FUN\\(\\).*$")
|
private val TEST_LINE_NUMBER_PATTERN = Pattern.compile("^.*test.$LINE_NUMBER_FUN\\(\\).*$")
|
||||||
|
|
||||||
private fun createLineNumberDeclaration() =
|
private fun createLineNumberDeclaration() =
|
||||||
@@ -69,90 +166,5 @@ abstract class AbstractLineNumberTest : CodegenTestCase() {
|
|||||||
|
|
||||||
private fun getActualLineNumbersAsString(lines: List<String>) =
|
private fun getActualLineNumbersAsString(lines: List<String>) =
|
||||||
lines.joinToString(" ", "// ")
|
lines.joinToString(" ", "// ")
|
||||||
|
|
||||||
private fun extractActualLineNumbersFromBytecode(factory: ClassFileFactory, testFunInvoke: Boolean) =
|
|
||||||
factory.getClassFiles().flatMap { outputFile ->
|
|
||||||
val cr = ClassReader(outputFile.asByteArray())
|
|
||||||
if (testFunInvoke) readTestFunLineNumbers(cr) else readAllLineNumbers(cr)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractSelectedLineNumbersFromSource(file: KtFile): List<String> {
|
|
||||||
val fileContent = file.text
|
|
||||||
val lineNumbers = arrayListOf<String>()
|
|
||||||
val lines = StringUtil.convertLineSeparators(fileContent).split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
||||||
|
|
||||||
for (i in lines.indices) {
|
|
||||||
val matcher = TEST_LINE_NUMBER_PATTERN.matcher(lines[i])
|
|
||||||
if (matcher.matches()) {
|
|
||||||
lineNumbers.add(Integer.toString(i + 1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lineNumbers
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readTestFunLineNumbers(cr: ClassReader): List<String> {
|
|
||||||
val labels = arrayListOf<Label>()
|
|
||||||
val labels2LineNumbers = HashMap<Label, String>()
|
|
||||||
|
|
||||||
val visitor = object : ClassVisitor(Opcodes.ASM5) {
|
|
||||||
override fun visitMethod(
|
|
||||||
access: Int,
|
|
||||||
name: String,
|
|
||||||
desc: String,
|
|
||||||
signature: String?,
|
|
||||||
exceptions: Array<String>?
|
|
||||||
): MethodVisitor {
|
|
||||||
return object : MethodVisitor(Opcodes.ASM5) {
|
|
||||||
private var lastLabel: Label? = null
|
|
||||||
|
|
||||||
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
|
|
||||||
if (LINE_NUMBER_FUN == name) {
|
|
||||||
labels.add(lastLabel ?: error("A function call with no preceding label"))
|
|
||||||
}
|
|
||||||
lastLabel = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitLabel(label: Label) {
|
|
||||||
lastLabel = label
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitLineNumber(line: Int, start: Label) {
|
|
||||||
labels2LineNumbers[start] = Integer.toString(line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cr.accept(visitor, ClassReader.SKIP_FRAMES)
|
|
||||||
|
|
||||||
return labels.map { label ->
|
|
||||||
labels2LineNumbers[label] ?: error("No line number found for a label")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readAllLineNumbers(reader: ClassReader): List<String> {
|
|
||||||
val result = ArrayList<String>()
|
|
||||||
val visitedLabels = HashSet<String>()
|
|
||||||
|
|
||||||
reader.accept(object : ClassVisitor(Opcodes.ASM5) {
|
|
||||||
override fun visitMethod(
|
|
||||||
access: Int,
|
|
||||||
name: String,
|
|
||||||
desc: String,
|
|
||||||
signature: String?,
|
|
||||||
exceptions: Array<String>?
|
|
||||||
): MethodVisitor {
|
|
||||||
return object : MethodVisitor(Opcodes.ASM5) {
|
|
||||||
override fun visitLineNumber(line: Int, label: Label) {
|
|
||||||
val overrides = !visitedLabels.add(label.toString())
|
|
||||||
|
|
||||||
result.add((if (overrides) "+" else "") + line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, ClassReader.SKIP_FRAMES)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.ir
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.AbstractLineNumberTest
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
|
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||||
|
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractIrLineNumberTest : AbstractLineNumberTest() {
|
||||||
|
override fun extractConfigurationKind(files: MutableList<TestFile>): ConfigurationKind {
|
||||||
|
return ConfigurationKind.ALL
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateConfiguration(configuration: CompilerConfiguration) {
|
||||||
|
super.updateConfiguration(configuration)
|
||||||
|
configuration.put(JVMConfigurationKeys.IR, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareCustom(psiFile: KtFile, wholeFile: File) {
|
||||||
|
val fileText = psiFile.text
|
||||||
|
val expectedLineNumbers = normalize(
|
||||||
|
fileText.substring(fileText.indexOf("//") + 2)
|
||||||
|
.trim().split(" ").map { it.trim() }.toMutableList()
|
||||||
|
)
|
||||||
|
val actualLineNumbers = normalize(extractActualLineNumbersFromBytecode(classFileFactory, false))
|
||||||
|
KtUsefulTestCase.assertSameElements(actualLineNumbers, expectedLineNumbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readAllLineNumbers(reader: ClassReader) =
|
||||||
|
normalize(super.readAllLineNumbers(reader))
|
||||||
|
|
||||||
|
override fun extractSelectedLineNumbersFromSource(file: KtFile) =
|
||||||
|
normalize(super.extractSelectedLineNumbersFromSource(file))
|
||||||
|
|
||||||
|
override fun getTestFunLineNumbersMethodVisitor(
|
||||||
|
labels: ArrayList<Label>,
|
||||||
|
labels2LineNumbers: java.util.HashMap<Label, String>
|
||||||
|
): MethodVisitor {
|
||||||
|
return object : MethodVisitor(Opcodes.ASM5) {
|
||||||
|
private var lastLabel: Label? = null
|
||||||
|
private var lastLine = -1
|
||||||
|
|
||||||
|
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
|
||||||
|
if (LINE_NUMBER_FUN == name) {
|
||||||
|
labels.add(lastLabel ?: error("A function call with no preceding label"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLabel(label: Label) {
|
||||||
|
if (lastLabel != null && !labels2LineNumbers.containsKey(lastLabel) && lastLine >= 0) {
|
||||||
|
labels2LineNumbers[lastLabel!!] = Integer.toString(lastLine) // Inherited line number
|
||||||
|
}
|
||||||
|
lastLabel = label
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLineNumber(line: Int, start: Label) {
|
||||||
|
labels2LineNumbers[start] = Integer.toString(line)
|
||||||
|
lastLine = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readTestFunLineNumbers(cr: ClassReader) =
|
||||||
|
normalize(super.readTestFunLineNumbers(cr))
|
||||||
|
|
||||||
|
private fun normalize(numbers: List<String>) =
|
||||||
|
numbers
|
||||||
|
.map { if (it.startsWith('+')) it.substring(1) else it }
|
||||||
|
.toSet()
|
||||||
|
.toMutableList()
|
||||||
|
.sortedBy { it.toInt() }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
+244
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.ir;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TargetBackend;
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
@TestMetadata("compiler/testData/lineNumber")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class IrLineNumberTestGenerated extends AbstractIrLineNumberTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInLineNumber() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/lineNumber"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("anonymousFunction.kt")
|
||||||
|
public void testAnonymousFunction() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/anonymousFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("class.kt")
|
||||||
|
public void testClass() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/class.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classObject.kt")
|
||||||
|
public void testClassObject() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/classObject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("defaultParameter.kt")
|
||||||
|
public void testDefaultParameter() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/defaultParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("enum.kt")
|
||||||
|
public void testEnum() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/enum.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("for.kt")
|
||||||
|
public void testFor() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/for.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("if.kt")
|
||||||
|
public void testIf() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/if.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSimpleCall.kt")
|
||||||
|
public void testInlineSimpleCall() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/inlineSimpleCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunction.kt")
|
||||||
|
public void testLocalFunction() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/localFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("object.kt")
|
||||||
|
public void testObject() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/object.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyAccessor.kt")
|
||||||
|
public void testPropertyAccessor() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/propertyAccessor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("psvm.kt")
|
||||||
|
public void testPsvm() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/psvm.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleSmap.kt")
|
||||||
|
public void testSimpleSmap() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/simpleSmap.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevel.kt")
|
||||||
|
public void testTopLevel() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/topLevel.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("trait.kt")
|
||||||
|
public void testTrait() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/trait.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("tryCatch.kt")
|
||||||
|
public void testTryCatch() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/tryCatch.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("while.kt")
|
||||||
|
public void testWhile() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/while.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/lineNumber/custom")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Custom extends AbstractIrLineNumberTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInCustom() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/lineNumber/custom"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("beforeGotoToWhileStart.kt")
|
||||||
|
public void testBeforeGotoToWhileStart() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/beforeGotoToWhileStart.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callWithCallInArguments.kt")
|
||||||
|
public void testCallWithCallInArguments() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/callWithCallInArguments.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callWithReceiver.kt")
|
||||||
|
public void testCallWithReceiver() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/callWithReceiver.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("chainCall.kt")
|
||||||
|
public void testChainCall() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/chainCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compileTimeConstant.kt")
|
||||||
|
public void testCompileTimeConstant() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/compileTimeConstant.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionCallWithDefault.kt")
|
||||||
|
public void testFunctionCallWithDefault() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/functionCallWithDefault.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionCallWithInlinedLambdaParam.kt")
|
||||||
|
public void testFunctionCallWithInlinedLambdaParam() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/functionCallWithInlinedLambdaParam.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionCallWithLambdaParam.kt")
|
||||||
|
public void testFunctionCallWithLambdaParam() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/functionCallWithLambdaParam.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifThen.kt")
|
||||||
|
public void testIfThen() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/ifThen.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifThenElse.kt")
|
||||||
|
public void testIfThenElse() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/ifThenElse.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
|
||||||
|
public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/inTheEndOfLambdaArgumentOfInlineCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multilineFunctionCall.kt")
|
||||||
|
public void testMultilineFunctionCall() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/multilineFunctionCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multilineInfixCall.kt")
|
||||||
|
public void testMultilineInfixCall() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/multilineInfixCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noParametersArgumentCallInExpression.kt")
|
||||||
|
public void testNoParametersArgumentCallInExpression() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/noParametersArgumentCallInExpression.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("smapInlineAsArgument.kt")
|
||||||
|
public void testSmapInlineAsArgument() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/smapInlineAsArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("smapInlineAsInfixArgument.kt")
|
||||||
|
public void testSmapInlineAsInfixArgument() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/smapInlineAsInfixArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("smapInlineAsInlineArgument.kt")
|
||||||
|
public void testSmapInlineAsInlineArgument() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/smapInlineAsInlineArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("smapInlineInIntrinsicArgument.kt")
|
||||||
|
public void testSmapInlineInIntrinsicArgument() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/smapInlineInIntrinsicArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("tryCatchExpression.kt")
|
||||||
|
public void testTryCatchExpression() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/tryCatchExpression.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("tryCatchFinally.kt")
|
||||||
|
public void testTryCatchFinally() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/tryCatchFinally.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("tryFinally.kt")
|
||||||
|
public void testTryFinally() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/tryFinally.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("when.kt")
|
||||||
|
public void testWhen() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/when.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenSubject.kt")
|
||||||
|
public void testWhenSubject() throws Exception {
|
||||||
|
runTest("compiler/testData/lineNumber/custom/whenSubject.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,10 +27,7 @@ import org.jetbrains.kotlin.cli.AbstractCliTest
|
|||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.defaultConstructor.AbstractDefaultArgumentsReflectionTest
|
import org.jetbrains.kotlin.codegen.defaultConstructor.AbstractDefaultArgumentsReflectionTest
|
||||||
import org.jetbrains.kotlin.codegen.flags.AbstractWriteFlagsTest
|
import org.jetbrains.kotlin.codegen.flags.AbstractWriteFlagsTest
|
||||||
import org.jetbrains.kotlin.codegen.ir.AbstractIrBlackBoxAgainstJavaCodegenTest
|
import org.jetbrains.kotlin.codegen.ir.*
|
||||||
import org.jetbrains.kotlin.codegen.ir.AbstractIrBlackBoxCodegenTest
|
|
||||||
import org.jetbrains.kotlin.codegen.ir.AbstractIrBlackBoxInlineCodegenTest
|
|
||||||
import org.jetbrains.kotlin.codegen.ir.AbstractIrCheckLocalVariablesTableTest
|
|
||||||
import org.jetbrains.kotlin.generators.tests.generator.testGroup
|
import org.jetbrains.kotlin.generators.tests.generator.testGroup
|
||||||
import org.jetbrains.kotlin.generators.util.KT_OR_KTS_WITHOUT_DOTS_IN_NAME
|
import org.jetbrains.kotlin.generators.util.KT_OR_KTS_WITHOUT_DOTS_IN_NAME
|
||||||
import org.jetbrains.kotlin.integration.AbstractAntTaskTest
|
import org.jetbrains.kotlin.integration.AbstractAntTaskTest
|
||||||
@@ -359,6 +356,10 @@ fun main(args: Array<String>) {
|
|||||||
model("checkLocalVariablesTable", targetBackend = TargetBackend.JVM_IR)
|
model("checkLocalVariablesTable", targetBackend = TargetBackend.JVM_IR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractIrLineNumberTest> {
|
||||||
|
model("lineNumber", targetBackend = TargetBackend.JVM_IR)
|
||||||
|
}
|
||||||
|
|
||||||
testClass<AbstractIrBlackBoxInlineCodegenTest> {
|
testClass<AbstractIrBlackBoxInlineCodegenTest> {
|
||||||
model("codegen/boxInline", targetBackend = TargetBackend.JVM_IR)
|
model("codegen/boxInline", targetBackend = TargetBackend.JVM_IR)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user