From 6e55ea6480e55933c4f386ba3f81e48440b94f41 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Tue, 17 Mar 2015 15:54:48 +0300 Subject: [PATCH] Skip smap generation if there is no source information #KT-7022 Fixed --- .../jetbrains/kotlin/codegen/inline/SMAP.kt | 42 ++++++++--- .../kotlin/codegen/inline/SMAPParser.kt | 18 +++-- .../inlineFunWithoutDebugInfo/source.kt | 13 ++++ .../inlineFunWithoutDebugInfo/sourceInline.kt | 9 +++ ...ompileKotlinAgainstCustomBinariesTest.java | 72 +++++++++++++++++-- 5 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/source.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/sourceInline.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt index 68f679d1ebb..4a5783d2df7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt @@ -35,12 +35,17 @@ public class SMAPBuilder(val source: String, val header = "SMAP\n$source\nKotlin\n*S Kotlin" fun build(): String? { - if (fileMappings.isEmpty()) { + var realMappings = fileMappings.filter { + val mappings = it.lineMappings + mappings.isNotEmpty() && mappings.first() != RangeMapping.SKIP + } + + if (realMappings.isEmpty()) { return null; } - val fileIds = "*F" + fileMappings.mapIndexed {(id, file) -> "\n${file.toSMAPFile(id + 1)}" }.join("") - val lineMappings = "*L" + fileMappings.map { it.toSMAPMapping() }.join("") + val fileIds = "*F" + realMappings.mapIndexed {(id, file) -> "\n${file.toSMAPFile(id + 1)}" }.join("") + val lineMappings = "*L" + realMappings.map { it.toSMAPMapping() }.join("") return "$header\n$fileIds\n$lineMappings\n*E\n" } @@ -212,6 +217,10 @@ public open class DefaultSourceMapper(val sourceInfo: SourceInfo, override val p } override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) { + if (lineNumber < 0) { + //no source information, so just skip this linenumber + return + } val mappedLineIndex = createMapping(lineNumber) iv.visitLineNumber(mappedLineIndex, start) } @@ -305,7 +314,7 @@ class RawFileMapping(val name: String, val path: String) { } } -public class FileMapping(val name: String, val path: String) { +open public class FileMapping(val name: String, val path: String) { val lineMappings = arrayListOf() var id = -1; @@ -314,18 +323,24 @@ public class FileMapping(val name: String, val path: String) { lineMappings.add(lineMapping) lineMapping.parent = this } + + public object SKIP : FileMapping("no-source-info", "no-source-info") { + init { + addRangeMapping(RangeMapping.SKIP) + } + } } //TODO comparable -data public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) { +data open public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) { var parent: FileMapping? = null; - fun contains(destLine: Int): Boolean { + open fun contains(destLine: Int): Boolean { return dest <= destLine && destLine < dest + range } - fun map(destLine: Int): Int { + open fun map(destLine: Int): Int { return source + (destLine - dest) } @@ -342,4 +357,15 @@ data public class RangeMapping(val source: Int, val dest: Int, var range: Int = } } } -} \ No newline at end of file + + public object SKIP : RangeMapping(-1, -1, 1) { + override fun contains(destLine: Int): Boolean { + return true + } + + override fun map(destLine: Int): Int { + return -1 + } + } +} + diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt index 4ef1ac619ee..9005af1de40 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt @@ -16,17 +16,25 @@ package org.jetbrains.kotlin.codegen.inline +import com.intellij.util.SmartFMap import kotlin.platform.platformStatic object SMAPParser { [platformStatic] - public fun parseOrCreateDefault(mappingInfo: String?, source: String, path: String, methodStartLine: Int, methodEndLine: Int): SMAP { + /*null smap means that there is no any debug info in file (e.g. sourceName)*/ + public fun parseOrCreateDefault(mappingInfo: String?, source: String?, path: String, methodStartLine: Int, methodEndLine: Int): SMAP { if (mappingInfo == null || mappingInfo.isEmpty()) { - val fm = FileMapping(source, path) - if (methodStartLine <= methodEndLine) { - //one to one - fm.addRangeMapping(RangeMapping(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)) + val fm: FileMapping + if (source == null || source.isEmpty()) { + fm = FileMapping.SKIP + } + else { + fm = FileMapping(source, path) + if (methodStartLine <= methodEndLine) { + //one to one + fm.addRangeMapping(RangeMapping(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)) + } } return SMAP(listOf(fm)) } diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/source.kt new file mode 100644 index 00000000000..ea7c8fd526d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/source.kt @@ -0,0 +1,13 @@ +package test + +public class B { + + public fun test(): String { + var p = "fail" + A().test { + p = "OK" + } + return p + } + +} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/sourceInline.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/sourceInline.kt new file mode 100644 index 00000000000..7e738e1c9ed --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/inlineFunWithoutDebugInfo/sourceInline.kt @@ -0,0 +1,9 @@ +package test + +public class A { + + inline public fun test(s: () -> Unit) { + + } + +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java index fa21943e785..21b94681b0a 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.jvm.compiler; import com.google.common.collect.Iterables; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.analyzer.AnalysisResult; @@ -38,11 +39,12 @@ import org.jetbrains.kotlin.test.*; import org.jetbrains.kotlin.test.util.DescriptorValidator; import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator; import org.jetbrains.kotlin.utils.UtilsPackage; +import org.jetbrains.org.objectweb.asm.ClassReader; +import org.jetbrains.org.objectweb.asm.ClassVisitor; +import org.jetbrains.org.objectweb.asm.ClassWriter; +import org.jetbrains.org.objectweb.asm.Opcodes; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -210,4 +212,66 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir { JetTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), output); } + + /*test source mapping generation when source info is absent*/ + public void testInlineFunWithoutDebugInfo() throws Exception { + File inlineSource = new File(getTestDataDirectory(), "sourceInline.kt"); + + CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList( + inlineSource.getPath(), + "-d", tmpdir.getPath() + )); + + File inlineFunClass = new File(tmpdir.getAbsolutePath(), "test/A.class"); + ClassReader reader = new ClassReader(new FileInputStream(inlineFunClass)); + ClassWriter cw = new ClassWriter(Opcodes.ASM5); + reader.accept(new ClassVisitor(Opcodes.ASM5, cw) { + @Override + public void visitSource(String source, String debug) { + //skip debug info + } + }, 0); + + assert inlineFunClass.delete(); + assert !inlineFunClass.exists(); + + FileOutputStream stream = new FileOutputStream(inlineFunClass); + try { + stream.write(cw.toByteArray()); + } + finally { + stream.close(); + } + + File resultSource = new File(getTestDataDirectory(), "source.kt"); + CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList( + resultSource.getPath(), + "-classpath", tmpdir.getPath(), + "-d", tmpdir.getPath() + )); + + File resultFile = new File(tmpdir.getAbsolutePath(), "test/B.class"); + reader = new ClassReader(new FileInputStream(resultFile)); + final Ref debugInfo = new Ref(); + reader.accept(new ClassVisitor(Opcodes.ASM5) { + @Override + public void visitSource(String source, String debug) { + //skip debug info + debugInfo.set(debug); + } + }, 0); + + String expected = "SMAP\n" + + "source.kt\n" + + "Kotlin\n" + + "*S Kotlin\n" + + "*F\n" + + "+ 1 source.kt\n" + + "test/B\n" + + "*L\n" + + "1#1,13:1\n" + + "*E\n"; + + assertEquals(expected, debugInfo.get()); + } }