From aa7ee4083975a169bed149bf5c857375f46e0657 Mon Sep 17 00:00:00 2001 From: Abdelmonm Alsnajleh Date: Wed, 10 Jun 2026 16:42:13 +0300 Subject: [PATCH] Refactor file path handling in JavaCodeProvider and enhance output file retrieval in JadxDecompiler (if class file is changed because if bad name, jadx change it and the file name changes so it cant find it, but this fixes it) --- src/decompiler/impl/JadxDecompiler.ts | 53 ++++++++++++++++++++++++++- src/provider/JavaCodeProvider.ts | 2 +- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/decompiler/impl/JadxDecompiler.ts b/src/decompiler/impl/JadxDecompiler.ts index 0972cc9..5af9ef0 100644 --- a/src/decompiler/impl/JadxDecompiler.ts +++ b/src/decompiler/impl/JadxDecompiler.ts @@ -25,6 +25,55 @@ export class JadxDecompiler implements SmaliDecompiler { return join(this.sourceOutputDir, (smaliClassName.includes("/") ? "" : "defpackage/") + smaliClassName + ".java") } + private async collectJavaFiles(directoryPath: string): Promise { + const entries = await fsAsync.readdir(directoryPath, { withFileTypes: true }) + const results: string[] = [] + for (const entry of entries) { + const entryPath = join(directoryPath, entry.name) + if (entry.isDirectory()) { + results.push(...await this.collectJavaFiles(entryPath)) + } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".java")) { + results.push(entryPath) + } + } + return results + } + + private async findGeneratedOutputFilePath(smaliClassName: string): Promise { + const expectedOutputFilePath = this.getOutputFilePath(smaliClassName) + try { + await fsAsync.stat(expectedOutputFilePath) + return expectedOutputFilePath + } catch { + try { + const javaCandidates = await this.collectJavaFiles(this.sourceOutputDir) + const normalizedExpectedOutputFilePath = expectedOutputFilePath.replace(/\\/g, "/") + const normalizedExpectedBaseName = expectedOutputFilePath.split(/[\\/]/).pop()!.toLowerCase() + const suffixMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.replace(/\\/g, "/").endsWith(normalizedExpectedOutputFilePath)) + if (suffixMatchedCandidates.length === 1) { + return suffixMatchedCandidates[0] + } + const baseNameMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.split(/[\\/]/).pop()!.toLowerCase() === normalizedExpectedBaseName) + if (baseNameMatchedCandidates.length === 1) { + return baseNameMatchedCandidates[0] + } + const jadxPrefixedBaseNameMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.split(/[\\/]/).pop()!.toLowerCase() === `c${normalizedExpectedBaseName}`) + if (jadxPrefixedBaseNameMatchedCandidates.length === 1) { + return jadxPrefixedBaseNameMatchedCandidates[0] + } + const suffixWithoutRootDirectoryMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.replace(/\\/g, "/").endsWith(`/${normalizedExpectedOutputFilePath.split("/").slice(-2).join("/").replace(normalizedExpectedBaseName, `c${normalizedExpectedBaseName}`)}`)) + if (suffixWithoutRootDirectoryMatchedCandidates.length === 1) { + return suffixWithoutRootDirectoryMatchedCandidates[0] + } + if (javaCandidates.length === 1) { + return javaCandidates[0] + } + } catch { + } + throw new DecompileError(`Error is caught when reading ${expectedOutputFilePath}: file not found`) + } + } + private async loadConfig(): Promise { const config = workspace.getConfiguration("smali2java.decompiler.jadx") return { @@ -39,7 +88,6 @@ export class JadxDecompiler implements SmaliDecompiler { const config = await this.loadConfig() if (!config.path) throw new DecompileError("The jadx executable path has not been configured") if (!(await fsAsync.stat(config.path)).isFile()) throw new DecompileError("Illegal jadx executable path") - const outputFilePath = this.getOutputFilePath(smaliClassName) const { stdout, stderr } = await execAsync(`${await config.path} ${this.quote(smaliFileUri.fsPath)} -ds ${this.quote(this.sourceOutputDir)} ${config.options ?? ""}`) this.outputChannel.append(stdout) if (stderr && stderr.length > 0) { @@ -47,6 +95,7 @@ export class JadxDecompiler implements SmaliDecompiler { this.outputChannel.append(stderr) throw new DecompileError("View the output for details") } + const outputFilePath = await this.findGeneratedOutputFilePath(smaliClassName) try { await fsAsync.stat(outputFilePath) } catch(e) { @@ -54,7 +103,7 @@ export class JadxDecompiler implements SmaliDecompiler { } return Uri.from({ scheme: JavaCodeProvider.scheme, - path: outputFilePath + path: process.platform == "win32" ? outputFilePath.replace(/\\/g, "/") : outputFilePath }) } diff --git a/src/provider/JavaCodeProvider.ts b/src/provider/JavaCodeProvider.ts index bad691e..104a17f 100644 --- a/src/provider/JavaCodeProvider.ts +++ b/src/provider/JavaCodeProvider.ts @@ -12,7 +12,7 @@ export default class JavaCodeProvider implements TextDocumentContentProvider { } async provideTextDocumentContent(uri: Uri): Promise { - const buffer = await fs.readFile(uri.path) + const buffer = await fs.readFile(uri.fsPath) return buffer.toString() } } \ No newline at end of file