#!/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[: class internal name regex] [...] Dump a jar file. If a filename contains a ':', then the following part will be used to filter files in the jar file that matches against class internal names. 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 # 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 # - Sometimes the javap shows mysterious warnings, so remove them too. # # `/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' \ -e '/\[warning\]/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 } # Read jar file names and remove the .class suffix. # Also remove non-class files. to_internal_names() { sed -ne 's/\.class$//p' } 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="${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" | sort | to_internal_names | grep -- "$regex" | while read -r class ; do echo "## Class: $class.class" javap $dump_code_opt $JAVAP_OPTS -cp "$file" "${class}" done else echo "Unknown file type: $file" 1>&2 exit 1 fi done | filter_output | write_to_out