[JS CLI] Support sourcemap generation for IR BE in CLI

#KT-46551 In Progress
This commit is contained in:
Zalim Bashorov
2021-07-01 21:54:46 +03:00
committed by teamcityserver
parent 5a3efc1a98
commit 2460f5f9ae
6 changed files with 90 additions and 24 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -516,7 +516,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
} }
@NotNull @NotNull
private static String calculateSourceMapSourceRoot( static String calculateSourceMapSourceRoot(
@NotNull MessageCollector messageCollector, @NotNull MessageCollector messageCollector,
@NotNull K2JSCompilerArguments arguments @NotNull K2JSCompilerArguments arguments
) { ) {
@@ -284,10 +284,10 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
), ),
) )
val jsCode = if (arguments.irDce && !arguments.irDceDriven) compiledModule.outputsAfterDce!! else compiledModule.outputs!! val outputs = if (arguments.irDce && !arguments.irDceDriven) compiledModule.outputsAfterDce!! else compiledModule.outputs!!
outputFile.writeText(jsCode.jsCode) outputFile.write(outputs)
jsCode.dependencies.forEach { (name, content) -> outputs.dependencies.forEach { (name, content) ->
outputFile.resolveSibling("$name.js").writeText(content) outputFile.resolveSibling("$name.js").write(content)
} }
if (arguments.generateDts) { if (arguments.generateDts) {
val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!! val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!!
@@ -298,6 +298,15 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
return OK return OK
} }
private fun File.write(outputs: CompilationOutputs) {
writeText(outputs.jsCode)
outputs.sourceMap?.let {
val mapFile = resolveSibling("$name.map")
appendText("\n//# sourceMappingURL=${mapFile.name}")
mapFile.writeText(it)
}
}
override fun setupPlatformSpecificArgumentsAndServices( override fun setupPlatformSpecificArgumentsAndServices(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
arguments: K2JSCompilerArguments, arguments: K2JSCompilerArguments,
@@ -310,9 +319,22 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
} }
configuration.put(JSConfigurationKeys.TARGET, EcmaVersion.defaultVersion()) configuration.put(JSConfigurationKeys.TARGET, EcmaVersion.defaultVersion())
// TODO: Support source maps
if (arguments.sourceMap) { if (arguments.sourceMap) {
messageCollector.report(WARNING, "source-map argument is not supported yet", null) configuration.put(JSConfigurationKeys.SOURCE_MAP, true)
if (arguments.sourceMapPrefix != null) {
configuration.put(JSConfigurationKeys.SOURCE_MAP_PREFIX, arguments.sourceMapPrefix!!)
}
var sourceMapSourceRoots = arguments.sourceMapBaseDirs
if (sourceMapSourceRoots == null && StringUtil.isNotEmpty(arguments.sourceMapPrefix)) {
sourceMapSourceRoots = K2JSCompiler.calculateSourceMapSourceRoot(messageCollector, arguments)
}
if (sourceMapSourceRoots != null) {
val sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator)
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList)
}
} else { } else {
if (arguments.sourceMapPrefix != null) { if (arguments.sourceMapPrefix != null) {
messageCollector.report(WARNING, "source-map-prefix argument has no effect without source map", null) messageCollector.report(WARNING, "source-map-prefix argument has no effect without source map", null)
@@ -32,7 +32,11 @@ class CompilerResult(
val tsDefinitions: String? = null val tsDefinitions: String? = null
) )
class CompilationOutputs(val jsCode: String, val dependencies: Iterable<Pair<String, String>> = emptyList()) class CompilationOutputs(
val jsCode: String,
val sourceMap: String? = null,
val dependencies: Iterable<Pair<String, CompilationOutputs>> = emptyList()
)
fun compile( fun compile(
project: Project, project: Project,
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelGenerator import org.jetbrains.kotlin.ir.backend.js.export.ExportModelGenerator
@@ -19,9 +19,17 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
import org.jetbrains.kotlin.js.backend.NoOpSourceLocationConsumer
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer
import org.jetbrains.kotlin.js.util.TextOutputImpl
import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.DFS
import java.io.File
class IrModuleToJsTransformer( class IrModuleToJsTransformer(
private val backendContext: JsIrBackendContext, private val backendContext: JsIrBackendContext,
@@ -104,16 +112,14 @@ class IrModuleToJsTransformer(
) )
}.reversed() }.reversed()
return CompilationOutputs(mainModule, dependencies) return CompilationOutputs(mainModule.jsCode, mainModule.sourceMap, dependencies)
} else { } else {
return CompilationOutputs( return generateWrappedModuleBody2(
generateWrappedModuleBody2( modules,
modules, emptyList(),
emptyList(), exportedModule,
exportedModule, namer,
namer, EmptyCrossModuleReferenceInfo
EmptyCrossModuleReferenceInfo
)
) )
} }
} }
@@ -124,7 +130,7 @@ class IrModuleToJsTransformer(
exportedModule: ExportedModule, exportedModule: ExportedModule,
namer: NameTables, namer: NameTables,
refInfo: CrossModuleReferenceInfo refInfo: CrossModuleReferenceInfo
): String { ): CompilationOutputs {
val nameGenerator = refInfo.withReferenceTracking( val nameGenerator = refInfo.withReferenceTracking(
IrNamerImpl(newNameTables = namer, backendContext), IrNamerImpl(newNameTables = namer, backendContext),
@@ -188,7 +194,41 @@ class IrModuleToJsTransformer(
) )
} }
return program.toString() val jsCode = TextOutputImpl()
val configuration = backendContext.configuration
val sourceMapPrefix = configuration.get(JSConfigurationKeys.SOURCE_MAP_PREFIX, "")
val sourceMapsEnabled = configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
val sourceMapBuilder = SourceMap3Builder(null, jsCode, sourceMapPrefix)
val sourceMapBuilderConsumer =
if (sourceMapsEnabled) {
val sourceRoots = configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, emptyList<String>()).map(::File)
val generateRelativePathsInSourceMap = sourceMapPrefix.isEmpty() && sourceRoots.isEmpty()
val outputDir = if (generateRelativePathsInSourceMap) configuration.get(JSConfigurationKeys.OUTPUT_DIR) else null
val pathResolver = SourceFilePathResolver(sourceRoots, outputDir)
val sourceMapContentEmbedding =
configuration.get(JSConfigurationKeys.SOURCE_MAP_EMBED_SOURCES, SourceMapSourceEmbedding.INLINING)
SourceMapBuilderConsumer(
File("."),
sourceMapBuilder,
pathResolver,
sourceMapContentEmbedding == SourceMapSourceEmbedding.ALWAYS,
sourceMapContentEmbedding != SourceMapSourceEmbedding.NEVER
)
} else {
null
}
program.accept(JsToStringGenerationVisitor(jsCode, sourceMapBuilderConsumer ?: NoOpSourceLocationConsumer))
return CompilationOutputs(
jsCode.toString(),
if(sourceMapsEnabled) sourceMapBuilder.build() else null
)
} }
private fun IrModuleFragment.externalModuleName(): String { private fun IrModuleFragment.externalModuleName(): String {
@@ -62,7 +62,7 @@ public class SourceMap3Builder implements SourceMapBuilder {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
json.getProperties().put("version", new JsonNumber(3)); json.getProperties().put("version", new JsonNumber(3));
json.getProperties().put("file", new JsonString(generatedFile.getName())); if (generatedFile != null) json.getProperties().put("file", new JsonString(generatedFile.getName()));
appendSources(json); appendSources(json);
appendSourcesContent(json); appendSourcesContent(json);
json.getProperties().put("names", new JsonArray()); json.getProperties().put("names", new JsonArray());
@@ -230,8 +230,8 @@ abstract class BasicIrBoxTest(
val dependencyPaths = mutableListOf<String>() val dependencyPaths = mutableListOf<String>()
dependencies.forEach { (moduleId, code) -> dependencies.forEach { (moduleId, outputs) ->
val wrappedCode = wrapWithModuleEmulationMarkers(code, config.moduleKind, moduleId) val wrappedCode = wrapWithModuleEmulationMarkers(outputs.jsCode, config.moduleKind, moduleId)
val dependencyPath = outputFile.absolutePath.replace("_v5.js", "-${moduleId}_v5.js") val dependencyPath = outputFile.absolutePath.replace("_v5.js", "-${moduleId}_v5.js")
dependencyPaths += dependencyPath dependencyPaths += dependencyPath
File(dependencyPath).write(wrappedCode) File(dependencyPath).write(wrappedCode)