Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: components/cronet/tools/api_static_checks.py

Issue 2440613003: [Cronet] Enforce implementation does not call through API classes (Closed)
Patch Set: finish Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/cronet/android/BUILD.gn ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """api_static_checks.py - Check Cronet implementation does not call through
7 API classes.
8 """
9
10 import argparse
11 import os
12 import re
13 import shutil
14 import sys
15 import tempfile
16
17 REPOSITORY_ROOT = os.path.abspath(os.path.join(
18 os.path.dirname(__file__), '..', '..', '..'))
19
20 sys.path.append(os.path.join(REPOSITORY_ROOT, 'build/android/gyp/util'))
21 import build_utils
22
23 # These regular expressions catch the beginning of lines that declar classes and
kapishnikov 2016/12/01 20:34:17 declar -> declare
pauljensen 2016/12/02 18:24:14 Done.
24 # methods. The first group returned by a match is the class or method name.
25 CLASS_RE = re.compile(r'.*class ([^ ]*) .*{')
kapishnikov 2016/12/01 20:34:16 I think '\' is missing before '{' since we want to
pauljensen 2016/12/02 18:24:14 Done.
26 METHOD_RE = re.compile(r'.* ([^ ]*)\(.*\);')
27
28 # Allowed exceptions. Adding anything to this list is dangerous and should be
29 # avoided if possible. For now these exceptions are for APIs that existed in
30 # the first version of Cronet and will be supported forever.
31 # TODO(pauljensen): Remove these.
32 ALLOWED_EXCEPTIONS = [
33 'org.chromium.net.impl.CronetEngineBuilderImpl/build ->'
34 ' org/chromium/net/ExperimentalCronetEngine/getVersionString',
35 'org.chromium.net.urlconnection.CronetFixedModeOutputStream$UploadDataProviderI'
36 'mpl/read -> org/chromium/net/UploadDataSink/onReadSucceeded',
37 'org.chromium.net.urlconnection.CronetFixedModeOutputStream$UploadDataProviderI'
38 'mpl/rewind -> org/chromium/net/UploadDataSink/onRewindError',
39 'org.chromium.net.urlconnection.CronetHttpURLConnection/disconnect ->'
40 ' org/chromium/net/UrlRequest/cancel',
41 'org.chromium.net.urlconnection.CronetHttpURLConnection/disconnect ->'
42 ' org/chromium/net/UrlResponseInfo/getHttpStatusText',
43 'org.chromium.net.urlconnection.CronetHttpURLConnection/disconnect ->'
44 ' org/chromium/net/UrlResponseInfo/getHttpStatusCode',
45 'org.chromium.net.urlconnection.CronetHttpURLConnection/getHeaderField ->'
46 ' org/chromium/net/UrlResponseInfo/getHttpStatusCode',
47 'org.chromium.net.urlconnection.CronetHttpURLConnection/getErrorStream ->'
48 ' org/chromium/net/UrlResponseInfo/getHttpStatusCode',
49 'org.chromium.net.urlconnection.CronetHttpURLConnection/setConnectTimeout ->'
50 ' org/chromium/net/UrlRequest/read',
51 'org.chromium.net.urlconnection.CronetHttpURLConnection$CronetUrlRequestCallbac'
52 'k/onRedirectReceived -> org/chromium/net/UrlRequest/followRedirect',
53 'org.chromium.net.urlconnection.CronetHttpURLConnection$CronetUrlRequestCallbac'
54 'k/onRedirectReceived -> org/chromium/net/UrlRequest/cancel',
55 'org.chromium.net.urlconnection.CronetChunkedOutputStream$UploadDataProviderImp'
56 'l/read -> org/chromium/net/UploadDataSink/onReadSucceeded',
57 'org.chromium.net.urlconnection.CronetChunkedOutputStream$UploadDataProviderImp'
58 'l/rewind -> org/chromium/net/UploadDataSink/onRewindError',
59 'org.chromium.net.urlconnection.CronetBufferedOutputStream$UploadDataProviderIm'
60 'pl/read -> org/chromium/net/UploadDataSink/onReadSucceeded',
61 'org.chromium.net.urlconnection.CronetBufferedOutputStream$UploadDataProviderIm'
62 'pl/rewind -> org/chromium/net/UploadDataSink/onRewindSucceeded',
63 ]
64
65
66 def find_api_calls(dump, api_classes, bad_calls):
67 # Given a dump of an implementation class, find calls through API classes.
68 # |dump| is the output of "javap -c" on the implementation class files.
69 # |api_classes| is the list of classes comprising the API.
70 # |bad_calls| is the list of calls through API classes. This list is built up
71 # by this function.
72
73 for line in dump:
74 if CLASS_RE.match(line):
75 caller_class = CLASS_RE.match(line).group(1)
76 if METHOD_RE.match(line):
77 caller_method = METHOD_RE.match(line).group(1)
78 if line[8:16] == ': invoke':
kapishnikov 2016/12/01 20:34:17 This looks a little fragile. Can the output format
pauljensen 2016/12/02 18:24:14 I doubt the format will ever change; for example "
kapishnikov 2016/12/28 21:54:41 Acknowledged.
79 callee = line[54:].split(':')[0]
kapishnikov 2016/12/01 20:34:16 1. Can we add an assertion that |caller_class| and
pauljensen 2016/12/02 18:24:14 Done.
80 callee_class = callee.split('.')[0]
81 if callee_class in api_classes:
82 callee_method = callee.split('.')[1]
83 # Ignore default constructor calls
84 if callee_method == '"<init>"':
kapishnikov 2016/12/01 20:34:17 Should we also track the usage of constructors, i.
pauljensen 2016/12/02 18:24:14 We construct plenty of API classes from the impl.
kapishnikov 2016/12/28 21:54:41 Acknowledged.
85 continue
86 # Ignore VersionSafe calls
87 if 'VersionSafeCallbacks' in caller_class:
88 continue
89 bad_call = '%s/%s -> %s/%s' % (caller_class, caller_method,
90 callee_class, callee_method)
91 if bad_call in ALLOWED_EXCEPTIONS:
92 continue
93 bad_calls += [bad_call]
94
95
96 def main(args):
97 # Returns True if no calls through API classes in implementation.
98
99 parser = argparse.ArgumentParser(
100 description='Check modules do not contain ARM Neon instructions.')
101 parser.add_argument('--api_jar',
102 help='Path to API jar (i.e. cronet_api.jar)',
103 required=True,
104 metavar='path/to/cronet_api.jar')
105 parser.add_argument('--impl_jar',
106 help='Path to implementation jar '
107 '(i.e. cronet_impl_native_java.jar)',
108 required=True,
109 metavar='path/to/cronet_impl_native_java.jar',
110 action='append')
111 parser.add_argument('--stamp', help='Path to touch on success.')
112 opts = parser.parse_args(args)
113
114 temp_dir = tempfile.mkdtemp()
115
116 # Extract API class files from jar
117 jar_cmd = ['jar', 'xf', os.path.abspath(opts.api_jar)]
118 build_utils.CheckOutput(jar_cmd, cwd=temp_dir)
119 shutil.rmtree(os.path.join(temp_dir, 'META-INF'))
120
121 # Collect names of API classes
122 api_classes = []
123 for root, _, filenames in os.walk(temp_dir):
kapishnikov 2016/12/01 20:34:17 Should |root| be renamed to |dirpath|? I guess |te
pauljensen 2016/12/02 18:24:14 Done.
124 if not filenames:
125 continue
126 package = root[len(temp_dir + '/'):]
127 for filename in filenames:
128 if filename.endswith('.class'):
129 classname = filename[:-len('.class')]
130 api_classes += [package + '/' + classname]
131
132 shutil.rmtree(temp_dir)
133 temp_dir = tempfile.mkdtemp()
134
135 # Extract impl class files from jars
136 for impl_jar in opts.impl_jar:
137 jar_cmd = ['jar', 'xf', os.path.abspath(impl_jar)]
138 build_utils.CheckOutput(jar_cmd, cwd=temp_dir)
139 shutil.rmtree(os.path.join(temp_dir, 'META-INF'))
140
141 # Process classes
142 api_calls = []
kapishnikov 2016/12/01 20:34:16 Should we rename it to |bad_api_calls|?
pauljensen 2016/12/02 18:24:14 Done.
143 for root, _, filenames in os.walk(temp_dir):
144 if not filenames:
145 continue
146 # Dump classes
147 dump_file = os.path.join(temp_dir, 'dump.txt')
148 if os.system('javap -c %s > %s' % (
149 ' '.join(os.path.join(root, f) for f in filenames).replace('$', '\\$'),
150 dump_file)):
151 print 'ERROR: javap failed on ' + filenames
kapishnikov 2016/12/01 20:34:16 I think it is not allowed to concatenate string wi
pauljensen 2016/12/02 18:24:14 Done.
152 return False
153 # Process class dump
154 with open(dump_file, 'r') as dump:
155 find_api_calls(dump, api_classes, api_calls)
156
157 shutil.rmtree(temp_dir)
158
159 if api_calls:
160 print 'ERROR: Found the following calls from implementation classes through'
161 print ' API classes. These could fail if older API is used that'
162 print ' does not contain newer methods. Please call through a'
163 print ' wrapper class from VersionSafeCallbacks.'
164 print '\n'.join(api_calls)
165
166 if not api_calls and opts.stamp:
167 build_utils.Touch(opts.stamp)
168 return not api_calls
169
170
171 if __name__ == '__main__':
172 sys.exit(0 if main(sys.argv[1:]) != 0 else -1)
173
OLDNEW
« no previous file with comments | « components/cronet/android/BUILD.gn ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698