[JVM IR] Support target templates in
AbstractCheckLocalVariablesTableTest and fix `checkLocalVariablesTable/localFun.kt` for JVM IR. Also improved diagnosability of CheckLocalVariablesTableTest failures.
This commit is contained in:
committed by
Alexander Udalov
parent
152d88e747
commit
70b304e6e4
+11
-1
@@ -1,8 +1,18 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
|
val x = 1
|
||||||
fun bar() {
|
fun bar() {
|
||||||
|
val y = x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Local function bodies are in a separate class (implementing FunctionN) for non-IR, and are static methods in the enclosing class for IR.
|
||||||
|
|
||||||
|
// JVM_TEMPLATES
|
||||||
// METHOD : LocalFunKt$foo$1.invoke()V
|
// METHOD : LocalFunKt$foo$1.invoke()V
|
||||||
|
// VARIABLE : NAME=y TYPE=I INDEX=1
|
||||||
// VARIABLE : NAME=this TYPE=LLocalFunKt$foo$1; INDEX=0
|
// VARIABLE : NAME=this TYPE=LLocalFunKt$foo$1; INDEX=0
|
||||||
|
|
||||||
|
// JVM_IR_TEMPLATES
|
||||||
|
// METHOD : LocalFunKt.foo$bar(I)V
|
||||||
|
// VARIABLE : NAME=y TYPE=I INDEX=1
|
||||||
|
// VARIABLE : NAME=$x TYPE=I INDEX=0
|
||||||
|
|||||||
+74
-23
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.Comparing
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.TargetBackend
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
|
import org.junit.ComparisonFailure
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@@ -33,43 +35,85 @@ abstract class AbstractCheckLocalVariablesTableTest : CodegenTestCase() {
|
|||||||
compile(files)
|
compile(files)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val classAndMethod = parseClassAndMethodSignature(wholeFile)
|
val filteredLines = filterTestFileForTargetBackend(wholeFile)
|
||||||
|
val classAndMethod = parseClassAndMethodSignature(filteredLines)
|
||||||
val split = classAndMethod.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
val split = classAndMethod.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
assert(split.size == 2) { "Exactly one dot is expected: $classAndMethod" }
|
assert(split.size == 2) { "Exactly one dot is expected: $classAndMethod" }
|
||||||
val classFileRegex = StringUtil.escapeToRegexp(split[0] + ".class").replace("\\*", ".+")
|
val classFileRegex = StringUtil.escapeToRegexp(split[0] + ".class").replace("\\*", ".+")
|
||||||
val methodName = split[1]
|
val methodName = split[1]
|
||||||
|
|
||||||
val outputFiles = (classFileFactory as OutputFileCollection).asList()
|
val outputFiles = (classFileFactory as OutputFileCollection).asList()
|
||||||
val outputFile = outputFiles.first { file -> file.relativePath.matches(classFileRegex.toRegex()) }
|
val outputFile = outputFiles.firstOrNull { file -> file.relativePath.matches(classFileRegex.toRegex()) }
|
||||||
|
checkNotNull(outputFile) {
|
||||||
val pathsString = outputFiles.joinToString { it.relativePath }
|
val pathsString = outputFiles.joinToString { it.relativePath }
|
||||||
assertNotNull("Couldn't find class file for pattern $classFileRegex in: $pathsString", outputFile)
|
"Couldn't find class file for pattern $classFileRegex in: $pathsString"
|
||||||
|
}
|
||||||
|
|
||||||
val classReader = ClassReader(outputFile.asByteArray())
|
val classReader = ClassReader(outputFile.asByteArray())
|
||||||
val actualLocalVariables = readLocalVariable(classReader, methodName)
|
val actualLocalVariables = readLocalVariable(classReader, methodName)
|
||||||
checkLocalVariableTypes(classReader, methodName, actualLocalVariables)
|
checkLocalVariableTypes(classReader, methodName, actualLocalVariables)
|
||||||
|
|
||||||
doCompare(wholeFile, files.single().content, actualLocalVariables)
|
doCompare(filteredLines, actualLocalVariables)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
printReport(wholeFile)
|
printReport(wholeFile)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun doCompare(
|
// TODO: Refactor test infrastructure to share filtering with AbstractBytecodeTextTest
|
||||||
testFile: File,
|
private fun filterTestFileForTargetBackend(testFile: File): List<String> {
|
||||||
text: String,
|
val filteredLines = mutableListOf<String>()
|
||||||
actualLocalVariables: List<LocalVariable>
|
var currentBackend = TargetBackend.ANY
|
||||||
) {
|
for (line in testFile.readLines()) {
|
||||||
KotlinTestUtils.assertEqualsToFile(
|
if (line.contains(JVM_TEMPLATES)) {
|
||||||
testFile,
|
currentBackend = TargetBackend.JVM
|
||||||
text.substringBefore("// VARIABLE : ") + getActualVariablesAsString(
|
} else if (line.contains(JVM_IR_TEMPLATES)) {
|
||||||
actualLocalVariables
|
currentBackend = TargetBackend.JVM_IR
|
||||||
)
|
} else if (currentBackend == TargetBackend.ANY || currentBackend == backend) {
|
||||||
)
|
filteredLines.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredLines
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class LocalVariable internal constructor(
|
private fun doCompare(
|
||||||
|
testFileLines: List<String>,
|
||||||
|
actualLocalVariables: List<LocalVariable>
|
||||||
|
) {
|
||||||
|
val actual = getActualVariablesAsString(actualLocalVariables)
|
||||||
|
val expected = getExpectedVariablesAsString(testFileLines)
|
||||||
|
if (!Comparing.equal(expected, actual)) {
|
||||||
|
throw ComparisonFailure(
|
||||||
|
"Variables differ from expected",
|
||||||
|
expected,
|
||||||
|
actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getActualVariablesAsString(list: List<LocalVariable>) = if (backend.isIR) {
|
||||||
|
// Ignore local index.
|
||||||
|
list.map { it.toString().replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") }
|
||||||
|
.sorted()
|
||||||
|
.joinToString("\n")
|
||||||
|
} else {
|
||||||
|
list.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExpectedVariablesAsString(testFileLines: List<String>): String {
|
||||||
|
val variableLines = testFileLines.asSequence().filter { line -> line.startsWith("// VARIABLE ") }
|
||||||
|
return if (backend.isIR) {
|
||||||
|
// Ignore local index.
|
||||||
|
variableLines
|
||||||
|
.map { it.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") }
|
||||||
|
.sorted()
|
||||||
|
.joinToString("\n")
|
||||||
|
} else {
|
||||||
|
variableLines.joinToString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalVariable internal constructor(
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: String,
|
val type: String,
|
||||||
val index: Int,
|
val index: Int,
|
||||||
@@ -82,8 +126,8 @@ abstract class AbstractCheckLocalVariablesTableTest : CodegenTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseClassAndMethodSignature(testFile: File): String {
|
private fun parseClassAndMethodSignature(testFileLines: List<String>): String {
|
||||||
for (line in testFile.readLines()) {
|
for (line in testFileLines) {
|
||||||
val methodMatcher = methodPattern.matcher(line)
|
val methodMatcher = methodPattern.matcher(line)
|
||||||
if (methodMatcher.matches()) {
|
if (methodMatcher.matches()) {
|
||||||
return methodMatcher.group(1)
|
return methodMatcher.group(1)
|
||||||
@@ -95,7 +139,9 @@ abstract class AbstractCheckLocalVariablesTableTest : CodegenTestCase() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private fun getActualVariablesAsString(list: List<LocalVariable>) = list.joinToString("\n")
|
private const val JVM_TEMPLATES = "// JVM_TEMPLATES"
|
||||||
|
|
||||||
|
private const val JVM_IR_TEMPLATES = "// JVM_IR_TEMPLATES"
|
||||||
|
|
||||||
private val methodPattern = Pattern.compile("^// METHOD : *(.*)")
|
private val methodPattern = Pattern.compile("^// METHOD : *(.*)")
|
||||||
|
|
||||||
@@ -104,6 +150,7 @@ abstract class AbstractCheckLocalVariablesTableTest : CodegenTestCase() {
|
|||||||
class Visitor : ClassVisitor(Opcodes.API_VERSION) {
|
class Visitor : ClassVisitor(Opcodes.API_VERSION) {
|
||||||
var readVariables: MutableList<LocalVariable> = ArrayList()
|
var readVariables: MutableList<LocalVariable> = ArrayList()
|
||||||
var methodFound = false
|
var methodFound = false
|
||||||
|
var methodsFound = mutableListOf<String>()
|
||||||
|
|
||||||
override fun visitMethod(
|
override fun visitMethod(
|
||||||
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
|
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
|
||||||
@@ -128,6 +175,7 @@ abstract class AbstractCheckLocalVariablesTableTest : CodegenTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
methodsFound.add(name + desc)
|
||||||
super.visitMethod(access, name, desc, signature, exceptions)
|
super.visitMethod(access, name, desc, signature, exceptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +183,10 @@ abstract class AbstractCheckLocalVariablesTableTest : CodegenTestCase() {
|
|||||||
|
|
||||||
val visitor = Visitor()
|
val visitor = Visitor()
|
||||||
cr.accept(visitor, ClassReader.SKIP_FRAMES)
|
cr.accept(visitor, ClassReader.SKIP_FRAMES)
|
||||||
TestCase.assertTrue("method not found: $methodName", visitor.methodFound)
|
TestCase.assertTrue(
|
||||||
|
"Method not found: $methodName. Methods found were: ${visitor.methodsFound.joinToString()}",
|
||||||
|
visitor.methodFound
|
||||||
|
)
|
||||||
return visitor.readVariables
|
return visitor.readVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-34
@@ -5,43 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.ir
|
package org.jetbrains.kotlin.codegen.ir
|
||||||
|
|
||||||
import com.intellij.openapi.util.Comparing
|
|
||||||
import org.jetbrains.kotlin.codegen.AbstractCheckLocalVariablesTableTest
|
import org.jetbrains.kotlin.codegen.AbstractCheckLocalVariablesTableTest
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
import org.jetbrains.kotlin.test.TargetBackend
|
||||||
import org.junit.ComparisonFailure
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class AbstractIrCheckLocalVariablesTableTest : AbstractCheckLocalVariablesTableTest() {
|
abstract class AbstractIrCheckLocalVariablesTableTest : AbstractCheckLocalVariablesTableTest() {
|
||||||
override fun doCompare(
|
|
||||||
testFile: File,
|
|
||||||
text: String,
|
|
||||||
actualLocalVariables: List<LocalVariable>
|
|
||||||
) {
|
|
||||||
val actual = getActualVariablesAsList(actualLocalVariables)
|
|
||||||
val expected = getExpectedVariablesAsList(testFile)
|
|
||||||
if (!Comparing.equal(expected, actual)) {
|
|
||||||
throw ComparisonFailure(
|
|
||||||
"Variables differ from expected",
|
|
||||||
expected.joinToString("\n"),
|
|
||||||
actual.joinToString("\n")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getActualVariablesAsList(list: List<LocalVariable>): List<String> {
|
|
||||||
return list.map { it.toString() }
|
|
||||||
// Ignore local index.
|
|
||||||
.map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") }
|
|
||||||
.sorted()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getExpectedVariablesAsList(testFile: File): List<String> {
|
|
||||||
return testFile.readLines()
|
|
||||||
.filter { line -> line.startsWith("// VARIABLE ") }
|
|
||||||
// Ignore local index.
|
|
||||||
.map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") }
|
|
||||||
.sorted()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getBackend() = TargetBackend.JVM_IR
|
override fun getBackend() = TargetBackend.JVM_IR
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user