Fix breakpoints in inline functions after dexing (KT-12896)

#KT-12896 In Progress
This commit is contained in:
Nikolay Krasko
2016-07-21 15:37:17 +03:00
parent 64979ae190
commit 5df7358dab
24 changed files with 289 additions and 27 deletions
@@ -744,13 +744,13 @@ fun main(args: Array<String>) {
}
testClass<AbstractKotlinSteppingTest>() {
model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", testMethod = "doStepIntoTest", testClassName = "StepInto")
model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", testMethod = "doSmartStepIntoTest", testClassName = "SmartStepInto")
model("debugger/tinyApp/src/stepping/stepInto", testMethod = "doStepIntoTest", testClassName = "StepIntoOnly")
model("debugger/tinyApp/src/stepping/stepOut", testMethod = "doStepOutTest")
model("debugger/tinyApp/src/stepping/stepOver", testMethod = "doStepOverTest")
model("debugger/tinyApp/src/stepping/filters", testMethod = "doStepIntoTest")
model("debugger/tinyApp/src/stepping/custom", testMethod = "doCustomTest")
model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest", testClassName = "StepInto")
model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doSmartStepIntoTest", testClassName = "SmartStepInto")
model("debugger/tinyApp/src/stepping/stepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest", testClassName = "StepIntoOnly")
model("debugger/tinyApp/src/stepping/stepOut", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOutTest")
model("debugger/tinyApp/src/stepping/stepOver", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOverTest")
model("debugger/tinyApp/src/stepping/filters", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest")
model("debugger/tinyApp/src/stepping/custom", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doCustomTest")
}
testClass<AbstractKotlinEvaluateExpressionTest>() {
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil.findFilesWithExactPackage
import org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompositeBindingContext
@@ -46,11 +47,13 @@ object DebuggerUtils {
scope: GlobalSearchScope,
className: JvmClassName,
fileName: String): KtFile? {
return findSourceFileForClass(
project,
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
className,
fileName)
return runReadAction {
findSourceFileForClass(
project,
listOf(scope, KotlinSourceFilterScope.librarySources(GlobalSearchScope.allScope(project), project)),
className,
fileName)
}
}
fun findSourceFileForClass(
@@ -256,12 +256,24 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
throw NoDataException.INSTANCE
}
try {
if (myDebugProcess.isDexDebug()) {
val inlineLocations = noStrataLocationsOfLineForInlineFunctions(type, position, myDebugProcess.searchScope)
if (!inlineLocations.isEmpty()) {
return inlineLocations
}
}
val line = position.line + 1
val locations = if (myDebugProcess.virtualMachineProxy.versionHigher("1.4"))
type.locationsOfLine(KOTLIN_STRATA_NAME, null, line).filter { it.sourceName(KOTLIN_STRATA_NAME) == position.file.name }
else
type.locationsOfLine(line)
if (locations == null || locations.isEmpty()) throw NoDataException.INSTANCE
if (locations == null || locations.isEmpty()) {
throw NoDataException.INSTANCE
}
return locations
}
catch (e: AbsentInformationException) {
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2016 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.idea.debugger
import com.intellij.debugger.SourcePosition
import com.intellij.psi.search.GlobalSearchScope
import com.sun.jdi.Location
import com.sun.jdi.ReferenceType
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.psiUtil.parents
fun noStrataLocationsOfLineForInlineFunctions(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List<Location> {
val line = position.line
val file = position.file
val project = position.file.project
val lineStartOffset = file.getLineStartOffset(line) ?: return listOf()
val element = file.findElementAt(lineStartOffset) ?: return listOf()
val isInInline = runReadAction { element.parents.any { it is KtFunction && it.hasModifier(KtTokens.INLINE_KEYWORD) } }
if (!isInInline) return listOf()
val lines = inlinedLinesNumbers(line + 1, position.file.name, FqName(type.name()), type.sourceName(), project, sourceSearchScope)
val inlineLocations = lines.flatMap { type.locationsOfLine(it) }
return inlineLocations
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.debugger
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.compiler.CompilerPaths
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
@@ -53,6 +54,27 @@ fun inlineLineAndFileByPosition(lineNumber: Int, fqName: FqName, fileName: Strin
return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind.EXECUTED_LINE, searchScope)
}
internal fun inlinedLinesNumbers(
inlineLineNumber: Int, inlineFileName: String,
destinationTypeFqName: FqName, destinationFileName: String,
project: Project, sourceSearchScope: GlobalSearchScope): List<Int> {
val internalName = destinationTypeFqName.asString().replace('.', '/')
val jvmClassName = JvmClassName.byInternalName(internalName)
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, sourceSearchScope, jvmClassName, destinationFileName) ?:
return listOf()
val virtualFile = file.virtualFile ?: return listOf()
val bytes = readClassFile(project, jvmClassName, virtualFile) ?: return listOf()
val smapData = readDebugInfo(bytes) ?: return listOf()
val smap = smapData.kotlinStrata ?: return listOf()
val mappingToInlinedFile = smap.fileMappings.firstOrNull() { it.name == inlineFileName } ?: return listOf()
return mappingToInlinedFile.lineMappings.map { it.mapSourceToDest(inlineLineNumber) }
}
fun isInlineFunctionLineNumber(file: VirtualFile, lineNumber: Int, project: Project): Boolean {
val linesInFile = file.toPsiFile(project)?.getLineCount() ?: return false
return lineNumber > linesInFile
@@ -79,9 +101,9 @@ fun readClassFile(project: Project,
val outputDir = CompilerPaths.getModuleOutputDirectory(module, /*forTests = */ false) ?: return null
val className = fqNameWithInners.asString().replace('.', '$')
val classByByDirectory = findClassFileByPath(jvmName.packageFqName.asString(), className, outputDir) ?: return null
val classByDirectory = findClassFileByPath(jvmName.packageFqName.asString(), className, outputDir) ?: return null
return classByByDirectory.readBytes()
return classByDirectory.readBytes()
}
else -> return null
@@ -94,6 +116,13 @@ private fun findClassFileByPath(packageName: String, className: String, outputDi
val parentDirectory = File(outDirFile, packageName.replace(".", File.separator))
if (!parentDirectory.exists()) return null
if (ApplicationManager.getApplication().isUnitTestMode) {
val beforeDexFileClassFile = File(parentDirectory, className + ".class.before_dex")
if (beforeDexFileClassFile.exists()) {
return beforeDexFileClassFile
}
}
val classFile = File(parentDirectory, className + ".class")
if (classFile.exists()) {
return classFile
@@ -0,0 +1,7 @@
LineBreakpoint created at dexManyFilesWithInlineCalls1.First.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! dexManyFilesWithInlineCalls1.DexManyFilesWithInlineCalls1Kt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
dexManyFilesWithInlineCalls1.First.kt:5
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,7 @@
LineBreakpoint created at dexManyFilesWithInlineCalls2.Second.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! dexManyFilesWithInlineCalls2.DexManyFilesWithInlineCalls2Kt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
dexManyFilesWithInlineCalls2.Second.kt:5
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,10 @@
LineBreakpoint created at dexSeveralInlineCallsFromOtherFile.Other.kt:6
LineBreakpoint created at dexSeveralInlineCallsFromOtherFile.kt:5
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! dexSeveralInlineCallsFromOtherFile.DexSeveralInlineCallsFromOtherFileKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
dexSeveralInlineCallsFromOtherFile.kt:5
dexSeveralInlineCallsFromOtherFile.Other.kt:6
dexSeveralInlineCallsFromOtherFile.Other.kt:6
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at dexStopInInlineFun.kt:10
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! dexStopInInlineFun.DexStopInInlineFunKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
dexStopInInlineFun.kt:10
dexStopInInlineFun.kt:11
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
LineBreakpoint created at dexStopInInlineInOtherFile.Other.kt:6
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! dexStopInInlineInOtherFile.DexStopInInlineInOtherFileKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
dexStopInInlineInOtherFile.Other.kt:6
dexStopInInlineInOtherFile.Other.kt:7
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,6 @@
package dexManyFilesWithInlineCalls1.first
inline fun firstInline() {
// Breakpoint 1
1 + 1
}
@@ -0,0 +1,5 @@
package dexManyFilesWithInlineCalls1.second
inline fun secondInline() {
1 + 1
}
@@ -0,0 +1,14 @@
package dexManyFilesWithInlineCalls1
import dexManyFilesWithInlineCalls1.first.*
import dexManyFilesWithInlineCalls1.second.*
fun main(args: Array<String>) {
firstInline()
}
fun unused() {
secondInline()
}
// ADDITIONAL_BREAKPOINT: dexManyFilesWithInlineCalls1.First.kt: Breakpoint 1
@@ -0,0 +1,5 @@
package dexManyFilesWithInlineCalls2.first
inline fun firstInline() {
1 + 1
}
@@ -0,0 +1,6 @@
package dexManyFilesWithInlineCalls2.second
inline fun secondInline() {
// Breakpoint 1
1 + 1
}
@@ -0,0 +1,14 @@
package dexManyFilesWithInlineCalls2
import dexManyFilesWithInlineCalls2.first.*
import dexManyFilesWithInlineCalls2.second.*
fun main(args: Array<String>) {
secondInline()
}
fun unused() {
firstInline()
}
// ADDITIONAL_BREAKPOINT: dexManyFilesWithInlineCalls2.Second.kt: Breakpoint 1
@@ -0,0 +1,8 @@
package dexSeveralInlineCallsFromOtherFile
inline fun inlineFun() {
var i = 1
// Breakpoint 1
i++
i++
}
@@ -0,0 +1,18 @@
package dexSeveralInlineCallsFromOtherFile
fun main(args: Array<String>) {
//Breakpoint!
firstCall()
secondCall()
}
fun firstCall() {
inlineFun()
}
fun secondCall() {
inlineFun()
}
// RESUME: 2
// ADDITIONAL_BREAKPOINT: dexSeveralInlineCallsFromOtherFile.Other.kt: Breakpoint 1
@@ -0,0 +1,12 @@
package dexStopInInlineFun
fun main(args: Array<String>) {
inlineFun()
}
inline fun inlineFun() {
var i = 1
//Breakpoint!
i++
i++
}
@@ -0,0 +1,8 @@
package dexStopInInlineInOtherFile
inline fun inlineFun() {
var i = 1
// Breakpoint 1
i++
i++
}
@@ -0,0 +1,7 @@
package dexStopInInlineInOtherFile
fun main(args: Array<String>) {
inlineFun()
}
// ADDITIONAL_BREAKPOINT: dexStopInInlineInOtherFile.Other.kt: Breakpoint 1
@@ -52,7 +52,7 @@ abstract class AbstractKotlinSteppingTest : KotlinDebuggerTestBase() {
val fileText = FileUtil.loadFile(File(path))
configureSettings(fileText)
createAdditionalBreakpoints(fileText)
createDebugProcess(path)
val prefix = "// $command: "
@@ -34,7 +34,7 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class StepInto extends AbstractKotlinSteppingTest {
public void testAllFilesPresentInStepInto() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("classObjectFunFromClass.kt")
@@ -121,7 +121,7 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class SmartStepInto extends AbstractKotlinSteppingTest {
public void testAllFilesPresentInSmartStepInto() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("classObjectFunFromClass.kt")
@@ -214,7 +214,7 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
}
public void testAllFilesPresentInStepIntoOnly() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepInto"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepInto"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("continueLabel.kt")
@@ -313,7 +313,7 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class StepOut extends AbstractKotlinSteppingTest {
public void testAllFilesPresentInStepOut() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepOut"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepOut"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("fwBackingField.kt")
@@ -370,7 +370,19 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class StepOver extends AbstractKotlinSteppingTest {
public void testAllFilesPresentInStepOver() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepOver"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepOver"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("dexStopInInlineFun.kt")
public void testDexStopInInlineFun() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/dexStopInInlineFun.kt");
doStepOverTest(fileName);
}
@TestMetadata("dexStopInInlineInOtherFile.kt")
public void testDexStopInInlineInOtherFile() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/dexStopInInlineInOtherFile.kt");
doStepOverTest(fileName);
}
@TestMetadata("ifCapturedVariableKt9118.kt")
@@ -475,7 +487,7 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class Filters extends AbstractKotlinSteppingTest {
public void testAllFilesPresentInFilters() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/filters"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/filters"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("checkNotNull.kt")
@@ -556,7 +568,7 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
@RunWith(JUnit3RunnerWithInners.class)
public static class Custom extends AbstractKotlinSteppingTest {
public void testAllFilesPresentInCustom() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/custom"), Pattern.compile("^(.+)\\.kt$"), true);
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/custom"), Pattern.compile("^([^.]+)\\.kt$"), true);
}
@TestMetadata("crossinlineLiteral.kt")
@@ -565,6 +577,24 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
doCustomTest(fileName);
}
@TestMetadata("dexManyFilesWithInlineCalls1.kt")
public void testDexManyFilesWithInlineCalls1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/dexManyFilesWithInlineCalls1.kt");
doCustomTest(fileName);
}
@TestMetadata("dexManyFilesWithInlineCalls2.kt")
public void testDexManyFilesWithInlineCalls2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/dexManyFilesWithInlineCalls2.kt");
doCustomTest(fileName);
}
@TestMetadata("dexSeveralInlineCallsFromOtherFile.kt")
public void testDexSeveralInlineCallsFromOtherFile() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/dexSeveralInlineCallsFromOtherFile.kt");
doCustomTest(fileName);
}
@TestMetadata("funLiteral.kt")
public void testFunLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/funLiteral.kt");
@@ -22,16 +22,16 @@ import org.jetbrains.org.objectweb.asm.ClassWriter
import org.jetbrains.org.objectweb.asm.Opcodes
import java.io.File
val DEX_BEFORE_PATCH_EXTENSION = "before_dex"
fun patchDexTests(dir: File) {
dir.listFiles({ file -> file.isDirectory && file.name.startsWith("dex") }).forEach { dir ->
dir.listFiles { testOutputFile -> testOutputFile.extension == "class" }.forEach { classFile ->
applyDexLikePatch(classFile)
}
dir.listFiles { testOutputFile -> testOutputFile.extension == "class" }.forEach(::applyDexLikePatch)
}
}
private fun applyDexLikePatch(file: File) {
file.copyTo(File(file.absolutePath + ".temp"))
file.copyTo(File(file.absolutePath + ".$DEX_BEFORE_PATCH_EXTENSION"))
val reader = ClassReader(file.readBytes())
val writer = ClassWriter(ClassWriter.COMPUTE_FRAMES)