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