• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/bash
2# Copyright (C) 2023 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16set -e
17
18
19help() {
20    cat <<'EOF'
21
22  dump-jar: Dump java classes in jar files
23
24    Usage:
25      dump-jar [-v] CLASS-FILE [...]
26
27        Dump a *.class file
28
29      dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: class internal name regex] [...]
30
31        Dump a jar file.
32
33        If a filename contains a ':', then the following part
34        will be used to filter files in the jar file that matches against class internal names.
35
36        For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar.
37
38    Options:
39      -v: Enable verbose output.
40
41      -s: Simple output mode, used to check HostStubGen output jars.
42
43      -o: Write the output to a specified file.
44EOF
45}
46
47# Parse the options.
48
49verbose=0
50simple=0
51output=""
52while getopts "hvso:" opt; do
53case "$opt" in
54    h)
55        help
56        exit 0
57        ;;
58    v)
59        verbose=1
60        ;;
61    s)
62        simple=1
63        ;;
64    o)
65        output="$OPTARG"
66        ;;
67    '?')
68        help
69        exit 1
70        ;;
71esac
72done
73shift $(($OPTIND - 1))
74
75JAVAP_OPTS="${JAVAP_OPTS:--v -p -s -sysinfo -constants}"
76
77if (( $simple )) ; then
78  JAVAP_OPTS="-p -c -v"
79fi
80
81# Convert the output for `-s` as needed.
82filter_output() {
83  if (( $simple )) ; then
84    # For "simple output" mode,
85    # - Normalize the constant numbers (replace with "#x")
86    # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:")
87    # - Remove the constant pool
88    # - Remove the line number table
89    # - Some other transient lines
90    # - Sometimes the javap shows mysterious warnings, so remove them too.
91    #
92    # Most conversion are simple per-line deletion or simple inline replacement with a regex.
93    #
94    # But removing the constant pool is a bit tricky. It looks like this in the output:
95    #---------------------------
96    #Constant pool:
97    #    #1 = Methodref          #31.#88       // java/lang/Object."<init>":()V
98    #    #2 = Class              #89           // java/lang/UnsupportedOperationException
99    #    :
100    #{ // Or something, I'm not sure if it always ends with a "{".
101    #---------------------------
102    # i.e. we want to delete all lines from "Constant pool:" as long as the first character
103    # is a space.
104    #
105    # If we simply use '/^Constant pool:/,/^[^ ]/d', then it'll delete the "Constant pool:"
106    # line and "{" line too, but again the last line might be important, so we don't want to
107    # delete it.
108    #
109    # So we instead, use '/^Constant pool:/,/^[^ ]/{/^ /d}', which mean:
110    # between lines matching '/^Constant pool:/' and '/^[^ ]/', delete lines that start with
111    # a space. (=='/^ /d').
112    #
113    sed -e 's/#[0-9][0-9]*/#x/g' \
114        -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
115        -e '/^Constant pool:/,/^[^ ]/{/^ /d}' \
116        -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
117        -e '/^ *SHA-256 checksum/d' \
118        -e '/^ *Last modified/d' \
119        -e '/^Classfile jar/d' \
120        -e '/\[warning\]/d'
121  else
122    cat # Print as-is.
123  fi
124}
125
126# Write to the output file (specified with -o) as needed.
127write_to_out() {
128  if [[ -n "$output" ]] ; then
129    cat >"$output"
130    echo "Wrote output to $output" 1>&2
131  else
132    cat # print to stdout
133  fi
134}
135
136# Read jar file names and remove the .class suffix.
137# Also remove non-class files.
138to_internal_names() {
139    sed -ne 's/\.class$//p'
140}
141
142for file in "${@}"; do
143
144    # *.class?
145    if echo "$file" | grep -qE '\.class$' ; then
146        echo "# Class: $file" 1>&2
147        javap $dump_code_opt $JAVAP_OPTS $file
148
149    # *.jar?
150    elif echo "$file" | grep -qE '\.jar(:.*)?$' ; then
151        # Take the regex. Remove everything up to : in $file
152        regex=""
153        if [[ "$file" =~ : ]] ; then
154            regex="${file##*:}"
155        fi
156
157        # Remove everything after ':', inclusively, in $file.
158        file="${file%:*}"
159
160        # Print the filename and the regex.
161        if ! (( $simple )) ; then
162          echo -n "# Jar: $file"
163          if [[ "$regex" != "" ]] ;then
164              echo -n "  (regex: $regex)"
165          fi
166          echo
167        fi
168
169        jar tf "$file" | sort | to_internal_names | grep -- "$regex" | while read -r class ; do
170            echo "## Class: $class.class"
171            javap $dump_code_opt $JAVAP_OPTS -cp "$file" "${class}"
172        done
173
174    else
175        echo "Unknown file type: $file" 1>&2
176        exit 1
177    fi
178done | filter_output | write_to_out
179