Files
luma/Sources/LumaCore/Models/AddressAnchor.swift
Ole André Vadla Ravnås c6d07ca32a Derive hook and insight names from symbol
Modules-view actions built names via anchor.displayString,
yielding module!symbol for hooks and module+offset for
disassembly/memory insights. Route both through the symbol the
tracer search already uses.

Add AddressAnchor.symbolName; default tracer hook names to it.
Thread the context anchor hint into getOrCreateInsight so
insights opened on exports and imports inherit the symbol name.
Drop the unused title parameter from ModuleDetailView.
2026-06-22 22:26:27 +02:00

189 lines
5.8 KiB
Swift

import Foundation
public typealias JSONObject = [String: Any]
public enum AddressAnchor: Codable, Hashable, Sendable {
case absolute(UInt64)
case moduleOffset(name: String, offset: UInt64)
case moduleExport(name: String, export: String)
case objcMethod(selector: String)
case swiftFunc(module: String, function: String)
case debugSymbol(name: String)
case javaMethod(className: String, methodName: String)
public var moduleGroupName: String {
switch self {
case .moduleOffset(let name, _),
.moduleExport(let name, _):
return name
case .swiftFunc(let module, _):
return module
case .objcMethod:
return "Objective-C"
case .javaMethod:
return "Java"
case .debugSymbol:
return "Debug Symbols"
case .absolute:
return "Absolute Addresses"
}
}
public var displayString: String {
switch self {
case .absolute(let a):
return String(format: "0x%llx", a)
case .moduleOffset(let name, let offset):
return "\(moduleBasename(name))+\(String(format: "0x%llx", offset))"
case .moduleExport(let name, let export):
return "\(moduleBasename(name))!\(export)"
case .objcMethod(let selector):
return selector
case .swiftFunc(let module, let function):
return "\(moduleBasename(module))!\(function)"
case .debugSymbol(let name):
return name
case .javaMethod(let className, let methodName):
return "\(className).\(methodName)"
}
}
public var symbolName: String {
switch self {
case .moduleExport(_, let export):
return export
case .swiftFunc(_, let function):
return function
default:
return displayString
}
}
public static func fromJSON(_ object: JSONObject) throws -> AddressAnchor {
guard let type = object["type"] as? String else {
throw LumaCoreError.invalidArgument("Anchor missing 'type'")
}
switch type {
case "absolute":
return .absolute(try parseAnchorAddress(object["address"]))
case "moduleOffset":
return .moduleOffset(
name: try parseAnchorString(object["name"], field: "name"),
offset: try parseAnchorOffset(object["offset"])
)
case "moduleExport":
return .moduleExport(
name: try parseAnchorString(object["name"], field: "name"),
export: try parseAnchorString(object["export"], field: "export")
)
case "objcMethod":
return .objcMethod(selector: try parseAnchorString(object["selector"], field: "selector"))
case "swiftFunc":
return .swiftFunc(
module: try parseAnchorString(object["module"], field: "module"),
function: try parseAnchorString(object["function"], field: "function")
)
case "debugSymbol":
return .debugSymbol(name: try parseAnchorString(object["name"], field: "name"))
case "javaMethod":
return .javaMethod(
className: try parseAnchorString(object["className"], field: "className"),
methodName: try parseAnchorString(object["methodName"], field: "methodName")
)
default:
throw LumaCoreError.invalidArgument("Unknown anchor type '\(type)'")
}
}
public func toJSON() -> JSONObject {
switch self {
case .absolute(let a):
return [
"type": "absolute",
"address": a,
]
case .moduleOffset(let name, let offset):
return [
"type": "moduleOffset",
"name": name,
"offset": offset,
]
case .moduleExport(let name, let export):
return [
"type": "moduleExport",
"name": name,
"export": export,
]
case .objcMethod(let selector):
return [
"type": "objcMethod",
"selector": selector,
]
case .swiftFunc(let module, let function):
return [
"type": "swiftFunc",
"module": module,
"function": function,
]
case .debugSymbol(let name):
return [
"type": "debugSymbol",
"name": name,
]
case .javaMethod(let className, let methodName):
return [
"type": "javaMethod",
"className": className,
"methodName": methodName,
]
}
}
}
private func moduleBasename(_ name: String) -> String {
guard let slash = name.lastIndex(of: "/") else { return name }
return String(name[name.index(after: slash)...])
}
private func parseAnchorString(_ value: Any?, field: String) throws -> String {
guard let s = value as? String else {
throw LumaCoreError.invalidArgument("Anchor field '\(field)' is not a String")
}
return s
}
private func parseAnchorAddress(_ value: Any?) throws -> UInt64 {
if let u = value as? UInt64 {
return u
}
if let s = value as? String {
return try parseAgentHexAddress(s)
}
throw LumaCoreError.invalidArgument("Anchor 'address' is not a UInt64 or hex String")
}
private func parseAnchorOffset(_ value: Any?) throws -> UInt64 {
if let u = value as? UInt64 {
return u
}
if let n = value as? Double {
return UInt64(n)
}
if let i = value as? Int {
return UInt64(i)
}
throw LumaCoreError.invalidArgument("Anchor 'offset' is not numeric")
}