From 6e24eda13fa69986d442d6ae44d331585eeac5a6 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Mon, 5 Aug 2024 15:55:48 +0300 Subject: [PATCH 01/22] Implement ets loading with dot dump --- .../main/kotlin/org/jacodb/ets/utils/Dot.kt | 41 ++++++ .../org/jacodb/ets/utils/EtsFileDtoToDot.kt | 32 +++++ .../org/jacodb/ets/utils/EtsFileToDot.kt | 27 ++++ .../org/jacodb/ets/utils/LoadEtsFile.kt | 46 ++++--- .../main/kotlin/org/jacodb/ets/utils/Utils.kt | 54 ++++++++ .../org/jacodb/ets/test/EtsFromJsonTest.kt | 8 +- .../kotlin/org/jacodb/ets/test/utils/Dot.kt | 125 ------------------ .../org/jacodb/ets/test/utils/Entrypoints.kt | 55 ++++++++ 8 files changed, 239 insertions(+), 149 deletions(-) create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt create mode 100644 jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt delete mode 100644 jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Dot.kt create mode 100644 jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt new file mode 100644 index 000000000..09d2c8bd4 --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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) { + 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") + val cmd: List = listOf( + "dot", + "-T$format", + "$dotFile", + "-o", + "$formatFile" + ) + runProcess(cmd, 60.seconds) + logger.info { "Generated ${format.uppercase()} file: ${formatFile.absolutePath}" } + } +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt index d692b9867..d2b573247 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt @@ -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 @@ -182,6 +183,37 @@ fun EtsFileDto.toDot(useLR: Boolean = false): String { return lines.joinToString("\n") } +fun EtsFileDto.dumpHuimpleTo(output: BufferedWriter) { + 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") + } + } + } + } +} + fun EtsFileDto.dumpDot(file: File) { file.writeText(toDot()) } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt index f29a63b9d..1624c724b 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt @@ -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 @@ -146,6 +147,32 @@ fun EtsFile.toDot(useLR: Boolean = false): String { return lines.joinToString("\n") } +fun EtsFile.dumpHuimpleTo(output: BufferedWriter) { + 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()) } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt index fcfec2ed7..ba65d4f9f 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt @@ -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 {} @@ -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()}'.") @@ -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 } } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt new file mode 100644 index 000000000..addf9e9db --- /dev/null +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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, 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() + } +} diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt index 9cf77e3e7..4bb49a70e 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt @@ -40,7 +40,7 @@ import org.jacodb.ets.dto.convertToEtsMethod 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 @@ -72,10 +72,10 @@ 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) + val etsFile = loadEtsFileAutoConvertWithDot(res.path.drop(1), res.path.drop(1).substringBeforeLast("/")) println("etsFile = $etsFile") } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Dot.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Dot.kt deleted file mode 100644 index cf6d764e6..000000000 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Dot.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2022 UnitTestBot contributors (utbot.org) - *

- * 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 - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * 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.test.utils - -import org.jacodb.ets.dto.EtsFileDto -import org.jacodb.ets.utils.dumpDot -import java.io.File - -/** - * Visualize classes and methods in [EtsFileDto]. - */ -object DumpEtsFileDtoToDot { - private const val BASE_PATH = "/etsir/samples" - private const val NAME = "basic" // <-- change it - private const val DOT_PATH = "ir.dot" - - @JvmStatic - fun main(args: Array) { - val path = "$BASE_PATH/${NAME}.ts.json" - val etsFileDto: EtsFileDto = loadEtsFileDtoFromResource(path) - - println("EtsFileDto '${etsFileDto.name}':") - etsFileDto.classes.forEach { clazz -> - println("= CLASS '${clazz.signature}':") - println(" superClass = '${clazz.superClassName}'") - println(" typeParameters = ${clazz.typeParameters}") - println(" modifiers = ${clazz.modifiers}") - println(" fields: ${clazz.fields.size}") - clazz.fields.forEach { field -> - println(" - FIELD '${field.signature}'") - println(" typeParameters = ${field.typeParameters}") - println(" modifiers = ${field.modifiers}") - println(" isOptional = ${field.isOptional}") - println(" isDefinitelyAssigned = ${field.isDefinitelyAssigned}") - } - println(" methods: ${clazz.methods.size}") - clazz.methods.forEach { method -> - println(" - METHOD '${method.signature}':") - println(" locals = ${method.body.locals}") - println(" typeParameters = ${method.typeParameters}") - println(" blocks: ${method.body.cfg.blocks.size}") - method.body.cfg.blocks.forEach { block -> - println(" - BLOCK ${block.id} with ${block.stmts.size} statements:") - block.stmts.forEachIndexed { i, inst -> - println(" ${i + 1}. $inst") - } - } - } - } - - println("Rendering EtsFileDto to DOT...") - render(DOT_PATH) { file -> - etsFileDto.dumpDot(file) - } - } -} - -object DumpEtsFileToDot { - private const val BASE_PATH = "/etsir/samples" - private const val NAME = "basic" // <-- change it - private const val DOT_PATH = "ets.dot" - - @JvmStatic - fun main(args: Array) { - val path = "$BASE_PATH/${NAME}.ts.json" - val etsFile = loadEtsFileFromResource(path) - - println("EtsFile '${etsFile.name}':") - etsFile.classes.forEach { clazz -> - println("= CLASS '${clazz.signature}':") - println(" superClass = '${clazz.superClass}'") - println(" fields: ${clazz.fields.size}") - clazz.fields.forEach { field -> - println(" - FIELD '${field.signature}'") - } - println(" constructor = '${clazz.ctor.signature}'") - println(" stmts: ${clazz.ctor.cfg.stmts.size}") - clazz.ctor.cfg.stmts.forEachIndexed { i, stmt -> - println(" ${i + 1}. $stmt") - } - println(" methods: ${clazz.methods.size}") - clazz.methods.forEach { method -> - println(" - METHOD '${method.signature}':") - println(" locals = ${method.localsCount}") - println(" stmts: ${method.cfg.stmts.size}") - method.cfg.stmts.forEachIndexed { i, stmt -> - println(" ${i + 1}. $stmt") - } - } - } - - println("Rendering EtsFile to DOT...") - render(DOT_PATH) { file -> - etsFile.dumpDot(file) - } - } -} - -private fun render(path: String, dump: (File) -> Unit) { - val dotFile = File(path) - dump(dotFile) - println("Generated DOT file: ${dotFile.absolutePath}") - for (format in listOf("pdf")) { - val formatFile = dotFile.resolveSibling(dotFile.nameWithoutExtension + ".$format") - val p = Runtime.getRuntime().exec("dot -T$format $dotFile -o $formatFile") - p.waitFor() - print(p.inputStream.bufferedReader().readText()) - print(p.errorStream.bufferedReader().readText()) - println("Generated ${format.uppercase()} file: ${formatFile.absolutePath}") - } -} diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt new file mode 100644 index 000000000..3c8e4b762 --- /dev/null +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.test.utils + +import org.jacodb.ets.dto.EtsFileDto +import org.jacodb.ets.utils.dumpDot +import org.jacodb.ets.utils.dumpHuimpleTo +import org.jacodb.ets.utils.render + +/** + * Visualize classes and methods in [EtsFileDto]. + */ +object DumpEtsFileDtoToDot { + private const val PATH = "/etsir/samples/basic.ts.json" + private const val DOT_PATH = "basicDto.dot" + + @JvmStatic + fun main(args: Array) { + val etsFileDto: EtsFileDto = loadEtsFileDtoFromResource(PATH) + val output = System.out.bufferedWriter() + etsFileDto.dumpHuimpleTo(output) + render(DOT_PATH) { file -> + etsFileDto.dumpDot(file) + } + } +} + +object DumpEtsFileToDot { + private const val PATH = "/etsir/samples/basic.ts.json" + private const val DOT_PATH = "basic.dot" + + @JvmStatic + fun main(args: Array) { + val etsFile = loadEtsFileFromResource(PATH) + val output = System.out.bufferedWriter() + etsFile.dumpHuimpleTo(output) + render(DOT_PATH) { file -> + etsFile.dumpDot(file) + } + } +} From c6c300a1184881abe3d0224c658bb8f08d9ce501 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Mon, 5 Aug 2024 17:29:28 +0300 Subject: [PATCH 02/22] Add entrypoint for converting Ets files to DOT format --- .../org/jacodb/ets/test/utils/Entrypoints.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt index 3c8e4b762..a1e6aec85 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -17,9 +17,15 @@ package org.jacodb.ets.test.utils import org.jacodb.ets.dto.EtsFileDto +import org.jacodb.ets.model.EtsFile import org.jacodb.ets.utils.dumpDot import org.jacodb.ets.utils.dumpHuimpleTo import org.jacodb.ets.utils.render +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +private val logger = mu.KotlinLogging.logger {} /** * Visualize classes and methods in [EtsFileDto]. @@ -53,3 +59,57 @@ object DumpEtsFileToDot { } } } + +object DumpEtsFilesToDot { + private const val DIR = "/etsir/samples/" + private const val DOT_DIR = "dot" + + @JvmStatic + fun main(args: Array) { + val baseDirUrl = object {}::class.java.getResource(DIR) + val baseDir = Paths.get(baseDirUrl?.toURI() ?: error("Resource not found")) + + val dotDirPath = baseDir.resolveSibling(DOT_DIR) + if (!Files.exists(dotDirPath)) { + Files.createDirectories(dotDirPath) + } + + Files.walk(baseDir) + .filter { it.toString().endsWith(".json") } + .forEach { path -> + val relativePath = baseDir.relativize(path) + val fileName = path.fileName.toString().substringBefore(".") + + processFile(relativePath, fileName, dotDirPath, ::loadEtsFileDtoFromResource, "Dto") + processFile(relativePath, fileName, dotDirPath, ::loadEtsFileFromResource, "") + + logger.info { "Processed: $path" } + } + } + + private fun processFile( + relativePath: Path, + fileName: String, + dotDirPath: Path, + loadFunction: (String) -> T, + suffix: String + ) { + try { + val resourcePath = DIR + relativePath.toString() + val fileData = loadFunction(resourcePath) + val outputDir = dotDirPath.resolve(relativePath.parent ?: dotDirPath) + if (!Files.exists(outputDir)) { + Files.createDirectories(outputDir) + } + val outputPath = outputDir.resolve("${fileName}$suffix.dot").toString() + render(outputPath) { file -> + when (fileData) { + is EtsFileDto -> fileData.dumpDot(file) + is EtsFile -> fileData.dumpDot(file) + } + } + } catch (e: Exception) { + logger.error { e.stackTraceToString() } + } + } +} From 599b59156bb558822162255cf9971779fc6896f7 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Mon, 5 Aug 2024 17:40:03 +0300 Subject: [PATCH 03/22] Add new test for ets file loading with dot --- .../kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt index 4bb49a70e..d69e3ac12 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt @@ -40,6 +40,7 @@ import org.jacodb.ets.dto.convertToEtsMethod 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 @@ -75,7 +76,16 @@ class EtsFromJsonTest { val path = "/source/example.ts" val res = this::class.java.getResource(path) ?: error("Resource not found: $path") - val etsFile = loadEtsFileAutoConvertWithDot(res.path.drop(1), res.path.drop(1).substringBeforeLast("/")) + 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("/")) println("etsFile = $etsFile") } From 2cdfcf70f9b418fcf05d3f7c58777d63bb912d22 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 5 Aug 2024 23:15:13 +0300 Subject: [PATCH 04/22] Cleanup --- .../org/jacodb/ets/test/utils/Entrypoints.kt | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt index a1e6aec85..7e6c2ba58 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -21,9 +21,14 @@ import org.jacodb.ets.model.EtsFile import org.jacodb.ets.utils.dumpDot import org.jacodb.ets.utils.dumpHuimpleTo import org.jacodb.ets.utils.render -import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.name +import kotlin.io.path.nameWithoutExtension +import kotlin.io.path.walk private val logger = mu.KotlinLogging.logger {} @@ -37,14 +42,19 @@ object DumpEtsFileDtoToDot { @JvmStatic fun main(args: Array) { val etsFileDto: EtsFileDto = loadEtsFileDtoFromResource(PATH) + val output = System.out.bufferedWriter() etsFileDto.dumpHuimpleTo(output) + render(DOT_PATH) { file -> etsFileDto.dumpDot(file) } } } +/** + * Visualize classes and methods in [EtsFile]. + */ object DumpEtsFileToDot { private const val PATH = "/etsir/samples/basic.ts.json" private const val DOT_PATH = "basic.dot" @@ -52,14 +62,17 @@ object DumpEtsFileToDot { @JvmStatic fun main(args: Array) { val etsFile = loadEtsFileFromResource(PATH) + val output = System.out.bufferedWriter() etsFile.dumpHuimpleTo(output) + render(DOT_PATH) { file -> etsFile.dumpDot(file) } } } +@OptIn(ExperimentalPathApi::class) object DumpEtsFilesToDot { private const val DIR = "/etsir/samples/" private const val DOT_DIR = "dot" @@ -67,41 +80,46 @@ object DumpEtsFilesToDot { @JvmStatic fun main(args: Array) { val baseDirUrl = object {}::class.java.getResource(DIR) - val baseDir = Paths.get(baseDirUrl?.toURI() ?: error("Resource not found")) + ?: error("Resource not found: '$DIR'") + val baseDir = Paths.get(baseDirUrl.toURI()) - val dotDirPath = baseDir.resolveSibling(DOT_DIR) - if (!Files.exists(dotDirPath)) { - Files.createDirectories(dotDirPath) + val dotDir = baseDir.resolveSibling(DOT_DIR) + if (!dotDir.exists()) { + dotDir.createDirectories() } - Files.walk(baseDir) - .filter { it.toString().endsWith(".json") } + baseDir.walk() + .filter { it.name.endsWith(".json") } .forEach { path -> val relativePath = baseDir.relativize(path) - val fileName = path.fileName.toString().substringBefore(".") + val fileName = path.nameWithoutExtension - processFile(relativePath, fileName, dotDirPath, ::loadEtsFileDtoFromResource, "Dto") - processFile(relativePath, fileName, dotDirPath, ::loadEtsFileFromResource, "") + process(relativePath, fileName, dotDir, "Dto") { + loadEtsFileDtoFromResource(it) + } + process(relativePath, fileName, dotDir, "") { + loadEtsFileFromResource(it) + } logger.info { "Processed: $path" } } } - private fun processFile( - relativePath: Path, - fileName: String, - dotDirPath: Path, - loadFunction: (String) -> T, - suffix: String + private fun process( + path: Path, + name: String, + dotDir: Path, + suffix: String, + load: (String) -> T, ) { try { - val resourcePath = DIR + relativePath.toString() - val fileData = loadFunction(resourcePath) - val outputDir = dotDirPath.resolve(relativePath.parent ?: dotDirPath) - if (!Files.exists(outputDir)) { - Files.createDirectories(outputDir) + val resourcePath = DIR + path.toString() + val fileData = load(resourcePath) + val outputDir = dotDir.resolve(path.parent ?: dotDir) + if (!outputDir.exists()) { + outputDir.createDirectories() } - val outputPath = outputDir.resolve("${fileName}$suffix.dot").toString() + val outputPath = outputDir.resolve("${name}${suffix}.dot").toString() render(outputPath) { file -> when (fileData) { is EtsFileDto -> fileData.dumpDot(file) From 4c4ebad75703ba2daa93ca44b49f005f16cb513f Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 5 Aug 2024 23:47:10 +0300 Subject: [PATCH 05/22] Use Kotlin functions --- .../src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt index ba65d4f9f..7365dfdc1 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt @@ -23,7 +23,7 @@ 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 kotlin.io.path.Path import kotlin.io.path.exists import kotlin.io.path.inputStream import kotlin.io.path.pathString @@ -41,9 +41,9 @@ private const val ENV_VAR_NODE_EXECUTABLE = "NODE_EXECUTABLE" private const val DEFAULT_NODE_EXECUTABLE = "node" fun generateEtsFileIR(tsPath: String): Path { - val arkAnalyzerDir = Paths.get(System.getenv(ENV_VAR_ARK_ANALYZER_DIR) ?: DEFAULT_ARK_ANALYZER_DIR) + val arkAnalyzerDir = Path(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()}'.") + 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 '${Path("").toAbsolutePath()}'.") } val scriptPath = System.getenv(ENV_VAR_SERIALIZE_SCRIPT_PATH) ?: DEFAULT_SERIALIZE_SCRIPT_PATH From 8ef881666afc9943fde86b9bc51ac402f427c711 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Mon, 5 Aug 2024 23:47:26 +0300 Subject: [PATCH 06/22] Remove unnecessary logger --- jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt index 7365dfdc1..cf89e8b6c 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt @@ -16,7 +16,6 @@ package org.jacodb.ets.utils -import mu.KotlinLogging import org.jacodb.ets.dto.EtsFileDto import org.jacodb.ets.dto.convertToEtsFile import org.jacodb.ets.model.EtsFile @@ -29,8 +28,6 @@ import kotlin.io.path.inputStream import kotlin.io.path.pathString import kotlin.time.Duration.Companion.seconds -private val logger = KotlinLogging.logger {} - private const val ENV_VAR_ARK_ANALYZER_DIR = "ARKANALYZER_DIR" private const val DEFAULT_ARK_ANALYZER_DIR = "arkanalyzer" From c01356fc7486cbf04d5246aedb3b6606d87344c5 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 00:17:56 +0300 Subject: [PATCH 07/22] Specify exceptionFormat="full" for tests --- buildSrc/src/main/kotlin/Tests.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Tests.kt b/buildSrc/src/main/kotlin/Tests.kt index 109b15574..aff1eb7ac 100644 --- a/buildSrc/src/main/kotlin/Tests.kt +++ b/buildSrc/src/main/kotlin/Tests.kt @@ -1,6 +1,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test -import java.util.* +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import java.util.StringTokenizer object Tests { val lifecycleTag = "lifecycle" @@ -10,6 +11,7 @@ object Tests { fun Test.setup(jacocoTestReport: TaskProvider<*>) { testLogging { events("passed", "skipped", "failed") + exceptionFormat = TestExceptionFormat.FULL } finalizedBy(jacocoTestReport) // report is always generated after tests run val majorJavaVersion = From 63e6b6cdff80907f60b2645227e3c7082927011d Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 00:21:58 +0300 Subject: [PATCH 08/22] Set up GraphViz in GHA --- .github/workflows/build-and-test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index ebd4a2a9f..3851c8d8e 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -50,6 +50,9 @@ jobs: # Builds on other branches will only read existing entries from the cache. cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} + - name: Set up GraphViz + uses: ts-graphviz/setup-graphviz@v2 + - name: Set up Node uses: actions/setup-node@v4 with: From d424c08cb8419822e0f9393f3a02bcd3d6ff55d9 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 14:36:41 +0300 Subject: [PATCH 09/22] Fix and improve --- .../main/kotlin/org/jacodb/ets/dto/Convert.kt | 11 +++ .../main/kotlin/org/jacodb/ets/utils/Dot.kt | 21 +++--- .../org/jacodb/ets/utils/EtsFileDtoToDot.kt | 39 ++--------- .../org/jacodb/ets/utils/EtsFileToDot.kt | 34 ++------- .../org/jacodb/ets/utils/LoadEtsFile.kt | 54 +++++++++------ .../main/kotlin/org/jacodb/ets/utils/Utils.kt | 59 ++++++++++++++++ .../org/jacodb/ets/test/EtsFromJsonTest.kt | 15 ++-- .../org/jacodb/ets/test/utils/Entrypoints.kt | 69 ++++++++----------- .../org/jacodb/ets/test/utils/LoadEts.kt | 1 + 9 files changed, 162 insertions(+), 141 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt index e4b701aef..f9797bc70 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt @@ -339,6 +339,17 @@ class EtsMethodBuilder( // so we just *unsafely* extract the type name from the "pseudo-local" here: "instanceof" -> EtsInstanceOfExpr(left, (right as EtsLocal).name) + // TODO: remove this when ArkIR is fixed + Ops.EQ_EQ -> EtsEqExpr(left, right) + Ops.NOT_EQ -> EtsNotEqExpr(left, right) + Ops.EQ_EQ_EQ -> EtsStrictEqExpr(left, right) + Ops.NOT_EQ_EQ -> EtsStrictNotEqExpr(left, right) + Ops.LT -> EtsLtExpr(left, right) + Ops.LT_EQ -> EtsLtEqExpr(left, right) + Ops.GT -> EtsGtExpr(left, right) + Ops.GT_EQ -> EtsGtEqExpr(left, right) + Ops.IN -> EtsInExpr(left, right) + else -> error("Unknown binop: ${value.op}") } } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt index 09d2c8bd4..7bf94ca98 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt @@ -17,25 +17,30 @@ package org.jacodb.ets.utils import mu.KotlinLogging -import java.io.File +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.io.path.absolute +import kotlin.io.path.nameWithoutExtension import kotlin.time.Duration.Companion.seconds private val logger = KotlinLogging.logger {} -fun render(path: String, dump: (File) -> Unit) { - val dotFile = File(path) - dump(dotFile) - logger.info { "Generated DOT file: ${dotFile.absolutePath}" } +fun render(dotPath: Path) { for (format in listOf("pdf")) { - val formatFile = dotFile.resolveSibling(dotFile.nameWithoutExtension + ".$format") + logger.info { "Rendering DOT to ${format.uppercase()}..." } + val formatFile = dotPath.resolveSibling(dotPath.nameWithoutExtension + ".$format") val cmd: List = listOf( "dot", "-T$format", - "$dotFile", + "$dotPath", "-o", "$formatFile" ) runProcess(cmd, 60.seconds) - logger.info { "Generated ${format.uppercase()} file: ${formatFile.absolutePath}" } + logger.info { "Generated ${format.uppercase()} file: ${formatFile.absolute()}" } } } + +fun render(dotPath: String) { + render(Path(dotPath)) +} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt index d2b573247..a96dcde95 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt @@ -27,6 +27,7 @@ import org.jacodb.ets.dto.SwitchStmtDto import java.io.BufferedWriter import java.io.File import java.nio.file.Path +import kotlin.io.path.createDirectories import kotlin.io.path.writeText fun EtsFileDto.toDot(useLR: Boolean = false): String { @@ -183,43 +184,13 @@ fun EtsFileDto.toDot(useLR: Boolean = false): String { return lines.joinToString("\n") } -fun EtsFileDto.dumpHuimpleTo(output: BufferedWriter) { - 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") - } - } - } - } +fun EtsFileDto.dumpDot(path: Path) { + path.parent?.createDirectories() + path.writeText(toDot()) } fun EtsFileDto.dumpDot(file: File) { - file.writeText(toDot()) -} - -fun EtsFileDto.dumpDot(path: Path) { - path.writeText(toDot()) + dumpDot(file.toPath()) } fun EtsFileDto.dumpDot(path: String) { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt index 1624c724b..7344e9134 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileToDot.kt @@ -23,6 +23,7 @@ import org.jacodb.ets.model.EtsMethod import java.io.BufferedWriter import java.io.File import java.nio.file.Path +import kotlin.io.path.createDirectories import kotlin.io.path.writeText fun EtsFile.toDot(useLR: Boolean = false): String { @@ -147,38 +148,13 @@ fun EtsFile.toDot(useLR: Boolean = false): String { return lines.joinToString("\n") } -fun EtsFile.dumpHuimpleTo(output: BufferedWriter) { - 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(path: Path) { + path.parent?.createDirectories() + path.writeText(toDot()) } fun EtsFile.dumpDot(file: File) { - file.writeText(toDot()) -} - -fun EtsFile.dumpDot(path: Path) { - path.writeText(toDot()) + dumpDot(file.toPath()) } fun EtsFile.dumpDot(path: String) { diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt index cf89e8b6c..47cb3e48f 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt @@ -19,12 +19,14 @@ package org.jacodb.ets.utils import org.jacodb.ets.dto.EtsFileDto 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 kotlin.io.path.Path +import kotlin.io.path.createTempDirectory +import kotlin.io.path.div import kotlin.io.path.exists import kotlin.io.path.inputStream +import kotlin.io.path.nameWithoutExtension import kotlin.io.path.pathString import kotlin.time.Duration.Companion.seconds @@ -37,31 +39,39 @@ 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 generateEtsFileIR(tsPath: String): Path { +fun generateEtsFileIR(tsPath: Path): Path { val arkAnalyzerDir = Path(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 '${Path("").toAbsolutePath()}'.") + 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 '${Path("").toAbsolutePath()}'." + ) } val scriptPath = System.getenv(ENV_VAR_SERIALIZE_SCRIPT_PATH) ?: DEFAULT_SERIALIZE_SCRIPT_PATH val script = arkAnalyzerDir.resolve(scriptPath) if (!script.exists()) { - throw FileNotFoundException("Script file not found: '$script'. Did you forget to execute 'npm run build' in the arkanalyzer project?") + throw FileNotFoundException( + "Script file not found: '$script'. " + + "Did you forget to execute 'npm run build' in the arkanalyzer project?" + ) } val node = System.getenv(ENV_VAR_NODE_EXECUTABLE) ?: DEFAULT_NODE_EXECUTABLE - val output = kotlin.io.path.createTempFile(prefix = File(tsPath).nameWithoutExtension + "_", suffix = ".json") + val output = kotlin.io.path.createTempFile(prefix = tsPath.nameWithoutExtension, suffix = ".json") val cmd: List = listOf( node, script.pathString, - tsPath, + tsPath.pathString, output.pathString, ) runProcess(cmd, 60.seconds) return output } -fun loadEtsFileAutoConvert(tsPath: String): EtsFile { +fun loadEtsFileAutoConvert(tsPath: Path): EtsFile { val irFilePath = generateEtsFileIR(tsPath) irFilePath.inputStream().use { stream -> val etsFileDto = EtsFileDto.loadFromJson(stream) @@ -70,19 +80,21 @@ fun loadEtsFileAutoConvert(tsPath: String): EtsFile { } } -fun loadEtsFileAutoConvertWithDot(tsPath: String, dotDir: String): EtsFile { - val irFilePath = generateEtsFileIR(tsPath) - irFilePath.inputStream().use { stream -> - val etsFileDto = EtsFileDto.loadFromJson(stream) - 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 +fun loadEtsFileAutoConvertWithDot(tsPath: Path): EtsFile { + val irPath = generateEtsFileIR(tsPath) + val dotDir = createTempDirectory(prefix = "dot_" + tsPath.nameWithoutExtension) + + val etsFileDto = irPath.inputStream().use { stream -> + EtsFileDto.loadFromJson(stream) } + val etsFileDtoDotPath = dotDir / (tsPath.nameWithoutExtension + "_DTO.dot") + etsFileDto.dumpDot(etsFileDtoDotPath) + render(etsFileDtoDotPath) + + val etsFile = convertToEtsFile(etsFileDto) + val etsFileDotPath = dotDir / (tsPath.nameWithoutExtension + ".dot") + etsFile.dumpDot(etsFileDotPath) + render(etsFileDotPath) + + return etsFile } diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index addf9e9db..d4ceff6e1 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -17,6 +17,8 @@ package org.jacodb.ets.utils import mu.KotlinLogging +import org.jacodb.ets.dto.EtsFileDto +import org.jacodb.ets.model.EtsFile import java.io.BufferedWriter import java.util.concurrent.TimeUnit import kotlin.time.Duration @@ -52,3 +54,60 @@ internal fun runProcess(cmd: List, timeout: Duration? = null) { process.destroy() } } + +fun EtsFileDto.dumpContentTo(output: BufferedWriter) { + 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") + } + } + } + } +} + +fun EtsFile.dumpContentTo(output: BufferedWriter) { + 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") + } + } + } +} diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt index d69e3ac12..43a951249 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/EtsFromJsonTest.kt @@ -44,13 +44,10 @@ import org.jacodb.ets.utils.loadEtsFileAutoConvert import org.jacodb.ets.utils.loadEtsFileAutoConvertWithDot import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import kotlin.io.path.toPath class EtsFromJsonTest { - companion object { - private const val BASE_PATH = "etsir/samples" - } - private val json = Json { // classDiscriminator = "_" prettyPrint = true @@ -65,7 +62,7 @@ class EtsFromJsonTest { @Test fun testLoadEtsFileFromJson() { - val etsDto = loadEtsFileDtoFromResource("/$BASE_PATH/basic.ts.json") + val etsDto = loadEtsFileDtoFromResource("/etsir/samples/basic.ts.json") println("etsDto = $etsDto") val ets = convertToEtsFile(etsDto) println("ets = $ets") @@ -74,18 +71,18 @@ class EtsFromJsonTest { @Test fun testLoadEtsFileAutoConvert() { val path = "/source/example.ts" - val res = this::class.java.getResource(path) + val res = this::class.java.getResource(path)?.toURI()?.toPath() ?: error("Resource not found: $path") - val etsFile = loadEtsFileAutoConvert(res.path) + val etsFile = loadEtsFileAutoConvert(res) println("etsFile = $etsFile") } @Test fun testLoadEtsFileAutoConvertWithDot() { val path = "/source/example.ts" - val res = this::class.java.getResource(path) + val res = this::class.java.getResource(path)?.toURI()?.toPath() ?: error("Resource not found: $path") - val etsFile = loadEtsFileAutoConvertWithDot(res.path, res.path.substringBeforeLast("/")) + val etsFile = loadEtsFileAutoConvertWithDot(res) println("etsFile = $etsFile") } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt index 7e6c2ba58..135e0ae2f 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -18,16 +18,17 @@ package org.jacodb.ets.test.utils import org.jacodb.ets.dto.EtsFileDto import org.jacodb.ets.model.EtsFile +import org.jacodb.ets.utils.dumpContentTo import org.jacodb.ets.utils.dumpDot -import org.jacodb.ets.utils.dumpHuimpleTo import org.jacodb.ets.utils.render import java.nio.file.Path -import java.nio.file.Paths import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path import kotlin.io.path.createDirectories -import kotlin.io.path.exists import kotlin.io.path.name import kotlin.io.path.nameWithoutExtension +import kotlin.io.path.relativeTo +import kotlin.io.path.toPath import kotlin.io.path.walk private val logger = mu.KotlinLogging.logger {} @@ -44,11 +45,10 @@ object DumpEtsFileDtoToDot { val etsFileDto: EtsFileDto = loadEtsFileDtoFromResource(PATH) val output = System.out.bufferedWriter() - etsFileDto.dumpHuimpleTo(output) + etsFileDto.dumpContentTo(output) - render(DOT_PATH) { file -> - etsFileDto.dumpDot(file) - } + etsFileDto.dumpDot(DOT_PATH) + render(DOT_PATH) } } @@ -64,40 +64,38 @@ object DumpEtsFileToDot { val etsFile = loadEtsFileFromResource(PATH) val output = System.out.bufferedWriter() - etsFile.dumpHuimpleTo(output) + etsFile.dumpContentTo(output) - render(DOT_PATH) { file -> - etsFile.dumpDot(file) - } + etsFile.dumpDot(DOT_PATH) + render(DOT_PATH) } } @OptIn(ExperimentalPathApi::class) object DumpEtsFilesToDot { - private const val DIR = "/etsir/samples/" - private const val DOT_DIR = "dot" + private const val ETSIR_DIR = "/etsir/samples" + private const val DOT_DIR = "dot/samples" @JvmStatic fun main(args: Array) { - val baseDirUrl = object {}::class.java.getResource(DIR) - ?: error("Resource not found: '$DIR'") - val baseDir = Paths.get(baseDirUrl.toURI()) + val baseDir = object {}::class.java.getResource(ETSIR_DIR)?.toURI()?.toPath() + ?: error("Resource not found: '$ETSIR_DIR'") + logger.info { "baseDir = $baseDir" } - val dotDir = baseDir.resolveSibling(DOT_DIR) - if (!dotDir.exists()) { - dotDir.createDirectories() - } + val dotDir = Path(DOT_DIR) + dotDir.createDirectories() + logger.info { "dotDir = $dotDir" } baseDir.walk() .filter { it.name.endsWith(".json") } .forEach { path -> - val relativePath = baseDir.relativize(path) - val fileName = path.nameWithoutExtension + val relative = path.relativeTo(baseDir) + val name = path.nameWithoutExtension - process(relativePath, fileName, dotDir, "Dto") { + process(relative, name, dotDir, "_DTO") { loadEtsFileDtoFromResource(it) } - process(relativePath, fileName, dotDir, "") { + process(relative, name, dotDir, "") { loadEtsFileFromResource(it) } @@ -112,22 +110,13 @@ object DumpEtsFilesToDot { suffix: String, load: (String) -> T, ) { - try { - val resourcePath = DIR + path.toString() - val fileData = load(resourcePath) - val outputDir = dotDir.resolve(path.parent ?: dotDir) - if (!outputDir.exists()) { - outputDir.createDirectories() - } - val outputPath = outputDir.resolve("${name}${suffix}.dot").toString() - render(outputPath) { file -> - when (fileData) { - is EtsFileDto -> fileData.dumpDot(file) - is EtsFile -> fileData.dumpDot(file) - } - } - } catch (e: Exception) { - logger.error { e.stackTraceToString() } + val resourcePath = "$ETSIR_DIR/$path" + val dotPath = dotDir.resolve("${name}${suffix}.dot") + when (val f = load(resourcePath)) { + is EtsFileDto -> f.dumpDot(dotPath) + is EtsFile -> f.dumpDot(dotPath) + else -> error("Unknown type: $f") } + render(dotPath) } } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/LoadEts.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/LoadEts.kt index c0226b457..1e693df76 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/LoadEts.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/LoadEts.kt @@ -21,6 +21,7 @@ import org.jacodb.ets.dto.convertToEtsFile import org.jacodb.ets.model.EtsFile fun loadEtsFileDtoFromResource(jsonPath: String): EtsFileDto { + require(jsonPath.startsWith("/")) { "Resource path must start with '/': $jsonPath" } val sampleFilePath = object {}::class.java.getResourceAsStream(jsonPath) ?: error("Resource not found: $jsonPath") return EtsFileDto.loadFromJson(sampleFilePath) From 3123d0cc9c2feee4f716b01382d70da498eb8bb2 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 16:26:30 +0300 Subject: [PATCH 10/22] Refactor `dumpContentTo` to use `with` func --- .../main/kotlin/org/jacodb/ets/utils/Utils.kt | 92 ++++++++++--------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index d4ceff6e1..e5e198899 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -56,30 +56,32 @@ internal fun runProcess(cmd: List, timeout: Duration? = null) { } fun EtsFileDto.dumpContentTo(output: BufferedWriter) { - 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") + with(output) { + writeln("EtsFileDto '${name}':") + classes.forEach { clazz -> + writeln("= CLASS '${clazz.signature}':") + writeln(" superClass = '${clazz.superClassName}'") + writeln(" typeParameters = ${clazz.typeParameters}") + writeln(" modifiers = ${clazz.modifiers}") + writeln(" fields: ${clazz.fields.size}") + clazz.fields.forEach { field -> + writeln(" - FIELD '${field.signature}'") + writeln(" typeParameters = ${field.typeParameters}") + writeln(" modifiers = ${field.modifiers}") + writeln(" isOptional = ${field.isOptional}") + writeln(" isDefinitelyAssigned = ${field.isDefinitelyAssigned}") + } + writeln(" methods: ${clazz.methods.size}") + clazz.methods.forEach { method -> + writeln(" - METHOD '${method.signature}':") + writeln(" locals = ${method.body.locals}") + writeln(" typeParameters = ${method.typeParameters}") + writeln(" blocks: ${method.body.cfg.blocks.size}") + method.body.cfg.blocks.forEach { block -> + writeln(" - BLOCK ${block.id} with ${block.stmts.size} statements:") + block.stmts.forEachIndexed { i, inst -> + writeln(" ${i + 1}. $inst") + } } } } @@ -87,26 +89,28 @@ fun EtsFileDto.dumpContentTo(output: BufferedWriter) { } fun EtsFile.dumpContentTo(output: BufferedWriter) { - 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") + with(output) { + writeln("EtsFile '${name}':") + classes.forEach { clazz -> + writeln("= CLASS '${clazz.signature}':") + writeln(" superClass = '${clazz.superClass}'") + writeln(" fields: ${clazz.fields.size}") + clazz.fields.forEach { field -> + writeln(" - FIELD '${field.signature}'") + } + writeln(" constructor = '${clazz.ctor.signature}'") + writeln(" stmts: ${clazz.ctor.cfg.stmts.size}") + clazz.ctor.cfg.stmts.forEachIndexed { i, stmt -> + writeln(" ${i + 1}. $stmt") + } + writeln(" methods: ${clazz.methods.size}") + clazz.methods.forEach { method -> + writeln(" - METHOD '${method.signature}':") + writeln(" locals = ${method.localsCount}") + writeln(" stmts: ${method.cfg.stmts.size}") + method.cfg.stmts.forEachIndexed { i, stmt -> + writeln(" ${i + 1}. $stmt") + } } } } From a6ba551381b91d34c262e518cbf5097950c1e2bf Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 16:32:36 +0300 Subject: [PATCH 11/22] Extract list of formats to argument with default value --- jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt index 7bf94ca98..d5f19ad12 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt @@ -25,8 +25,8 @@ import kotlin.time.Duration.Companion.seconds private val logger = KotlinLogging.logger {} -fun render(dotPath: Path) { - for (format in listOf("pdf")) { +fun render(dotPath: Path, listOfFormats: List = listOf("pdf")) { + for (format in listOfFormats) { logger.info { "Rendering DOT to ${format.uppercase()}..." } val formatFile = dotPath.resolveSibling(dotPath.nameWithoutExtension + ".$format") val cmd: List = listOf( From dd0290373fdbaa55ec81a315b85654a3a901a28b Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 16:41:05 +0300 Subject: [PATCH 12/22] Add comment --- .../src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt index 135e0ae2f..989fd9395 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -71,6 +71,9 @@ object DumpEtsFileToDot { } } +/** + * Visualize classes and methods in [EtsFileDto] and [EtsFile] from directory. + */ @OptIn(ExperimentalPathApi::class) object DumpEtsFilesToDot { private const val ETSIR_DIR = "/etsir/samples" From 8195731e2e043a46d2aa202527ca65532bab0688 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 16:43:38 +0300 Subject: [PATCH 13/22] Cleanup --- jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt index d5f19ad12..d576dff30 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt @@ -25,8 +25,11 @@ import kotlin.time.Duration.Companion.seconds private val logger = KotlinLogging.logger {} -fun render(dotPath: Path, listOfFormats: List = listOf("pdf")) { - for (format in listOfFormats) { +fun render( + dotPath: Path, + formats: List = listOf("pdf"), // See: https://graphviz.org/docs/outputs/ +) { + for (format in formats) { logger.info { "Rendering DOT to ${format.uppercase()}..." } val formatFile = dotPath.resolveSibling(dotPath.nameWithoutExtension + ".$format") val cmd: List = listOf( From 9408dd29abcf5df217b4c80a3cb3b3f5b15649dd Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 16:44:37 +0300 Subject: [PATCH 14/22] Reduce indentation --- .../main/kotlin/org/jacodb/ets/utils/Utils.kt | 94 +++++++++---------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index e5e198899..d63441a14 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -55,63 +55,59 @@ internal fun runProcess(cmd: List, timeout: Duration? = null) { } } -fun EtsFileDto.dumpContentTo(output: BufferedWriter) { - with(output) { - writeln("EtsFileDto '${name}':") - classes.forEach { clazz -> - writeln("= CLASS '${clazz.signature}':") - writeln(" superClass = '${clazz.superClassName}'") - writeln(" typeParameters = ${clazz.typeParameters}") - writeln(" modifiers = ${clazz.modifiers}") - writeln(" fields: ${clazz.fields.size}") - clazz.fields.forEach { field -> - writeln(" - FIELD '${field.signature}'") - writeln(" typeParameters = ${field.typeParameters}") - writeln(" modifiers = ${field.modifiers}") - writeln(" isOptional = ${field.isOptional}") - writeln(" isDefinitelyAssigned = ${field.isDefinitelyAssigned}") - } - writeln(" methods: ${clazz.methods.size}") - clazz.methods.forEach { method -> - writeln(" - METHOD '${method.signature}':") - writeln(" locals = ${method.body.locals}") - writeln(" typeParameters = ${method.typeParameters}") - writeln(" blocks: ${method.body.cfg.blocks.size}") - method.body.cfg.blocks.forEach { block -> - writeln(" - BLOCK ${block.id} with ${block.stmts.size} statements:") - block.stmts.forEachIndexed { i, inst -> - writeln(" ${i + 1}. $inst") - } +fun EtsFileDto.dumpContentTo(output: BufferedWriter): Unit = with(output) { + writeln("EtsFileDto '${name}':") + classes.forEach { clazz -> + writeln("= CLASS '${clazz.signature}':") + writeln(" superClass = '${clazz.superClassName}'") + writeln(" typeParameters = ${clazz.typeParameters}") + writeln(" modifiers = ${clazz.modifiers}") + writeln(" fields: ${clazz.fields.size}") + clazz.fields.forEach { field -> + writeln(" - FIELD '${field.signature}'") + writeln(" typeParameters = ${field.typeParameters}") + writeln(" modifiers = ${field.modifiers}") + writeln(" isOptional = ${field.isOptional}") + writeln(" isDefinitelyAssigned = ${field.isDefinitelyAssigned}") + } + writeln(" methods: ${clazz.methods.size}") + clazz.methods.forEach { method -> + writeln(" - METHOD '${method.signature}':") + writeln(" locals = ${method.body.locals}") + writeln(" typeParameters = ${method.typeParameters}") + writeln(" blocks: ${method.body.cfg.blocks.size}") + method.body.cfg.blocks.forEach { block -> + writeln(" - BLOCK ${block.id} with ${block.stmts.size} statements:") + block.stmts.forEachIndexed { i, inst -> + writeln(" ${i + 1}. $inst") } } } } } -fun EtsFile.dumpContentTo(output: BufferedWriter) { - with(output) { - writeln("EtsFile '${name}':") - classes.forEach { clazz -> - writeln("= CLASS '${clazz.signature}':") - writeln(" superClass = '${clazz.superClass}'") - writeln(" fields: ${clazz.fields.size}") - clazz.fields.forEach { field -> - writeln(" - FIELD '${field.signature}'") - } - writeln(" constructor = '${clazz.ctor.signature}'") - writeln(" stmts: ${clazz.ctor.cfg.stmts.size}") - clazz.ctor.cfg.stmts.forEachIndexed { i, stmt -> +fun EtsFile.dumpContentTo(output: BufferedWriter): Unit = with(output) { + writeln("EtsFile '${name}':") + classes.forEach { clazz -> + writeln("= CLASS '${clazz.signature}':") + writeln(" superClass = '${clazz.superClass}'") + writeln(" fields: ${clazz.fields.size}") + clazz.fields.forEach { field -> + writeln(" - FIELD '${field.signature}'") + } + writeln(" constructor = '${clazz.ctor.signature}'") + writeln(" stmts: ${clazz.ctor.cfg.stmts.size}") + clazz.ctor.cfg.stmts.forEachIndexed { i, stmt -> + writeln(" ${i + 1}. $stmt") + } + writeln(" methods: ${clazz.methods.size}") + clazz.methods.forEach { method -> + writeln(" - METHOD '${method.signature}':") + writeln(" locals = ${method.localsCount}") + writeln(" stmts: ${method.cfg.stmts.size}") + method.cfg.stmts.forEachIndexed { i, stmt -> writeln(" ${i + 1}. $stmt") } - writeln(" methods: ${clazz.methods.size}") - clazz.methods.forEach { method -> - writeln(" - METHOD '${method.signature}':") - writeln(" locals = ${method.localsCount}") - writeln(" stmts: ${method.cfg.stmts.size}") - method.cfg.stmts.forEachIndexed { i, stmt -> - writeln(" ${i + 1}. $stmt") - } - } } } } From 596482d50a715d00345c0de5e1e0d449b0304160 Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 16:45:19 +0300 Subject: [PATCH 15/22] Reorder --- .../src/main/kotlin/org/jacodb/ets/utils/Utils.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index d63441a14..f2d737b27 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -25,11 +25,6 @@ import kotlin.time.Duration private val logger = KotlinLogging.logger {} -internal fun BufferedWriter.writeln(s: String) { - write(s) - newLine() -} - internal fun runProcess(cmd: List, timeout: Duration? = null) { logger.info { "Running: '${cmd.joinToString(" ")}'" } val process = ProcessBuilder(cmd).start() @@ -55,6 +50,11 @@ internal fun runProcess(cmd: List, timeout: Duration? = null) { } } +internal fun BufferedWriter.writeln(s: String) { + write(s) + newLine() +} + fun EtsFileDto.dumpContentTo(output: BufferedWriter): Unit = with(output) { writeln("EtsFileDto '${name}':") classes.forEach { clazz -> From 874850d67306c4df7c613a33906a830f2f184550 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 17:02:26 +0300 Subject: [PATCH 16/22] Add successors & predecessors info --- .../src/main/kotlin/org/jacodb/ets/utils/Utils.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index f2d737b27..e0dd5cb6b 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -72,18 +72,21 @@ fun EtsFileDto.dumpContentTo(output: BufferedWriter): Unit = with(output) { } writeln(" methods: ${clazz.methods.size}") clazz.methods.forEach { method -> - writeln(" - METHOD '${method.signature}':") + writeln(" - METHOD '${method.signature}'") writeln(" locals = ${method.body.locals}") writeln(" typeParameters = ${method.typeParameters}") writeln(" blocks: ${method.body.cfg.blocks.size}") method.body.cfg.blocks.forEach { block -> - writeln(" - BLOCK ${block.id} with ${block.stmts.size} statements:") + writeln(" - BLOCK ${block.id}") + writeln(" successors = ${block.successors}") + writeln(" predecessors = ${block.predecessors}") + writeln(" statements: ${block.stmts.size}") block.stmts.forEachIndexed { i, inst -> writeln(" ${i + 1}. $inst") } + } } } - } } fun EtsFile.dumpContentTo(output: BufferedWriter): Unit = with(output) { @@ -107,6 +110,8 @@ fun EtsFile.dumpContentTo(output: BufferedWriter): Unit = with(output) { writeln(" stmts: ${method.cfg.stmts.size}") method.cfg.stmts.forEachIndexed { i, stmt -> writeln(" ${i + 1}. $stmt") + val pad = " ".repeat(2 + "${i + 1}".length) // number + dot + space + writeln(" ${pad}successors = ${method.cfg.successors(stmt)}") } } } From 096a59988bab92e7b2511914310684073cd11735 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 17:12:27 +0300 Subject: [PATCH 17/22] Build text representation for EtsFile and EtsFileDto --- .../main/kotlin/org/jacodb/ets/utils/Utils.kt | 81 ++++++++++--------- .../org/jacodb/ets/test/utils/Entrypoints.kt | 10 +-- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index e0dd5cb6b..07618b1a8 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -55,64 +55,69 @@ internal fun BufferedWriter.writeln(s: String) { newLine() } -fun EtsFileDto.dumpContentTo(output: BufferedWriter): Unit = with(output) { - writeln("EtsFileDto '${name}':") + +fun EtsFileDto.toText(): String { + val lines: MutableList = mutableListOf() + lines += "EtsFileDto '${name}':" classes.forEach { clazz -> - writeln("= CLASS '${clazz.signature}':") - writeln(" superClass = '${clazz.superClassName}'") - writeln(" typeParameters = ${clazz.typeParameters}") - writeln(" modifiers = ${clazz.modifiers}") - writeln(" fields: ${clazz.fields.size}") + lines += "= CLASS '${clazz.signature}':" + lines += " superClass = '${clazz.superClassName}'" + lines += " typeParameters = ${clazz.typeParameters}" + lines += " modifiers = ${clazz.modifiers}" + lines += " fields: ${clazz.fields.size}" clazz.fields.forEach { field -> - writeln(" - FIELD '${field.signature}'") - writeln(" typeParameters = ${field.typeParameters}") - writeln(" modifiers = ${field.modifiers}") - writeln(" isOptional = ${field.isOptional}") - writeln(" isDefinitelyAssigned = ${field.isDefinitelyAssigned}") + lines += " - FIELD '${field.signature}'" + lines += " typeParameters = ${field.typeParameters}" + lines += " modifiers = ${field.modifiers}" + lines += " isOptional = ${field.isOptional}" + lines += " isDefinitelyAssigned = ${field.isDefinitelyAssigned}" } - writeln(" methods: ${clazz.methods.size}") + lines += " methods: ${clazz.methods.size}" clazz.methods.forEach { method -> - writeln(" - METHOD '${method.signature}'") - writeln(" locals = ${method.body.locals}") - writeln(" typeParameters = ${method.typeParameters}") - writeln(" blocks: ${method.body.cfg.blocks.size}") + lines += " - METHOD '${method.signature}'" + lines += " locals = ${method.body.locals}" + lines += " typeParameters = ${method.typeParameters}" + lines += " blocks: ${method.body.cfg.blocks.size}" method.body.cfg.blocks.forEach { block -> - writeln(" - BLOCK ${block.id}") - writeln(" successors = ${block.successors}") - writeln(" predecessors = ${block.predecessors}") - writeln(" statements: ${block.stmts.size}") + lines += " - BLOCK ${block.id}" + lines += " successors = ${block.successors}" + lines += " predecessors = ${block.predecessors}" + lines += " statements: ${block.stmts.size}" block.stmts.forEachIndexed { i, inst -> - writeln(" ${i + 1}. $inst") - } + lines += " ${i + 1}. $inst" } } } + } + return lines.joinToString("\n") } -fun EtsFile.dumpContentTo(output: BufferedWriter): Unit = with(output) { - writeln("EtsFile '${name}':") +fun EtsFile.toText(): String { + val lines: MutableList = mutableListOf() + lines += "EtsFile '${name}':" classes.forEach { clazz -> - writeln("= CLASS '${clazz.signature}':") - writeln(" superClass = '${clazz.superClass}'") - writeln(" fields: ${clazz.fields.size}") + lines += "= CLASS '${clazz.signature}':" + lines += " superClass = '${clazz.superClass}'" + lines += " fields: ${clazz.fields.size}" clazz.fields.forEach { field -> - writeln(" - FIELD '${field.signature}'") + lines += " - FIELD '${field.signature}'" } - writeln(" constructor = '${clazz.ctor.signature}'") - writeln(" stmts: ${clazz.ctor.cfg.stmts.size}") + lines += " constructor = '${clazz.ctor.signature}'" + lines += " stmts: ${clazz.ctor.cfg.stmts.size}" clazz.ctor.cfg.stmts.forEachIndexed { i, stmt -> - writeln(" ${i + 1}. $stmt") + lines += " ${i + 1}. $stmt" } - writeln(" methods: ${clazz.methods.size}") + lines += " methods: ${clazz.methods.size}" clazz.methods.forEach { method -> - writeln(" - METHOD '${method.signature}':") - writeln(" locals = ${method.localsCount}") - writeln(" stmts: ${method.cfg.stmts.size}") + lines += " - METHOD '${method.signature}':" + lines += " locals = ${method.localsCount}" + lines += " stmts: ${method.cfg.stmts.size}" method.cfg.stmts.forEachIndexed { i, stmt -> - writeln(" ${i + 1}. $stmt") + lines += " ${i + 1}. $stmt" val pad = " ".repeat(2 + "${i + 1}".length) // number + dot + space - writeln(" ${pad}successors = ${method.cfg.successors(stmt)}") + lines += " ${pad}successors = ${method.cfg.successors(stmt)}" } } } + return lines.joinToString("\n") } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt index 989fd9395..3004afc5a 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -18,9 +18,9 @@ package org.jacodb.ets.test.utils import org.jacodb.ets.dto.EtsFileDto import org.jacodb.ets.model.EtsFile -import org.jacodb.ets.utils.dumpContentTo import org.jacodb.ets.utils.dumpDot import org.jacodb.ets.utils.render +import org.jacodb.ets.utils.toText import java.nio.file.Path import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path @@ -44,8 +44,8 @@ object DumpEtsFileDtoToDot { fun main(args: Array) { val etsFileDto: EtsFileDto = loadEtsFileDtoFromResource(PATH) - val output = System.out.bufferedWriter() - etsFileDto.dumpContentTo(output) + val text = etsFileDto.toText() + logger.info { "Text representation of EtsFileDto:\n$text" } etsFileDto.dumpDot(DOT_PATH) render(DOT_PATH) @@ -63,8 +63,8 @@ object DumpEtsFileToDot { fun main(args: Array) { val etsFile = loadEtsFileFromResource(PATH) - val output = System.out.bufferedWriter() - etsFile.dumpContentTo(output) + val text = etsFile.toText() + logger.info { "Text representation of EtsFile:\n$text" } etsFile.dumpDot(DOT_PATH) render(DOT_PATH) From 1cffd4748598c198c809b80c214f8b60c2e98574 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 17:19:21 +0300 Subject: [PATCH 18/22] Use location index --- .../main/kotlin/org/jacodb/ets/utils/Utils.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index 07618b1a8..ddc75f363 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -55,7 +55,6 @@ internal fun BufferedWriter.writeln(s: String) { newLine() } - fun EtsFileDto.toText(): String { val lines: MutableList = mutableListOf() lines += "EtsFileDto '${name}':" @@ -83,8 +82,8 @@ fun EtsFileDto.toText(): String { lines += " successors = ${block.successors}" lines += " predecessors = ${block.predecessors}" lines += " statements: ${block.stmts.size}" - block.stmts.forEachIndexed { i, inst -> - lines += " ${i + 1}. $inst" + block.stmts.forEachIndexed { i, stmt -> + lines += " ${i + 1}. $stmt" } } } @@ -104,18 +103,20 @@ fun EtsFile.toText(): String { } lines += " constructor = '${clazz.ctor.signature}'" lines += " stmts: ${clazz.ctor.cfg.stmts.size}" - clazz.ctor.cfg.stmts.forEachIndexed { i, stmt -> - lines += " ${i + 1}. $stmt" + clazz.ctor.cfg.stmts.forEach { stmt -> + lines += " ${stmt.location.index}. $stmt" + val pad = " ".repeat("${stmt.location.index}".length + 2) // number + dot + space + lines += " ${pad}successors = ${clazz.ctor.cfg.successors(stmt).map { it.location.index }}" } lines += " methods: ${clazz.methods.size}" clazz.methods.forEach { method -> lines += " - METHOD '${method.signature}':" lines += " locals = ${method.localsCount}" lines += " stmts: ${method.cfg.stmts.size}" - method.cfg.stmts.forEachIndexed { i, stmt -> - lines += " ${i + 1}. $stmt" - val pad = " ".repeat(2 + "${i + 1}".length) // number + dot + space - lines += " ${pad}successors = ${method.cfg.successors(stmt)}" + method.cfg.stmts.forEach { stmt -> + lines += " ${stmt.location.index}. $stmt" + val pad = " ".repeat("${stmt.location.index}".length + 2) // number + dot + space + lines += " ${pad}successors = ${method.cfg.successors(stmt).map { it.location.index }}" } } } From 93c6a56e2e7b82e93670de18559c7c086f22f604 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 17:19:46 +0300 Subject: [PATCH 19/22] Remove unused code --- jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index ddc75f363..c91fe317a 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -50,11 +50,6 @@ internal fun runProcess(cmd: List, timeout: Duration? = null) { } } -internal fun BufferedWriter.writeln(s: String) { - write(s) - newLine() -} - fun EtsFileDto.toText(): String { val lines: MutableList = mutableListOf() lines += "EtsFileDto '${name}':" From 1654ce1e4db8d7730c97ac96e1165e56447a488d Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 19:16:59 +0300 Subject: [PATCH 20/22] Add format directories for dot rendering --- .../main/kotlin/org/jacodb/ets/utils/Dot.kt | 17 +++--- .../org/jacodb/ets/utils/LoadEtsFile.kt | 5 +- .../org/jacodb/ets/test/utils/Entrypoints.kt | 59 ++++++++++--------- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt index d576dff30..36e9f4de9 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Dot.kt @@ -18,20 +18,25 @@ package org.jacodb.ets.utils import mu.KotlinLogging import java.nio.file.Path -import kotlin.io.path.Path import kotlin.io.path.absolute +import kotlin.io.path.createDirectories +import kotlin.io.path.div import kotlin.io.path.nameWithoutExtension import kotlin.time.Duration.Companion.seconds private val logger = KotlinLogging.logger {} fun render( - dotPath: Path, + dotDir: Path, + relative: Path, formats: List = listOf("pdf"), // See: https://graphviz.org/docs/outputs/ ) { + val dotPath = dotDir / relative for (format in formats) { logger.info { "Rendering DOT to ${format.uppercase()}..." } - val formatFile = dotPath.resolveSibling(dotPath.nameWithoutExtension + ".$format") + val formatFile = (dotDir.resolveSibling(format) / relative) + .resolveSibling("${relative.nameWithoutExtension}.$format") + formatFile.parent?.createDirectories() val cmd: List = listOf( "dot", "-T$format", @@ -39,11 +44,7 @@ fun render( "-o", "$formatFile" ) - runProcess(cmd, 60.seconds) + runProcess(cmd, 10.seconds) logger.info { "Generated ${format.uppercase()} file: ${formatFile.absolute()}" } } } - -fun render(dotPath: String) { - render(Path(dotPath)) -} diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt index 47cb3e48f..3bc696992 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/LoadEtsFile.kt @@ -28,6 +28,7 @@ import kotlin.io.path.exists import kotlin.io.path.inputStream import kotlin.io.path.nameWithoutExtension import kotlin.io.path.pathString +import kotlin.io.path.relativeTo import kotlin.time.Duration.Companion.seconds private const val ENV_VAR_ARK_ANALYZER_DIR = "ARKANALYZER_DIR" @@ -89,12 +90,12 @@ fun loadEtsFileAutoConvertWithDot(tsPath: Path): EtsFile { } val etsFileDtoDotPath = dotDir / (tsPath.nameWithoutExtension + "_DTO.dot") etsFileDto.dumpDot(etsFileDtoDotPath) - render(etsFileDtoDotPath) + render(dotDir, etsFileDtoDotPath.relativeTo(dotDir)) val etsFile = convertToEtsFile(etsFileDto) val etsFileDotPath = dotDir / (tsPath.nameWithoutExtension + ".dot") etsFile.dumpDot(etsFileDotPath) - render(etsFileDotPath) + render(dotDir, etsFileDotPath.relativeTo(dotDir)) return etsFile } diff --git a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt index 3004afc5a..134665611 100644 --- a/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt +++ b/jacodb-ets/src/test/kotlin/org/jacodb/ets/test/utils/Entrypoints.kt @@ -24,7 +24,7 @@ import org.jacodb.ets.utils.toText import java.nio.file.Path import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path -import kotlin.io.path.createDirectories +import kotlin.io.path.div import kotlin.io.path.name import kotlin.io.path.nameWithoutExtension import kotlin.io.path.relativeTo @@ -37,8 +37,10 @@ private val logger = mu.KotlinLogging.logger {} * Visualize classes and methods in [EtsFileDto]. */ object DumpEtsFileDtoToDot { - private const val PATH = "/etsir/samples/basic.ts.json" - private const val DOT_PATH = "basicDto.dot" + private const val NAME = "basic" + private const val PATH = "/etsir/samples/$NAME.ts.json" + private val DOT_DIR = Path("dot") + private val DOT_PATH = Path("$NAME.dto.dot") // relative to DOT_DIR @JvmStatic fun main(args: Array) { @@ -47,8 +49,8 @@ object DumpEtsFileDtoToDot { val text = etsFileDto.toText() logger.info { "Text representation of EtsFileDto:\n$text" } - etsFileDto.dumpDot(DOT_PATH) - render(DOT_PATH) + etsFileDto.dumpDot(DOT_DIR / DOT_PATH) + render(DOT_DIR, DOT_PATH) } } @@ -56,8 +58,10 @@ object DumpEtsFileDtoToDot { * Visualize classes and methods in [EtsFile]. */ object DumpEtsFileToDot { - private const val PATH = "/etsir/samples/basic.ts.json" - private const val DOT_PATH = "basic.dot" + private const val NAME = "basic" + private const val PATH = "/etsir/samples/$NAME.ts.json" + private val DOT_DIR = Path("dot") + private val DOT_PATH = Path("$NAME.dto.dot") // relative to DOT_DIR @JvmStatic fun main(args: Array) { @@ -66,8 +70,8 @@ object DumpEtsFileToDot { val text = etsFile.toText() logger.info { "Text representation of EtsFile:\n$text" } - etsFile.dumpDot(DOT_PATH) - render(DOT_PATH) + etsFile.dumpDot(DOT_DIR / DOT_PATH) + render(DOT_DIR, DOT_PATH) } } @@ -76,29 +80,26 @@ object DumpEtsFileToDot { */ @OptIn(ExperimentalPathApi::class) object DumpEtsFilesToDot { - private const val ETSIR_DIR = "/etsir/samples" - private const val DOT_DIR = "dot/samples" + private const val ETSIR_BASE = "/etsir" + private const val ETSIR_DIR = "samples" // relative to BASE + private val DOT_DIR = Path("generated/dot") @JvmStatic fun main(args: Array) { - val baseDir = object {}::class.java.getResource(ETSIR_DIR)?.toURI()?.toPath() - ?: error("Resource not found: '$ETSIR_DIR'") - logger.info { "baseDir = $baseDir" } + val resPath = "$ETSIR_BASE/$ETSIR_DIR" + val etsirDir = object {}::class.java.getResource(resPath)?.toURI()?.toPath() + ?: error("Resource not found: '$resPath'") + logger.info { "baseDir = $etsirDir" } - val dotDir = Path(DOT_DIR) - dotDir.createDirectories() - logger.info { "dotDir = $dotDir" } - - baseDir.walk() + etsirDir.walk() .filter { it.name.endsWith(".json") } .forEach { path -> - val relative = path.relativeTo(baseDir) - val name = path.nameWithoutExtension + val relative = path.relativeTo(etsirDir) - process(relative, name, dotDir, "_DTO") { + process(relative, ".dto") { loadEtsFileDtoFromResource(it) } - process(relative, name, dotDir, "") { + process(relative, "") { loadEtsFileFromResource(it) } @@ -107,19 +108,19 @@ object DumpEtsFilesToDot { } private fun process( - path: Path, - name: String, - dotDir: Path, + relative: Path, suffix: String, load: (String) -> T, ) { - val resourcePath = "$ETSIR_DIR/$path" - val dotPath = dotDir.resolve("${name}${suffix}.dot") + val resourcePath = "$ETSIR_BASE/$ETSIR_DIR/$relative" + val relativeDot = (Path(ETSIR_DIR) / relative) + .resolveSibling("${relative.nameWithoutExtension}$suffix.dot") + val dotPath = DOT_DIR / relativeDot when (val f = load(resourcePath)) { is EtsFileDto -> f.dumpDot(dotPath) is EtsFile -> f.dumpDot(dotPath) else -> error("Unknown type: $f") } - render(dotPath) + render(DOT_DIR, relativeDot) } } From 9f40d3f8de59e4004a2810e64bb58dae5fe97649 Mon Sep 17 00:00:00 2001 From: Vladislav Kasimov Date: Tue, 6 Aug 2024 19:31:23 +0300 Subject: [PATCH 21/22] Remove unused import --- jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt index c91fe317a..569b984f5 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/Utils.kt @@ -19,7 +19,6 @@ package org.jacodb.ets.utils import mu.KotlinLogging import org.jacodb.ets.dto.EtsFileDto import org.jacodb.ets.model.EtsFile -import java.io.BufferedWriter import java.util.concurrent.TimeUnit import kotlin.time.Duration From 0181afe9aceea36bf0270d646ab7090cc449e43c Mon Sep 17 00:00:00 2001 From: Konstantin Chukharev Date: Tue, 6 Aug 2024 19:57:30 +0300 Subject: [PATCH 22/22] Remove unnecessary workaround --- .../src/main/kotlin/org/jacodb/ets/dto/Convert.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt index f9797bc70..e4b701aef 100644 --- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt +++ b/jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt @@ -339,17 +339,6 @@ class EtsMethodBuilder( // so we just *unsafely* extract the type name from the "pseudo-local" here: "instanceof" -> EtsInstanceOfExpr(left, (right as EtsLocal).name) - // TODO: remove this when ArkIR is fixed - Ops.EQ_EQ -> EtsEqExpr(left, right) - Ops.NOT_EQ -> EtsNotEqExpr(left, right) - Ops.EQ_EQ_EQ -> EtsStrictEqExpr(left, right) - Ops.NOT_EQ_EQ -> EtsStrictNotEqExpr(left, right) - Ops.LT -> EtsLtExpr(left, right) - Ops.LT_EQ -> EtsLtEqExpr(left, right) - Ops.GT -> EtsGtExpr(left, right) - Ops.GT_EQ -> EtsGtEqExpr(left, right) - Ops.IN -> EtsInExpr(left, right) - else -> error("Unknown binop: ${value.op}") } }