[JS IR] Support the sourceMapEmbedSources setting (partially)
This doesn't work with external libraries yet (including stdlib).
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.ir.backend.js
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
data class SourceMapsInfo(
|
||||||
|
val sourceMapPrefix: String,
|
||||||
|
val sourceRoots: List<String>,
|
||||||
|
val outputDir: File?,
|
||||||
|
val sourceMapContentEmbedding: SourceMapSourceEmbedding,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(configuration: CompilerConfiguration): SourceMapsInfo? =
|
||||||
|
if (configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
|
||||||
|
SourceMapsInfo(
|
||||||
|
configuration.get(JSConfigurationKeys.SOURCE_MAP_PREFIX, ""),
|
||||||
|
configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, emptyList()),
|
||||||
|
configuration.get(JSConfigurationKeys.OUTPUT_DIR),
|
||||||
|
configuration.get(JSConfigurationKeys.SOURCE_MAP_EMBED_SOURCES, SourceMapSourceEmbedding.INLINING),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val JsCommonBackendContext.sourceMapsInfo: SourceMapsInfo?
|
||||||
|
get() = SourceMapsInfo.from(configuration)
|
||||||
+1
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.ic
|
package org.jetbrains.kotlin.ir.backend.js.ic
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
|
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
|
||||||
|
|||||||
-21
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||||
|
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
|
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.*
|
import org.jetbrains.kotlin.ir.backend.js.export.*
|
||||||
@@ -471,23 +470,3 @@ fun generateSingleWrappedModuleBody(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SourceMapsInfo(
|
|
||||||
val sourceMapPrefix: String,
|
|
||||||
val sourceRoots: List<String>,
|
|
||||||
val outputDir: File?,
|
|
||||||
val sourceMapContentEmbedding: SourceMapSourceEmbedding,
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun from(configuration: CompilerConfiguration): SourceMapsInfo? =
|
|
||||||
if (configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
|
|
||||||
SourceMapsInfo(
|
|
||||||
configuration.get(JSConfigurationKeys.SOURCE_MAP_PREFIX, ""),
|
|
||||||
configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, emptyList<String>()),
|
|
||||||
configuration.get(JSConfigurationKeys.OUTPUT_DIR),
|
|
||||||
configuration.get(JSConfigurationKeys.SOURCE_MAP_EMBED_SOURCES, SourceMapSourceEmbedding.INLINING),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+29
-8
@@ -9,8 +9,8 @@ import org.jetbrains.kotlin.backend.common.compilationException
|
|||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrFileEntry
|
import org.jetbrains.kotlin.ir.IrFileEntry
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.sourceMapsInfo
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
@@ -23,8 +23,13 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||||
|
import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
fun jsVar(name: JsName, initializer: IrExpression?, context: JsGenerationContext): JsVars {
|
fun jsVar(name: JsName, initializer: IrExpression?, context: JsGenerationContext): JsVars {
|
||||||
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), context)
|
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), context)
|
||||||
@@ -492,17 +497,33 @@ internal fun <T : JsNode> T.withSource(node: IrElement, context: JsGenerationCon
|
|||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun <T : JsNode> T.addSourceInfoIfNeed(node: IrElement, context: JsGenerationContext) {
|
private inline fun <T : JsNode> T.addSourceInfoIfNeed(node: IrElement, context: JsGenerationContext) {
|
||||||
if (!context.staticContext.genSourcemaps) return
|
|
||||||
|
|
||||||
var cachedLocation = context.getLocationFromCache(node)
|
val sourceMapsInfo = context.staticContext.backendContext.sourceMapsInfo ?: return
|
||||||
if (cachedLocation == null) {
|
|
||||||
cachedLocation = node.getSourceInfo(context.currentFile.fileEntry) ?: return
|
val location = context.getLocationForIrElement(node) ?: return
|
||||||
context.saveLocationToCache(node, cachedLocation)
|
|
||||||
}
|
val isNodeFromCurrentModule = context.currentFile.module.descriptor == context.staticContext.backendContext.module
|
||||||
|
|
||||||
// TODO maybe it's better to fix in JsExpressionStatement
|
// TODO maybe it's better to fix in JsExpressionStatement
|
||||||
val locationTarget = if (this is JsExpressionStatement) this.expression else this
|
val locationTarget = if (this is JsExpressionStatement) this.expression else this
|
||||||
locationTarget.source = cachedLocation
|
|
||||||
|
locationTarget.source = when (sourceMapsInfo.sourceMapContentEmbedding) {
|
||||||
|
SourceMapSourceEmbedding.NEVER -> location
|
||||||
|
SourceMapSourceEmbedding.INLINING -> if (isNodeFromCurrentModule) location else location.withEmbeddedSource(context)
|
||||||
|
SourceMapSourceEmbedding.ALWAYS -> location.withEmbeddedSource(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsLocation.withEmbeddedSource(
|
||||||
|
context: JsGenerationContext
|
||||||
|
) = JsLocationWithEmbeddedSource(this, identityObject = context.currentFile) {
|
||||||
|
try {
|
||||||
|
InputStreamReader(FileInputStream(file), StandardCharsets.UTF_8)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// TODO: If the source file is not available at path (e. g. it's an stdlib file), use heuristics to find it.
|
||||||
|
// If all heuristics fail, use dumpKotlinLike() on freshly deserialized IrFile.
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrElement.getSourceInfo(container: IrDeclaration): JsLocation? {
|
fun IrElement.getSourceInfo(container: IrDeclaration): JsLocation? {
|
||||||
|
|||||||
+6
-3
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.getSourceInfo
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
|
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
|
||||||
@@ -86,7 +87,9 @@ class JsGenerationContext(
|
|||||||
|
|
||||||
fun checkIfHasAssociatedJsCode(symbol: IrFunctionSymbol): Boolean = staticContext.backendContext.getJsCodeForFunction(symbol) != null
|
fun checkIfHasAssociatedJsCode(symbol: IrFunctionSymbol): Boolean = staticContext.backendContext.getJsCodeForFunction(symbol) != null
|
||||||
|
|
||||||
fun getLocationFromCache(node: IrElement) = locationCache[node.startOffset]
|
fun getLocationForIrElement(irElement: IrElement): JsLocation? {
|
||||||
|
return locationCache.getOrPut(irElement.startOffset) {
|
||||||
fun saveLocationToCache(node: IrElement, location: JsLocation) = locationCache.put(node.startOffset, location)
|
irElement.getSourceInfo(currentFile.fileEntry) ?: return null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,4 @@ class JsStaticContext(
|
|||||||
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
||||||
|
|
||||||
val initializerBlock = JsCompositeBlock()
|
val initializerBlock = JsCompositeBlock()
|
||||||
|
|
||||||
val genSourcemaps = backendContext.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.ir.backend.js.*
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrFragmentAndBinaryAst
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrFragmentAndBinaryAst
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.SourceMapsInfo
|
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.SourceMapsInfo
|
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||||
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
||||||
|
|||||||
+47
-7
@@ -776,17 +776,21 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DisplayName("sources can be embedded into source map")
|
@DisplayName("sources of all modules are embedded into source map")
|
||||||
@DisabledIf(
|
|
||||||
"org.jetbrains.kotlin.gradle.AbstractKotlin2JsGradlePluginIT#getIrBackend",
|
|
||||||
disabledReason = "Source maps are not supported in IR backend"
|
|
||||||
)
|
|
||||||
@GradleTest
|
@GradleTest
|
||||||
fun testKotlinJsSourceMapEmbedSources(gradleVersion: GradleVersion) {
|
fun testKotlinJsSourceMapEmbedSourcesAlways(gradleVersion: GradleVersion) {
|
||||||
project("kotlin2JsProjectWithSourceMap", gradleVersion) {
|
project("kotlin2JsProjectWithSourceMap", gradleVersion) {
|
||||||
buildGradleKts.appendText(
|
buildGradleKts.appendText(
|
||||||
"""
|
"""
|
||||||
|allprojects {
|
|project("lib") {
|
||||||
|
| tasks.withType<KotlinJsCompile>() {
|
||||||
|
| kotlinOptions {
|
||||||
|
| sourceMap = true
|
||||||
|
| sourceMapEmbedSources = "always"
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
|project("app") {
|
||||||
| tasks.withType<KotlinJsCompile> {
|
| tasks.withType<KotlinJsCompile> {
|
||||||
| kotlinOptions.sourceMapEmbedSources = "always"
|
| kotlinOptions.sourceMapEmbedSources = "always"
|
||||||
| }
|
| }
|
||||||
@@ -805,6 +809,42 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DisplayName("only sources of external modules are embedded into source map")
|
||||||
|
@GradleTest
|
||||||
|
fun testKotlinJsSourceMapEmbedSourcesInlining(gradleVersion: GradleVersion) {
|
||||||
|
project("kotlin2JsProjectWithSourceMap", gradleVersion) {
|
||||||
|
buildGradleKts.appendText(
|
||||||
|
"""
|
||||||
|
|project("lib") {
|
||||||
|
| tasks.withType<KotlinJsCompile>() {
|
||||||
|
| kotlinOptions {
|
||||||
|
| sourceMap = true
|
||||||
|
| sourceMapEmbedSources = "always"
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
|project("app") {
|
||||||
|
| tasks.withType<KotlinJsCompile> {
|
||||||
|
| kotlinOptions.sourceMapEmbedSources = "inlining"
|
||||||
|
| }
|
||||||
|
|}
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
build(if (irBackend) "compileDevelopmentExecutableKotlinJs" else "compileKotlinJs") {
|
||||||
|
val mapFilePath = projectPath.resolve("build/js/packages/$projectName-app/kotlin/$projectName-app.js.map")
|
||||||
|
assertFileDoesNotContain(
|
||||||
|
mapFilePath,
|
||||||
|
"\"fun main(args: Array<String>) {"
|
||||||
|
)
|
||||||
|
assertFileContains(
|
||||||
|
mapFilePath,
|
||||||
|
"\"inline fun foo(): String {",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@DisplayName("incremental compilation for js works")
|
@DisplayName("incremental compilation for js works")
|
||||||
@GradleTest
|
@GradleTest
|
||||||
fun testIncrementalCompilation(gradleVersion: GradleVersion) {
|
fun testIncrementalCompilation(gradleVersion: GradleVersion) {
|
||||||
|
|||||||
Reference in New Issue
Block a user