JS: generate paths in source maps relative to .map file location

See KT-19818
This commit is contained in:
Alexey Andreev
2017-08-29 14:28:15 +03:00
parent 93a3026c6d
commit 9008791a54
31 changed files with 236 additions and 63 deletions
@@ -251,8 +251,20 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
}
}
File outputDir = outputFile.getParentFile();
if (outputDir == null) {
outputDir = outputFile.getAbsoluteFile().getParentFile();
}
try {
config.getConfiguration().put(JSConfigurationKeys.OUTPUT_DIR, outputDir.getCanonicalFile());
}
catch (IOException e) {
messageCollector.report(ERROR, "Could not resolve output directory", null);
return ExitCode.COMPILATION_ERROR;
}
if (config.getConfiguration().getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
checkDuplicateSourceFileNames(messageCollector, sourcesFiles, config.getSourceMapRoots());
checkDuplicateSourceFileNames(messageCollector, sourcesFiles, config);
}
MainCallParameters mainCallParameters = createMainCallParameters(arguments.getMain());
@@ -280,11 +292,6 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
return ExitCode.COMPILATION_ERROR;
}
File outputDir = outputFile.getParentFile();
if (outputDir == null) {
outputDir = outputFile.getAbsoluteFile().getParentFile();
}
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
OutputUtilsKt.writeAll(outputFiles, outputDir, messageCollector,
@@ -296,12 +303,11 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
private static void checkDuplicateSourceFileNames(
@NotNull MessageCollector log,
@NotNull List<KtFile> sourceFiles,
@NotNull List<String> sourceRoots
@NotNull JsConfig config
) {
if (sourceRoots.isEmpty()) return;
if (config.getSourceMapRoots().isEmpty()) return;
List<File> sourceRootFiles = sourceRoots.stream().map(File::new).collect(Collectors.toList());
SourceFilePathResolver pathResolver = new SourceFilePathResolver(sourceRootFiles);
SourceFilePathResolver pathResolver = SourceFilePathResolver.create(config);
Map<String, String> pathMap = new HashMap<>();
Set<String> duplicatePaths = new HashSet<>();
@@ -357,11 +363,15 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
configuration.put(JSConfigurationKeys.SOURCE_MAP_PREFIX, arguments.getSourceMapPrefix());
}
String sourceMapSourceRoots = arguments.getSourceMapBaseDirs() != null ?
arguments.getSourceMapBaseDirs() :
calculateSourceMapSourceRoot(messageCollector, arguments);
List<String> sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator);
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList);
String sourceMapSourceRoots = arguments.getSourceMapBaseDirs();
if (sourceMapSourceRoots == null && StringUtil.isNotEmpty(arguments.getSourceMapPrefix())) {
sourceMapSourceRoots = calculateSourceMapSourceRoot(messageCollector, arguments);
}
if (sourceMapSourceRoots != null) {
List<String> sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator);
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList);
}
}
else {
if (arguments.getSourceMapPrefix() != null) {
+2
View File
@@ -1,5 +1,7 @@
$TESTDATA_DIR$/sourceMap.kt
-no-stdlib
-source-map
-source-map-prefix
./
-output
$TEMP_DIR$/out.js
+1 -1
View File
@@ -1,2 +1,2 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "sourceMap.kt"
// CONTAINS: out.js.map, "./sourceMap.kt"
+2
View File
@@ -1,6 +1,8 @@
$TESTDATA_DIR$/sourceMap.kt
-no-stdlib
-source-map
-source-map-prefix
./
-source-map-embed-sources
always
-output
+1 -1
View File
@@ -1,3 +1,3 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "sourceMap.kt"
// CONTAINS: out.js.map, "./sourceMap.kt"
// CONTAINS: out.js.map, "var log = \"\"\n\nfun foo(x: String) {
+14
View File
@@ -0,0 +1,14 @@
$TESTDATA_DIR$/sourceMapRelativeRoot/lib/src/lib.kt
-no-stdlib
-source-map
-meta-info
-output
$TESTDATA_DIR$/sourceMapRelativeRoot/lib/out/lib.js
---
$TESTDATA_DIR$/sourceMapRelativeRoot/main/src/main.kt
-no-stdlib
-libraries
$TESTDATA_DIR$/sourceMapRelativeRoot/lib/out/
-source-map
-output
$TESTDATA_DIR$/sourceMapRelativeRoot/main/out/main.js
+1
View File
@@ -0,0 +1 @@
OK
+5
View File
@@ -0,0 +1,5 @@
// EXISTS: $TESTDATA_DIR$/sourceMapRelativeRoot/lib/out/lib.js
// EXISTS: $TESTDATA_DIR$/sourceMapRelativeRoot/main/out/main.js
// CONTAINS: $TESTDATA_DIR$/sourceMapRelativeRoot/lib/out/lib.js.map, "../src/lib.kt"
// CONTAINS: $TESTDATA_DIR$/sourceMapRelativeRoot/main/out/main.js.map, "../src/main.kt"
// CONTAINS: $TESTDATA_DIR$/sourceMapRelativeRoot/main/out/main.js.map, "../../lib/src/lib.kt"
@@ -0,0 +1 @@
out
@@ -0,0 +1,4 @@
@Suppress("NOTHING_TO_INLINE")
inline fun foo() = 23
fun bar() = 42
@@ -0,0 +1 @@
fun box() = foo() + bar()
+2
View File
@@ -2,5 +2,7 @@ $TESTDATA_DIR$/sourceMapRoot/foo/file1.kt
$TESTDATA_DIR$/sourceMapRoot/bar/file2.kt
-no-stdlib
-source-map
-source-map-prefix
./
-output
$TEMP_DIR$/out.js
+2 -2
View File
@@ -1,3 +1,3 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "foo/file1.kt"
// CONTAINS: out.js.map, "bar/file2.kt"
// CONTAINS: out.js.map, "./foo/file1.kt"
// CONTAINS: out.js.map, "./bar/file2.kt"
@@ -43,19 +43,29 @@ import org.junit.Assert;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractCliTest extends TestCaseWithTmpdir {
@NotNull
private static final String TESTDATA_DIR = "$TESTDATA_DIR$";
public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLITool<?> compiler, @NotNull List<String> args) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream origErr = System.err;
try {
System.setErr(new PrintStream(bytes));
ExitCode exitCode = CLITool.doMainNoExit(compiler, ArrayUtil.toStringArray(args));
ExitCode exitCode;
int index = 0;
do {
int next = args.subList(index, args.size()).indexOf("---");
if (next == -1) {
next = args.size();
}
exitCode = CLITool.doMainNoExit(compiler, ArrayUtil.toStringArray(args.subList(index, next)));
if (exitCode != ExitCode.OK) break;
index = next + 1;
} while (index < args.size());
return new Pair<>(bytes.toString("utf-8"), exitCode);
}
catch (Exception e) {
@@ -70,8 +80,8 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) {
String testDataAbsoluteDir = new File(testDataDir).getAbsolutePath();
String normalizedOutputWithoutExitCode = StringUtil.convertLineSeparators(pureOutput)
.replace(testDataAbsoluteDir, "$TESTDATA_DIR$")
.replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), "$TESTDATA_DIR$")
.replace(testDataAbsoluteDir, TESTDATA_DIR)
.replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), TESTDATA_DIR)
.replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$")
.replace("expected version is " + JvmMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
.replace("expected version is " + JsMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
@@ -81,7 +91,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
return normalizedOutputWithoutExitCode + exitCode + "\n";
}
private void doTest(@NotNull String fileName, @NotNull CLITool<?> compiler) throws Exception {
private void doTest(@NotNull String fileName, @NotNull CLITool<?> compiler) {
System.setProperty("java.awt.headless", "true");
Pair<String, ExitCode> outputAndExitCode = executeCompilerGrabOutput(compiler, readArgs(fileName, tmpdir.getPath()));
String actual = getNormalizedCompilerOutput(
@@ -93,17 +103,17 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
File additionalTestConfig = new File(fileName.replaceFirst("\\.args$", ".test"));
if (additionalTestConfig.exists()) {
doTestAdditionalChecks(additionalTestConfig);
doTestAdditionalChecks(additionalTestConfig, fileName);
}
}
private void doTestAdditionalChecks(@NotNull File testConfigFile) throws IOException {
private void doTestAdditionalChecks(@NotNull File testConfigFile, @NotNull String argsFilePath) {
List<String> diagnostics = new ArrayList<>(0);
String content = FilesKt.readText(testConfigFile, Charsets.UTF_8);
List<String> existsList = InTextDirectivesUtils.findListWithPrefixes(content, "// EXISTS: ");
for (String fileName : existsList) {
File file = new File(tmpdir, fileName);
File file = checkedPathToFile(fileName, argsFilePath);
if (!file.exists()) {
diagnostics.add("File does not exist, but should: " + fileName);
}
@@ -114,7 +124,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
List<String> absentList = InTextDirectivesUtils.findListWithPrefixes(content, "// ABSENT: ");
for (String fileName : absentList) {
File file = new File(tmpdir, fileName);
File file = checkedPathToFile(fileName, argsFilePath);
if (file.exists() && file.isFile()) {
diagnostics.add("File exists, but shouldn't: " + fileName);
}
@@ -125,7 +135,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
String[] parts = containsSpec.split(",", 2);
String fileName = parts[0].trim();
String contentToSearch = parts[1].trim();
File file = new File(tmpdir, fileName);
File file = checkedPathToFile(fileName, argsFilePath);
if (!file.exists()) {
diagnostics.add("File does not exist: " + fileName);
}
@@ -147,7 +157,17 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
}
@NotNull
private static List<String> readArgs(@NotNull String argsFilePath, @NotNull String tempDir) throws IOException {
private File checkedPathToFile(@NotNull String path, @NotNull String argsFilePath) {
if (path.startsWith(TESTDATA_DIR + "/")) {
return new File(new File(argsFilePath).getParent(), path.substring(TESTDATA_DIR.length() + 1));
}
else {
return new File(tmpdir, path);
}
}
@NotNull
private static List<String> readArgs(@NotNull String argsFilePath, @NotNull String tempDir) {
List<String> lines = FilesKt.readLines(new File(argsFilePath), Charsets.UTF_8);
return CollectionsKt.mapNotNull(lines, arg -> {
@@ -163,7 +183,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
return argsWithColonsReplaced
.replace("$TEMP_DIR$", tempDir)
.replace("$TESTDATA_DIR$", new File(argsFilePath).getParent())
.replace(TESTDATA_DIR, new File(argsFilePath).getParent())
.replace(
"$FOREIGN_ANNOTATIONS_DIR$",
new File(AbstractForeignAnnotationsTestKt.getFOREIGN_ANNOTATIONS_SOURCES_PATH()).getPath()
@@ -171,15 +191,15 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
});
}
protected void doJvmTest(@NotNull String fileName) throws Exception {
protected void doJvmTest(@NotNull String fileName) {
doTest(fileName, new K2JVMCompiler());
}
protected void doJsTest(@NotNull String fileName) throws Exception {
protected void doJsTest(@NotNull String fileName) {
doTest(fileName, new K2JSCompiler());
}
protected void doJsDceTest(@NotNull String fileName) throws Exception {
protected void doJsDceTest(@NotNull String fileName) {
doTest(fileName, new K2JSDce());
}
@@ -665,6 +665,12 @@ public class CliTestGenerated extends AbstractCliTest {
doJsTest(fileName);
}
@TestMetadata("sourceMapRelativeRoot.args")
public void testSourceMapRelativeRoot() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapRelativeRoot.args");
doJsTest(fileName);
}
@TestMetadata("sourceMapRootAuto.args")
public void testSourceMapRootAuto() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapRootAuto.args");
@@ -64,7 +64,7 @@ object JsLibraryUtils {
private fun File.runIfFileExists(relativePath: String, action: (JsLibrary) -> Unit) {
if (isFile) {
action(JsLibrary(readText(), relativePath, correspondingSourceMapFile().contentIfExists()))
action(JsLibrary(readText(), relativePath, correspondingSourceMapFile().contentIfExists(), this))
}
}
@@ -126,7 +126,7 @@ object JsLibraryUtils {
val stream = zipFile.getInputStream(entry)
val content = FileUtil.loadTextAndClose(stream)
librariesWithoutSourceMaps += JsLibrary(content, relativePath, null)
librariesWithoutSourceMaps += JsLibrary(content, relativePath, null, null)
}
else if (entryName.endsWith(KotlinJavascriptMetadataUtils.JS_MAP_EXT)) {
val correspondingJsPath = entryName.removeSuffix(KotlinJavascriptMetadataUtils.JS_MAP_EXT) +
@@ -171,4 +171,4 @@ object JsLibraryUtils {
}
}
data class JsLibrary(val content: String, val path: String, val sourceMapContent: String?)
data class JsLibrary(val content: String, val path: String, val sourceMapContent: String?, val file: File?)
@@ -359,7 +359,7 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
buildAllModules().assertSuccessful()
val sourceMapContent = File(getOutputDir(PROJECT_NAME), "$PROJECT_NAME.js.map").readText()
val expectedPath = "prefix-dir/pkg/test1.kt"
val expectedPath = "prefix-dir/src/pkg/test1.kt"
assertTrue("Source map file should contain relative path ($expectedPath)", sourceMapContent.contains("\"$expectedPath\""))
val librarySourceMapFile = File(getOutputDir(PROJECT_NAME), "lib/kotlin.js.map")
@@ -67,6 +67,7 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.utils.*
import org.jetbrains.org.objectweb.asm.ClassReader
import java.io.File
import java.net.URI
import java.util.*
class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
@@ -647,7 +648,10 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(representativeModule)
val k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(representativeModule)
val sourceRoots = KotlinSourceFileCollector.getRelevantSourceRoots(representativeTarget).map { it.file }
val sourceRoots = representativeModule.contentRootsList.urls
.map { URI.create(it) }
.filter { it.scheme == "file" }
.map { File(it) }
val friendPaths = KotlinBuilderModuleScriptGenerator.getProductionModulesWhichInternalsAreVisible(representativeTarget).mapNotNull {
val file = getOutputMetaFile(it, false)
@@ -119,7 +119,8 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit
val sourceMapFile = File(file.outputPath + ".map")
val textOutput = TextOutputImpl()
val sourceMapBuilder = SourceMap3Builder(File(file.outputPath), textOutput, "")
val consumer = SourceMapBuilderConsumer(sourceMapBuilder, SourceFilePathResolver(mutableListOf()), true, true)
val sourcePathResolver = SourceFilePathResolver(mutableListOf(), File(file.outputPath).parentFile)
val consumer = SourceMapBuilderConsumer(sourceMapBuilder, sourcePathResolver, true, true)
block.accept(JsToStringGenerationVisitor(textOutput, consumer))
val sourceMapContent = sourceMapBuilder.build()
sourceMapBuilder.addLink()
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider;
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer;
import org.jetbrains.kotlin.serialization.js.ModuleKind;
import java.io.File;
import java.util.List;
public class JSConfigurationKeys {
@@ -30,6 +31,9 @@ public class JSConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> SOURCE_MAP =
CompilerConfigurationKey.create("generate source map");
public static final CompilerConfigurationKey<File> OUTPUT_DIR =
CompilerConfigurationKey.create("output directory");
public static final CompilerConfigurationKey<String> SOURCE_MAP_PREFIX =
CompilerConfigurationKey.create("prefix to add to paths in source map");
@@ -125,7 +125,11 @@ public class JsConfig {
@NotNull
public List<String> getSourceMapRoots() {
return configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, Collections.singletonList("."));
return configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, Collections.emptyList());
}
public boolean shouldGenerateRelativePathsInSourceMap() {
return getSourceMapPrefix().isEmpty() && getSourceMapRoots().isEmpty();
}
@NotNull
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.inline.util.*
import org.jetbrains.kotlin.js.parser.OffsetToSourceMapping
@@ -77,7 +78,8 @@ class FunctionReader(
val kotlinVariable: String,
val specialFunctions: Map<String, SpecialFunction>,
offsetToSourceMappingProvider: () -> OffsetToSourceMapping,
val sourceMap: SourceMap?
val sourceMap: SourceMap?,
val outputDir: File?
) {
val offsetToSourceMapping by lazy(offsetToSourceMappingProvider)
@@ -89,7 +91,7 @@ class FunctionReader(
private val moduleNameToInfo by lazy {
val result = HashMultimap.create<String, ModuleInfo>()
JsLibraryUtils.traverseJsLibraries(config.libraries.map(::File)) { (content, path, sourceMapContent) ->
JsLibraryUtils.traverseJsLibraries(config.libraries.map(::File)) { (content, path, sourceMapContent, file) ->
var current = 0
while (true) {
@@ -131,7 +133,8 @@ class FunctionReader(
kotlinVariable = kotlinVariable,
specialFunctions = specialFunctions,
offsetToSourceMappingProvider = { OffsetToSourceMapping(content) },
sourceMap = sourceMap
sourceMap = sourceMap,
outputDir = file?.parentFile
)
result.put(moduleName, moduleInfo)
@@ -142,6 +145,8 @@ class FunctionReader(
}
private val moduleNameMap: Map<String, JsExpression>
private val shouldRemapPathToRelativeForm = config.shouldGenerateRelativePathsInSourceMap()
private val relativePathCalculator = config.configuration[JSConfigurationKeys.OUTPUT_DIR]?.let { RelativePathCalculator(it) }
init {
moduleNameMap = buildModuleNameMap(fragments)
@@ -242,7 +247,9 @@ class FunctionReader(
val sourceMap = info.sourceMap
if (sourceMap != null) {
val remapper = SourceMapLocationRemapper(sourceMap)
val remapper = SourceMapLocationRemapper(sourceMap) {
remapPath(removeRedundantPathPrefix(it), info)
}
remapper.remap(function)
wrapperStatements?.forEach { remapper.remap(it) }
}
@@ -301,6 +308,25 @@ class FunctionReader(
param.hasDefaultValue = true
}
}
private fun removeRedundantPathPrefix(path: String): String {
var index = 0
while (index + 2 <= path.length && path.substring(index, index + 2) == "./") {
index += 2
while (index < path.length && path[index] == '/') {
++index
}
}
return path.substring(index)
}
private fun remapPath(path: String, info: ModuleInfo): String {
if (!shouldRemapPathToRelativeForm) return path
val outputDir = info.outputDir ?: return path
val calculator = relativePathCalculator ?: return path
return calculator.calculateRelativePathTo(File(outputDir, path)) ?: path
}
}
private val Char.isWhitespaceOrComma: Boolean
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.jetbrains.kotlin.js.inline.util
import java.io.File
class RelativePathCalculator(baseDir: File) {
private val baseDirPath = generateSequence(baseDir.canonicalFile) { it.parentFile }.toList().asReversed()
fun calculateRelativePathTo(file: File): String? {
val path = generateSequence(file.canonicalFile) { it.parentFile }.toList().asReversed()
if (baseDirPath[0] != path[0]) return null
val commonLength = baseDirPath.zip(path).takeWhile { (first, second) -> first == second }.size
val sb = StringBuilder()
for (i in commonLength until baseDirPath.size) {
sb.append("../")
}
for (i in commonLength until path.size) {
sb.append(path[i].name).append('/')
}
sb.setLength(sb.lastIndex)
return sb.toString()
}
}
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.js.parser.sourcemaps
import org.jetbrains.kotlin.js.backend.ast.*
class SourceMapLocationRemapper(private val sourceMap: SourceMap) {
class SourceMapLocationRemapper(private val sourceMap: SourceMap, private val sourceMapPathMapper: (String) -> String = { it }) {
fun remap(node: JsNode) {
val listCollector = JsNodeFlatListCollector()
node.accept(listCollector)
@@ -65,7 +65,7 @@ class SourceMapLocationRemapper(private val sourceMap: SourceMap) {
val segment = findCorrespondingSegment(node)
val sourceFileName = segment?.sourceFileName
node.source = if (sourceFileName != null) {
val location = JsLocation(segment.sourceFileName, segment.sourceLineNumber, segment.sourceColumnNumber)
val location = JsLocation(sourceMapPathMapper(sourceFileName), segment.sourceLineNumber, segment.sourceColumnNumber)
JsLocationWithEmbeddedSource(location, null) { sourceMap.sourceContentResolver(segment.sourceFileName) }
}
else {
@@ -439,7 +439,7 @@ abstract class BasicBoxTest(
generatedProgram.accept(AmbiguousAstSourcePropagation())
val output = TextOutputImpl()
val pathResolver = SourceFilePathResolver(mutableListOf(File(".")))
val pathResolver = SourceFilePathResolver(mutableListOf(File(".")), null)
val sourceMapBuilder = SourceMap3Builder(outputFile, output, "")
generatedProgram.accept(JsToStringGenerationVisitor(output, SourceMapBuilderConsumer(sourceMapBuilder, pathResolver, false, false)))
val code = output.toString()
@@ -128,8 +128,7 @@ public final class K2JSTranslator {
ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor();
Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics();
List<File> sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList());
SourceFilePathResolver pathResolver = new SourceFilePathResolver(sourceRoots);
SourceFilePathResolver pathResolver = SourceFilePathResolver.create(config);
AstGenerationResult translationResult = Translation.generateAst(
bindingTrace, units, mainCallParameters, moduleDescriptor, config, pathResolver);
@@ -155,7 +154,6 @@ public final class K2JSTranslator {
ExpandIsCallsKt.expandIsCalls(newFragments);
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
JsAstSerializer serializer = new JsAstSerializer(file -> {
try {
return pathResolver.getPathRelativeToSourceRoots(file);
@@ -65,9 +65,8 @@ abstract class TranslationResult protected constructor(val diagnostics: Diagnost
val sourceMapBuilder = SourceMap3Builder(outputFile, output, config.sourceMapPrefix)
val sourceMapBuilderConsumer =
if (config.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
val sourceRoots = config.sourceMapRoots.map { File(it) }
val sourceMapContentEmbedding = config.sourceMapContentEmbedding
val pathResolver = SourceFilePathResolver(sourceRoots)
val pathResolver = SourceFilePathResolver.create(config)
SourceMapBuilderConsumer(
sourceMapBuilder,
pathResolver,
@@ -18,23 +18,33 @@ package org.jetbrains.kotlin.js.sourceMap;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.config.JSConfigurationKeys;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.inline.util.RelativePathCalculator;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class SourceFilePathResolver {
@NotNull
private final Set<File> sourceRoots;
@Nullable
private final RelativePathCalculator outputDirPathResolver;
@NotNull
private final Map<File, String> cache = new HashMap<>();
public SourceFilePathResolver(@NotNull List<File> sourceRoots) {
public SourceFilePathResolver(@NotNull List<File> sourceRoots, @Nullable File outputDir) {
this.sourceRoots = new HashSet<>();
for (File sourceRoot : sourceRoots) {
this.sourceRoots.add(sourceRoot.getAbsoluteFile());
}
outputDirPathResolver = outputDir != null ? new RelativePathCalculator(outputDir) : null;
}
@NotNull
@@ -47,7 +57,11 @@ public class SourceFilePathResolver {
return path;
}
@NotNull
private String calculatePathRelativeToSourceRoots(@NotNull File file) throws IOException {
String pathRelativeToOutput = calculatePathRelativeToOutput(file);
if (pathRelativeToOutput != null) return pathRelativeToOutput;
List<String> parts = new ArrayList<>();
File currentFile = file.getCanonicalFile();
@@ -64,4 +78,19 @@ public class SourceFilePathResolver {
}
return file.getName();
}
@Nullable
private String calculatePathRelativeToOutput(@NotNull File file) {
return outputDirPathResolver != null ? outputDirPathResolver.calculateRelativePathTo(file) : null;
}
@NotNull
public static SourceFilePathResolver create(@NotNull JsConfig config) {
List<File> sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList());
File outputDir = null;
if (config.shouldGenerateRelativePathsInSourceMap()) {
outputDir = config.getConfiguration().get(JSConfigurationKeys.OUTPUT_DIR);
}
return new SourceFilePathResolver(sourceRoots, outputDir);
}
}
@@ -195,7 +195,7 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
assertFileExists(mapFilePath)
val map = fileInWorkingDir(mapFilePath).readText()
val sourceFilePath = "prefixprefix/example/Dummy.kt"
val sourceFilePath = "prefixprefix/src/main/kotlin/example/Dummy.kt"
assertTrue("Source map should contain reference to $sourceFilePath") { map.contains("\"$sourceFilePath\"") }
}
}
@@ -211,8 +211,8 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
assertFileExists(mapFilePath)
val map = fileInWorkingDir(mapFilePath).readText()
assertTrue("Source map should contain reference to main.kt") { map.contains("\"main.kt\"") }
assertTrue("Source map should contain reference to foo.kt") { map.contains("\"foo.kt\"") }
assertTrue("Source map should contain reference to main.kt") { map.contains("\"./src/main/kotlin/main.kt\"") }
assertTrue("Source map should contain reference to foo.kt") { map.contains("\"./src/main/kotlin/foo.kt\"") }
assertTrue("Source map should contain source of main.kt") { map.contains("\"fun main(args: Array<String>) {\\n") }
assertTrue("Source map should contain source of foo.kt") { map.contains("\"inline fun foo(): String {\\n") }
}
@@ -24,6 +24,7 @@ allprojects {
compileKotlin2Js {
kotlinOptions.freeCompilerArgs = [ "-Xskip-metadata-version-check" ]
kotlinOptions.sourceMap = true
kotlinOptions.sourceMapPrefix = "./"
kotlinOptions.sourceMapEmbedSources = "always"
}
}
@@ -461,11 +461,9 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
args.friendModules = friendDependency
args.sourceMapBaseDirs = source.orEmpty()
.asSequence()
.filterIsInstance<SourceDirectorySet>()
.flatMap { it.srcDirs.asSequence() }
.joinToString(File.pathSeparator) { it.absolutePath }
if (args.sourceMapBaseDirs == null && !args.sourceMapPrefix.isNullOrEmpty()) {
args.sourceMapBaseDirs = project.projectDir.absolutePath
}
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")