fix(decompile): handle partial-success flows (#10)
Allow jadx-only mode to succeed when jadx exits non-zero after writing usable Java output. Keep both-mode resilient when jadx partially succeeds, normalize Fernflower APK output handling, and make timeout/no-output failures explicit for Vineflower runs. Co-authored-by: root <root@dbyqhnca.colocrossing.cloud>
This commit is contained in:
parent
5bc7cd53e6
commit
c25dfd78d2
|
|
@ -163,6 +163,8 @@ find_dex2jar() {
|
||||||
# --- jadx decompilation ---
|
# --- jadx decompilation ---
|
||||||
run_jadx() {
|
run_jadx() {
|
||||||
local out_dir="$1"
|
local out_dir="$1"
|
||||||
|
local jadx_status=0
|
||||||
|
local count=0
|
||||||
|
|
||||||
if ! command -v jadx &>/dev/null; then
|
if ! command -v jadx &>/dev/null; then
|
||||||
echo "Error: jadx is not installed or not in PATH." >&2
|
echo "Error: jadx is not installed or not in PATH." >&2
|
||||||
|
|
@ -177,20 +179,41 @@ run_jadx() {
|
||||||
args+=("$INPUT_FILE_ABS")
|
args+=("$INPUT_FILE_ABS")
|
||||||
|
|
||||||
echo "Running: jadx ${args[*]}"
|
echo "Running: jadx ${args[*]}"
|
||||||
jadx "${args[@]}"
|
if jadx "${args[@]}"; then
|
||||||
|
jadx_status=0
|
||||||
|
else
|
||||||
|
jadx_status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
echo "jadx output: $out_dir/sources/"
|
echo "jadx output: $out_dir/sources/"
|
||||||
if [[ -d "$out_dir/sources" ]]; then
|
if [[ -d "$out_dir/sources" ]]; then
|
||||||
local count
|
|
||||||
count=$(find "$out_dir/sources" -name "*.java" | wc -l)
|
count=$(find "$out_dir/sources" -name "*.java" | wc -l)
|
||||||
echo "Java files decompiled by jadx: $count"
|
echo "Java files decompiled by jadx: $count"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ $jadx_status -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $count -gt 0 ]]; then
|
||||||
|
echo "Warning: jadx exited with status $jadx_status after writing $count Java files; treating this as partial success." >&2
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Error: jadx failed with status $jadx_status and produced no Java output." >&2
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Fernflower decompilation ---
|
# --- Fernflower decompilation ---
|
||||||
run_fernflower() {
|
run_fernflower() {
|
||||||
local out_dir="$1"
|
local out_dir="$1"
|
||||||
local jar_to_decompile=""
|
local jar_to_decompile=""
|
||||||
|
local converted_jar=""
|
||||||
|
local intermediate_dir="$out_dir/intermediate"
|
||||||
|
local ff_status=0
|
||||||
|
local d2j_status=0
|
||||||
|
local count=0
|
||||||
|
local ff_timeout_seconds="${FERNFLOWER_TIMEOUT_SECONDS:-900}"
|
||||||
|
|
||||||
local ff_jar
|
local ff_jar
|
||||||
if ! ff_jar=$(find_fernflower_jar); then
|
if ! ff_jar=$(find_fernflower_jar); then
|
||||||
|
|
@ -211,12 +234,20 @@ run_fernflower() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Converting $ext_lower to JAR with dex2jar..."
|
echo "Converting $ext_lower to JAR with dex2jar..."
|
||||||
local converted_jar="$out_dir/${BASENAME}-dex2jar.jar"
|
mkdir -p "$intermediate_dir"
|
||||||
"$d2j" -f -o "$converted_jar" "$INPUT_FILE_ABS" 2>&1 || true
|
converted_jar="$intermediate_dir/${BASENAME}-dex2jar.jar"
|
||||||
|
if "$d2j" -f -o "$converted_jar" "$INPUT_FILE_ABS" 2>&1; then
|
||||||
|
d2j_status=0
|
||||||
|
else
|
||||||
|
d2j_status=$?
|
||||||
|
fi
|
||||||
if [[ ! -f "$converted_jar" ]]; then
|
if [[ ! -f "$converted_jar" ]]; then
|
||||||
echo "Error: dex2jar conversion failed." >&2
|
echo "Error: dex2jar conversion failed with status $d2j_status." >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if [[ $d2j_status -ne 0 ]]; then
|
||||||
|
echo "Warning: dex2jar exited with status $d2j_status but produced $converted_jar; continuing." >&2
|
||||||
|
fi
|
||||||
jar_to_decompile="$converted_jar"
|
jar_to_decompile="$converted_jar"
|
||||||
else
|
else
|
||||||
jar_to_decompile="$INPUT_FILE_ABS"
|
jar_to_decompile="$INPUT_FILE_ABS"
|
||||||
|
|
@ -233,25 +264,83 @@ run_fernflower() {
|
||||||
ff_args+=("$out_dir")
|
ff_args+=("$out_dir")
|
||||||
|
|
||||||
echo "Running: java -jar $ff_jar ${ff_args[*]}"
|
echo "Running: java -jar $ff_jar ${ff_args[*]}"
|
||||||
java -jar "$ff_jar" "${ff_args[@]}"
|
if command -v timeout &>/dev/null && [[ "$ff_timeout_seconds" =~ ^[0-9]+$ ]] && (( ff_timeout_seconds > 0 )); then
|
||||||
|
echo "Fernflower timeout: ${ff_timeout_seconds}s (override with FERNFLOWER_TIMEOUT_SECONDS)"
|
||||||
|
if timeout "${ff_timeout_seconds}s" java -jar "$ff_jar" "${ff_args[@]}"; then
|
||||||
|
ff_status=0
|
||||||
|
else
|
||||||
|
ff_status=$?
|
||||||
|
fi
|
||||||
|
elif java -jar "$ff_jar" "${ff_args[@]}"; then
|
||||||
|
ff_status=0
|
||||||
|
else
|
||||||
|
ff_status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
# Fernflower outputs a JAR containing .java files — extract it
|
# Fernflower outputs a JAR containing .java files — extract it
|
||||||
local result_jar="$out_dir/$(basename "$jar_to_decompile")"
|
local result_jar="$out_dir/$(basename "$jar_to_decompile")"
|
||||||
if [[ -f "$result_jar" ]]; then
|
if [[ -f "$result_jar" ]]; then
|
||||||
local sources_dir="$out_dir/sources"
|
local sources_dir="$out_dir/sources"
|
||||||
mkdir -p "$sources_dir"
|
mkdir -p "$sources_dir"
|
||||||
unzip -qo "$result_jar" -d "$sources_dir"
|
if unzip -qo "$result_jar" -d "$sources_dir"; then
|
||||||
rm -f "$result_jar"
|
rm -f "$result_jar"
|
||||||
echo "Fernflower output: $sources_dir/"
|
else
|
||||||
local count
|
echo "Warning: Fernflower result jar $result_jar could not be extracted; checking for direct folder output." >&2
|
||||||
count=$(find "$sources_dir" -name "*.java" | wc -l)
|
fi
|
||||||
echo "Java files decompiled by Fernflower: $count"
|
fi
|
||||||
|
|
||||||
|
local sources_dir="$out_dir/sources"
|
||||||
|
mkdir -p "$sources_dir"
|
||||||
|
count=$(find "$sources_dir" -name "*.java" | wc -l)
|
||||||
|
|
||||||
|
# Vineflower may write sources directly into the destination folder tree instead of a result jar.
|
||||||
|
if [[ $count -eq 0 ]]; then
|
||||||
|
local direct_count=0
|
||||||
|
direct_count=$(find "$out_dir" \
|
||||||
|
-path "$sources_dir" -prune -o \
|
||||||
|
-path "$intermediate_dir" -prune -o \
|
||||||
|
-name "*.java" -type f -print | wc -l)
|
||||||
|
if [[ $direct_count -gt 0 ]]; then
|
||||||
|
while IFS= read -r -d '' entry; do
|
||||||
|
mv "$entry" "$sources_dir"/
|
||||||
|
done < <(find "$out_dir" -mindepth 1 -maxdepth 1 \
|
||||||
|
! -name "sources" \
|
||||||
|
! -name "intermediate" \
|
||||||
|
-print0)
|
||||||
|
count=$(find "$sources_dir" -name "*.java" | wc -l)
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean up intermediate dex2jar output
|
# Clean up intermediate dex2jar output
|
||||||
if [[ -n "${converted_jar:-}" ]] && [[ -f "${converted_jar:-}" ]]; then
|
if [[ $count -gt 0 ]]; then
|
||||||
rm -f "$converted_jar"
|
echo "Fernflower output: $sources_dir/"
|
||||||
|
echo "Java files decompiled by Fernflower: $count"
|
||||||
|
if [[ -n "${converted_jar:-}" ]] && [[ -f "${converted_jar:-}" ]]; then
|
||||||
|
rm -f "$converted_jar"
|
||||||
|
fi
|
||||||
|
if [[ -d "$intermediate_dir" ]]; then
|
||||||
|
rmdir "$intermediate_dir" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ $ff_status -ne 0 ]]; then
|
||||||
|
echo "Warning: Fernflower/Vineflower exited with status $ff_status after writing $count Java files; treating this as partial success." >&2
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${converted_jar:-}" ]] && [[ -f "${converted_jar:-}" ]]; then
|
||||||
|
echo "Error: Fernflower/Vineflower produced no Java output. Intermediate dex2jar artifact kept at $converted_jar" >&2
|
||||||
|
else
|
||||||
|
echo "Error: Fernflower/Vineflower produced no Java output." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ff_status -ne 0 ]]; then
|
||||||
|
if [[ $ff_status -eq 124 ]]; then
|
||||||
|
echo "Error: Fernflower/Vineflower exceeded timeout (${ff_timeout_seconds}s)." >&2
|
||||||
|
fi
|
||||||
|
echo "Error: Fernflower/Vineflower exited with status $ff_status." >&2
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Summary helper ---
|
# --- Summary helper ---
|
||||||
|
|
@ -259,9 +348,28 @@ print_structure() {
|
||||||
local src_dir="$1"
|
local src_dir="$1"
|
||||||
local label="$2"
|
local label="$2"
|
||||||
if [[ -d "$src_dir" ]]; then
|
if [[ -d "$src_dir" ]]; then
|
||||||
|
local packages=()
|
||||||
echo
|
echo
|
||||||
echo "Top-level packages ($label):"
|
echo "Top-level packages ($label):"
|
||||||
find "$src_dir" -mindepth 1 -maxdepth 3 -type d | head -20 | sed "s|$src_dir/||" | grep -v '^$' | sort
|
while IFS= read -r pkg; do
|
||||||
|
[[ -n "$pkg" ]] && packages+=("$pkg")
|
||||||
|
done < <(find "$src_dir" -mindepth 1 -maxdepth 3 -type d -printf '%P\n' | sort)
|
||||||
|
|
||||||
|
local limit=${#packages[@]}
|
||||||
|
if (( limit > 20 )); then
|
||||||
|
limit=20
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( limit == 0 )); then
|
||||||
|
echo "(none)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local i=0
|
||||||
|
while (( i < limit )); do
|
||||||
|
echo "${packages[$i]}"
|
||||||
|
((i += 1))
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,19 +392,63 @@ decompile_single() {
|
||||||
|
|
||||||
case "$ENGINE" in
|
case "$ENGINE" in
|
||||||
jadx)
|
jadx)
|
||||||
run_jadx "$out_dir"
|
local jadx_status=0
|
||||||
|
if run_jadx "$out_dir"; then
|
||||||
|
jadx_status=0
|
||||||
|
else
|
||||||
|
jadx_status=$?
|
||||||
|
fi
|
||||||
print_structure "$out_dir/sources" "jadx"
|
print_structure "$out_dir/sources" "jadx"
|
||||||
|
if [[ $jadx_status -eq 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ $jadx_status -eq 2 ]]; then
|
||||||
|
echo "jadx completed with warnings but produced usable output."
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
fernflower)
|
fernflower)
|
||||||
run_fernflower "$out_dir"
|
local ff_status=0
|
||||||
|
if run_fernflower "$out_dir"; then
|
||||||
|
ff_status=0
|
||||||
|
else
|
||||||
|
ff_status=$?
|
||||||
|
fi
|
||||||
print_structure "$out_dir/sources" "fernflower"
|
print_structure "$out_dir/sources" "fernflower"
|
||||||
|
if [[ $ff_status -eq 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ $ff_status -eq 2 ]]; then
|
||||||
|
echo "Fernflower completed with warnings but produced usable output."
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
both)
|
both)
|
||||||
|
local jadx_status=0
|
||||||
|
local ff_status=0
|
||||||
echo "--- Pass 1: jadx ---"
|
echo "--- Pass 1: jadx ---"
|
||||||
run_jadx "$out_dir/jadx"
|
if run_jadx "$out_dir/jadx"; then
|
||||||
|
jadx_status=0
|
||||||
|
else
|
||||||
|
jadx_status=$?
|
||||||
|
fi
|
||||||
|
if [[ $jadx_status -eq 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ $jadx_status -eq 2 ]]; then
|
||||||
|
echo "Continuing to Fernflower because jadx produced usable output despite warnings."
|
||||||
|
fi
|
||||||
echo
|
echo
|
||||||
echo "--- Pass 2: Fernflower ---"
|
echo "--- Pass 2: Fernflower ---"
|
||||||
run_fernflower "$out_dir/fernflower"
|
if run_fernflower "$out_dir/fernflower"; then
|
||||||
|
ff_status=0
|
||||||
|
else
|
||||||
|
ff_status=$?
|
||||||
|
fi
|
||||||
|
if [[ $ff_status -eq 1 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ $ff_status -eq 2 ]]; then
|
||||||
|
echo "Continuing with Fernflower output because it produced usable sources despite warnings."
|
||||||
|
fi
|
||||||
|
|
||||||
print_structure "$out_dir/jadx/sources" "jadx"
|
print_structure "$out_dir/jadx/sources" "jadx"
|
||||||
print_structure "$out_dir/fernflower/sources" "fernflower"
|
print_structure "$out_dir/fernflower/sources" "fernflower"
|
||||||
|
|
@ -314,8 +466,14 @@ decompile_single() {
|
||||||
echo "Fernflower: $ff_count Java files"
|
echo "Fernflower: $ff_count Java files"
|
||||||
|
|
||||||
if [[ -d "$out_dir/jadx/sources" ]]; then
|
if [[ -d "$out_dir/jadx/sources" ]]; then
|
||||||
|
local jadx_error_files
|
||||||
local jadx_errors
|
local jadx_errors
|
||||||
jadx_errors=$(grep -rl 'JADX WARNING\|JADX WARN\|JADX ERROR\|Code decompiled incorrectly' "$out_dir/jadx/sources" 2>/dev/null | wc -l || echo 0)
|
jadx_error_files=$(grep -rl 'JADX WARNING\|JADX WARN\|JADX ERROR\|Code decompiled incorrectly' "$out_dir/jadx/sources" 2>/dev/null || true)
|
||||||
|
if [[ -n "$jadx_error_files" ]]; then
|
||||||
|
jadx_errors=$(printf '%s\n' "$jadx_error_files" | wc -l)
|
||||||
|
else
|
||||||
|
jadx_errors=0
|
||||||
|
fi
|
||||||
echo "jadx files with warnings/errors: $jadx_errors"
|
echo "jadx files with warnings/errors: $jadx_errors"
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue