Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor DOT dump for Ets files #251

Merged
merged 22 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.ets.utils

import mu.KotlinLogging
import java.io.File
import kotlin.time.Duration.Companion.seconds

private val logger = KotlinLogging.logger {}

fun render(path: String, dump: (File) -> Unit) {
Lipen marked this conversation as resolved.
Show resolved Hide resolved
val dotFile = File(path)
dump(dotFile)
logger.info { "Generated DOT file: ${dotFile.absolutePath}" }
for (format in listOf("pdf")) {
val formatFile = dotFile.resolveSibling(dotFile.nameWithoutExtension + ".$format")
rudolf101 marked this conversation as resolved.
Show resolved Hide resolved
val cmd: List<String> = listOf(
"dot",
"-T$format",
"$dotFile",
"-o",
"$formatFile"
)
runProcess(cmd, 60.seconds)
logger.info { "Generated ${format.uppercase()} file: ${formatFile.absolutePath}" }
}
}
32 changes: 32 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.jacodb.ets.dto.MethodDto
import org.jacodb.ets.dto.NopStmtDto
import org.jacodb.ets.dto.StmtDto
import org.jacodb.ets.dto.SwitchStmtDto
import java.io.BufferedWriter
import java.io.File
import java.nio.file.Path
import kotlin.io.path.writeText
Expand Down Expand Up @@ -182,6 +183,37 @@ fun EtsFileDto.toDot(useLR: Boolean = false): String {
return lines.joinToString("\n")
}

fun EtsFileDto.dumpHuimpleTo(output: BufferedWriter) {
rudolf101 marked this conversation as resolved.
Show resolved Hide resolved
output.writeln("EtsFileDto '${name}':")
classes.forEach { clazz ->
output.writeln("= CLASS '${clazz.signature}':")
output.writeln(" superClass = '${clazz.superClassName}'")
output.writeln(" typeParameters = ${clazz.typeParameters}")
output.writeln(" modifiers = ${clazz.modifiers}")
output.writeln(" fields: ${clazz.fields.size}")
clazz.fields.forEach { field ->
output.writeln(" - FIELD '${field.signature}'")
output.writeln(" typeParameters = ${field.typeParameters}")
output.writeln(" modifiers = ${field.modifiers}")
output.writeln(" isOptional = ${field.isOptional}")
output.writeln(" isDefinitelyAssigned = ${field.isDefinitelyAssigned}")
}
output.writeln(" methods: ${clazz.methods.size}")
clazz.methods.forEach { method ->
output.writeln(" - METHOD '${method.signature}':")
output.writeln(" locals = ${method.body.locals}")
output.writeln(" typeParameters = ${method.typeParameters}")
output.writeln(" blocks: ${method.body.cfg.blocks.size}")
method.body.cfg.blocks.forEach { block ->
output.writeln(" - BLOCK ${block.id} with ${block.stmts.size} statements:")
block.stmts.forEachIndexed { i, inst ->
output.writeln(" ${i + 1}. $inst")
rudolf101 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
}

fun EtsFileDto.dumpDot(file: File) {
file.writeText(toDot())
}
Expand Down
27 changes: 27 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.jacodb.ets.base.EtsStmt
import org.jacodb.ets.model.EtsClass
import org.jacodb.ets.model.EtsFile
import org.jacodb.ets.model.EtsMethod
import java.io.BufferedWriter
import java.io.File
import java.nio.file.Path
import kotlin.io.path.writeText
Expand Down Expand Up @@ -146,6 +147,32 @@ fun EtsFile.toDot(useLR: Boolean = false): String {
return lines.joinToString("\n")
}

fun EtsFile.dumpHuimpleTo(output: BufferedWriter) {
Lipen marked this conversation as resolved.
Show resolved Hide resolved
output.writeln("EtsFile '${name}':")
classes.forEach { clazz ->
output.writeln("= CLASS '${clazz.signature}':")
output.writeln(" superClass = '${clazz.superClass}'")
output.writeln(" fields: ${clazz.fields.size}")
clazz.fields.forEach { field ->
output.writeln(" - FIELD '${field.signature}'")
}
output.writeln(" constructor = '${clazz.ctor.signature}'")
output.writeln(" stmts: ${clazz.ctor.cfg.stmts.size}")
clazz.ctor.cfg.stmts.forEachIndexed { i, stmt ->
output.writeln(" ${i + 1}. $stmt")
}
output.writeln(" methods: ${clazz.methods.size}")
clazz.methods.forEach { method ->
output.writeln(" - METHOD '${method.signature}':")
output.writeln(" locals = ${method.localsCount}")
output.writeln(" stmts: ${method.cfg.stmts.size}")
method.cfg.stmts.forEachIndexed { i, stmt ->
output.writeln(" ${i + 1}. $stmt")
}
}
}
}

fun EtsFile.dumpDot(file: File) {
file.writeText(toDot())
}
Expand Down
46 changes: 26 additions & 20 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import org.jacodb.ets.dto.convertToEtsFile
import org.jacodb.ets.model.EtsFile
import java.io.File
import java.io.FileNotFoundException
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.notExists
import kotlin.io.path.pathString
import kotlin.time.Duration.Companion.seconds

private val logger = KotlinLogging.logger {}

Expand All @@ -40,7 +40,7 @@ private const val DEFAULT_SERIALIZE_SCRIPT_PATH = "out/src/save/serializeArkIR.j
private const val ENV_VAR_NODE_EXECUTABLE = "NODE_EXECUTABLE"
private const val DEFAULT_NODE_EXECUTABLE = "node"

fun loadEtsFileAutoConvert(tsPath: String): EtsFile {
fun generateEtsFileIR(tsPath: String): Path {
val arkAnalyzerDir = Paths.get(System.getenv(ENV_VAR_ARK_ANALYZER_DIR) ?: DEFAULT_ARK_ANALYZER_DIR)
if (!arkAnalyzerDir.exists()) {
throw FileNotFoundException("ArkAnalyzer directory does not exist: '$arkAnalyzerDir'. Did you forget to set the '$ENV_VAR_ARK_ANALYZER_DIR' environment variable? Current value is '${System.getenv(ENV_VAR_ARK_ANALYZER_DIR)}', current dir is '${Paths.get("").toAbsolutePath()}'.")
Expand All @@ -60,26 +60,32 @@ fun loadEtsFileAutoConvert(tsPath: String): EtsFile {
tsPath,
output.pathString,
)
logger.info { "Running: '${cmd.joinToString(" ")}'" }
val process = ProcessBuilder(cmd).start()
val ok = process.waitFor(1, TimeUnit.MINUTES)

val stdout = process.inputStream.bufferedReader().readText().trim()
if (stdout.isNotBlank()) {
logger.info { "STDOUT:\n$stdout" }
}
val stderr = process.errorStream.bufferedReader().readText().trim()
if (stderr.isNotBlank()) {
logger.info { "STDERR:\n$stderr" }
}
runProcess(cmd, 60.seconds)
return output
}

if (!ok) {
logger.info { "Timeout!" }
process.destroy()
fun loadEtsFileAutoConvert(tsPath: String): EtsFile {
val irFilePath = generateEtsFileIR(tsPath)
irFilePath.inputStream().use { stream ->
val etsFileDto = EtsFileDto.loadFromJson(stream)
val etsFile = convertToEtsFile(etsFileDto)
return etsFile
}
}

output.inputStream().use { stream ->
fun loadEtsFileAutoConvertWithDot(tsPath: String, dotDir: String): EtsFile {
val irFilePath = generateEtsFileIR(tsPath)
irFilePath.inputStream().use { stream ->
val etsFileDto = EtsFileDto.loadFromJson(stream)
return convertToEtsFile(etsFileDto)
val etsFile = convertToEtsFile(etsFileDto)
val etsFileDtoDotFileName = "$dotDir/${tsPath.substringAfterLast("/")}".replace(".ts", "Dto.dot")
val etsFileDotFileName = "$dotDir/${tsPath.substringAfterLast("/")}".replace(".ts", ".dot")
render(etsFileDtoDotFileName) { file ->
etsFileDto.dumpDot(file)
}
render(etsFileDotFileName) { file ->
etsFile.dumpDot(file)
}
return etsFile
}
}
54 changes: 54 additions & 0 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.ets.utils

import mu.KotlinLogging
import java.io.BufferedWriter
import java.util.concurrent.TimeUnit
import kotlin.time.Duration

private val logger = KotlinLogging.logger {}

internal fun BufferedWriter.writeln(s: String) {
write(s)
newLine()
}

internal fun runProcess(cmd: List<String>, timeout: Duration? = null) {
logger.info { "Running: '${cmd.joinToString(" ")}'" }
val process = ProcessBuilder(cmd).start()
val ok = if (timeout == null) {
process.waitFor()
true
} else {
process.waitFor(timeout.inWholeNanoseconds, TimeUnit.NANOSECONDS)
}

val stdout = process.inputStream.bufferedReader().readText().trim()
if (stdout.isNotBlank()) {
logger.info { "STDOUT:\n$stdout" }
}
val stderr = process.errorStream.bufferedReader().readText().trim()
if (stderr.isNotBlank()) {
logger.info { "STDERR:\n$stderr" }
}

if (!ok) {
logger.info { "Timeout!" }
process.destroy()
}
}
14 changes: 12 additions & 2 deletions jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import org.jacodb.ets.model.EtsClassSignature
import org.jacodb.ets.model.EtsMethodSignature
import org.jacodb.ets.test.utils.loadEtsFileDtoFromResource
import org.jacodb.ets.utils.loadEtsFileAutoConvert
import org.jacodb.ets.utils.loadEtsFileAutoConvertWithDot
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

Expand Down Expand Up @@ -72,13 +73,22 @@ class EtsFromJsonTest {

@Test
fun testLoadEtsFileAutoConvert() {
val path = "source/example.ts"
val res = this::class.java.getResource("/$path")
val path = "/source/example.ts"
val res = this::class.java.getResource(path)
?: error("Resource not found: $path")
val etsFile = loadEtsFileAutoConvert(res.path)
println("etsFile = $etsFile")
}

@Test
fun testLoadEtsFileAutoConvertWithDot() {
val path = "/source/example.ts"
val res = this::class.java.getResource(path)
?: error("Resource not found: $path")
val etsFile = loadEtsFileAutoConvertWithDot(res.path, res.path.substringBeforeLast("/"))
rudolf101 marked this conversation as resolved.
Show resolved Hide resolved
println("etsFile = $etsFile")
}

@Test
fun testLoadValueFromJson() {
val jsonString = """
Expand Down
Loading
Loading