@@ -2,7 +2,7 @@ import SwiftUI
22import Combine
33
44func debug( _ message: String ) {
5- fputs ( " >>> \( message ) \n " , stderr )
5+ // Debug logging disabled for performance
66}
77
88@MainActor
@@ -90,6 +90,7 @@ class AppState: ObservableObject {
9090 private let stringAnalyzer = StringAnalyzer ( )
9191 private let xrefAnalyzer = XRefAnalyzer ( )
9292 private let decompiler = Decompiler ( )
93+ private let javaDecompiler = JavaDecompiler ( )
9394
9495 // MARK: - File Operations
9596
@@ -139,71 +140,80 @@ class AppState: ObservableObject {
139140 }
140141
141142 func loadFile( url: URL ) async {
142- debug ( " loadFile called for: \( url. path) " )
143143 isLoading = true
144144 loadingProgress = 0
145145 loadingMessage = " Loading file... "
146146 errorMessage = nil
147147
148+ // Capture references for background work
149+ let loader = binaryLoader
150+ let strAnalyzer = stringAnalyzer
151+
148152 do {
149- // Load binary
150153 loadingMessage = " Parsing binary format... "
151154 loadingProgress = 0.1
152- debug ( " Parsing binary... " )
153-
154- let binary = try await binaryLoader. load ( from: url)
155- debug ( " Binary loaded: \( binary. name) " )
156- debug ( " Format: \( binary. format) , Arch: \( binary. architecture) " )
157- debug ( " Sections: \( binary. sections. count) , Symbols: \( binary. symbols. count) " )
158-
159- // Set the current file first
160- self . currentFile = binary
161- debug ( " currentFile set " )
162155
163- // Get first code section
164- self . selectedSection = binary. sections. first { $0. containsCode }
165- debug ( " Selected section: \( selectedSection? . name ?? " none " ) (flags: \( String ( format: " 0x%X " , selectedSection? . flags ?? 0 ) ) ) " )
166-
167- // Extract symbols
168- loadingMessage = " Processing symbols... "
169- loadingProgress = 0.5
170- self . imports = binary. symbols. filter { $0. isImport }
171- self . exports = binary. symbols. filter { $0. isExport }
172- self . symbols = binary. symbols
173- debug ( " Symbols processed: \( symbols. count) " )
174-
175- // Get functions from symbols
176- loadingMessage = " Extracting functions... "
177- loadingProgress = 0.7
178- self . functions = binary. symbols
179- . filter { $0. type == . function && $0. address != 0 }
180- . map { Function ( name: $0. name, startAddress: $0. address, endAddress: $0. address + max( $0. size, 4 ) ) }
181- . sorted { $0. startAddress < $1. startAddress }
182- debug ( " Functions: \( functions. count) " )
183-
184- // Build lookup caches for O(1) access (keep first occurrence for duplicates)
185- loadingMessage = " Building lookup caches... "
186- self . symbolsByAddress = symbols. reduce ( into: [ : ] ) { dict, symbol in
187- if symbol. address != 0 && dict [ symbol. address] == nil {
188- dict [ symbol. address] = symbol
189- }
190- }
191- self . symbolsByName = symbols. reduce ( into: [ : ] ) { dict, symbol in
192- if dict [ symbol. name] == nil {
193- dict [ symbol. name] = symbol
194- }
195- }
196- self . functionsByAddress = functions. reduce ( into: [ : ] ) { dict, func_ in
197- if dict [ func_. startAddress] == nil {
198- dict [ func_. startAddress] = func_
156+ // Use continuation with explicit GCD for guaranteed background execution
157+ let result : ( BinaryFile , [ Symbol ] , [ Symbol ] , [ Symbol ] , [ Function ] , [ UInt64 : Symbol ] , [ String : Symbol ] , [ UInt64 : Function ] , [ StringReference ] ) = try await withCheckedThrowingContinuation { continuation in
158+ DispatchQueue . global ( qos: . userInitiated) . async {
159+ do {
160+ // Load binary on background thread
161+ let binary = try loader. loadSync ( from: url)
162+
163+ // Process symbols
164+ let imports = binary. symbols. filter { $0. isImport }
165+ let exports = binary. symbols. filter { $0. isExport }
166+ let symbols = binary. symbols
167+
168+ // Get functions from symbols
169+ let functions = binary. symbols
170+ . filter { $0. type == . function && $0. address != 0 }
171+ . map { Function ( name: $0. name, startAddress: $0. address, endAddress: $0. address + max( $0. size, 4 ) ) }
172+ . sorted { $0. startAddress < $1. startAddress }
173+
174+ // Build lookup caches
175+ let symbolsByAddress = symbols. reduce ( into: [ UInt64: Symbol] ( ) ) { dict, symbol in
176+ if symbol. address != 0 && dict [ symbol. address] == nil {
177+ dict [ symbol. address] = symbol
178+ }
179+ }
180+ let symbolsByName = symbols. reduce ( into: [ String: Symbol] ( ) ) { dict, symbol in
181+ if dict [ symbol. name] == nil {
182+ dict [ symbol. name] = symbol
183+ }
184+ }
185+ let functionsByAddress = functions. reduce ( into: [ UInt64: Function] ( ) ) { dict, func_ in
186+ if dict [ func_. startAddress] == nil {
187+ dict [ func_. startAddress] = func_
188+ }
189+ }
190+
191+ // Extract strings
192+ let strings = strAnalyzer. analyze ( binary: binary)
193+
194+ continuation. resume ( returning: ( binary, imports, exports, symbols, functions, symbolsByAddress, symbolsByName, functionsByAddress, strings) )
195+ } catch {
196+ continuation. resume ( throwing: error)
197+ }
199198 }
200199 }
201200
202- // Extract strings
203- loadingMessage = " Extracting strings ..."
201+ // Update UI on main thread
202+ loadingMessage = " Finalizing ..."
204203 loadingProgress = 0.9
205- self . strings = stringAnalyzer. analyze ( binary: binary)
206- debug ( " Strings: \( strings. count) " )
204+
205+ let ( binary, imports, exports, symbols, functions, symbolsByAddress, symbolsByName, functionsByAddress, strings) = result
206+
207+ self . currentFile = binary
208+ self . selectedSection = binary. sections. first { $0. containsCode }
209+ self . imports = imports
210+ self . exports = exports
211+ self . symbols = symbols
212+ self . functions = functions
213+ self . symbolsByAddress = symbolsByAddress
214+ self . symbolsByName = symbolsByName
215+ self . functionsByAddress = functionsByAddress
216+ self . strings = strings
207217
208218 // Initialize patcher
209219 self . patcher = BinaryPatcher ( binary: binary)
@@ -214,7 +224,6 @@ class AppState: ObservableObject {
214224 loadingProgress = 1.0
215225 loadingMessage = " Ready "
216226 isLoading = false
217- debug ( " Loading complete! isLoading= \( isLoading) " )
218227
219228 } catch {
220229 debug ( " ERROR: \( error) " )
@@ -608,6 +617,12 @@ class AppState: ObservableObject {
608617 guard let function = selectedFunction,
609618 let binary = currentFile else { return }
610619
620+ // Check if this is a Java class file
621+ if let javaClasses = binary. javaClasses, !javaClasses. isEmpty {
622+ decompileJavaMethod ( function: function, javaClasses: javaClasses)
623+ return
624+ }
625+
611626 Task {
612627 let instructions = await disassembleFunction ( function)
613628 decompilerOutput = decompiler. decompile (
@@ -618,6 +633,52 @@ class AppState: ObservableObject {
618633 }
619634 }
620635
636+ private func decompileJavaMethod( function: Function , javaClasses: [ JARLoader . JavaClass ] ) {
637+ let functionName = function. name
638+
639+ // Find matching class and method - use exact matching
640+ for javaClass in javaClasses {
641+ let className = javaClass. thisClass. replacingOccurrences ( of: " / " , with: " . " )
642+
643+ for method in javaClass. methods {
644+ let methodFullName = " \( className) . \( method. name) \( method. descriptor) "
645+
646+ if methodFullName == functionName {
647+ // Found the exact method - decompile just this method
648+ let decompiledMethod = javaDecompiler. decompileMethod ( method, in: javaClass)
649+
650+ var output = " // Function at 0x \( String ( format: " %X " , function. startAddress) ) \n "
651+ output += " // Size: \( method. code? . code. count ?? 0 ) bytes (bytecode) \n "
652+ output += " // Class: \( className) \n "
653+ output += " // Method: \( method. name) \( method. descriptor) \n \n "
654+ output += " \( decompiledMethod. signature) { \n "
655+
656+ let bodyLines = decompiledMethod. body. split ( separator: " \n " , omittingEmptySubsequences: false )
657+ for line in bodyLines {
658+ if !line. isEmpty {
659+ output += " \( line) \n "
660+ } else {
661+ output += " \n "
662+ }
663+ }
664+ output += " } \n "
665+
666+ decompilerOutput = output
667+ return
668+ }
669+ }
670+ }
671+
672+ // Fallback: couldn't find the method
673+ decompilerOutput = " // Could not find Java method for: \( functionName) \n // Available methods in loaded classes: \n "
674+ for javaClass in javaClasses. prefix ( 5 ) {
675+ let className = javaClass. thisClass. replacingOccurrences ( of: " / " , with: " . " )
676+ for method in javaClass. methods. prefix ( 3 ) {
677+ decompilerOutput += " // \( className) . \( method. name) \( method. descriptor) \n "
678+ }
679+ }
680+ }
681+
621682 // MARK: - Patching
622683
623684 func patchBytes( at address: UInt64 , newBytes: [ UInt8 ] , description: String ) {
0 commit comments