diff --git a/jadx-cli/build.gradle.kts b/jadx-cli/build.gradle.kts index 68b2b56eb8d..7aeac96cd99 100644 --- a/jadx-cli/build.gradle.kts +++ b/jadx-cli/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { runtimeOnly(project(":jadx-plugins:jadx-smali-input")) runtimeOnly(project(":jadx-plugins:jadx-rename-mappings")) runtimeOnly(project(":jadx-plugins:jadx-kotlin-metadata")) + runtimeOnly(project(":jadx-plugins:jadx-kotlin-source-debug-extension")) runtimeOnly(project(":jadx-plugins:jadx-script:jadx-script-plugin")) runtimeOnly(project(":jadx-plugins:jadx-xapk-input")) runtimeOnly(project(":jadx-plugins:jadx-aab-input")) diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/build.gradle.kts b/jadx-plugins/jadx-kotlin-source-debug-extension/build.gradle.kts new file mode 100644 index 00000000000..f29faa010eb --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("jadx-library") + id("jadx-kotlin") +} + +dependencies { + api(project(":jadx-core")) + + testImplementation(project.project(":jadx-core").sourceSets.getByName("test").output) + testImplementation("org.apache.commons:commons-lang3:3.17.0") + + testRuntimeOnly(project(":jadx-plugins:jadx-smali-input")) +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapOptions.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapOptions.kt new file mode 100644 index 00000000000..66fb8d846aa --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapOptions.kt @@ -0,0 +1,24 @@ +package jadx.plugins.kotlin.smap + +import jadx.api.plugins.options.impl.BasePluginOptionsBuilder +import jadx.plugins.kotlin.smap.KotlinSmapPlugin.Companion.PLUGIN_ID + +class KotlinSmapOptions : BasePluginOptionsBuilder() { + var isClassAliasSourceDbg: Boolean = true + private set + + override fun registerOptions() { + boolOption(CLASS_ALIAS_SOURCE_DBG_OPT) + .description("rename class alias from SourceDebugExtension") + .defaultValue(false) + .setter { isClassAliasSourceDbg = it } + } + + fun isClassSourceDbg(): Boolean { + return isClassAliasSourceDbg + } + + companion object { + const val CLASS_ALIAS_SOURCE_DBG_OPT = "$PLUGIN_ID.class-alias-source-dbg" + } +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapPlugin.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapPlugin.kt new file mode 100644 index 00000000000..dc0db850d3f --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/KotlinSmapPlugin.kt @@ -0,0 +1,27 @@ +package jadx.plugins.kotlin.smap + +import jadx.api.plugins.JadxPlugin +import jadx.api.plugins.JadxPluginContext +import jadx.api.plugins.JadxPluginInfo +import jadx.plugins.kotlin.smap.pass.KotlinSourceDebugExtensionPass + +class KotlinSmapPlugin : JadxPlugin { + + private val options = KotlinSmapOptions() + + override fun getPluginInfo(): JadxPluginInfo { + return JadxPluginInfo(PLUGIN_ID, "Kotlin SMAP", "Use kotlin.SourceDebugExtension annotation for rename class alias") + } + + override fun init(context: JadxPluginContext) { + context.registerOptions(options) + + if (options.isClassSourceDbg()) { + context.addPass(KotlinSourceDebugExtensionPass(options)) + } + } + + companion object { + const val PLUGIN_ID = "kotlin-smap" + } +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/ClassAliasRename.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/ClassAliasRename.kt new file mode 100644 index 00000000000..11ad2f6b5de --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/ClassAliasRename.kt @@ -0,0 +1,6 @@ +package jadx.plugins.kotlin.smap.model + +data class ClassAliasRename( + val pkg: String, + val name: String, +) diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/Constants.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/Constants.kt new file mode 100644 index 00000000000..798bc95d7a4 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/Constants.kt @@ -0,0 +1,5 @@ +package jadx.plugins.kotlin.smap.model + +object Constants { + const val KOTLIN_SOURCE_DEBUG_EXTENSION = "Lkotlin/jvm/internal/SourceDebugExtension;" +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SMAP.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SMAP.kt new file mode 100644 index 00000000000..8f0296292d7 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SMAP.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2024 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 jadx.plugins.kotlin.smap.model + +import kotlin.math.max + +const val KOTLIN_STRATA_NAME = "Kotlin" +const val KOTLIN_DEBUG_STRATA_NAME = "KotlinDebug" + +/** + * Represents SMAP as a structure that is contained in `SourceDebugExtension` attribute of a class. + * This structure is immutable, we can only query for a result. + */ +class SMAP(val fileMappings: List) { + // assuming disjoint line mappings (otherwise binary search can't be used anyway) + private val intervals = fileMappings.flatMap { it.lineMappings }.sortedBy { it.dest } + + fun findRange(lineNumber: Int): RangeMapping? { + val index = intervals.binarySearch { if (lineNumber in it) 0 else it.dest - lineNumber } + return if (index < 0) null else intervals[index] + } + + companion object { + const val FILE_SECTION = "*F" + const val LINE_SECTION = "*L" + const val STRATA_SECTION = "*S" + const val END = "*E" + } +} + +class FileMapping(val name: String, val path: String) { + val lineMappings = arrayListOf() + + fun toSourceInfo(): SourceInfo = + SourceInfo( + name, + path, + lineMappings.fold(0) { result, mapping -> max(result, mapping.source + mapping.range - 1) }, + ) + + fun mapNewLineNumber(source: Int, currentIndex: Int, callSite: SourcePosition?): Int { + // Save some space in the SMAP by reusing (or extending if it's the last one) the existing range. + // TODO some *other* range may already cover `source`; probably too slow to check them all though. + // Maybe keep the list ordered by `source` and use binary search to locate the closest range on the left? + val mapping = lineMappings.lastOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSite) } + ?: lineMappings.firstOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSite) } + ?: mapNewInterval(source, currentIndex + 1, 1, callSite) + mapping.range = max(mapping.range, source - mapping.source + 1) + return mapping.mapSourceToDest(source) + } + + private fun RangeMapping.canReuseFor(newSource: Int, globalMaxDest: Int, newCallSite: SourcePosition?): Boolean = + callSite == newCallSite && (newSource - source) in 0 until range + (if (globalMaxDest in this) 10 else 0) + + fun mapNewInterval(source: Int, dest: Int, range: Int, callSite: SourcePosition? = null): RangeMapping = + RangeMapping(source, dest, range, callSite, parent = this).also { lineMappings.add(it) } +} + +data class RangeMapping(val source: Int, val dest: Int, var range: Int, val callSite: SourcePosition?, val parent: FileMapping) { + operator fun contains(destLine: Int): Boolean = + dest <= destLine && destLine < dest + range + + fun hasMappingForSource(sourceLine: Int): Boolean = + source <= sourceLine && sourceLine < source + range + + fun mapDestToSource(destLine: Int): SourcePosition = + SourcePosition(source + (destLine - dest), parent.name, parent.path) + + fun mapSourceToDest(sourceLine: Int): Int = + dest + (sourceLine - source) +} + +val RangeMapping.toRange: IntRange + get() = dest until dest + range + +data class SourcePosition(val line: Int, val file: String, val path: String) diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SourceInfo.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SourceInfo.kt new file mode 100644 index 00000000000..224f0b0a69f --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/model/SourceInfo.kt @@ -0,0 +1,7 @@ +package jadx.plugins.kotlin.smap.model + +data class SourceInfo( + val sourceFileName: String?, + val pathOrCleanFQN: String, + val linesInFile: Int, +) diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/pass/KotlinSourceDebugExtensionPass.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/pass/KotlinSourceDebugExtensionPass.kt new file mode 100644 index 00000000000..95a9e75f657 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/pass/KotlinSourceDebugExtensionPass.kt @@ -0,0 +1,39 @@ +package jadx.plugins.kotlin.smap.pass + +import jadx.api.plugins.pass.JadxPassInfo +import jadx.api.plugins.pass.impl.OrderedJadxPassInfo +import jadx.api.plugins.pass.types.JadxPreparePass +import jadx.core.dex.attributes.AFlag +import jadx.core.dex.nodes.RootNode +import jadx.plugins.kotlin.smap.KotlinSmapOptions +import jadx.plugins.kotlin.smap.utils.KotlinSmapUtils + +class KotlinSourceDebugExtensionPass( + private val options: KotlinSmapOptions, +) : JadxPreparePass { + + override fun getInfo(): JadxPassInfo { + return OrderedJadxPassInfo( + "SourceDebugExtensionPrepare", + "Use kotlin.jvm.internal.SourceDebugExtension annotation to rename class & package", + ) + .before("RenameVisitor") + } + + override fun init(root: RootNode) { + if (options.isClassAliasSourceDbg) { + for (cls in root.classes) { + if (cls.contains(AFlag.DONT_RENAME)) { + continue + } + + // rename class & package + val kotlinCls = KotlinSmapUtils.getClassAlias(cls) + if (kotlinCls != null) { + cls.rename(kotlinCls.name) + cls.packageNode.rename(kotlinCls.pkg) + } + } + } + } +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/Extensions.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/Extensions.kt new file mode 100644 index 00000000000..65945040102 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/Extensions.kt @@ -0,0 +1,24 @@ +@file:Suppress("UNCHECKED_CAST") + +package jadx.plugins.kotlin.smap.utils + +import jadx.api.plugins.input.data.annotations.EncodedType +import jadx.api.plugins.input.data.annotations.EncodedValue +import jadx.api.plugins.input.data.annotations.IAnnotation +import jadx.core.dex.nodes.ClassNode +import jadx.plugins.kotlin.smap.model.Constants +import jadx.plugins.kotlin.smap.model.SMAP + +fun ClassNode.getSourceDebugExtension(): SMAP? { + val annotation: IAnnotation? = getAnnotation(Constants.KOTLIN_SOURCE_DEBUG_EXTENSION) + return annotation?.run { + val smapParser = SMAPParser.parseOrNull(getParamsAsList("value")?.get(0)?.value.toString()) + return smapParser + } +} + +private fun IAnnotation.getParamsAsList(paramName: String): List? { + val encodedValue = values[paramName] + ?.takeIf { it.type == EncodedType.ENCODED_ARRAY && it.value is List<*> } + return encodedValue?.value?.let { it as List } +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/KotlinSmapUtils.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/KotlinSmapUtils.kt new file mode 100644 index 00000000000..3d56c6a8829 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/KotlinSmapUtils.kt @@ -0,0 +1,72 @@ +package jadx.plugins.kotlin.smap.utils + +import jadx.core.deobf.NameMapper +import jadx.core.dex.attributes.nodes.RenameReasonAttr +import jadx.core.dex.nodes.ClassNode +import jadx.core.utils.Utils +import jadx.plugins.kotlin.smap.model.ClassAliasRename +import jadx.plugins.kotlin.smap.model.SMAP +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import kotlin.jvm.java + +object KotlinSmapUtils { + + val LOG: Logger = LoggerFactory.getLogger(KotlinSmapUtils::class.java) + + @JvmStatic + fun getClassAlias(cls: ClassNode): ClassAliasRename? { + val annotation = cls.getSourceDebugExtension() ?: return null + return getClassAlias(cls, annotation) + } + + private fun getClassAlias(cls: ClassNode, annotation: SMAP): ClassAliasRename? { + val firstValue = annotation.fileMappings[0].path.replace("/", ".") + try { + val clsName = firstValue.trim() + .takeUnless(String::isEmpty) + ?.let(Utils::cleanObjectName) + ?: return null + + val alias = splitAndCheckClsName(cls, clsName) + if (alias != null) { + RenameReasonAttr.forNode(cls).append("from SourceDebugExtension") + return alias + } + } catch (e: Exception) { + LOG.error("Failed to parse SourceDebugExtension", e) + } + return null + } + + // Don't use ClassInfo facility to not pollute class into cache + private fun splitAndCheckClsName(originCls: ClassNode, fullClsName: String): ClassAliasRename? { + if (!NameMapper.isValidFullIdentifier(fullClsName)) { + return null + } + val pkg: String + val name: String + val dot = fullClsName.lastIndexOf('.') + if (dot == -1) { + pkg = "" + name = fullClsName + } else { + pkg = fullClsName.substring(0, dot) + name = fullClsName.substring(dot + 1) + } + val originClsInfo = originCls.classInfo + val originName = originClsInfo.shortName + if (originName == name || name.contains("$") || + !NameMapper.isValidIdentifier(name) || pkg.startsWith("java.") + ) { + return null + } + val newClsNode = originCls.root().resolveClass(fullClsName) + return if (newClsNode != null) { + // class with alias name already exist + null + } else { + ClassAliasRename(pkg, name) + } + } +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/SMAPParser.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/SMAPParser.kt new file mode 100644 index 00000000000..25209dd89ed --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/kotlin/jadx/plugins/kotlin/smap/utils/SMAPParser.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2010-2015 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 jadx.plugins.kotlin.smap.utils + +import jadx.plugins.kotlin.smap.model.FileMapping +import jadx.plugins.kotlin.smap.model.KOTLIN_DEBUG_STRATA_NAME +import jadx.plugins.kotlin.smap.model.KOTLIN_STRATA_NAME +import jadx.plugins.kotlin.smap.model.SMAP + +object SMAPParser { + fun parseOrNull(mappingInfo: String): SMAP? = + if (mappingInfo.isNotEmpty()) { + parseStratum(mappingInfo, KOTLIN_STRATA_NAME, parseStratum(mappingInfo, KOTLIN_DEBUG_STRATA_NAME, null)) + } else { + null + } + + private class SMAPTokenizer(private val text: String, private val headerString: String) : Iterator { + + private var pos = 0 + private var currentLine: String? = null + + init { + advance() + while (currentLine != null && currentLine != headerString) { + advance() + } + if (currentLine == headerString) { + advance() + } + } + + private fun advance() { + if (pos >= text.length) { + currentLine = null + return + } + val fromPos = pos + while (pos < text.length && text[pos] != '\n' && text[pos] != '\r') pos++ + currentLine = text.substring(fromPos, pos) + pos++ + } + + override fun hasNext(): Boolean { + return currentLine != null + } + + override fun next(): String { + val res = currentLine ?: throw NoSuchElementException() + advance() + return res + } + } + + private fun parseStratum(mappingInfo: String, stratum: String, callSites: SMAP?): SMAP? { + val fileMappings = linkedMapOf() + val iterator = SMAPTokenizer(mappingInfo, "${SMAP.STRATA_SECTION} $stratum") + // JSR-045 allows the line section to come before the file section, but we don't generate SMAPs like this. + if (!iterator.hasNext() || iterator.next() != SMAP.FILE_SECTION) return null + + for (line in iterator) { + when { + line == SMAP.LINE_SECTION -> break + line == SMAP.FILE_SECTION || line == SMAP.END || line.startsWith(SMAP.STRATA_SECTION) -> return null + } + + val indexAndFileInternalName = if (line.startsWith("+ ")) line.substring(2) else line + val fileIndex = indexAndFileInternalName.substringBefore(' ').toInt() + val fileName = indexAndFileInternalName.substringAfter(' ') + val path = if (line.startsWith("+ ")) iterator.next() else fileName + fileMappings[fileIndex] = FileMapping(fileName, path) + } + + for (line in iterator) { + when { + line == SMAP.LINE_SECTION || line == SMAP.FILE_SECTION -> return null + line == SMAP.END || line.startsWith(SMAP.STRATA_SECTION) -> break + } + + // #,:, + val fileSeparator = line.indexOf('#') + if (fileSeparator < 0) return null + val destSeparator = line.indexOf(':', fileSeparator) + if (destSeparator < 0) return null + val sourceRangeSeparator = line.indexOf(',').let { if (it !in fileSeparator..destSeparator) destSeparator else it } + val destMultiplierSeparator = line.indexOf(',', destSeparator).let { if (it < 0) line.length else it } + + val file = fileMappings[line.substring(fileSeparator + 1, sourceRangeSeparator).toInt()] ?: return null + val source = line.substring(0, fileSeparator).toInt() + val dest = line.substring(destSeparator + 1, destMultiplierSeparator).toInt() + val range = when { + // These two fields have a different meaning, but for compatibility we treat them the same. See `SMAPBuilder`. + destMultiplierSeparator != line.length -> line.substring(destMultiplierSeparator + 1).toInt() + sourceRangeSeparator != destSeparator -> line.substring(sourceRangeSeparator + 1, destSeparator).toInt() + else -> 1 + } + // Here we assume that each range in `Kotlin` is entirely within at most one range in `KotlinDebug`. + file.mapNewInterval(source, dest, range, callSites?.findRange(dest)?.let { it.mapDestToSource(it.dest) }) + } + + return SMAP(fileMappings.values.toList()) + } +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin new file mode 100644 index 00000000000..a3232e26c37 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin @@ -0,0 +1 @@ +jadx.plugins.kotlin.smap.KotlinSmapPlugin diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/test/kotlin/TestSourceDebugExtension.kt b/jadx-plugins/jadx-kotlin-source-debug-extension/src/test/kotlin/TestSourceDebugExtension.kt new file mode 100644 index 00000000000..3aa34d8c584 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/test/kotlin/TestSourceDebugExtension.kt @@ -0,0 +1,34 @@ +package jadx.plugins.kotlin.metadata.tests + +import jadx.plugins.kotlin.smap.KotlinSmapOptions.Companion.CLASS_ALIAS_SOURCE_DBG_OPT +import jadx.tests.api.SmaliTest +import jadx.tests.api.utils.assertj.JadxAssertions.assertThat +import jadx.tests.api.utils.assertj.JadxCodeAssertions +import org.junit.jupiter.api.Test + +class TestSourceDebugExtension : SmaliTest() { + + @Test + fun testRenameClass() { + setupArgs { + this[CLASS_ALIAS_SOURCE_DBG_OPT] = true + } + assertThatClass() + .containsOne("androidx.compose.ui") + .containsOne("public final class ActualKt") + .countString(1, "reason: from SourceDebugExtension") + } + + private fun setupArgs(builder: MutableMap.() -> Unit = {}) { + val allOff = mutableMapOf( + CLASS_ALIAS_SOURCE_DBG_OPT to false, + ) + args.pluginOptions = allOff.apply(builder).mapValues { + if (it.value) "yes" else "no" + } + } + + private fun assertThatClass(): JadxCodeAssertions = + assertThat(getClassNodeFromSmaliFiles("deobf", "TestKotlinSourceDebugExtension", "C6")) + .code() +} diff --git a/jadx-plugins/jadx-kotlin-source-debug-extension/src/test/smali/deobf/TestKotlinSourceDebugExtension/C6.smali b/jadx-plugins/jadx-kotlin-source-debug-extension/src/test/smali/deobf/TestKotlinSourceDebugExtension/C6.smali new file mode 100644 index 00000000000..f04758e67c8 --- /dev/null +++ b/jadx-plugins/jadx-kotlin-source-debug-extension/src/test/smali/deobf/TestKotlinSourceDebugExtension/C6.smali @@ -0,0 +1,64 @@ +.class public final Ldeobf/C6; +.super Ljava/lang/Object; +.source "SourceFile" + + +# annotations +.annotation runtime Lkotlin/Metadata; + d1 = { + "\u0000\u000e\n\u0002\u0010\u0000\n\u0002\u0008\u0002\n\u0002\u0010\u000b\n\u0000\u001a\u0018\u0010\u0001\u001a\u00020\u00032\u0006\u0010\u0001\u001a\u00020\u00002\u0006\u0010\u0002\u001a\u00020\u0000H\u0000\u00a8\u0006\u0004" + } + d2 = { + "", + "a", + "b", + "", + "ui_release" + } + k = 0x2 + mv = { + 0x1, + 0x8, + 0x0 + } +.end annotation + +.annotation build Lkotlin/jvm/internal/SourceDebugExtension; + value = { + "SMAP\nActual.kt\nKotlin\n*S Kotlin\n*F\n+ 1 Actual.kt\nandroidx/compose/ui/ActualKt\n+ 2 _Arrays.kt\nkotlin/collections/ArraysKt___ArraysKt\n+ 3 ListUtils.kt\nandroidx/compose/ui/util/ListUtilsKt\n*L\n1#1,50:1\n6442#2:51\n33#3,6:52\n*S KotlinDebug\n*F\n+ 1 Actual.kt\nandroidx/compose/ui/ActualKt\n*L\n35#1:51\n36#1:52,6\n*E\n" + } +.end annotation + + +# direct methods +.method public static final a(Ljava/lang/Object;Ljava/lang/Object;)Z + .locals 1 + + const-string v0, "a" + + invoke-static {p0, v0}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V + + const-string v0, "b" + + invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V + + invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object p0 + + invoke-virtual {p1}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object p1 + + if-ne p0, p1, :cond_0 + + const/4 p0, 0x1 + + goto :goto_0 + + :cond_0 + const/4 p0, 0x0 + + :goto_0 + return p0 +.end method diff --git a/settings.gradle.kts b/settings.gradle.kts index 67f9946dcd6..a797dd99fa2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,6 +24,7 @@ include("jadx-plugins:jadx-smali-input") include("jadx-plugins:jadx-java-convert") include("jadx-plugins:jadx-rename-mappings") include("jadx-plugins:jadx-kotlin-metadata") +include("jadx-plugins:jadx-kotlin-source-debug-extension") include("jadx-plugins:jadx-xapk-input") include("jadx-plugins:jadx-aab-input") include("jadx-plugins:jadx-apkm-input")