Skip to content

Commit f571149

Browse files
committed
JS and WASM compilation with BTA
1 parent 7a31e9f commit f571149

File tree

2 files changed

+69
-166
lines changed

2 files changed

+69
-166
lines changed

src/main/kotlin/com/compiler/server/compiler/components/CliUtils.kt

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
package com.compiler.server.compiler.components
22

33
import com.compiler.server.model.*
4-
import org.jetbrains.kotlin.cli.common.CLICompiler
5-
import org.jetbrains.kotlin.cli.common.CLICompiler.Companion.doMainNoExit
6-
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
7-
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
8-
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
9-
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
104
import java.io.File
115
import java.nio.file.Path
126
import java.util.*
137
import kotlin.io.path.*
148

15-
private fun minusOne(value: Int) = if (value > 0) value - 1 else value
16-
17-
private const val UNRESOLVED_REFERENCE_PREFIX = "Unresolved reference: "
18-
199
sealed class CompilationResult<out T> {
2010
abstract val compilerDiagnostics: CompilerDiagnostics
2111

@@ -44,70 +34,6 @@ data class Compiled<T>(override val compilerDiagnostics: CompilerDiagnostics, va
4434

4535
data class NotCompiled(override val compilerDiagnostics: CompilerDiagnostics) : CompilationResult<Nothing>()
4636

47-
fun CLICompiler<*>.tryCompilation(inputDirectory: Path, inputFiles: List<Path>, arguments: List<String>): CompilationResult<Unit> = tryCompilation(inputDirectory, inputFiles, arguments) {}
48-
49-
fun <T> CLICompiler<*>.tryCompilation(inputDirectory: Path, inputFiles: List<Path>, arguments: List<String>, onSuccess: () -> T): CompilationResult<T> {
50-
fun Path.outputFilePathString() = inputDirectory.relativize(this).pathString
51-
52-
val diagnosticsMap = mutableMapOf<String, MutableList<ErrorDescriptor>>().apply {
53-
inputFiles.forEach { put(it.outputFilePathString(), mutableListOf()) }
54-
}
55-
val defaultFileName = inputFiles.singleOrNull()?.outputFilePathString() ?: ""
56-
val exitCode = doMainNoExit(this, arguments.toTypedArray(), object : MessageRenderer {
57-
override fun renderPreamble(): String = ""
58-
59-
override fun render(
60-
severity: CompilerMessageSeverity,
61-
message: String,
62-
location: CompilerMessageSourceLocation?
63-
): String {
64-
when {
65-
// suppress -XXLanguage:+ExplicitBackingFields
66-
severity == STRONG_WARNING && message.contains("ExplicitBackingFields") ->
67-
return ""
68-
}
69-
70-
val messageSeverity: ProjectSeveriry = when (severity) {
71-
EXCEPTION, ERROR -> ProjectSeveriry.ERROR
72-
STRONG_WARNING, WARNING, FIXED_WARNING -> ProjectSeveriry.WARNING
73-
INFO, LOGGING, OUTPUT -> return ""
74-
}
75-
val textInterval = location?.let {
76-
TextInterval(
77-
start = TextInterval.TextPosition(minusOne(location.line), minusOne(location.column)),
78-
end = TextInterval.TextPosition(minusOne(location.lineEnd), minusOne(location.columnEnd))
79-
)
80-
}
81-
82-
val errorFilePath = location?.path?.let(::Path)?.outputFilePathString() ?: defaultFileName
83-
84-
val className = if (!message.startsWith(UNRESOLVED_REFERENCE_PREFIX) && severity == ERROR) "red_wavy_line" else messageSeverity.name
85-
val errorDescriptor = ErrorDescriptor(textInterval, message, messageSeverity, className)
86-
87-
diagnosticsMap.getOrPut(errorFilePath) { mutableListOf() }.add(errorDescriptor)
88-
return ""
89-
}
90-
91-
override fun renderUsage(usage: String): String =
92-
render(STRONG_WARNING, usage, null)
93-
94-
override fun renderConclusion(): String = ""
95-
96-
override fun getName(): String = "Redirector"
97-
})
98-
val diagnostics = CompilerDiagnostics(diagnosticsMap)
99-
return when {
100-
diagnostics.any { it.severity == ProjectSeveriry.ERROR } -> NotCompiled(diagnostics)
101-
exitCode.code != 0 -> ErrorDescriptor(
102-
severity = ProjectSeveriry.ERROR,
103-
message = "Compiler finished with non-null exit code ${exitCode.code}: ${exitCode.name}",
104-
interval = null
105-
).let { NotCompiled(CompilerDiagnostics(mapOf(defaultFileName to listOf(it)))) }
106-
107-
else -> Compiled(result = onSuccess(), compilerDiagnostics = diagnostics)
108-
}
109-
}
110-
11137
@OptIn(ExperimentalPathApi::class)
11238
fun <T> usingTempDirectory(action: (path: Path) -> T): T {
11339
val path = getTempDirectory()

src/main/kotlin/com/compiler/server/compiler/components/KotlinToJSTranslator.kt

Lines changed: 69 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi
99
import org.jetbrains.kotlin.buildtools.api.KotlinToolchains
1010
import org.jetbrains.kotlin.buildtools.api.arguments.ExperimentalCompilerArgument
1111
import org.jetbrains.kotlin.buildtools.api.js.JsPlatformToolchain
12-
import org.jetbrains.kotlin.cli.js.K2JSCompiler
1312
import org.springframework.stereotype.Service
13+
import java.nio.file.Path
1414
import kotlin.io.path.*
1515

1616
@Service
@@ -83,116 +83,74 @@ class KotlinToJSTranslator(
8383
): CompilationResult<String> = usingTempDirectory { inputDir ->
8484
usingTempDirectory { outputDir ->
8585
files.writeToIoFiles(inputDir)
86-
val klibPath = (outputDir / "klib").toFile().canonicalPath
87-
val additionalCompilerArgumentsForKLib = compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(
88-
jsCompilerArguments,
89-
compilerArgumentsUtil.PREDEFINED_JS_FIRST_PHASE_ARGUMENTS,
90-
userCompilerArguments.firstPhase
91-
) + "-ir-output-dir=$klibPath"
92-
9386

9487
val sources = inputDir.listDirectoryEntries()
95-
9688
val logger = CompilationLogger()
9789
logger.compilationLogs = sources.filter { it.name.endsWith(".kt") }.associate { it.name to mutableListOf() }
98-
9990
val toolchains = KotlinToolchains.loadImplementation(ClassLoader.getSystemClassLoader())
10091
val jsToolchain = toolchains.getToolchain(JsPlatformToolchain::class.java)
10192

102-
toolchains.createBuildSession().use { session ->
103-
val result = try {
104-
val operation = jsToolchain.createJsCompilationOperation(sources, outputDir)
105-
operation.compilerArguments.applyArgumentStrings(arguments + additionalCompilerArgumentsForKLib)
93+
val klibPath = (outputDir / "klib").toFile().canonicalPath
94+
val additionalCompilerArgumentsForKLib = compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(
95+
jsCompilerArguments,
96+
compilerArgumentsUtil.PREDEFINED_JS_FIRST_PHASE_ARGUMENTS,
97+
userCompilerArguments.firstPhase
98+
) + "-ir-output-dir=$klibPath"
10699

107-
session.executeOperation(operation, toolchains.createInProcessExecutionPolicy(), logger)
108-
} catch (e: Exception) {
109-
throw Exception("Exception executing compilation operation", e)
110-
}
111-
return@use toCompilationResult(result, logger).flatMap {
100+
toolchains.createBuildSession().use { session ->
101+
tryCompilation(
102+
jsToolchain,
103+
sources,
104+
outputDir,
105+
arguments + additionalCompilerArgumentsForKLib,
106+
session,
107+
toolchains,
108+
logger,
109+
Unit
110+
).flatMap {
112111
val secondPhaseArguments = compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(
113112
jsCompilerArguments,
114113
compilerArgumentsUtil.PREDEFINED_JS_SECOND_PHASE_ARGUMENTS,
115114
userCompilerArguments.secondPhase
116115
) + "-ir-output-dir=${(outputDir / "js").toFile().canonicalPath}" + "-Xinclude=$klibPath"
117116

118-
val result = try {
119-
val operation = jsToolchain.createJsCompilationOperation(sources, outputDir)
120-
operation.compilerArguments.applyArgumentStrings(secondPhaseArguments)
121-
122-
session.executeOperation(operation, toolchains.createInProcessExecutionPolicy(), logger)
123-
} catch (e: Exception) {
124-
throw Exception("Exception executing compilation operation", e)
125-
}
126-
toCompilationResultString(result, logger)
127-
117+
tryCompilation(
118+
jsToolchain, sources, outputDir, secondPhaseArguments, session, toolchains, logger, ""
119+
)
128120
}.map { (outputDir / "js" / "$JS_DEFAULT_MODULE_NAME.js").readText() }
129121
.map { it.withMainArgumentsIr(arguments) }.map(::redirectOutput)
130122
}
131-
132-
// k2JSCompiler.tryCompilation(inputDir, ioFiles, filePaths + additionalCompilerArgumentsForKLib).flatMap {
133-
// val secondPhaseArguments = compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(
134-
// jsCompilerArguments,
135-
// compilerArgumentsUtil.PREDEFINED_JS_SECOND_PHASE_ARGUMENTS,
136-
// userCompilerArguments.secondPhase
137-
// ) + "-ir-output-dir=${(outputDir / "js").toFile().canonicalPath}" + "-Xinclude=$klibPath"
138-
// k2JSCompiler.tryCompilation(inputDir, ioFiles, secondPhaseArguments)
139-
// }.map { (outputDir / "js" / "$JS_DEFAULT_MODULE_NAME.js").readText() }
140-
// .map { it.withMainArgumentsIr(arguments) }.map(::redirectOutput)
141123
}
142124
}
143125

144-
private fun toCompilationResult(
145-
buildResult: org.jetbrains.kotlin.buildtools.api.CompilationResult,
126+
@OptIn(ExperimentalBuildToolsApi::class, ExperimentalCompilerArgument::class)
127+
private fun <T> tryCompilation(
128+
jsToolchain: JsPlatformToolchain,
129+
sources: List<Path>,
130+
outputDir: Path,
131+
arguments: List<String>,
132+
session: KotlinToolchains.BuildSession,
133+
toolchains: KotlinToolchains,
146134
logger: CompilationLogger,
147-
): CompilationResult<Unit> = when (buildResult) {
148-
org.jetbrains.kotlin.buildtools.api.CompilationResult.COMPILATION_SUCCESS -> {
149-
val compilerDiagnostics = CompilerDiagnostics(logger.compilationLogs)
150-
// val outputFiles = buildMap {
151-
// outputDir.visitFileTree {
152-
// onVisitFile { file, _ ->
153-
// put(file.relativeTo(outputDir).pathString, file.readBytes())
154-
// FileVisitResult.CONTINUE
155-
// }
156-
// }
157-
// }
158-
Compiled(result = Unit, compilerDiagnostics = compilerDiagnostics)
135+
successValue: T,
136+
): CompilationResult<T> {
137+
val result = try {
138+
val operation = jsToolchain.createJsCompilationOperation(sources, outputDir)
139+
operation.compilerArguments.applyArgumentStrings(arguments)
159140

160-
// Compiled(
161-
// compilerDiagnostics = compilerDiagnostics,
162-
// result = JvmClasses(
163-
// files = outputFiles,
164-
// )
165-
// )
141+
session.executeOperation(operation, toolchains.createInProcessExecutionPolicy(), logger)
142+
} catch (e: Exception) {
143+
throw Exception("Exception executing compilation operation", e)
166144
}
167145

168-
else -> NotCompiled(CompilerDiagnostics(logger.compilationLogs))
169-
}
170-
171-
private fun toCompilationResultString(
172-
buildResult: org.jetbrains.kotlin.buildtools.api.CompilationResult,
173-
logger: CompilationLogger,
174-
): CompilationResult<String> = when (buildResult) {
175-
org.jetbrains.kotlin.buildtools.api.CompilationResult.COMPILATION_SUCCESS -> {
176-
val compilerDiagnostics = CompilerDiagnostics(logger.compilationLogs)
177-
// val outputFiles = buildMap {
178-
// outputDir.visitFileTree {
179-
// onVisitFile { file, _ ->
180-
// put(file.relativeTo(outputDir).pathString, file.readBytes())
181-
// FileVisitResult.CONTINUE
182-
// }
183-
// }
184-
// }
185-
Compiled(result = "", compilerDiagnostics = compilerDiagnostics)
146+
return when (result) {
147+
org.jetbrains.kotlin.buildtools.api.CompilationResult.COMPILATION_SUCCESS -> {
148+
val compilerDiagnostics = CompilerDiagnostics(logger.compilationLogs)
149+
Compiled(result = successValue, compilerDiagnostics = compilerDiagnostics)
150+
}
186151

187-
// Compiled(
188-
// compilerDiagnostics = compilerDiagnostics,
189-
// result = JvmClasses(
190-
// files = outputFiles,
191-
// )
192-
// )
152+
else -> NotCompiled(CompilerDiagnostics(logger.compilationLogs))
193153
}
194-
195-
else -> NotCompiled(CompilerDiagnostics(logger.compilationLogs))
196154
}
197155

198156
private fun redirectOutput(code: String): String {
@@ -203,7 +161,7 @@ class KotlinToJSTranslator(
203161
return listLines.joinToString("\n")
204162
}
205163

206-
164+
@OptIn(ExperimentalBuildToolsApi::class, ExperimentalCompilerArgument::class)
207165
fun doTranslateWithWasm(
208166
files: List<ProjectFile>,
209167
projectType: ProjectType,
@@ -215,7 +173,7 @@ class KotlinToJSTranslator(
215173
ProjectType.WASM -> Triple(
216174
wasmCompilerArguments,
217175
compilerArgumentsUtil.PREDEFINED_WASM_FIRST_PHASE_ARGUMENTS,
218-
compilerArgumentsUtil.PREDEFINED_WASM_SECOND_PHASE_ARGUMENTS,
176+
compilerArgumentsUtil.PREDEFINED_WASM_SECOND_PHASE_ARGUMENTS
219177
)
220178

221179
ProjectType.COMPOSE_WASM -> Triple(
@@ -226,23 +184,41 @@ class KotlinToJSTranslator(
226184

227185
else -> throw IllegalStateException("Wasm should have wasm or compose-wasm project type")
228186
}
229-
val ioFiles = files.writeToIoFiles(inputDir)
230-
val k2JSCompiler = K2JSCompiler()
231-
val filePaths = ioFiles.map { it.toFile().canonicalPath }
187+
files.writeToIoFiles(inputDir)
188+
val sources = inputDir.listDirectoryEntries()
189+
val logger = CompilationLogger().apply {
190+
compilationLogs = sources.filter { it.name.endsWith(".kt") }.associate { it.name to mutableListOf() }
191+
}
192+
val toolchains = KotlinToolchains.loadImplementation(ClassLoader.getSystemClassLoader())
193+
val jsToolchain = toolchains.getToolchain(JsPlatformToolchain::class.java)
194+
232195
val klibPath = (outputDir / "klib").toFile().canonicalPath
233196

234197
val additionalCompilerArgumentsForKLib = compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(
235198
defaultCompilerArgs, firstPhasePredefinedArguments, userCompilerArguments.firstPhase
236199
) + "-ir-output-dir=$klibPath"
237200

238-
k2JSCompiler.tryCompilation(inputDir, ioFiles, filePaths + additionalCompilerArgumentsForKLib).flatMap {
201+
toolchains.createBuildSession().use { session ->
202+
tryCompilation(
203+
jsToolchain,
204+
sources,
205+
outputDir,
206+
additionalCompilerArgumentsForKLib,
207+
session,
208+
toolchains,
209+
logger,
210+
Unit
211+
).flatMap {
212+
239213
val secondPhaseArguments = (compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(
240214
defaultCompilerArgs, secondPhasePredefinedArguments, userCompilerArguments.secondPhase
241215
) + "-ir-output-dir=${(outputDir / "wasm").toFile().canonicalPath}" + "-Xinclude=$klibPath").toMutableList()
242216

243217
if (debugInfo) secondPhaseArguments.add("-Xwasm-generate-wat")
244218

245-
k2JSCompiler.tryCompilation(inputDir, ioFiles, secondPhaseArguments)
219+
tryCompilation(
220+
jsToolchain, sources, outputDir, secondPhaseArguments, session, toolchains, logger, Unit
221+
)
246222
}.map {
247223
WasmTranslationSuccessfulOutput(
248224
jsCode = (outputDir / "wasm" / "$WASM_DEFAULT_MODULE_NAME.uninstantiated.mjs").readText(),
@@ -251,6 +227,7 @@ class KotlinToJSTranslator(
251227
wat = if (debugInfo) (outputDir / "wasm" / "$WASM_DEFAULT_MODULE_NAME.wat").readText() else null,
252228
)
253229
}
230+
}
254231
}
255232
}
256233
}

0 commit comments

Comments
 (0)