xref: /aosp_15_r20/external/angle/build/android/pylib/utils/dexdump.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2016 The Chromium Authors
2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
4*8975f5c5SAndroid Build Coastguard Worker
5*8975f5c5SAndroid Build Coastguard Workerimport os
6*8975f5c5SAndroid Build Coastguard Workerimport re
7*8975f5c5SAndroid Build Coastguard Workerimport shutil
8*8975f5c5SAndroid Build Coastguard Workerimport sys
9*8975f5c5SAndroid Build Coastguard Workerimport tempfile
10*8975f5c5SAndroid Build Coastguard Workerfrom xml.etree import ElementTree
11*8975f5c5SAndroid Build Coastguard Workerfrom collections import namedtuple
12*8975f5c5SAndroid Build Coastguard Workerfrom typing import Dict
13*8975f5c5SAndroid Build Coastguard Worker
14*8975f5c5SAndroid Build Coastguard Workerfrom devil.utils import cmd_helper
15*8975f5c5SAndroid Build Coastguard Workerfrom pylib import constants
16*8975f5c5SAndroid Build Coastguard Worker
17*8975f5c5SAndroid Build Coastguard Workersys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'gyp'))
18*8975f5c5SAndroid Build Coastguard Workerfrom util import build_utils
19*8975f5c5SAndroid Build Coastguard Worker
20*8975f5c5SAndroid Build Coastguard WorkerDEXDUMP_PATH = os.path.join(constants.ANDROID_SDK_TOOLS, 'dexdump')
21*8975f5c5SAndroid Build Coastguard Worker
22*8975f5c5SAndroid Build Coastguard Worker
23*8975f5c5SAndroid Build Coastguard Worker# Annotations dict format:
24*8975f5c5SAndroid Build Coastguard Worker#   {
25*8975f5c5SAndroid Build Coastguard Worker#     'empty-annotation-class-name': None,
26*8975f5c5SAndroid Build Coastguard Worker#     'annotation-class-name': {
27*8975f5c5SAndroid Build Coastguard Worker#       'fieldA': 'primitive-value',
28*8975f5c5SAndroid Build Coastguard Worker#       'fieldB': [ 'array-item-1', 'array-item-2', ... ],
29*8975f5c5SAndroid Build Coastguard Worker#       'fieldC': {  # CURRENTLY UNSUPPORTED.
30*8975f5c5SAndroid Build Coastguard Worker#         /* Object value */
31*8975f5c5SAndroid Build Coastguard Worker#         'field': 'primitive-value',
32*8975f5c5SAndroid Build Coastguard Worker#         'field': [ 'array-item-1', 'array-item-2', ... ],
33*8975f5c5SAndroid Build Coastguard Worker#         'field': { /* Object value */ }
34*8975f5c5SAndroid Build Coastguard Worker#       }
35*8975f5c5SAndroid Build Coastguard Worker#     }
36*8975f5c5SAndroid Build Coastguard Worker#   }
37*8975f5c5SAndroid Build Coastguard WorkerAnnotations = namedtuple('Annotations',
38*8975f5c5SAndroid Build Coastguard Worker                         ['classAnnotations', 'methodsAnnotations'])
39*8975f5c5SAndroid Build Coastguard Worker
40*8975f5c5SAndroid Build Coastguard Worker# Finds each space-separated "foo=..." (where ... can contain spaces).
41*8975f5c5SAndroid Build Coastguard Worker_ANNOTATION_VALUE_MATCHER = re.compile(r'\w+=.*?(?:$|(?= \w+=))')
42*8975f5c5SAndroid Build Coastguard Worker
43*8975f5c5SAndroid Build Coastguard Worker
44*8975f5c5SAndroid Build Coastguard Workerdef Dump(apk_path):
45*8975f5c5SAndroid Build Coastguard Worker  """Dumps class and method information from a APK into a dict via dexdump.
46*8975f5c5SAndroid Build Coastguard Worker
47*8975f5c5SAndroid Build Coastguard Worker  Args:
48*8975f5c5SAndroid Build Coastguard Worker    apk_path: An absolute path to an APK file to dump.
49*8975f5c5SAndroid Build Coastguard Worker  Returns:
50*8975f5c5SAndroid Build Coastguard Worker    A dict in the following format:
51*8975f5c5SAndroid Build Coastguard Worker      {
52*8975f5c5SAndroid Build Coastguard Worker        <package_name>: {
53*8975f5c5SAndroid Build Coastguard Worker          'classes': {
54*8975f5c5SAndroid Build Coastguard Worker            <class_name>: {
55*8975f5c5SAndroid Build Coastguard Worker              'methods': [<method_1>, <method_2>],
56*8975f5c5SAndroid Build Coastguard Worker              'superclass': <string>,
57*8975f5c5SAndroid Build Coastguard Worker              'is_abstract': <boolean>,
58*8975f5c5SAndroid Build Coastguard Worker              'annotations': <Annotations>
59*8975f5c5SAndroid Build Coastguard Worker            }
60*8975f5c5SAndroid Build Coastguard Worker          }
61*8975f5c5SAndroid Build Coastguard Worker        }
62*8975f5c5SAndroid Build Coastguard Worker      }
63*8975f5c5SAndroid Build Coastguard Worker  """
64*8975f5c5SAndroid Build Coastguard Worker  try:
65*8975f5c5SAndroid Build Coastguard Worker    dexfile_dir = tempfile.mkdtemp()
66*8975f5c5SAndroid Build Coastguard Worker    parsed_dex_files = []
67*8975f5c5SAndroid Build Coastguard Worker    for dex_file in build_utils.ExtractAll(apk_path,
68*8975f5c5SAndroid Build Coastguard Worker                                           dexfile_dir,
69*8975f5c5SAndroid Build Coastguard Worker                                           pattern='*classes*.dex'):
70*8975f5c5SAndroid Build Coastguard Worker      output_xml = cmd_helper.GetCmdOutput(
71*8975f5c5SAndroid Build Coastguard Worker          [DEXDUMP_PATH, '-a', '-j', '-l', 'xml', dex_file])
72*8975f5c5SAndroid Build Coastguard Worker      # Dexdump doesn't escape its XML output very well; decode it as utf-8 with
73*8975f5c5SAndroid Build Coastguard Worker      # invalid sequences replaced, then remove forbidden characters and
74*8975f5c5SAndroid Build Coastguard Worker      # re-encode it (as etree expects a byte string as input so it can figure
75*8975f5c5SAndroid Build Coastguard Worker      # out the encoding itself from the XML declaration)
76*8975f5c5SAndroid Build Coastguard Worker      BAD_XML_CHARS = re.compile(
77*8975f5c5SAndroid Build Coastguard Worker          u'[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f-\x84\x86-\x9f' +
78*8975f5c5SAndroid Build Coastguard Worker          u'\ud800-\udfff\ufdd0-\ufddf\ufffe-\uffff]')
79*8975f5c5SAndroid Build Coastguard Worker
80*8975f5c5SAndroid Build Coastguard Worker      # Line duplicated to avoid pylint redefined-variable-type error.
81*8975f5c5SAndroid Build Coastguard Worker      clean_xml = BAD_XML_CHARS.sub(u'\ufffd', output_xml)
82*8975f5c5SAndroid Build Coastguard Worker
83*8975f5c5SAndroid Build Coastguard Worker      # Constructors are referenced as "<init>" in our annotations
84*8975f5c5SAndroid Build Coastguard Worker      # which will result in in the ElementTree failing to parse
85*8975f5c5SAndroid Build Coastguard Worker      # our xml as it won't find a closing tag for this
86*8975f5c5SAndroid Build Coastguard Worker      clean_xml = clean_xml.replace('<init>', 'constructor')
87*8975f5c5SAndroid Build Coastguard Worker
88*8975f5c5SAndroid Build Coastguard Worker      annotations = _ParseAnnotations(clean_xml)
89*8975f5c5SAndroid Build Coastguard Worker
90*8975f5c5SAndroid Build Coastguard Worker      parsed_dex_files.append(
91*8975f5c5SAndroid Build Coastguard Worker          _ParseRootNode(ElementTree.fromstring(clean_xml.encode('utf-8')),
92*8975f5c5SAndroid Build Coastguard Worker                         annotations))
93*8975f5c5SAndroid Build Coastguard Worker    return parsed_dex_files
94*8975f5c5SAndroid Build Coastguard Worker  finally:
95*8975f5c5SAndroid Build Coastguard Worker    shutil.rmtree(dexfile_dir)
96*8975f5c5SAndroid Build Coastguard Worker
97*8975f5c5SAndroid Build Coastguard Worker
98*8975f5c5SAndroid Build Coastguard Workerdef _ParseAnnotationValues(values_str):
99*8975f5c5SAndroid Build Coastguard Worker  if not values_str:
100*8975f5c5SAndroid Build Coastguard Worker    return None
101*8975f5c5SAndroid Build Coastguard Worker  ret = {}
102*8975f5c5SAndroid Build Coastguard Worker  for key_value in _ANNOTATION_VALUE_MATCHER.findall(values_str):
103*8975f5c5SAndroid Build Coastguard Worker    key, value_str = key_value.split('=', 1)
104*8975f5c5SAndroid Build Coastguard Worker    # TODO: support for dicts if ever needed.
105*8975f5c5SAndroid Build Coastguard Worker    if value_str.startswith('{ ') and value_str.endswith(' }'):
106*8975f5c5SAndroid Build Coastguard Worker      value = value_str[2:-2].split()
107*8975f5c5SAndroid Build Coastguard Worker    else:
108*8975f5c5SAndroid Build Coastguard Worker      value = value_str
109*8975f5c5SAndroid Build Coastguard Worker    ret[key] = value
110*8975f5c5SAndroid Build Coastguard Worker  return ret
111*8975f5c5SAndroid Build Coastguard Worker
112*8975f5c5SAndroid Build Coastguard Worker
113*8975f5c5SAndroid Build Coastguard Workerdef _ParseAnnotations(dexRaw: str) -> Dict[int, Annotations]:
114*8975f5c5SAndroid Build Coastguard Worker  """ Parse XML strings and return a list of Annotations mapped to
115*8975f5c5SAndroid Build Coastguard Worker  classes by index.
116*8975f5c5SAndroid Build Coastguard Worker
117*8975f5c5SAndroid Build Coastguard Worker  Annotations are written to the dex dump as human readable blocks of text
118*8975f5c5SAndroid Build Coastguard Worker  The only prescription is that they appear before the class in our xml file
119*8975f5c5SAndroid Build Coastguard Worker  They are not required to be nested within the package as our classes
120*8975f5c5SAndroid Build Coastguard Worker  It is simpler to parse for all the annotations and then associate them
121*8975f5c5SAndroid Build Coastguard Worker  back to the
122*8975f5c5SAndroid Build Coastguard Worker  classes
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker  Example:
125*8975f5c5SAndroid Build Coastguard Worker  Class #12 annotations:
126*8975f5c5SAndroid Build Coastguard Worker  Annotations on class
127*8975f5c5SAndroid Build Coastguard Worker    VISIBILITY_RUNTIME Ldalvik/annotation/EnclosingClass; value=...
128*8975f5c5SAndroid Build Coastguard Worker  Annotations on method #512 'example'
129*8975f5c5SAndroid Build Coastguard Worker    VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value=...
130*8975f5c5SAndroid Build Coastguard Worker    VISIBILITY_RUNTIME Landroidx/test/filters/SmallTest;
131*8975f5c5SAndroid Build Coastguard Worker    VISIBILITY_RUNTIME Lorg/chromium/base/test/util/Feature; value={ Cronet }
132*8975f5c5SAndroid Build Coastguard Worker    VISIBILITY_RUNTIME LFoo; key1={ A B } key2=4104 key3=null
133*8975f5c5SAndroid Build Coastguard Worker  """
134*8975f5c5SAndroid Build Coastguard Worker
135*8975f5c5SAndroid Build Coastguard Worker  # We want to find the lines matching the annotations header pattern
136*8975f5c5SAndroid Build Coastguard Worker  # Eg: Class #12 annotations -> true
137*8975f5c5SAndroid Build Coastguard Worker  annotationsBlockMatcher = re.compile(u'^Class #.*annotations:$')
138*8975f5c5SAndroid Build Coastguard Worker  # We want to retrieve the index of the class
139*8975f5c5SAndroid Build Coastguard Worker  # Eg: Class #12 annotations -> 12
140*8975f5c5SAndroid Build Coastguard Worker  classIndexMatcher = re.compile(u'(?<=#)[0-9]*')
141*8975f5c5SAndroid Build Coastguard Worker  # We want to retrieve the method name from between the quotes
142*8975f5c5SAndroid Build Coastguard Worker  # of the annotations line
143*8975f5c5SAndroid Build Coastguard Worker  # Eg: Annotations on method #512 'example'  -> example
144*8975f5c5SAndroid Build Coastguard Worker  methodMatcher = re.compile(u"(?<=')[^']*")
145*8975f5c5SAndroid Build Coastguard Worker  # We want to match everything after the last slash until before the semi colon
146*8975f5c5SAndroid Build Coastguard Worker  # Eg: Ldalvik/annotation/Signature; -> Signature
147*8975f5c5SAndroid Build Coastguard Worker  annotationMatcher = re.compile(u'([^/]+); ?(.*)?')
148*8975f5c5SAndroid Build Coastguard Worker
149*8975f5c5SAndroid Build Coastguard Worker  annotations = {}
150*8975f5c5SAndroid Build Coastguard Worker  currentAnnotationsForClass = None
151*8975f5c5SAndroid Build Coastguard Worker  currentAnnotationsBlock: Dict[str, None] = None
152*8975f5c5SAndroid Build Coastguard Worker
153*8975f5c5SAndroid Build Coastguard Worker  # This loop does four things
154*8975f5c5SAndroid Build Coastguard Worker  # 1. It looks for a line telling us we are describing annotations for
155*8975f5c5SAndroid Build Coastguard Worker  #  a new class
156*8975f5c5SAndroid Build Coastguard Worker  # 2. It looks for a line telling us if the annotations we find will be
157*8975f5c5SAndroid Build Coastguard Worker  #  for the class or for any of it's methods; we will keep reference to
158*8975f5c5SAndroid Build Coastguard Worker  #  this
159*8975f5c5SAndroid Build Coastguard Worker  # 3. It adds the annotations to whatever we are holding reference to
160*8975f5c5SAndroid Build Coastguard Worker  # 4. It looks for a line to see if we should start looking for a
161*8975f5c5SAndroid Build Coastguard Worker  #  new class again
162*8975f5c5SAndroid Build Coastguard Worker  for line in dexRaw.splitlines():
163*8975f5c5SAndroid Build Coastguard Worker    if currentAnnotationsForClass is None:
164*8975f5c5SAndroid Build Coastguard Worker      # Step 1
165*8975f5c5SAndroid Build Coastguard Worker      # We keep searching until we find an annotation descriptor
166*8975f5c5SAndroid Build Coastguard Worker      # This lets us know that we are storing annotations for a new class
167*8975f5c5SAndroid Build Coastguard Worker      if annotationsBlockMatcher.match(line):
168*8975f5c5SAndroid Build Coastguard Worker        currentClassIndex = int(classIndexMatcher.findall(line)[0])
169*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsForClass = Annotations(classAnnotations={},
170*8975f5c5SAndroid Build Coastguard Worker                                                 methodsAnnotations={})
171*8975f5c5SAndroid Build Coastguard Worker        annotations[currentClassIndex] = currentAnnotationsForClass
172*8975f5c5SAndroid Build Coastguard Worker    else:
173*8975f5c5SAndroid Build Coastguard Worker      # Step 2
174*8975f5c5SAndroid Build Coastguard Worker      # If we find a descriptor indicating we are tracking annotations
175*8975f5c5SAndroid Build Coastguard Worker      # for the class or it's methods, we'll keep a reference of this
176*8975f5c5SAndroid Build Coastguard Worker      # block for when we start finding annotation references
177*8975f5c5SAndroid Build Coastguard Worker      if line.startswith(u'Annotations on class'):
178*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsBlock = currentAnnotationsForClass.classAnnotations
179*8975f5c5SAndroid Build Coastguard Worker      elif line.startswith(u'Annotations on method'):
180*8975f5c5SAndroid Build Coastguard Worker        method = methodMatcher.findall(line)[0]
181*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsBlock = {}
182*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsForClass.methodsAnnotations[
183*8975f5c5SAndroid Build Coastguard Worker            method] = currentAnnotationsBlock
184*8975f5c5SAndroid Build Coastguard Worker
185*8975f5c5SAndroid Build Coastguard Worker      # If we match against any other type of annotations
186*8975f5c5SAndroid Build Coastguard Worker      # we will ignore them
187*8975f5c5SAndroid Build Coastguard Worker      elif line.startswith(u'Annotations on'):
188*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsBlock = None
189*8975f5c5SAndroid Build Coastguard Worker
190*8975f5c5SAndroid Build Coastguard Worker      # Step 3
191*8975f5c5SAndroid Build Coastguard Worker      # We are only adding runtime annotations as those are the types
192*8975f5c5SAndroid Build Coastguard Worker      # that will affect if we should run tests or not (where this is
193*8975f5c5SAndroid Build Coastguard Worker      # being used)
194*8975f5c5SAndroid Build Coastguard Worker      elif currentAnnotationsBlock is not None and line.strip().startswith(
195*8975f5c5SAndroid Build Coastguard Worker          'VISIBILITY_RUNTIME'):
196*8975f5c5SAndroid Build Coastguard Worker        annotationName, annotationValuesStr = annotationMatcher.findall(line)[0]
197*8975f5c5SAndroid Build Coastguard Worker        annotationValues = _ParseAnnotationValues(annotationValuesStr)
198*8975f5c5SAndroid Build Coastguard Worker
199*8975f5c5SAndroid Build Coastguard Worker        # Our instrumentation tests expect a mapping of "Annotation: Value"
200*8975f5c5SAndroid Build Coastguard Worker        # We aren't using the value for anything and this would increase
201*8975f5c5SAndroid Build Coastguard Worker        # the complexity of this parser so just mapping these to None
202*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsBlock.update({annotationName: annotationValues})
203*8975f5c5SAndroid Build Coastguard Worker
204*8975f5c5SAndroid Build Coastguard Worker      # Step 4
205*8975f5c5SAndroid Build Coastguard Worker      # Empty lines indicate that the annotation descriptions are complete
206*8975f5c5SAndroid Build Coastguard Worker      # and we should look for new classes
207*8975f5c5SAndroid Build Coastguard Worker      elif not line.strip():
208*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsForClass = None
209*8975f5c5SAndroid Build Coastguard Worker        currentAnnotationsBlock = None
210*8975f5c5SAndroid Build Coastguard Worker
211*8975f5c5SAndroid Build Coastguard Worker  return annotations
212*8975f5c5SAndroid Build Coastguard Worker
213*8975f5c5SAndroid Build Coastguard Worker
214*8975f5c5SAndroid Build Coastguard Workerdef _ParseRootNode(root, annotations: Dict[int, Annotations]):
215*8975f5c5SAndroid Build Coastguard Worker  """Parses the XML output of dexdump. This output is in the following format.
216*8975f5c5SAndroid Build Coastguard Worker
217*8975f5c5SAndroid Build Coastguard Worker  This is a subset of the information contained within dexdump output.
218*8975f5c5SAndroid Build Coastguard Worker
219*8975f5c5SAndroid Build Coastguard Worker  <api>
220*8975f5c5SAndroid Build Coastguard Worker    <package name="foo.bar">
221*8975f5c5SAndroid Build Coastguard Worker      <class name="Class" extends="foo.bar.SuperClass">
222*8975f5c5SAndroid Build Coastguard Worker        <field name="Field">
223*8975f5c5SAndroid Build Coastguard Worker        </field>
224*8975f5c5SAndroid Build Coastguard Worker        <constructor name="Method">
225*8975f5c5SAndroid Build Coastguard Worker          <parameter name="Param" type="int">
226*8975f5c5SAndroid Build Coastguard Worker          </parameter>
227*8975f5c5SAndroid Build Coastguard Worker        </constructor>
228*8975f5c5SAndroid Build Coastguard Worker        <method name="Method">
229*8975f5c5SAndroid Build Coastguard Worker          <parameter name="Param" type="int">
230*8975f5c5SAndroid Build Coastguard Worker          </parameter>
231*8975f5c5SAndroid Build Coastguard Worker        </method>
232*8975f5c5SAndroid Build Coastguard Worker      </class>
233*8975f5c5SAndroid Build Coastguard Worker    </package>
234*8975f5c5SAndroid Build Coastguard Worker  </api>
235*8975f5c5SAndroid Build Coastguard Worker  """
236*8975f5c5SAndroid Build Coastguard Worker  results = {}
237*8975f5c5SAndroid Build Coastguard Worker
238*8975f5c5SAndroid Build Coastguard Worker  # Annotations are referenced by the class order
239*8975f5c5SAndroid Build Coastguard Worker  # To match them, we need to keep track of the class number and
240*8975f5c5SAndroid Build Coastguard Worker  # match it to the appropriate annotation at that stage
241*8975f5c5SAndroid Build Coastguard Worker  classCount = 0
242*8975f5c5SAndroid Build Coastguard Worker
243*8975f5c5SAndroid Build Coastguard Worker  for child in root:
244*8975f5c5SAndroid Build Coastguard Worker    if child.tag == 'package':
245*8975f5c5SAndroid Build Coastguard Worker      package_name = child.attrib['name']
246*8975f5c5SAndroid Build Coastguard Worker      parsed_node, classCount = _ParsePackageNode(child, classCount,
247*8975f5c5SAndroid Build Coastguard Worker                                                  annotations)
248*8975f5c5SAndroid Build Coastguard Worker      if package_name in results:
249*8975f5c5SAndroid Build Coastguard Worker        results[package_name]['classes'].update(parsed_node['classes'])
250*8975f5c5SAndroid Build Coastguard Worker      else:
251*8975f5c5SAndroid Build Coastguard Worker        results[package_name] = parsed_node
252*8975f5c5SAndroid Build Coastguard Worker  return results
253*8975f5c5SAndroid Build Coastguard Worker
254*8975f5c5SAndroid Build Coastguard Worker
255*8975f5c5SAndroid Build Coastguard Workerdef _ParsePackageNode(package_node, classCount: int,
256*8975f5c5SAndroid Build Coastguard Worker                      annotations: Dict[int, Annotations]):
257*8975f5c5SAndroid Build Coastguard Worker  """Parses a <package> node from the dexdump xml output.
258*8975f5c5SAndroid Build Coastguard Worker
259*8975f5c5SAndroid Build Coastguard Worker  Returns:
260*8975f5c5SAndroid Build Coastguard Worker    A tuple in the format:
261*8975f5c5SAndroid Build Coastguard Worker      (classes: {
262*8975f5c5SAndroid Build Coastguard Worker        'classes': {
263*8975f5c5SAndroid Build Coastguard Worker          <class_1>: {
264*8975f5c5SAndroid Build Coastguard Worker            'methods': [<method_1>, <method_2>],
265*8975f5c5SAndroid Build Coastguard Worker            'superclass': <string>,
266*8975f5c5SAndroid Build Coastguard Worker            'is_abstract': <boolean>,
267*8975f5c5SAndroid Build Coastguard Worker            'annotations': <Annotations or None>
268*8975f5c5SAndroid Build Coastguard Worker          },
269*8975f5c5SAndroid Build Coastguard Worker          <class_2>: {
270*8975f5c5SAndroid Build Coastguard Worker            'methods': [<method_1>, <method_2>],
271*8975f5c5SAndroid Build Coastguard Worker            'superclass': <string>,
272*8975f5c5SAndroid Build Coastguard Worker            'is_abstract': <boolean>,
273*8975f5c5SAndroid Build Coastguard Worker            'annotations': <Annotations or None>
274*8975f5c5SAndroid Build Coastguard Worker          },
275*8975f5c5SAndroid Build Coastguard Worker        }
276*8975f5c5SAndroid Build Coastguard Worker      }, classCount: number)
277*8975f5c5SAndroid Build Coastguard Worker  """
278*8975f5c5SAndroid Build Coastguard Worker  classes = {}
279*8975f5c5SAndroid Build Coastguard Worker  for child in package_node:
280*8975f5c5SAndroid Build Coastguard Worker    if child.tag == 'class':
281*8975f5c5SAndroid Build Coastguard Worker      classes[child.attrib['name']] = _ParseClassNode(child, classCount,
282*8975f5c5SAndroid Build Coastguard Worker                                                      annotations)
283*8975f5c5SAndroid Build Coastguard Worker      classCount += 1
284*8975f5c5SAndroid Build Coastguard Worker  return ({'classes': classes}, classCount)
285*8975f5c5SAndroid Build Coastguard Worker
286*8975f5c5SAndroid Build Coastguard Worker
287*8975f5c5SAndroid Build Coastguard Workerdef _ParseClassNode(class_node, classIndex: int,
288*8975f5c5SAndroid Build Coastguard Worker                    annotations: Dict[int, Annotations]):
289*8975f5c5SAndroid Build Coastguard Worker  """Parses a <class> node from the dexdump xml output.
290*8975f5c5SAndroid Build Coastguard Worker
291*8975f5c5SAndroid Build Coastguard Worker  Returns:
292*8975f5c5SAndroid Build Coastguard Worker    A dict in the format:
293*8975f5c5SAndroid Build Coastguard Worker      {
294*8975f5c5SAndroid Build Coastguard Worker        'methods': [<method_1>, <method_2>],
295*8975f5c5SAndroid Build Coastguard Worker        'superclass': <string>,
296*8975f5c5SAndroid Build Coastguard Worker        'is_abstract': <boolean>
297*8975f5c5SAndroid Build Coastguard Worker      }
298*8975f5c5SAndroid Build Coastguard Worker  """
299*8975f5c5SAndroid Build Coastguard Worker  methods = []
300*8975f5c5SAndroid Build Coastguard Worker  for child in class_node:
301*8975f5c5SAndroid Build Coastguard Worker    if child.tag == 'method' and child.attrib['visibility'] == 'public':
302*8975f5c5SAndroid Build Coastguard Worker      methods.append(child.attrib['name'])
303*8975f5c5SAndroid Build Coastguard Worker  return {
304*8975f5c5SAndroid Build Coastguard Worker      'methods':
305*8975f5c5SAndroid Build Coastguard Worker      methods,
306*8975f5c5SAndroid Build Coastguard Worker      'superclass':
307*8975f5c5SAndroid Build Coastguard Worker      class_node.attrib['extends'],
308*8975f5c5SAndroid Build Coastguard Worker      'is_abstract':
309*8975f5c5SAndroid Build Coastguard Worker      class_node.attrib.get('abstract') == 'true',
310*8975f5c5SAndroid Build Coastguard Worker      'annotations':
311*8975f5c5SAndroid Build Coastguard Worker      annotations.get(classIndex,
312*8975f5c5SAndroid Build Coastguard Worker                      Annotations(classAnnotations={}, methodsAnnotations={}))
313*8975f5c5SAndroid Build Coastguard Worker  }
314