begin DAP port

This commit is contained in:
FalsePattern 2024-11-07 00:10:21 +01:00
parent fc760497a9
commit f8def6e1de
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
3 changed files with 798 additions and 0 deletions

View file

@ -0,0 +1,67 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.debugger.dap
import java.io.IOException
import java.io.InterruptedIOException
import java.io.PipedInputStream
import java.io.PipedOutputStream
class BlockingPipedInputStream(src: PipedOutputStream, pipeSize: Int) : PipedInputStream(src, pipeSize) {
var closed = false
override fun read(): Int {
if (closed) {
throw IOException("stream closed")
} else {
while (super.`in` < 0) {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
this as java.lang.Object
this.notifyAll()
try {
this.wait(750)
} catch (e: InterruptedException) {
throw InterruptedIOException()
}
}
val ret = buffer[this.out++].toUInt()
if (this.out >= buffer.size) {
this.out = 0
}
if (this.`in` == this.out) {
this.`in` = -1
}
return ret.toInt()
}
}
override fun close() {
closed = true
super.close()
}
}

View file

@ -0,0 +1,470 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.debugger.dap
import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.execution.ExecutionException
import com.intellij.execution.process.BaseProcessHandler
import com.intellij.execution.process.ProcessAdapter
import com.intellij.execution.process.ProcessEvent
import com.intellij.execution.process.ProcessOutputType
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.KeyWithDefaultValue
import com.intellij.util.progress.getMaybeCancellable
import com.intellij.util.system.CpuArch
import com.jetbrains.cidr.ArchitectureType
import com.jetbrains.cidr.execution.Installer
import com.jetbrains.cidr.execution.debugger.backend.*
import com.jetbrains.cidr.execution.debugger.memory.Address
import com.jetbrains.cidr.system.HostMachine
import com.jetbrains.cidr.system.LocalHost
import io.ktor.util.*
import kotlinx.coroutines.*
import kotlinx.coroutines.future.asDeferred
import org.eclipse.lsp4j.debug.*
import org.eclipse.lsp4j.debug.services.IDebugProtocolClient
import org.eclipse.lsp4j.debug.services.IDebugProtocolServer
import org.eclipse.lsp4j.jsonrpc.MessageConsumer
import org.eclipse.lsp4j.jsonrpc.debug.DebugLauncher
import org.eclipse.lsp4j.jsonrpc.messages.Either3
import java.io.File
import java.io.PipedOutputStream
import java.lang.Exception
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors
import kotlin.io.path.pathString
abstract class DAPDriver<Server : IDebugProtocolServer, Client : IDebugProtocolClient>(
handler: Handler,
) : DebuggerDriver(handler) {
private lateinit var driverName: String
private lateinit var processHandler: BaseProcessHandler<*>
protected lateinit var client: Client
protected lateinit var server: Server
@Volatile
protected lateinit var capabilities: Capabilities
protected lateinit var initializeFuture: Job
protected abstract fun createDebuggerClient(): Client
protected abstract fun getServerInterface(): Class<Server>
protected abstract fun wrapMessageConsumer(mc: MessageConsumer): MessageConsumer
protected abstract suspend fun postInitialize(capabilities: Capabilities)
fun initialize(config: DAPDebuggerDriverConfiguration) {
driverName = config.driverName
processHandler = createDebugProcessHandler(config.createDriverCommandLine(this, ArchitectureType.forVmCpuArch(
CpuArch.CURRENT
)), config)
val pipeOutput = PipedOutputStream()
val pipeInput = BlockingPipedInputStream(pipeOutput, 1024 * 1024)
processHandler.addProcessListener(object: ProcessAdapter() {
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
if (ProcessOutputType.isStdout(outputType)) {
val text = event.text ?: return
pipeOutput.write(text.toByteArray(Charsets.UTF_8))
pipeOutput.flush()
}
}
})
client = createDebuggerClient()
val executorServer = Executors.newSingleThreadExecutor()
val launcher = DebugLauncher.createLauncher(client, getServerInterface(), pipeInput, processHandler.processInput, executorServer, this::wrapMessageConsumer)
server = launcher.remoteProxy
launcher.startListening()
val initArgs = InitializeRequestArguments()
//Identification
initArgs.clientID = "zigbrains"
initArgs.clientName = "ZigBrains"
//IntelliJ editor thing
initArgs.linesStartAt1 = true
initArgs.columnsStartAt1 = true
initArgs.supportsMemoryReferences = true
initArgs.supportsVariableType = false
config.customizeInitializeArguments(initArgs)
initializeFuture = zigCoroutineScope.launch {
val caps = server.initialize(initArgs).asDeferred().await()
capabilities = caps
postInitialize(caps)
}
}
override fun supportsWatchpointLifetime(): Boolean {
return false
}
override fun supportsMemoryWrite(): Boolean {
return capabilities.supportsWriteMemoryRequest
}
override fun writeMemory(address: Address, bytes: ByteArray) {
val args = WriteMemoryArguments()
args.memoryReference = "0x${address.unsignedLongValue.toString(16)}"
args.data = bytes.encodeBase64()
server.writeMemory(args).getSafe()
}
override fun getProcessHandler(): BaseProcessHandler<*> {
return processHandler
}
override fun isInPromptMode(): Boolean {
return false
}
override fun getHostMachine(): HostMachine {
return LocalHost.INSTANCE
}
override fun setValuesFilteringEnabled(p0: Boolean) {
}
protected inner class DAPInferior: Inferior() {
override fun startImpl(): Long {
server.configurationDone(ConfigurationDoneArguments()).getSafe()
return -1
}
override fun detachImpl() {
val args = DisconnectArguments()
server.disconnect(args).getSafe()
}
override fun destroyImpl(): Boolean {
detachImpl()
return true
}
}
override fun loadForLaunch(installer: Installer, s: String?): Inferior {
val cli = installer.install()
val args = HashMap<String, Any>()
args["program"] = Util.toWinPath(cli.exePath)
args["cmd"] = cli.workDirectory.toString()
args["name"] = "CPP Debug"
args["type"] = "cppvsdbg"
args["request"] = "launch"
args["console"] = "integratedTerminal"
args["logging"] = mapOf("moduleLoad" to true)
args["__configurationTarget"] = 2
val params = cli.parametersList.array
if (params.isNotEmpty()) {
args["args"] = params
}
server.launch(args).getSafe()
runBlocking {
initializeFuture.join()
}
return DAPInferior()
}
override fun loadCoreDump(p0: File, p1: File?, p2: File?, p3: MutableList<PathMapping>): Inferior {
notSupported()
}
override fun loadCoreDump(
p0: File,
p1: File?,
p2: File?,
p3: MutableList<PathMapping>,
p4: MutableList<String>
): Inferior {
notSupported()
}
override fun loadForAttach(p0: Int): Inferior {
notSupported()
}
override fun loadForAttach(p0: String, p1: Boolean): Inferior {
notSupported()
}
override fun loadForRemote(p0: String, p1: File?, p2: File?, p3: MutableList<PathMapping>): Inferior {
notSupported()
}
override fun interrupt(): Boolean {
val pause = PauseArguments()
pause.threadId = -1
server.pause(pause)
return true
}
override fun resume(): Boolean {
val args = ContinueArguments()
server.continue_(args)
return true
}
@Suppress("OVERRIDE_DEPRECATION")
override fun stepOver(p0: Boolean) {
deprecated()
}
@Suppress("OVERRIDE_DEPRECATION")
override fun stepInto(p0: Boolean, p1: Boolean) {
deprecated()
}
@Suppress("OVERRIDE_DEPRECATION")
override fun stepOut(p0: Boolean) {
deprecated()
}
override fun stepOver(thread: LLThread, stepByInstruction: Boolean) {
val args = NextArguments()
args.threadId = Math.toIntExact(thread.id)
args.granularity = if (stepByInstruction)
SteppingGranularity.INSTRUCTION
else
SteppingGranularity.LINE
server.next(args)
}
override fun stepInto(thread: LLThread, forceStepIntoFramesWithNoDebugInfo: Boolean, stepByInstruction: Boolean) {
val args = StepInArguments()
args.targetId = Math.toIntExact(thread.id)
args.granularity = if (stepByInstruction)
SteppingGranularity.INSTRUCTION
else
SteppingGranularity.LINE
server.stepIn(args)
}
override fun stepOut(thread: LLThread, stopInFramesWithNoDebugInfo: Boolean) {
val args = StepOutArguments()
args.threadId = Math.toIntExact(thread.id)
server.stepOut(args)
}
override fun runTo(path: String, line: Int) {
val targetArgs = GotoTargetsArguments()
val src = Util.toSource(path)
targetArgs.source = src
targetArgs.line = line
zigCoroutineScope.launch {
val locations = server.gotoTargets(targetArgs).asDeferred().await()
val targets = locations.targets.firstOrNull() ?: throw RuntimeException("Could not find runTo target!")
val args = GotoArguments()
args.targetId = targets.id
args.threadId = -1
server.goto_(args)
}
}
override fun runTo(p0: Address) {
throw UnsupportedOperationException("RunTo address not implemented!")
}
override fun doExit(): Boolean {
val disconnectArgs = DisconnectArguments()
disconnectArgs.terminateDebuggee = true
server.disconnect(disconnectArgs)
return true
}
// TODO
override fun jumpToLine(thread: LLThread, path: String, line: Int, canLeaveFunction: Boolean): StopPlace {
throw DebuggerCommandException("Can't resolve address for line $path:$line")
}
// TODO
override fun jumpToAddress(thread: LLThread, address: Address, canLeaveFunction: Boolean): StopPlace {
throw DebuggerCommandException("Can't jump to address $address")
}
override fun addPathMapping(index: Int, from: String, to: String) {
throw ExecutionException("addPathMapping not implemented!")
}
override fun addForcedFileMapping(index: Int, from: String, hash: DebuggerSourceFileHash?, to: String) {
addPathMapping(index, from, to)
}
override fun completeConsoleCommand(p0: String, p1: Int): ResultList<String> {
throw ExecutionException("completeConsoleCommand")
}
override fun addWatchpoint(
threadId: Long,
frameIndex: Int,
value: LLValue,
expr: String,
lifetime: LLWatchpoint.Lifetime?,
accessType: LLWatchpoint.AccessType
): LLWatchpoint {
throw ExecutionException("TODO")
}
override fun removeWatchpoint(ids: MutableList<Int>) {
throw ExecutionException("TODO")
}
data class PathedSourceBreakpoint(
val path: String,
val src: SourceBreakpoint
)
data class MappedBreakpoint(
val id: Int,
val java: LLBreakpoint,
val loc: LLBreakpointLocation?,
val dap: Breakpoint,
val ref: Either3<PathedSourceBreakpoint, FunctionBreakpoint, InstructionBreakpoint>
) {
constructor(dap: Breakpoint, ref: Either3<PathedSourceBreakpoint, FunctionBreakpoint, InstructionBreakpoint>) :
this(dap.id, Util.breakpointJBFromDAP(dap), Util.getLocation(dap), dap, ref)
}
protected val breakpoints = HashMap<Int, MappedBreakpoint>()
data class MappedModule(
val java: LLModule,
val dap: Module
) {
constructor(dap: Module): this(Util.moduleJBFromDAP(dap), dap)
}
protected val modules = HashMap<Int, MappedModule>()
override fun addBreakpoint(path: String, line: Int, condition: String?, ignoreSourceHash: Boolean): AddBreakpointResult {
val bp = SourceBreakpoint()
bp.line = line + 1
bp.condition = condition
val bps = breakpoints.values.filter { it.ref.isFirst && it.ref.first.path == path }
.map { it.ref.first.src }
.toMutableList()
bps.add(bp)
val bpsRes = updateSourceBreakpoints(path, bps)
val dapBP = bpsRes.last()
val mbp = MappedBreakpoint(dapBP, Either3.forFirst(PathedSourceBreakpoint(path, bp)))
breakpoints.compute(dapBP.id, { _, _ -> mbp})
return AddBreakpointResult(mbp.java, listOfNotNull(mbp.loc))
}
private fun updateSourceBreakpoints(path: String, bps: List<SourceBreakpoint>): Array<Breakpoint> {
val args = SetBreakpointsArguments()
val src = Util.toSource(path)
args.source = src
args.breakpoints = bps.toTypedArray()
args.sourceModified = false
val res = server.setBreakpoints(args).getSafe()
return res.breakpoints
}
override fun addSymbolicBreakpoint(symBreakpoint: SymbolicBreakpoint): LLSymbolicBreakpoint? {
if (!capabilities.supportsFunctionBreakpoints)
throw DebuggerCommandException("Server doesn't support function breakpoints!")
val fbp = FunctionBreakpoint()
fbp.name = symBreakpoint.pattern
fbp.condition = symBreakpoint.condition
val bps = breakpoints.values.filter { it.ref.isSecond }.map { it.ref.second }.toMutableList()
bps.add(fbp)
val bpsRes = updateSymbolicBreakpoints(bps)
val dapBP = bpsRes.last()
val mbp = MappedBreakpoint(dapBP, Either3.forSecond(fbp))
breakpoints.compute(dapBP.id, {_, _ -> mbp})
return LLSymbolicBreakpoint(mbp.id)
}
private fun updateSymbolicBreakpoints(bps: List<FunctionBreakpoint>): Array<Breakpoint> {
val args = SetFunctionBreakpointsArguments()
args.breakpoints = bps.toTypedArray()
val res = server.setFunctionBreakpoints(args).getSafe()
return res.breakpoints
}
override fun addAddressBreakpoint(address: Address, condition: String?): AddBreakpointResult {
if (!capabilities.supportsInstructionBreakpoints)
throw DebuggerCommandException("Server doesn't support instruction breakpoints!")
val ibp = InstructionBreakpoint()
ibp.instructionReference = Util.stringifyAddress(address.unsignedLongValue)
ibp.condition = condition
val bps = breakpoints.values.filter { it.ref.isThird }.map { it.ref.third }.toMutableList()
bps.add(ibp)
val bpsRes = updateAddressBreakpoints(bps)
val dapBP = bpsRes.last()
val mbp = MappedBreakpoint(dapBP, Either3.forThird(ibp))
breakpoints.compute(dapBP.id, {_, _ -> mbp})
return AddBreakpointResult(mbp.java, listOfNotNull(mbp.loc))
}
private fun updateAddressBreakpoints(bps: List<InstructionBreakpoint>): Array<Breakpoint> {
val args = SetInstructionBreakpointsArguments()
args.breakpoints = bps.toTypedArray()
return server.setInstructionBreakpoints(args).getSafe().breakpoints
}
companion object {
val LLVALUE_FRAME = Key.create<Int>("DAPDriver.LLVALUE_FRAME")
val LLVALUE_CHILDREN_REF = KeyWithDefaultValue.create<Int>("DAPDriver.LLVALUE_CHILDREN_REF", 0)
val LLVALUE_DATA = Key.create<LLValueData>("DAPDriver.LLVALUE_DATA")
val LLVALUE_CHILDREN = Key.create<List<LLValue>>("DAPDriver.LLVALUE_CHILDREN")
}
}
@Throws(ExecutionException::class)
fun <T> CompletableFuture<T>.getSafe(): T {
try {
return getMaybeCancellable()
} catch (e: Exception) {
throw ExecutionException(e)
}
}
private fun notSupported(): Nothing {
throw ExecutionException("Not supported")
}
private fun deprecated(): Nothing {
throw ExecutionException("Deprecated")
}

View file

@ -0,0 +1,261 @@
/*
* Copyright 2023-2024 FalsePattern
*
* 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 com.falsepattern.zigbrains.debugger.dap
import com.falsepattern.zigbrains.debugger.ZigDebuggerLanguage
import com.intellij.openapi.application.readAction
import com.intellij.openapi.editor.Document
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.findDocument
import com.jetbrains.cidr.execution.debugger.backend.*
import com.jetbrains.cidr.execution.debugger.memory.Address
import com.jetbrains.cidr.execution.debugger.memory.AddressRange
import org.eclipse.lsp4j.debug.*
import java.nio.file.InvalidPathException
import java.nio.file.Path
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import java.util.regex.Pattern
object Util {
fun threadJBFromDAP(DAPThread: Thread): LLThread {
return LLThread(DAPThread.id.toLong(), null, null, DAPThread.name, null)
}
fun threadDAPFromJB(JBThread: LLThread): Thread {
val DAPThread = Thread()
DAPThread.id = JBThread.getId().toInt()
return DAPThread
}
fun breakpointJBFromDAP(DAPBreakpoint: Breakpoint): LLBreakpoint {
val source = DAPBreakpoint.source
var sourcePath = if (source == null) "" else Objects.requireNonNullElseGet(
source.path
) { Objects.requireNonNullElse(source.origin, "unknown") }
sourcePath = toJBPath(sourcePath)!!
return LLBreakpoint(
DAPBreakpoint.id,
sourcePath,
Objects.requireNonNullElse<Int>(DAPBreakpoint.line, 0) - 1,
null
)
}
fun getLocation(DAPBreakpoint: Breakpoint): LLBreakpointLocation? {
val ref = DAPBreakpoint.instructionReference ?: return null
val addr = ref.substring(2).toLong(16)
var fl: FileLocation? = null
val src = DAPBreakpoint.source
if (src != null) {
fl = FileLocation(src.path, DAPBreakpoint.line)
}
return LLBreakpointLocation(DAPBreakpoint.id.toString() + "", Address.fromUnsignedLong(addr), fl)
}
fun breakpointDAPFromJB(JBBreakpoint: LLBreakpoint): Breakpoint {
val DAPBreakpoint = Breakpoint()
DAPBreakpoint.id = JBBreakpoint.getId()
DAPBreakpoint.line = JBBreakpoint.getOrigLine() + 1
val source = Source()
source.path = JBBreakpoint.getOrigFile()
DAPBreakpoint.source = source
DAPBreakpoint.message = JBBreakpoint.getCondition()
return DAPBreakpoint
}
fun moduleJBFromDAP(DAPModule: Module): LLModule {
return LLModule(toJBPath(DAPModule.path))
}
fun moduleDAPFromJB(JBModule: LLModule): Module {
val DAPModule = Module()
DAPModule.path = toJBPath(JBModule.path)
DAPModule.name = JBModule.name
return DAPModule
}
fun frameJBFromDAP(
DAPFrame: StackFrame,
helperBreakpoint: DAPDriver.MappedBreakpoint?,
modules: Map<Int, DAPDriver.MappedModule>
): LLFrame {
val ptr = parseAddress(DAPFrame.instructionPointerReference)
val name = DAPFrame.name
val inline = name.startsWith("[Inline Frame] ")
val function = name.substring(name.indexOf('!') + 1, name.indexOf('('))
val moduleID = DAPFrame.moduleId
var moduleName: String? = null
if (moduleID != null) {
if (moduleID.isRight) {
moduleName = moduleID.right
} else {
val module = modules[moduleID.left]!!
moduleName = module.java.name
}
}
var line = DAPFrame.line
var sourcePath: String?
run {
val src = DAPFrame.source
sourcePath = if (src == null) null else toJBPath(src.path)
}
if (helperBreakpoint != null) {
if (line == 0) {
line = helperBreakpoint.dap.line
}
if (sourcePath == null) {
val src = helperBreakpoint.dap.source
if (src != null) {
sourcePath = toJBPath(src.path)
}
}
}
return LLFrame(
DAPFrame.id,
function,
sourcePath,
null,
line - 1,
ptr,
ZigDebuggerLanguage,
false,
inline,
moduleName
)
}
fun toSource(path: String): Source {
val src = Source()
val absolute = Path.of(path).toAbsolutePath()
src.name = absolute.fileName.toString()
src.path = toWinPath(absolute.toString())
return src
}
fun toWinPath(path: String): String {
return path.replace('/', '\\')
}
fun toJBPath(path: String): String {
return path.replace('\\', '/')
}
fun parseAddressNullable(address: String?): Long? {
if (address == null) return null
return parseAddress(address)
}
fun parseAddress(address: String?): Long {
if (address == null) return 0L
if (!address.startsWith("0x")) return java.lang.Long.parseUnsignedLong(address)
return java.lang.Long.parseUnsignedLong(address.substring(2), 16)
}
fun stringifyAddress(address: Long): String {
return "0x" + java.lang.Long.toHexString(address)
}
private val HEX_FIX_REGEX: Pattern = Pattern.compile("([0-9A-F]+)(?<!\\W)h")
suspend fun instructionJBFromDAP(
DAPInstruction: DisassembledInstruction,
loc: Source?,
startLine: Int?,
endLine: Int?,
uniq: Boolean,
symbol: LLSymbolOffset?
): LLInstruction {
var startLine = startLine
var endLine = endLine
val address: Address = Address.parseHexString(DAPInstruction.address)
val byteStrings =
DAPInstruction.instructionBytes.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val bytes = ArrayList<Byte>(byteStrings.size)
for (byteString in byteStrings) {
bytes.add(byteString.toInt(16) as Byte)
}
val result: ArrayList<LLInstruction> = ArrayList<LLInstruction>()
var comment: String? = null
if (loc != null && startLine != null && endLine != null && uniq) run {
val pathStr = toJBPath(loc.path)
val path: Path
try {
path = Path.of(pathStr)
} catch (ignored: InvalidPathException) {
return@run
}
val text = readAction {
val file = VfsUtil.findFile(path, true) ?: return@readAction null
val doc: Document = file.findDocument() ?: return@readAction null
doc.immutableCharSequence.toString().split("(\r\n|\r|\n)".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
}
if (text == null) return@run
startLine -= 1
endLine -= 1
if (text.size <= endLine) return@run
comment = text[endLine]
}
val nicerDisassembly = StringBuilder()
val disassembly = DAPInstruction.instruction
val matcher = HEX_FIX_REGEX.matcher(disassembly)
var prevEnd = 0
while (matcher.find()) {
nicerDisassembly.append(disassembly, prevEnd, matcher.start())
val hex = matcher.group(1).lowercase(Locale.getDefault())
nicerDisassembly.append("0x").append(hex)
prevEnd = matcher.end()
}
if (prevEnd < disassembly.length) nicerDisassembly.append(disassembly, prevEnd, disassembly.length)
return LLInstruction.create(
address,
bytes,
nicerDisassembly.toString(),
comment,
symbol
)
}
fun memoryJBFromDAP(DAPMemory: ReadMemoryResponse): LLMemoryHunk {
val address = parseAddress(DAPMemory.address)
val bytes = Base64.getDecoder().decode(DAPMemory.data)
val range = AddressRange(
Address.fromUnsignedLong(address),
Address.fromUnsignedLong(address + bytes.size - 1)
)
return LLMemoryHunk(range, bytes)
}
@Throws(com.intellij.execution.ExecutionException::class)
fun <T> get(future: CompletableFuture<T>): T {
try {
return future[4, TimeUnit.SECONDS]
} catch (e: InterruptedException) {
throw com.intellij.execution.ExecutionException(e)
} catch (e: TimeoutException) {
throw com.intellij.execution.ExecutionException(e)
} catch (e: ExecutionException) {
throw com.intellij.execution.ExecutionException(e.cause)
}
}
fun emptyIfNull(str: String?): String {
return str ?: ""
}
}