summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/android/pylib/utils/dexdump.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/build/android/pylib/utils/dexdump.py')
-rw-r--r--third_party/libwebrtc/build/android/pylib/utils/dexdump.py136
1 files changed, 136 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/pylib/utils/dexdump.py b/third_party/libwebrtc/build/android/pylib/utils/dexdump.py
new file mode 100644
index 0000000000..f81ac603d4
--- /dev/null
+++ b/third_party/libwebrtc/build/android/pylib/utils/dexdump.py
@@ -0,0 +1,136 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import re
+import shutil
+import sys
+import tempfile
+from xml.etree import ElementTree
+
+from devil.utils import cmd_helper
+from pylib import constants
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'gyp'))
+from util import build_utils
+
+DEXDUMP_PATH = os.path.join(constants.ANDROID_SDK_TOOLS, 'dexdump')
+
+
+def Dump(apk_path):
+ """Dumps class and method information from a APK into a dict via dexdump.
+
+ Args:
+ apk_path: An absolute path to an APK file to dump.
+ Returns:
+ A dict in the following format:
+ {
+ <package_name>: {
+ 'classes': {
+ <class_name>: {
+ 'methods': [<method_1>, <method_2>]
+ }
+ }
+ }
+ }
+ """
+ try:
+ dexfile_dir = tempfile.mkdtemp()
+ parsed_dex_files = []
+ for dex_file in build_utils.ExtractAll(apk_path,
+ dexfile_dir,
+ pattern='*classes*.dex'):
+ output_xml = cmd_helper.GetCmdOutput(
+ [DEXDUMP_PATH, '-l', 'xml', dex_file])
+ # Dexdump doesn't escape its XML output very well; decode it as utf-8 with
+ # invalid sequences replaced, then remove forbidden characters and
+ # re-encode it (as etree expects a byte string as input so it can figure
+ # out the encoding itself from the XML declaration)
+ BAD_XML_CHARS = re.compile(
+ u'[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f-\x84\x86-\x9f' +
+ u'\ud800-\udfff\ufdd0-\ufddf\ufffe-\uffff]')
+ if sys.version_info[0] < 3:
+ decoded_xml = output_xml.decode('utf-8', 'replace')
+ clean_xml = BAD_XML_CHARS.sub(u'\ufffd', decoded_xml)
+ else:
+ # Line duplicated to avoid pylint redefined-variable-type error.
+ clean_xml = BAD_XML_CHARS.sub(u'\ufffd', output_xml)
+ parsed_dex_files.append(
+ _ParseRootNode(ElementTree.fromstring(clean_xml.encode('utf-8'))))
+ return parsed_dex_files
+ finally:
+ shutil.rmtree(dexfile_dir)
+
+
+def _ParseRootNode(root):
+ """Parses the XML output of dexdump. This output is in the following format.
+
+ This is a subset of the information contained within dexdump output.
+
+ <api>
+ <package name="foo.bar">
+ <class name="Class" extends="foo.bar.SuperClass">
+ <field name="Field">
+ </field>
+ <constructor name="Method">
+ <parameter name="Param" type="int">
+ </parameter>
+ </constructor>
+ <method name="Method">
+ <parameter name="Param" type="int">
+ </parameter>
+ </method>
+ </class>
+ </package>
+ </api>
+ """
+ results = {}
+ for child in root:
+ if child.tag == 'package':
+ package_name = child.attrib['name']
+ parsed_node = _ParsePackageNode(child)
+ if package_name in results:
+ results[package_name]['classes'].update(parsed_node['classes'])
+ else:
+ results[package_name] = parsed_node
+ return results
+
+
+def _ParsePackageNode(package_node):
+ """Parses a <package> node from the dexdump xml output.
+
+ Returns:
+ A dict in the format:
+ {
+ 'classes': {
+ <class_1>: {
+ 'methods': [<method_1>, <method_2>]
+ },
+ <class_2>: {
+ 'methods': [<method_1>, <method_2>]
+ },
+ }
+ }
+ """
+ classes = {}
+ for child in package_node:
+ if child.tag == 'class':
+ classes[child.attrib['name']] = _ParseClassNode(child)
+ return {'classes': classes}
+
+
+def _ParseClassNode(class_node):
+ """Parses a <class> node from the dexdump xml output.
+
+ Returns:
+ A dict in the format:
+ {
+ 'methods': [<method_1>, <method_2>]
+ }
+ """
+ methods = []
+ for child in class_node:
+ if child.tag == 'method':
+ methods.append(child.attrib['name'])
+ return {'methods': methods, 'superclass': class_node.attrib['extends']}