blob: 992665ed58eeb6f273bb0cd523a78e189f554168 [file] [log] [blame]
#!/bin/bash
# Copyright (C) 2023 The Android Open Source Project
#
# 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.
set -e
help() {
cat <<'EOF'
dump-jar: Dump java classes in jar files
Usage:
dump-jar [-v] CLASS-FILE [...]
Dump a *.class file
dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: filename regex] [...]
Dump a jar file.
If a filename contains a ':', then the following part
will be used to filter files in the jar file.
For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar.
Options:
-v: Enable verbose output.
-s: Simple output mode, used to check HostStubGen output jars.
-o: Write the output to a specified file.
EOF
}
# Parse the options.
verbose=0
simple=0
output=""
while getopts "hvso:" opt; do
case "$opt" in
h)
help
exit 0
;;
v)
verbose=1
;;
s)
simple=1
;;
o)
output="$OPTARG"
;;
'?')
help
exit 1
;;
esac
done
shift $(($OPTIND - 1))
JAVAP_OPTS="${JAVAP_OPTS:--v -p -s -sysinfo -constants}"
if (( $simple )) ; then
JAVAP_OPTS="-p -c -v"
fi
# Normalize a java class name.
# Convert '.' to '/'
# Remove the *.class suffix.
normalize() {
local name="$1"
name="${name%.class}" # Remove the .class suffix.
echo "$name" | tr '.' '/'
}
# Convert the output for `-s` as needed.
filter_output() {
if (( $simple )) ; then
# For "simple output" mode,
# - Normalize the constant numbers (replace with "#x")
# - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:")
# - Remove the constant pool
# - Remove the line number table
# - Some other transient lines
#
# `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
# the start and the end lines.
sed -e 's/#[0-9][0-9]*/#x/g' \
-e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
-e '/^Constant pool:/,/^[^ ]/{//!d}' \
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
-e '/SHA-256 checksum/d' \
-e '/Last modified/d' \
-e '/^Classfile jar/d'
else
cat # Print as-is.
fi
}
# Write to the output file (specified with -o) as needed.
write_to_out() {
if [[ -n "$output" ]] ; then
cat >"$output"
echo "Wrote output to $output" 1>&2
else
cat # print to stdout
fi
}
for file in "${@}"; do
# *.class?
if echo "$file" | grep -qE '\.class$' ; then
echo "# Class: $file" 1>&2
javap $dump_code_opt $JAVAP_OPTS $file
# *.jar?
elif echo "$file" | grep -qE '\.jar(:.*)?$' ; then
# Take the regex. Remove everything up to : in $file
regex=""
if [[ "$file" =~ : ]] ; then
regex="$(normalize "${file##*:}")"
fi
# Remove everything after ':', inclusively, in $file.
file="${file%:*}"
# Print the filename and the regex.
if ! (( $simple )) ; then
echo -n "# Jar: $file"
if [[ "$regex" != "" ]] ;then
echo -n " (regex: $regex)"
fi
echo
fi
jar tf "$file" | grep '\.class$' | sort | while read -r class ; do
if normalize "$class" | grep -q -- "$regex" ; then
echo "## Class: $class"
javap $dump_code_opt $JAVAP_OPTS -cp $file ${class%.class}
else
(( $verbose )) && echo "## Skipping class: $class"
fi
done
else
echo "Unknown file type: $file" 1>&2
exit 1
fi
done | filter_output | write_to_out