Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.usvm.ts.calls

import org.jacodb.ets.dto.EtsFileDto
import org.jacodb.ets.dto.toEtsFile
import org.jacodb.ets.model.EtsFile
import org.jacodb.ets.model.EtsFileSignature
import org.jacodb.ets.model.EtsScene
import org.jacodb.ets.utils.EtsIrProvider
import org.jacodb.ets.utils.generateEtsIR
import org.jacodb.ets.utils.loadEtsFileAutoConvert
import org.jacodb.ets.utils.loadEtsProjectFromIR
import org.usvm.ts.pbt.fastcheck.TypeScriptSourceInspector
import java.nio.file.Files
Expand Down Expand Up @@ -35,7 +37,7 @@ internal fun loadCallsSourceProject(sourceRoot: Path, source: Path): CallsSource
val entryModule = sourceRoot.relativize(source).toString()
require(entryModule in closure.files) { "Dependency closure does not contain its entry source" }
if (closure.files.size == 1) {
val entry = loadEtsFileAutoConvert(source, provider = EtsIrProvider.TS_FRONTEND)
val entry = loadSingleCallsSource(source)
return CallsSourceProject.Loaded(
scene = EtsScene(projectFiles = listOf(entry)),
entryModule = source.fileName.toString(),
Expand All @@ -58,6 +60,7 @@ internal fun loadCallsSourceProject(sourceRoot: Path, source: Path): CallsSource
Files.copy(sourceRoot.resolve(relative), copy)
}
val generatedIr = generateEtsIR(
keepPartialOutputOnFailure = false,
projectPath = temporaryProject,
isProject = true,
loadEntrypoints = false,
Expand Down Expand Up @@ -87,3 +90,18 @@ internal fun loadCallsSourceProject(sourceRoot: Path, source: Path): CallsSource
temporaryProject.toFile().deleteRecursively()
}
}

private fun loadSingleCallsSource(source: Path): EtsFile {
val generatedIr = generateEtsIR(
keepPartialOutputOnFailure = false,
projectPath = source,
useArkAnalyzerTypeInference = 1,
provider = EtsIrProvider.TS_FRONTEND,
)

return try {
Files.newInputStream(generatedIr).use { stream -> EtsFileDto.loadFromJson(stream).toEtsFile() }
} finally {
Files.deleteIfExists(generatedIr)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.usvm.ts.calls

import org.junit.jupiter.api.Test
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.createTempDirectory
import kotlin.io.path.readText
import kotlin.io.path.writeText
import kotlin.test.assertFails
import kotlin.test.assertFalse

class CallsSourceProjectTest {
@Test
fun `single and multi file conversion failures remove partial IR`() {
checkCleanup(frontendExitCode = 1)
}

@Test
fun `single and multi file IR decoding failures remove generated output`() {
checkCleanup(frontendExitCode = 0)
}

private fun checkCleanup(frontendExitCode: Int) {
val root = createTempDirectory(prefix = "calls-frontend-cleanup-")
val frontend = root.resolve("frontend")
val script = frontend.resolve("dist/index.js")
val recordedPath = script.resolveSibling("generated-path")
val sourceRoot = root.resolve("sources").createDirectories()
val source = sourceRoot.resolve("entry.ts")
val previousFrontend = System.getProperty("ets.frontend.dir")
script.parent.createDirectories()
script.writeText(
"""
const fs = require('node:fs');
const path = require('node:path');
const output = process.argv.filter(arg => arg !== '-v').at(-1);
fs.writeFileSync(path.join(__dirname, 'generated-path'), output);
const file = fs.statSync(output).isDirectory() ? path.join(output, 'partial.json') : output;
fs.writeFileSync(file, '{');
process.exit($frontendExitCode);
""".trimIndent(),
)
System.setProperty("ets.frontend.dir", frontend.toString())

try {
source.writeText("export function entry(value: number): number { return value; }")
assertFails { loadCallsSourceProject(sourceRoot = sourceRoot, source = source) }
assertFalse(Files.exists(Path.of(recordedPath.readText())))

sourceRoot.resolve("helper.ts").writeText("export function helper(value: number): number { return value; }")
source.writeText(
"import { helper } from './helper'; " +
"export function entry(value: number): number { return helper(value); }",
)
assertFails { loadCallsSourceProject(sourceRoot = sourceRoot, source = source) }
assertFalse(Files.exists(Path.of(recordedPath.readText())))
} finally {
if (previousFrontend == null) {
System.clearProperty("ets.frontend.dir")
} else {
System.setProperty("ets.frontend.dir", previousFrontend)
}
root.toFile().deleteRecursively()
}
}
}
Loading
Loading