blob: ff5136e6507cb1ca22faf11bde1383b707c8bdae [file] [log] [blame]
Tao Baod7db5942015-01-28 10:07:51 -08001#!/usr/bin/python
2"""A glorified C pre-processor parser."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08003
Tao Baod7db5942015-01-28 10:07:51 -08004import ctypes
5import logging
6import os
7import re
8import site
9import utils
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080010
Tao Baod7db5942015-01-28 10:07:51 -080011top = os.getenv('ANDROID_BUILD_TOP')
12if top is None:
13 utils.panic('ANDROID_BUILD_TOP not set.\n')
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080014
Tao Baod7db5942015-01-28 10:07:51 -080015# Set up the env vars for libclang.
16site.addsitedir(os.path.join(top, 'external/clang/bindings/python'))
17os.putenv('LD_LIBRARY_PATH', os.path.join(top, 'prebuilts/sdk/tools/linux'))
18
19import clang.cindex
20from clang.cindex import conf
21from clang.cindex import Cursor
22from clang.cindex import CursorKind
23from clang.cindex import SourceLocation
24from clang.cindex import SourceRange
25from clang.cindex import TokenGroup
26from clang.cindex import TokenKind
27from clang.cindex import TranslationUnit
28
29from defaults import kCppUndefinedMacro
30from defaults import kernel_remove_config_macros
31from defaults import kernel_token_replacements
32
33
34debugBlockParser = False
35debugCppExpr = False
36debugOptimIf01 = False
37
38###############################################################################
39###############################################################################
40##### #####
41##### C P P T O K E N S #####
42##### #####
43###############################################################################
44###############################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080045
46# the list of supported C-preprocessor tokens
47# plus a couple of C tokens as well
Tao Baod7db5942015-01-28 10:07:51 -080048tokEOF = "\0"
49tokLN = "\n"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080050tokSTRINGIFY = "#"
Tao Baod7db5942015-01-28 10:07:51 -080051tokCONCAT = "##"
52tokLOGICAND = "&&"
53tokLOGICOR = "||"
54tokSHL = "<<"
55tokSHR = ">>"
56tokEQUAL = "=="
57tokNEQUAL = "!="
58tokLT = "<"
59tokLTE = "<="
60tokGT = ">"
61tokGTE = ">="
62tokELLIPSIS = "..."
63tokSPACE = " "
64tokDEFINED = "defined"
65tokLPAREN = "("
66tokRPAREN = ")"
67tokNOT = "!"
68tokPLUS = "+"
69tokMINUS = "-"
70tokMULTIPLY = "*"
71tokDIVIDE = "/"
72tokMODULUS = "%"
73tokBINAND = "&"
74tokBINOR = "|"
75tokBINXOR = "^"
76tokCOMMA = ","
77tokLBRACE = "{"
78tokRBRACE = "}"
79tokARROW = "->"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080080tokINCREMENT = "++"
81tokDECREMENT = "--"
Tao Baod7db5942015-01-28 10:07:51 -080082tokNUMBER = "<number>"
83tokIDENT = "<ident>"
84tokSTRING = "<string>"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080085
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080086
Tao Baod7db5942015-01-28 10:07:51 -080087class Token(clang.cindex.Token):
88 """A class that represents one token after parsing.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080089
Tao Baod7db5942015-01-28 10:07:51 -080090 It inherits the class in libclang, with an extra id property to hold the
91 new spelling of the token. The spelling property in the base class is
92 defined as read-only. New names after macro instantiation are saved in
93 their ids now. It also facilitates the renaming of directive optimizations
94 like replacing 'ifndef X' with 'if !defined(X)'.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080095
Tao Baod7db5942015-01-28 10:07:51 -080096 It also overrides the cursor property of the base class. Because the one
97 in libclang always queries based on a single token, which usually doesn't
98 hold useful information. The cursor in this class can be set by calling
99 CppTokenizer.getTokensWithCursors(). Otherwise it returns the one in the
100 base class.
101 """
102
103 def __init__(self, tu=None, group=None, int_data=None, ptr_data=None,
104 cursor=None):
105 clang.cindex.Token.__init__(self)
106 self._id = None
107 self._tu = tu
108 self._group = group
109 self._cursor = cursor
110 # self.int_data and self.ptr_data are from the base class. But
111 # self.int_data doesn't accept a None value.
112 if int_data is not None:
113 self.int_data = int_data
114 self.ptr_data = ptr_data
115
116 @property
117 def id(self):
118 """Name of the token."""
119 if self._id is None:
120 return self.spelling
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800121 else:
Tao Baod7db5942015-01-28 10:07:51 -0800122 return self._id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800123
Tao Baod7db5942015-01-28 10:07:51 -0800124 @id.setter
125 def id(self, new_id):
126 """Setting name of the token."""
127 self._id = new_id
128
129 @property
130 def cursor(self):
131 if self._cursor is None:
132 self._cursor = clang.cindex.Token.cursor
133 return self._cursor
134
135 @cursor.setter
136 def cursor(self, new_cursor):
137 self._cursor = new_cursor
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800138
139 def __repr__(self):
Tao Baod7db5942015-01-28 10:07:51 -0800140 if self.id == 'defined':
141 return self.id
142 elif self.kind == TokenKind.IDENTIFIER:
143 return "(ident %s)" % self.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800144
145 return self.id
146
147 def __str__(self):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800148 return self.id
149
Tao Baod7db5942015-01-28 10:07:51 -0800150
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800151class BadExpectedToken(Exception):
Tao Baod7db5942015-01-28 10:07:51 -0800152 """An exception that will be raised for unexpected tokens."""
153 pass
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800154
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800155
Tao Baod7db5942015-01-28 10:07:51 -0800156# The __contains__ function in libclang SourceRange class contains a bug. It
157# gives wrong result when dealing with single line range.
158# Bug filed with upstream:
159# http://llvm.org/bugs/show_bug.cgi?id=22243, http://reviews.llvm.org/D7277
160def SourceRange__contains__(self, other):
161 """Determine if a given location is inside the range."""
162 if not isinstance(other, SourceLocation):
163 return False
164 if other.file is None and self.start.file is None:
165 pass
166 elif (self.start.file.name != other.file.name or
167 other.file.name != self.end.file.name):
168 # same file name
169 return False
170 # same file, in between lines
171 if self.start.line < other.line < self.end.line:
172 return True
173 # same file, same line
174 elif self.start.line == other.line == self.end.line:
175 if self.start.column <= other.column <= self.end.column:
176 return True
177 elif self.start.line == other.line:
178 # same file first line
179 if self.start.column <= other.column:
180 return True
181 elif other.line == self.end.line:
182 # same file last line
183 if other.column <= self.end.column:
184 return True
185 return False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800186
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800187
Tao Baod7db5942015-01-28 10:07:51 -0800188SourceRange.__contains__ = SourceRange__contains__
189
190
191################################################################################
192################################################################################
193##### #####
194##### C P P T O K E N I Z E R #####
195##### #####
196################################################################################
197################################################################################
198
199
200class CppTokenizer(object):
201 """A tokenizer that converts some input text into a list of tokens.
202
203 It calls libclang's tokenizer to get the parsed tokens. In addition, it
204 updates the cursor property in each token after parsing, by calling
205 getTokensWithCursors().
206 """
207
208 clang_flags = ['-E', '-x', 'c']
209 options = TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800210
211 def __init__(self):
Tao Baod7db5942015-01-28 10:07:51 -0800212 """Initialize a new CppTokenizer object."""
213 self._indexer = clang.cindex.Index.create()
214 self._tu = None
215 self._index = 0
216 self.tokens = None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800217
Tao Baod7db5942015-01-28 10:07:51 -0800218 def _getTokensWithCursors(self):
219 """Helper method to return all tokens with their cursors.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800220
Tao Baod7db5942015-01-28 10:07:51 -0800221 The cursor property in a clang Token doesn't provide enough
222 information. Because it is queried based on single token each time
223 without any context, i.e. via calling conf.lib.clang_annotateTokens()
224 with only one token given. So we often see 'INVALID_FILE' in one
225 token's cursor. In this function it passes all the available tokens
226 to get more informative cursors.
227 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800228
Tao Baod7db5942015-01-28 10:07:51 -0800229 tokens_memory = ctypes.POINTER(clang.cindex.Token)()
230 tokens_count = ctypes.c_uint()
231
232 conf.lib.clang_tokenize(self._tu, self._tu.cursor.extent,
233 ctypes.byref(tokens_memory),
234 ctypes.byref(tokens_count))
235
236 count = int(tokens_count.value)
237
238 # If we get no tokens, no memory was allocated. Be sure not to return
239 # anything and potentially call a destructor on nothing.
240 if count < 1:
241 return
242
243 cursors = (Cursor * count)()
244 cursors_memory = ctypes.cast(cursors, ctypes.POINTER(Cursor))
245
246 conf.lib.clang_annotateTokens(self._tu, tokens_memory, count,
247 cursors_memory)
248
249 tokens_array = ctypes.cast(
250 tokens_memory,
251 ctypes.POINTER(clang.cindex.Token * count)).contents
252 token_group = TokenGroup(self._tu, tokens_memory, tokens_count)
253
254 tokens = []
255 for i in xrange(0, count):
256 token = Token(self._tu, token_group,
257 int_data=tokens_array[i].int_data,
258 ptr_data=tokens_array[i].ptr_data,
259 cursor=cursors[i])
260 # We only want non-comment tokens.
261 if token.kind != TokenKind.COMMENT:
262 tokens.append(token)
263
264 return tokens
265
266 def parseString(self, lines):
267 """Parse a list of text lines into a BlockList object."""
268 file_ = 'dummy.c'
269 self._tu = self._indexer.parse(file_, self.clang_flags,
270 unsaved_files=[(file_, lines)],
271 options=self.options)
272 self.tokens = self._getTokensWithCursors()
273
274 def parseFile(self, file_):
275 """Parse a file into a BlockList object."""
276 self._tu = self._indexer.parse(file_, self.clang_flags,
277 options=self.options)
278 self.tokens = self._getTokensWithCursors()
279
280 def nextToken(self):
281 """Return next token from the list."""
282 if self._index < len(self.tokens):
283 t = self.tokens[self._index]
284 self._index += 1
285 return t
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800286 else:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800287 return None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800288
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800289
Tao Baod7db5942015-01-28 10:07:51 -0800290class CppStringTokenizer(CppTokenizer):
291 """A CppTokenizer derived class that accepts a string of text as input."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800292
Tao Baod7db5942015-01-28 10:07:51 -0800293 def __init__(self, line):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800294 CppTokenizer.__init__(self)
Tao Baod7db5942015-01-28 10:07:51 -0800295 self.parseString(line)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800296
297
298class CppFileTokenizer(CppTokenizer):
Tao Baod7db5942015-01-28 10:07:51 -0800299 """A CppTokenizer derived class that accepts a file as input."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800300
Tao Baod7db5942015-01-28 10:07:51 -0800301 def __init__(self, file_):
302 CppTokenizer.__init__(self)
303 self.parseFile(file_)
304
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800305
306# Unit testing
307#
Tao Baod7db5942015-01-28 10:07:51 -0800308class CppTokenizerTester(object):
309 """A class used to test CppTokenizer classes."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800310
Tao Baod7db5942015-01-28 10:07:51 -0800311 def __init__(self, tokenizer=None):
312 self._tokenizer = tokenizer
313 self._token = None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800314
Tao Baod7db5942015-01-28 10:07:51 -0800315 def setTokenizer(self, tokenizer):
316 self._tokenizer = tokenizer
317
318 def expect(self, id):
319 self._token = self._tokenizer.nextToken()
320 if self._token is None:
321 tokid = ''
322 else:
323 tokid = self._token.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800324 if tokid == id:
325 return
Tao Baod7db5942015-01-28 10:07:51 -0800326 raise BadExpectedToken("### BAD TOKEN: '%s' expecting '%s'" % (
327 tokid, id))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800328
Tao Baod7db5942015-01-28 10:07:51 -0800329 def expectToken(self, id, line, col):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800330 self.expect(id)
Tao Baod7db5942015-01-28 10:07:51 -0800331 if self._token.location.line != line:
332 raise BadExpectedToken(
333 "### BAD LINENO: token '%s' got '%d' expecting '%d'" % (
334 id, self._token.lineno, line))
335 if self._token.location.column != col:
336 raise BadExpectedToken("### BAD COLNO: '%d' expecting '%d'" % (
337 self._token.colno, col))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800338
Tao Baod7db5942015-01-28 10:07:51 -0800339 def expectTokens(self, tokens):
340 for id, line, col in tokens:
341 self.expectToken(id, line, col)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800342
Tao Baod7db5942015-01-28 10:07:51 -0800343 def expectList(self, list_):
344 for item in list_:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800345 self.expect(item)
346
Tao Baod7db5942015-01-28 10:07:51 -0800347
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800348def test_CppTokenizer():
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800349 tester = CppTokenizerTester()
350
Tao Baod7db5942015-01-28 10:07:51 -0800351 tester.setTokenizer(CppStringTokenizer("#an/example && (01923_xy)"))
352 tester.expectList(["#", "an", "/", "example", tokLOGICAND, tokLPAREN,
353 "01923_xy", tokRPAREN])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800354
Tao Baod7db5942015-01-28 10:07:51 -0800355 tester.setTokenizer(CppStringTokenizer("FOO(BAR) && defined(BAZ)"))
356 tester.expectList(["FOO", tokLPAREN, "BAR", tokRPAREN, tokLOGICAND,
357 "defined", tokLPAREN, "BAZ", tokRPAREN])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800358
Tao Baod7db5942015-01-28 10:07:51 -0800359 tester.setTokenizer(CppStringTokenizer("/*\n#\n*/"))
360 tester.expectList([])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800361
Tao Baod7db5942015-01-28 10:07:51 -0800362 tester.setTokenizer(CppStringTokenizer("first\nsecond"))
363 tester.expectList(["first", "second"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800364
Tao Baod7db5942015-01-28 10:07:51 -0800365 tester.setTokenizer(CppStringTokenizer("first second\n third"))
366 tester.expectTokens([("first", 1, 1),
367 ("second", 1, 7),
368 ("third", 2, 3)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800369
Tao Baod7db5942015-01-28 10:07:51 -0800370 tester.setTokenizer(CppStringTokenizer("boo /* what the\nhell */"))
371 tester.expectTokens([("boo", 1, 1)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800372
Tao Baod7db5942015-01-28 10:07:51 -0800373 tester.setTokenizer(CppStringTokenizer("an \\\n example"))
374 tester.expectTokens([("an", 1, 1),
375 ("example", 2, 2)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800376 return True
377
378
Tao Baod7db5942015-01-28 10:07:51 -0800379################################################################################
380################################################################################
381##### #####
382##### C P P E X P R E S S I O N S #####
383##### #####
384################################################################################
385################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800386
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800387
Tao Baod7db5942015-01-28 10:07:51 -0800388class CppExpr(object):
389 """A class that models the condition of #if directives into an expr tree.
390
391 Each node in the tree is of the form (op, arg) or (op, arg1, arg2) where
392 "op" is a string describing the operation
393 """
394
395 unaries = ["!", "~"]
396 binaries = ["+", "-", "<", "<=", ">=", ">", "&&", "||", "*", "/", "%",
397 "&", "|", "^", "<<", ">>", "==", "!=", "?", ":"]
Elliott Hughes1198fd32013-11-21 11:12:34 -0800398 precedences = {
399 "?": 1, ":": 1,
400 "||": 2,
401 "&&": 3,
402 "|": 4,
403 "^": 5,
404 "&": 6,
405 "==": 7, "!=": 7,
406 "<": 8, "<=": 8, ">": 8, ">=": 8,
407 "<<": 9, ">>": 9,
408 "+": 10, "-": 10,
409 "*": 11, "/": 11, "%": 11,
410 "!": 12, "~": 12
411 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800412
413 def __init__(self, tokens):
Tao Baod7db5942015-01-28 10:07:51 -0800414 """Initialize a CppExpr. 'tokens' must be a CppToken list."""
415 self.tokens = tokens
416 self._num_tokens = len(tokens)
417 self._index = 0
418
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800419 if debugCppExpr:
420 print "CppExpr: trying to parse %s" % repr(tokens)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800421 self.expr = self.parseExpression(0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800422 if debugCppExpr:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800423 print "CppExpr: got " + repr(self.expr)
Tao Baod7db5942015-01-28 10:07:51 -0800424 if self._index != self._num_tokens:
425 self.throw(BadExpectedToken, "crap at end of input (%d != %d): %s"
426 % (self._index, self._num_tokens, repr(tokens)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800427
Elliott Hughes40596aa2013-11-05 14:54:29 -0800428 def throw(self, exception, msg):
Tao Baod7db5942015-01-28 10:07:51 -0800429 if self._index < self._num_tokens:
430 tok = self.tokens[self._index]
431 print "%d:%d: %s" % (tok.location.line, tok.location.column, msg)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800432 else:
433 print "EOF: %s" % msg
Elliott Hughes40596aa2013-11-05 14:54:29 -0800434 raise exception(msg)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800435
Elliott Hughes40596aa2013-11-05 14:54:29 -0800436 def expectId(self, id):
Tao Baod7db5942015-01-28 10:07:51 -0800437 """Check that a given token id is at the current position."""
438 token = self.tokens[self._index]
439 if self._index >= self._num_tokens or token.id != id:
440 self.throw(BadExpectedToken,
441 "### expecting '%s' in expression, got '%s'" % (
442 id, token.id))
443 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800444
445 def is_decimal(self):
Tao Baod7db5942015-01-28 10:07:51 -0800446 token = self.tokens[self._index].id
447 if token[-1] in "ULul":
448 token = token[:-1]
449 try:
450 val = int(token, 10)
451 self._index += 1
452 return ('int', val)
453 except ValueError:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800454 return None
455
Tao Baod7db5942015-01-28 10:07:51 -0800456 def is_octal(self):
457 token = self.tokens[self._index].id
458 if token[-1] in "ULul":
459 token = token[:-1]
460 if len(token) < 2 or token[0] != '0':
461 return None
462 try:
463 val = int(token, 8)
464 self._index += 1
465 return ('oct', val)
466 except ValueError:
467 return None
468
469 def is_hexadecimal(self):
470 token = self.tokens[self._index].id
471 if token[-1] in "ULul":
472 token = token[:-1]
473 if len(token) < 3 or (token[:2] != '0x' and token[:2] != '0X'):
474 return None
475 try:
476 val = int(token, 16)
477 self._index += 1
478 return ('hex', val)
479 except ValueError:
480 return None
481
482 def is_integer(self):
483 if self.tokens[self._index].kind != TokenKind.LITERAL:
484 return None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800485
Elliott Hughes40596aa2013-11-05 14:54:29 -0800486 c = self.is_hexadecimal()
Tao Baod7db5942015-01-28 10:07:51 -0800487 if c:
488 return c
489
490 c = self.is_octal()
491 if c:
492 return c
493
494 c = self.is_decimal()
495 if c:
496 return c
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800497
498 return None
499
Elliott Hughes40596aa2013-11-05 14:54:29 -0800500 def is_number(self):
Tao Baod7db5942015-01-28 10:07:51 -0800501 t = self.tokens[self._index]
502 if t.id == tokMINUS and self._index + 1 < self._num_tokens:
503 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800504 c = self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800505 if c:
Tao Baod7db5942015-01-28 10:07:51 -0800506 op, val = c
Elliott Hughes40596aa2013-11-05 14:54:29 -0800507 return (op, -val)
Tao Baod7db5942015-01-28 10:07:51 -0800508 if t.id == tokPLUS and self._index + 1 < self._num_tokens:
509 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800510 c = self.is_integer()
Tao Baod7db5942015-01-28 10:07:51 -0800511 if c:
512 return c
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800513
Elliott Hughes40596aa2013-11-05 14:54:29 -0800514 return self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800515
Elliott Hughes40596aa2013-11-05 14:54:29 -0800516 def is_defined(self):
Tao Baod7db5942015-01-28 10:07:51 -0800517 t = self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800518 if t.id != tokDEFINED:
519 return None
520
Tao Baod7db5942015-01-28 10:07:51 -0800521 # We have the defined keyword, check the rest.
522 self._index += 1
523 used_parens = False
524 if (self._index < self._num_tokens and
525 self.tokens[self._index].id == tokLPAREN):
526 used_parens = True
527 self._index += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800528
Tao Baod7db5942015-01-28 10:07:51 -0800529 if self._index >= self._num_tokens:
530 self.throw(BadExpectedToken,
531 "### 'defined' must be followed by macro name or left "
532 "paren")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800533
Tao Baod7db5942015-01-28 10:07:51 -0800534 t = self.tokens[self._index]
535 if t.kind != TokenKind.IDENTIFIER:
536 self.throw(BadExpectedToken,
537 "### 'defined' must be followed by macro name")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800538
Tao Baod7db5942015-01-28 10:07:51 -0800539 self._index += 1
Elliott Hughesfddbafd2014-05-01 10:17:27 -0700540 if used_parens:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800541 self.expectId(tokRPAREN)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800542
Tao Baod7db5942015-01-28 10:07:51 -0800543 return ("defined", t.id)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800544
Elliott Hughes40596aa2013-11-05 14:54:29 -0800545 def is_call_or_ident(self):
Tao Baod7db5942015-01-28 10:07:51 -0800546 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800547 return None
548
Tao Baod7db5942015-01-28 10:07:51 -0800549 t = self.tokens[self._index]
550 if t.kind != TokenKind.IDENTIFIER:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800551 return None
552
Tao Baod7db5942015-01-28 10:07:51 -0800553 name = t.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800554
Tao Baod7db5942015-01-28 10:07:51 -0800555 self._index += 1
556 if (self._index >= self._num_tokens or
557 self.tokens[self._index].id != tokLPAREN):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800558 return ("ident", name)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800559
Tao Baod7db5942015-01-28 10:07:51 -0800560 params = []
561 depth = 1
562 self._index += 1
563 j = self._index
564 while self._index < self._num_tokens:
565 id = self.tokens[self._index].id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800566 if id == tokLPAREN:
567 depth += 1
568 elif depth == 1 and (id == tokCOMMA or id == tokRPAREN):
Tao Baod7db5942015-01-28 10:07:51 -0800569 k = self._index
570 param = self.tokens[j:k]
Elliott Hughes40596aa2013-11-05 14:54:29 -0800571 params.append(param)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800572 if id == tokRPAREN:
573 break
Tao Baod7db5942015-01-28 10:07:51 -0800574 j = self._index + 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800575 elif id == tokRPAREN:
576 depth -= 1
Tao Baod7db5942015-01-28 10:07:51 -0800577 self._index += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800578
Tao Baod7db5942015-01-28 10:07:51 -0800579 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800580 return None
581
Tao Baod7db5942015-01-28 10:07:51 -0800582 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800583 return ("call", (name, params))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800584
Tao Baod7db5942015-01-28 10:07:51 -0800585 # Implements the "precedence climbing" algorithm from
586 # http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm.
587 # The "classic" algorithm would be fine if we were using a tool to
588 # generate the parser, but we're not. Dijkstra's "shunting yard"
589 # algorithm hasn't been necessary yet.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800590
Elliott Hughes40596aa2013-11-05 14:54:29 -0800591 def parseExpression(self, minPrecedence):
Tao Baod7db5942015-01-28 10:07:51 -0800592 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800593 return None
594
Elliott Hughes40596aa2013-11-05 14:54:29 -0800595 node = self.parsePrimary()
Tao Baod7db5942015-01-28 10:07:51 -0800596 while (self.token() and self.isBinary(self.token()) and
597 self.precedence(self.token()) >= minPrecedence):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800598 op = self.token()
599 self.nextToken()
600 rhs = self.parseExpression(self.precedence(op) + 1)
601 node = (op.id, node, rhs)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800602
Elliott Hughes40596aa2013-11-05 14:54:29 -0800603 return node
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800604
Elliott Hughes40596aa2013-11-05 14:54:29 -0800605 def parsePrimary(self):
606 op = self.token()
607 if self.isUnary(op):
608 self.nextToken()
609 return (op.id, self.parseExpression(self.precedence(op)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800610
Elliott Hughes40596aa2013-11-05 14:54:29 -0800611 primary = None
612 if op.id == tokLPAREN:
613 self.nextToken()
614 primary = self.parseExpression(0)
615 self.expectId(tokRPAREN)
Elliott Hughes1198fd32013-11-21 11:12:34 -0800616 elif op.id == "?":
617 self.nextToken()
618 primary = self.parseExpression(0)
619 self.expectId(":")
Tao Baod7db5942015-01-28 10:07:51 -0800620 elif op.id == '+' or op.id == '-' or op.kind == TokenKind.LITERAL:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800621 primary = self.is_number()
Tao Baod7db5942015-01-28 10:07:51 -0800622 # Checking for 'defined' needs to come first now because 'defined' is
623 # recognized as IDENTIFIER.
Elliott Hughes40596aa2013-11-05 14:54:29 -0800624 elif op.id == tokDEFINED:
625 primary = self.is_defined()
Tao Baod7db5942015-01-28 10:07:51 -0800626 elif op.kind == TokenKind.IDENTIFIER:
627 primary = self.is_call_or_ident()
Elliott Hughes40596aa2013-11-05 14:54:29 -0800628 else:
Tao Baod7db5942015-01-28 10:07:51 -0800629 self.throw(BadExpectedToken,
630 "didn't expect to see a %s in factor" % (
631 self.tokens[self._index].id))
632 return primary
Elliott Hughes40596aa2013-11-05 14:54:29 -0800633
634 def isBinary(self, token):
635 return token.id in self.binaries
636
Elliott Hughes40596aa2013-11-05 14:54:29 -0800637 def isUnary(self, token):
638 return token.id in self.unaries
639
Elliott Hughes40596aa2013-11-05 14:54:29 -0800640 def precedence(self, token):
641 return self.precedences.get(token.id)
642
Elliott Hughes40596aa2013-11-05 14:54:29 -0800643 def token(self):
Tao Baod7db5942015-01-28 10:07:51 -0800644 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800645 return None
Tao Baod7db5942015-01-28 10:07:51 -0800646 return self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800647
Elliott Hughes40596aa2013-11-05 14:54:29 -0800648 def nextToken(self):
Tao Baod7db5942015-01-28 10:07:51 -0800649 self._index += 1
650 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800651 return None
Tao Baod7db5942015-01-28 10:07:51 -0800652 return self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800653
Elliott Hughes40596aa2013-11-05 14:54:29 -0800654 def dump_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800655 op = e[0]
656 line = "(" + op
657 if op == "int":
658 line += " %d)" % e[1]
Tao Baod7db5942015-01-28 10:07:51 -0800659 elif op == "oct":
660 line += " 0%o)" % e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800661 elif op == "hex":
662 line += " 0x%x)" % e[1]
663 elif op == "ident":
664 line += " %s)" % e[1]
665 elif op == "defined":
666 line += " %s)" % e[1]
667 elif op == "call":
668 arg = e[1]
669 line += " %s [" % arg[0]
670 prefix = ""
671 for param in arg[1]:
672 par = ""
673 for tok in param:
674 par += str(tok)
675 line += "%s%s" % (prefix, par)
676 prefix = ","
677 line += "])"
678 elif op in CppExpr.unaries:
679 line += " %s)" % self.dump_node(e[1])
680 elif op in CppExpr.binaries:
681 line += " %s %s)" % (self.dump_node(e[1]), self.dump_node(e[2]))
682 else:
683 line += " ?%s)" % repr(e[1])
684
685 return line
686
687 def __repr__(self):
688 return self.dump_node(self.expr)
689
Elliott Hughes40596aa2013-11-05 14:54:29 -0800690 def source_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800691 op = e[0]
692 if op == "int":
693 return "%d" % e[1]
694 if op == "hex":
695 return "0x%x" % e[1]
Tao Baod7db5942015-01-28 10:07:51 -0800696 if op == "oct":
697 return "0%o" % e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800698 if op == "ident":
699 # XXX: should try to expand
700 return e[1]
701 if op == "defined":
702 return "defined(%s)" % e[1]
703
Tao Baod7db5942015-01-28 10:07:51 -0800704 prec = CppExpr.precedences.get(op, 1000)
705 arg = e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800706 if op in CppExpr.unaries:
707 arg_src = self.source_node(arg)
Tao Baod7db5942015-01-28 10:07:51 -0800708 arg_op = arg[0]
709 arg_prec = CppExpr.precedences.get(arg_op, 1000)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800710 if arg_prec < prec:
711 return "!(" + arg_src + ")"
712 else:
713 return "!" + arg_src
714 if op in CppExpr.binaries:
Tao Baod7db5942015-01-28 10:07:51 -0800715 arg2 = e[2]
716 arg1_op = arg[0]
717 arg2_op = arg2[0]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800718 arg1_src = self.source_node(arg)
719 arg2_src = self.source_node(arg2)
Tao Baod7db5942015-01-28 10:07:51 -0800720 if CppExpr.precedences.get(arg1_op, 1000) < prec:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800721 arg1_src = "(%s)" % arg1_src
Tao Baod7db5942015-01-28 10:07:51 -0800722 if CppExpr.precedences.get(arg2_op, 1000) < prec:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800723 arg2_src = "(%s)" % arg2_src
724
725 return "%s %s %s" % (arg1_src, op, arg2_src)
726 return "???"
727
728 def __str__(self):
729 return self.source_node(self.expr)
730
Tao Baod7db5942015-01-28 10:07:51 -0800731 @staticmethod
732 def int_node(e):
733 if e[0] in ["int", "oct", "hex"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800734 return e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800735 else:
736 return None
737
738 def toInt(self):
739 return self.int_node(self.expr)
740
Tao Baod7db5942015-01-28 10:07:51 -0800741 def optimize_node(self, e, macros=None):
742 if macros is None:
743 macros = {}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800744 op = e[0]
Tao Baod7db5942015-01-28 10:07:51 -0800745
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800746 if op == "defined":
Elliott Hughes40596aa2013-11-05 14:54:29 -0800747 op, name = e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800748 if macros.has_key(name):
749 if macros[name] == kCppUndefinedMacro:
750 return ("int", 0)
751 else:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700752 try:
753 value = int(macros[name])
754 return ("int", value)
Tao Baod7db5942015-01-28 10:07:51 -0800755 except ValueError:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700756 return ("defined", macros[name])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800757
758 if kernel_remove_config_macros and name.startswith("CONFIG_"):
759 return ("int", 0)
760
Elliott Hughes40596aa2013-11-05 14:54:29 -0800761 return e
762
763 elif op == "ident":
764 op, name = e
765 if macros.has_key(name):
766 try:
767 value = int(macros[name])
768 expanded = ("int", value)
Tao Baod7db5942015-01-28 10:07:51 -0800769 except ValueError:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800770 expanded = ("ident", macros[name])
771 return self.optimize_node(expanded, macros)
772 return e
773
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800774 elif op == "!":
775 op, v = e
776 v = self.optimize_node(v, macros)
777 if v[0] == "int":
778 if v[1] == 0:
779 return ("int", 1)
780 else:
781 return ("int", 0)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800782 return ('!', v)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800783
784 elif op == "&&":
785 op, l, r = e
Tao Baod7db5942015-01-28 10:07:51 -0800786 l = self.optimize_node(l, macros)
787 r = self.optimize_node(r, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800788 li = self.int_node(l)
789 ri = self.int_node(r)
Tao Baod7db5942015-01-28 10:07:51 -0800790 if li is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800791 if li == 0:
792 return ("int", 0)
793 else:
794 return r
Tao Baod7db5942015-01-28 10:07:51 -0800795 elif ri is not None:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800796 if ri == 0:
797 return ("int", 0)
798 else:
799 return l
800 return (op, l, r)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800801
802 elif op == "||":
803 op, l, r = e
Tao Baod7db5942015-01-28 10:07:51 -0800804 l = self.optimize_node(l, macros)
805 r = self.optimize_node(r, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800806 li = self.int_node(l)
807 ri = self.int_node(r)
Tao Baod7db5942015-01-28 10:07:51 -0800808 if li is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800809 if li == 0:
810 return r
811 else:
812 return ("int", 1)
Tao Baod7db5942015-01-28 10:07:51 -0800813 elif ri is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800814 if ri == 0:
815 return l
816 else:
817 return ("int", 1)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800818 return (op, l, r)
819
820 else:
821 return e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800822
Tao Baod7db5942015-01-28 10:07:51 -0800823 def optimize(self, macros=None):
824 if macros is None:
825 macros = {}
Elliott Hughes40596aa2013-11-05 14:54:29 -0800826 self.expr = self.optimize_node(self.expr, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800827
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800828
829def test_cpp_expr(expr, expected):
Tao Baod7db5942015-01-28 10:07:51 -0800830 e = CppExpr(CppStringTokenizer(expr).tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800831 s1 = repr(e)
832 if s1 != expected:
Tao Baod7db5942015-01-28 10:07:51 -0800833 print ("[FAIL]: expression '%s' generates '%s', should be "
834 "'%s'" % (expr, s1, expected))
Elliott Hughes40596aa2013-11-05 14:54:29 -0800835 global failure_count
836 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800837
Tao Baod7db5942015-01-28 10:07:51 -0800838
839def test_cpp_expr_optim(expr, expected, macros=None):
840 if macros is None:
841 macros = {}
842 e = CppExpr(CppStringTokenizer(expr).tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800843 e.optimize(macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800844 s1 = repr(e)
845 if s1 != expected:
Tao Baod7db5942015-01-28 10:07:51 -0800846 print ("[FAIL]: optimized expression '%s' generates '%s' with "
847 "macros %s, should be '%s'" % (expr, s1, macros, expected))
Elliott Hughes40596aa2013-11-05 14:54:29 -0800848 global failure_count
849 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800850
Tao Baod7db5942015-01-28 10:07:51 -0800851
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800852def test_cpp_expr_source(expr, expected):
Tao Baod7db5942015-01-28 10:07:51 -0800853 e = CppExpr(CppStringTokenizer(expr).tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800854 s1 = str(e)
855 if s1 != expected:
Tao Baod7db5942015-01-28 10:07:51 -0800856 print ("[FAIL]: source expression '%s' generates '%s', should "
857 "be '%s'" % (expr, s1, expected))
Elliott Hughes40596aa2013-11-05 14:54:29 -0800858 global failure_count
859 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800860
Tao Baod7db5942015-01-28 10:07:51 -0800861
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800862def test_CppExpr():
Elliott Hughes40596aa2013-11-05 14:54:29 -0800863 test_cpp_expr("0", "(int 0)")
864 test_cpp_expr("1", "(int 1)")
Tao Baod7db5942015-01-28 10:07:51 -0800865 test_cpp_expr("-5", "(int -5)")
866 test_cpp_expr("+1", "(int 1)")
867 test_cpp_expr("0U", "(int 0)")
868 test_cpp_expr("015", "(oct 015)")
869 test_cpp_expr("015l", "(oct 015)")
870 test_cpp_expr("0x3e", "(hex 0x3e)")
Elliott Hughes40596aa2013-11-05 14:54:29 -0800871 test_cpp_expr("(0)", "(int 0)")
872 test_cpp_expr("1 && 1", "(&& (int 1) (int 1))")
873 test_cpp_expr("1 && 0", "(&& (int 1) (int 0))")
874 test_cpp_expr("EXAMPLE", "(ident EXAMPLE)")
875 test_cpp_expr("EXAMPLE - 3", "(- (ident EXAMPLE) (int 3))")
876 test_cpp_expr("defined(EXAMPLE)", "(defined EXAMPLE)")
877 test_cpp_expr("defined ( EXAMPLE ) ", "(defined EXAMPLE)")
878 test_cpp_expr("!defined(EXAMPLE)", "(! (defined EXAMPLE))")
Tao Baod7db5942015-01-28 10:07:51 -0800879 test_cpp_expr("defined(ABC) || defined(BINGO)",
880 "(|| (defined ABC) (defined BINGO))")
881 test_cpp_expr("FOO(BAR,5)", "(call FOO [BAR,5])")
882 test_cpp_expr("A == 1 || defined(B)",
883 "(|| (== (ident A) (int 1)) (defined B))")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800884
Elliott Hughes40596aa2013-11-05 14:54:29 -0800885 test_cpp_expr_optim("0", "(int 0)")
886 test_cpp_expr_optim("1", "(int 1)")
887 test_cpp_expr_optim("1 && 1", "(int 1)")
Tao Baod7db5942015-01-28 10:07:51 -0800888 test_cpp_expr_optim("1 && +1", "(int 1)")
889 test_cpp_expr_optim("0x1 && 01", "(oct 01)")
Elliott Hughes40596aa2013-11-05 14:54:29 -0800890 test_cpp_expr_optim("1 && 0", "(int 0)")
891 test_cpp_expr_optim("0 && 1", "(int 0)")
892 test_cpp_expr_optim("0 && 0", "(int 0)")
893 test_cpp_expr_optim("1 || 1", "(int 1)")
894 test_cpp_expr_optim("1 || 0", "(int 1)")
895 test_cpp_expr_optim("0 || 1", "(int 1)")
896 test_cpp_expr_optim("0 || 0", "(int 0)")
897 test_cpp_expr_optim("A", "(ident A)")
Tao Baod7db5942015-01-28 10:07:51 -0800898 test_cpp_expr_optim("A", "(int 1)", {"A": 1})
899 test_cpp_expr_optim("A || B", "(int 1)", {"A": 1})
900 test_cpp_expr_optim("A || B", "(int 1)", {"B": 1})
901 test_cpp_expr_optim("A && B", "(ident B)", {"A": 1})
902 test_cpp_expr_optim("A && B", "(ident A)", {"B": 1})
Elliott Hughes40596aa2013-11-05 14:54:29 -0800903 test_cpp_expr_optim("A && B", "(&& (ident A) (ident B))")
904 test_cpp_expr_optim("EXAMPLE", "(ident EXAMPLE)")
905 test_cpp_expr_optim("EXAMPLE - 3", "(- (ident EXAMPLE) (int 3))")
906 test_cpp_expr_optim("defined(EXAMPLE)", "(defined EXAMPLE)")
Tao Baod7db5942015-01-28 10:07:51 -0800907 test_cpp_expr_optim("defined(EXAMPLE)", "(defined XOWOE)",
908 {"EXAMPLE": "XOWOE"})
909 test_cpp_expr_optim("defined(EXAMPLE)", "(int 0)",
910 {"EXAMPLE": kCppUndefinedMacro})
Elliott Hughes40596aa2013-11-05 14:54:29 -0800911 test_cpp_expr_optim("!defined(EXAMPLE)", "(! (defined EXAMPLE))")
Tao Baod7db5942015-01-28 10:07:51 -0800912 test_cpp_expr_optim("!defined(EXAMPLE)", "(! (defined XOWOE))",
913 {"EXAMPLE": "XOWOE"})
914 test_cpp_expr_optim("!defined(EXAMPLE)", "(int 1)",
915 {"EXAMPLE": kCppUndefinedMacro})
916 test_cpp_expr_optim("defined(A) || defined(B)",
917 "(|| (defined A) (defined B))")
918 test_cpp_expr_optim("defined(A) || defined(B)", "(int 1)", {"A": "1"})
919 test_cpp_expr_optim("defined(A) || defined(B)", "(int 1)", {"B": "1"})
920 test_cpp_expr_optim("defined(A) || defined(B)", "(defined A)",
921 {"B": kCppUndefinedMacro})
922 test_cpp_expr_optim("defined(A) || defined(B)", "(int 0)",
923 {"A": kCppUndefinedMacro, "B": kCppUndefinedMacro})
924 test_cpp_expr_optim("defined(A) && defined(B)",
925 "(&& (defined A) (defined B))")
926 test_cpp_expr_optim("defined(A) && defined(B)",
927 "(defined B)", {"A": "1"})
928 test_cpp_expr_optim("defined(A) && defined(B)",
929 "(defined A)", {"B": "1"})
930 test_cpp_expr_optim("defined(A) && defined(B)", "(int 0)",
931 {"B": kCppUndefinedMacro})
932 test_cpp_expr_optim("defined(A) && defined(B)",
933 "(int 0)", {"A": kCppUndefinedMacro})
934 test_cpp_expr_optim("A == 1 || defined(B)",
935 "(|| (== (ident A) (int 1)) (defined B))")
936 test_cpp_expr_optim(
937 "defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)",
938 "(|| (! (defined __GLIBC__)) (< (ident __GLIBC__) (int 2)))",
939 {"__KERNEL__": kCppUndefinedMacro})
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800940
Elliott Hughes40596aa2013-11-05 14:54:29 -0800941 test_cpp_expr_source("0", "0")
942 test_cpp_expr_source("1", "1")
943 test_cpp_expr_source("1 && 1", "1 && 1")
944 test_cpp_expr_source("1 && 0", "1 && 0")
945 test_cpp_expr_source("0 && 1", "0 && 1")
946 test_cpp_expr_source("0 && 0", "0 && 0")
947 test_cpp_expr_source("1 || 1", "1 || 1")
948 test_cpp_expr_source("1 || 0", "1 || 0")
949 test_cpp_expr_source("0 || 1", "0 || 1")
950 test_cpp_expr_source("0 || 0", "0 || 0")
951 test_cpp_expr_source("EXAMPLE", "EXAMPLE")
952 test_cpp_expr_source("EXAMPLE - 3", "EXAMPLE - 3")
953 test_cpp_expr_source("defined(EXAMPLE)", "defined(EXAMPLE)")
954 test_cpp_expr_source("defined EXAMPLE", "defined(EXAMPLE)")
955 test_cpp_expr_source("A == 1 || defined(B)", "A == 1 || defined(B)")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800956
957
Tao Baod7db5942015-01-28 10:07:51 -0800958################################################################################
959################################################################################
960##### #####
961##### C P P B L O C K #####
962##### #####
963################################################################################
964################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800965
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800966
Tao Baod7db5942015-01-28 10:07:51 -0800967class Block(object):
968 """A class used to model a block of input source text.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800969
Tao Baod7db5942015-01-28 10:07:51 -0800970 There are two block types:
971 - directive blocks: contain the tokens of a single pre-processor
972 directive (e.g. #if)
973 - text blocks, contain the tokens of non-directive blocks
974
975 The cpp parser class below will transform an input source file into a list
976 of Block objects (grouped in a BlockList object for convenience)
977 """
978
979 def __init__(self, tokens, directive=None, lineno=0, identifier=None):
980 """Initialize a new block, if 'directive' is None, it is a text block.
981
982 NOTE: This automatically converts '#ifdef MACRO' into
983 '#if defined(MACRO)' and '#ifndef MACRO' into '#if !defined(MACRO)'.
984 """
985
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800986 if directive == "ifdef":
987 tok = Token()
Tao Baod7db5942015-01-28 10:07:51 -0800988 tok.id = tokDEFINED
989 tokens = [tok] + tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800990 directive = "if"
991
992 elif directive == "ifndef":
993 tok1 = Token()
994 tok2 = Token()
Tao Baod7db5942015-01-28 10:07:51 -0800995 tok1.id = tokNOT
996 tok2.id = tokDEFINED
997 tokens = [tok1, tok2] + tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800998 directive = "if"
999
Tao Baod7db5942015-01-28 10:07:51 -08001000 self.tokens = tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001001 self.directive = directive
Tao Baod7db5942015-01-28 10:07:51 -08001002 self.define_id = identifier
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001003 if lineno > 0:
1004 self.lineno = lineno
1005 else:
Tao Baod7db5942015-01-28 10:07:51 -08001006 self.lineno = self.tokens[0].location.line
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001007
1008 if self.isIf():
Tao Baod7db5942015-01-28 10:07:51 -08001009 self.expr = CppExpr(self.tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001010
1011 def isDirective(self):
Tao Baod7db5942015-01-28 10:07:51 -08001012 """Return True iff this is a directive block."""
1013 return self.directive is not None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001014
1015 def isConditional(self):
Tao Baod7db5942015-01-28 10:07:51 -08001016 """Return True iff this is a conditional directive block."""
1017 return self.directive in ["if", "ifdef", "ifndef", "else", "elif",
1018 "endif"]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001019
1020 def isDefine(self):
Tao Baod7db5942015-01-28 10:07:51 -08001021 """Return the macro name in a #define directive, or None otherwise."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001022 if self.directive != "define":
1023 return None
Tao Baod7db5942015-01-28 10:07:51 -08001024 return self.define_id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001025
1026 def isIf(self):
Tao Baod7db5942015-01-28 10:07:51 -08001027 """Return True iff this is an #if-like directive block."""
1028 return self.directive in ["if", "ifdef", "ifndef", "elif"]
1029
1030 def isEndif(self):
1031 """Return True iff this is an #endif directive block."""
1032 return self.directive == "endif"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001033
1034 def isInclude(self):
Tao Baod7db5942015-01-28 10:07:51 -08001035 """Check whether this is a #include directive.
1036
1037 If true, returns the corresponding file name (with brackets or
1038 double-qoutes). None otherwise.
1039 """
1040
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001041 if self.directive != "include":
1042 return None
Tao Baod7db5942015-01-28 10:07:51 -08001043 return ''.join([str(x) for x in self.tokens])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001044
Tao Baod7db5942015-01-28 10:07:51 -08001045 @staticmethod
1046 def format_blocks(tokens, indent=0):
1047 """Return the formatted lines of strings with proper indentation."""
1048 newline = True
1049 result = []
1050 buf = ''
1051 i = 0
1052 while i < len(tokens):
1053 t = tokens[i]
1054 if t.id == '{':
1055 buf += ' {'
1056 result.append(strip_space(buf))
1057 indent += 2
1058 buf = ''
1059 newline = True
1060 elif t.id == '}':
1061 indent -= 2
1062 if not newline:
1063 result.append(strip_space(buf))
1064 # Look ahead to determine if it's the end of line.
1065 if (i + 1 < len(tokens) and
1066 (tokens[i+1].id == ';' or
1067 tokens[i+1].id in ['else', '__attribute__',
1068 '__attribute', '__packed'] or
1069 tokens[i+1].kind == TokenKind.IDENTIFIER)):
1070 buf = ' ' * indent + '}'
1071 newline = False
1072 else:
1073 result.append(' ' * indent + '}')
1074 buf = ''
1075 newline = True
1076 elif t.id == ';':
1077 result.append(strip_space(buf) + ';')
1078 buf = ''
1079 newline = True
1080 # We prefer a new line for each constant in enum.
1081 elif t.id == ',' and t.cursor.kind == CursorKind.ENUM_DECL:
1082 result.append(strip_space(buf) + ',')
1083 buf = ''
1084 newline = True
1085 else:
1086 if newline:
1087 buf += ' ' * indent + str(t)
1088 else:
1089 buf += ' ' + str(t)
1090 newline = False
1091 i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001092
Tao Baod7db5942015-01-28 10:07:51 -08001093 if buf:
1094 result.append(strip_space(buf))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001095
Tao Baod7db5942015-01-28 10:07:51 -08001096 return result, indent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001097
Tao Baod7db5942015-01-28 10:07:51 -08001098 def writeWithWarning(self, out, warning, left_count, repeat_count, indent):
1099 """Dump the current block with warnings."""
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001100 # removeWhiteSpace() will sometimes creates non-directive blocks
1101 # without any tokens. These come from blocks that only contained
1102 # empty lines and spaces. They should not be printed in the final
1103 # output, and then should not be counted for this operation.
1104 #
Tao Baod7db5942015-01-28 10:07:51 -08001105 if self.directive is None and not self.tokens:
1106 return left_count, indent
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001107
1108 if self.directive:
Tao Baod7db5942015-01-28 10:07:51 -08001109 out.write(str(self) + '\n')
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001110 left_count -= 1
1111 if left_count == 0:
1112 out.write(warning)
1113 left_count = repeat_count
1114
1115 else:
Tao Baod7db5942015-01-28 10:07:51 -08001116 lines, indent = self.format_blocks(self.tokens, indent)
1117 for line in lines:
1118 out.write(line + '\n')
1119 left_count -= 1
1120 if left_count == 0:
1121 out.write(warning)
1122 left_count = repeat_count
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001123
Tao Baod7db5942015-01-28 10:07:51 -08001124 return left_count, indent
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001125
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001126 def __repr__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001127 """Generate the representation of a given block."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001128 if self.directive:
1129 result = "#%s " % self.directive
1130 if self.isIf():
1131 result += repr(self.expr)
1132 else:
1133 for tok in self.tokens:
1134 result += repr(tok)
1135 else:
1136 result = ""
1137 for tok in self.tokens:
1138 result += repr(tok)
1139
1140 return result
1141
1142 def __str__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001143 """Generate the string representation of a given block."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001144 if self.directive:
Tao Baod7db5942015-01-28 10:07:51 -08001145 # "#if"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001146 if self.directive == "if":
1147 # small optimization to re-generate #ifdef and #ifndef
1148 e = self.expr.expr
1149 op = e[0]
1150 if op == "defined":
1151 result = "#ifdef %s" % e[1]
1152 elif op == "!" and e[1][0] == "defined":
1153 result = "#ifndef %s" % e[1][1]
1154 else:
1155 result = "#if " + str(self.expr)
Tao Baod7db5942015-01-28 10:07:51 -08001156
1157 # "#define"
1158 elif self.isDefine():
1159 result = "#%s %s" % (self.directive, self.define_id)
1160 if self.tokens:
1161 result += " "
1162 expr = strip_space(' '.join([tok.id for tok in self.tokens]))
1163 # remove the space between name and '(' in function call
1164 result += re.sub(r'(\w+) \(', r'\1(', expr)
1165
1166 # "#error"
1167 # Concatenating tokens with a space separator, because they may
1168 # not be quoted and broken into several tokens
1169 elif self.directive == "error":
1170 result = "#error %s" % ' '.join([tok.id for tok in self.tokens])
1171
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001172 else:
1173 result = "#%s" % self.directive
Tao Baod7db5942015-01-28 10:07:51 -08001174 if self.tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001175 result += " "
Tao Baod7db5942015-01-28 10:07:51 -08001176 result += ''.join([tok.id for tok in self.tokens])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001177 else:
Tao Baod7db5942015-01-28 10:07:51 -08001178 lines, _ = self.format_blocks(self.tokens)
1179 result = '\n'.join(lines)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001180
1181 return result
1182
Tao Baod7db5942015-01-28 10:07:51 -08001183
1184class BlockList(object):
1185 """A convenience class used to hold and process a list of blocks.
1186
1187 It calls the cpp parser to get the blocks.
1188 """
1189
1190 def __init__(self, blocks):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001191 self.blocks = blocks
1192
1193 def __len__(self):
1194 return len(self.blocks)
1195
Tao Baod7db5942015-01-28 10:07:51 -08001196 def __getitem__(self, n):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001197 return self.blocks[n]
1198
1199 def __repr__(self):
1200 return repr(self.blocks)
1201
1202 def __str__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001203 result = '\n'.join([str(b) for b in self.blocks])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001204 return result
1205
Tao Baod7db5942015-01-28 10:07:51 -08001206 def dump(self):
1207 """Dump all the blocks in current BlockList."""
1208 print '##### BEGIN #####'
1209 for i, b in enumerate(self.blocks):
1210 print '### BLOCK %d ###' % i
1211 print b
1212 print '##### END #####'
1213
1214 def optimizeIf01(self):
1215 """Remove the code between #if 0 .. #endif in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001216 self.blocks = optimize_if01(self.blocks)
1217
1218 def optimizeMacros(self, macros):
Tao Baod7db5942015-01-28 10:07:51 -08001219 """Remove known defined and undefined macros from a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001220 for b in self.blocks:
1221 if b.isIf():
1222 b.expr.optimize(macros)
1223
Tao Baod7db5942015-01-28 10:07:51 -08001224 def removeMacroDefines(self, macros):
1225 """Remove known macro definitions from a BlockList."""
1226 self.blocks = remove_macro_defines(self.blocks, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001227
Tao Baod7db5942015-01-28 10:07:51 -08001228 def optimizeAll(self, macros):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001229 self.optimizeMacros(macros)
1230 self.optimizeIf01()
1231 return
1232
1233 def findIncludes(self):
Tao Baod7db5942015-01-28 10:07:51 -08001234 """Return the list of included files in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001235 result = []
1236 for b in self.blocks:
1237 i = b.isInclude()
1238 if i:
1239 result.append(i)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001240 return result
1241
Tao Baod7db5942015-01-28 10:07:51 -08001242 def write(self, out):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001243 out.write(str(self))
1244
Tao Baod7db5942015-01-28 10:07:51 -08001245 def writeWithWarning(self, out, warning, repeat_count):
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001246 left_count = repeat_count
Tao Baod7db5942015-01-28 10:07:51 -08001247 indent = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001248 for b in self.blocks:
Tao Baod7db5942015-01-28 10:07:51 -08001249 left_count, indent = b.writeWithWarning(out, warning, left_count,
1250 repeat_count, indent)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001251
Tao Baod7db5942015-01-28 10:07:51 -08001252 def removeVarsAndFuncs(self, knownStatics=None):
1253 """Remove variable and function declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001254
Tao Baod7db5942015-01-28 10:07:51 -08001255 All extern and static declarations corresponding to variable and
1256 function declarations are removed. We only accept typedefs and
1257 enum/structs/union declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001258
Tao Baod7db5942015-01-28 10:07:51 -08001259 However, we keep the definitions corresponding to the set of known
1260 static inline functions in the set 'knownStatics', which is useful
1261 for optimized byteorder swap functions and stuff like that.
1262 """
1263
1264 # NOTE: It's also removing function-like macros, such as __SYSCALL(...)
1265 # in uapi/asm-generic/unistd.h, or KEY_FIELD(...) in linux/bcache.h.
1266 # It could be problematic when we have function-like macros but without
1267 # '}' following them. It will skip all the tokens/blocks until seeing a
1268 # '}' as the function end. Fortunately we don't have such cases in the
1269 # current kernel headers.
1270
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001271 # state = 0 => normal (i.e. LN + spaces)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001272 # state = 1 => typedef/struct encountered, ends with ";"
1273 # state = 2 => var declaration encountered, ends with ";"
1274 # state = 3 => func declaration encountered, ends with "}"
Tao Baod7db5942015-01-28 10:07:51 -08001275
1276 if knownStatics is None:
1277 knownStatics = set()
1278 state = 0
1279 depth = 0
1280 blocks2 = []
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001281 skipTokens = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001282 for b in self.blocks:
1283 if b.isDirective():
1284 blocks2.append(b)
1285 else:
Tao Baod7db5942015-01-28 10:07:51 -08001286 n = len(b.tokens)
1287 i = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001288 if skipTokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001289 first = n
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001290 else:
1291 first = 0
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001292 while i < n:
1293 tok = b.tokens[i]
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001294 tokid = tok.id
1295 # If we are not looking for the start of a new
1296 # type/var/func, then skip over tokens until
1297 # we find our terminator, managing the depth of
1298 # accolades as we go.
1299 if state > 0:
1300 terminator = False
1301 if tokid == '{':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001302 depth += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001303 elif tokid == '}':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001304 if depth > 0:
1305 depth -= 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001306 if (depth == 0) and (state == 3):
1307 terminator = True
1308 elif tokid == ';' and depth == 0:
1309 terminator = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001310
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001311 if terminator:
1312 # we found the terminator
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001313 state = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001314 if skipTokens:
1315 skipTokens = False
Tao Baod7db5942015-01-28 10:07:51 -08001316 first = i + 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001317
Tao Baod7db5942015-01-28 10:07:51 -08001318 i += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001319 continue
1320
1321 # Is it a new type definition, then start recording it
Tao Baod7db5942015-01-28 10:07:51 -08001322 if tok.id in ['struct', 'typedef', 'enum', 'union',
1323 '__extension__']:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001324 state = 1
Tao Baod7db5942015-01-28 10:07:51 -08001325 i += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001326 continue
1327
1328 # Is it a variable or function definition. If so, first
1329 # try to determine which type it is, and also extract
1330 # its name.
1331 #
1332 # We're going to parse the next tokens of the same block
1333 # until we find a semi-column or a left parenthesis.
1334 #
1335 # The semi-column corresponds to a variable definition,
1336 # the left-parenthesis to a function definition.
1337 #
1338 # We also assume that the var/func name is the last
1339 # identifier before the terminator.
1340 #
Tao Baod7db5942015-01-28 10:07:51 -08001341 j = i + 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001342 ident = ""
1343 while j < n:
1344 tokid = b.tokens[j].id
1345 if tokid == '(': # a function declaration
1346 state = 3
1347 break
Tao Baod7db5942015-01-28 10:07:51 -08001348 elif tokid == ';': # a variable declaration
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001349 state = 2
1350 break
Tao Baod7db5942015-01-28 10:07:51 -08001351 if b.tokens[j].kind == TokenKind.IDENTIFIER:
1352 ident = b.tokens[j].id
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001353 j += 1
1354
1355 if j >= n:
1356 # This can only happen when the declaration
1357 # does not end on the current block (e.g. with
1358 # a directive mixed inside it.
1359 #
1360 # We will treat it as malformed because
1361 # it's very hard to recover from this case
1362 # without making our parser much more
1363 # complex.
1364 #
Tao Baod7db5942015-01-28 10:07:51 -08001365 logging.debug("### skip unterminated static '%s'",
1366 ident)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001367 break
1368
1369 if ident in knownStatics:
Tao Baod7db5942015-01-28 10:07:51 -08001370 logging.debug("### keep var/func '%s': %s", ident,
1371 repr(b.tokens[i:j]))
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001372 else:
1373 # We're going to skip the tokens for this declaration
Tao Baod7db5942015-01-28 10:07:51 -08001374 logging.debug("### skip var/func '%s': %s", ident,
1375 repr(b.tokens[i:j]))
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001376 if i > first:
Tao Baod7db5942015-01-28 10:07:51 -08001377 blocks2.append(Block(b.tokens[first:i]))
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001378 skipTokens = True
Tao Baod7db5942015-01-28 10:07:51 -08001379 first = n
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001380
Tao Baod7db5942015-01-28 10:07:51 -08001381 i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001382
1383 if i > first:
Tao Baod7db5942015-01-28 10:07:51 -08001384 # print "### final '%s'" % repr(b.tokens[first:i])
1385 blocks2.append(Block(b.tokens[first:i]))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001386
1387 self.blocks = blocks2
1388
Tao Baod7db5942015-01-28 10:07:51 -08001389 def replaceTokens(self, replacements):
1390 """Replace tokens according to the given dict."""
Martin Storsjod32c8052010-12-08 11:38:14 +01001391 for b in self.blocks:
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001392 made_change = False
Tao Baod7db5942015-01-28 10:07:51 -08001393 if b.isInclude() is None:
Martin Storsjod32c8052010-12-08 11:38:14 +01001394 for tok in b.tokens:
Tao Baod7db5942015-01-28 10:07:51 -08001395 if tok.kind == TokenKind.IDENTIFIER:
1396 if tok.id in replacements:
1397 tok.id = replacements[tok.id]
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001398 made_change = True
1399
Tao Baod7db5942015-01-28 10:07:51 -08001400 if b.isDefine() and b.define_id in replacements:
1401 b.define_id = replacements[b.define_id]
1402 made_change = True
1403
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001404 if made_change and b.isIf():
1405 # Keep 'expr' in sync with 'tokens'.
1406 b.expr = CppExpr(b.tokens)
Martin Storsjod32c8052010-12-08 11:38:14 +01001407
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001408
Tao Baod7db5942015-01-28 10:07:51 -08001409def strip_space(s):
1410 """Strip out redundant space in a given string."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001411
Tao Baod7db5942015-01-28 10:07:51 -08001412 # NOTE: It ought to be more clever to not destroy spaces in string tokens.
1413 replacements = {' . ': '.',
1414 ' [': '[',
1415 '[ ': '[',
1416 ' ]': ']',
1417 '( ': '(',
1418 ' )': ')',
1419 ' ,': ',',
1420 '# ': '#',
1421 ' ;': ';',
1422 '~ ': '~',
1423 ' -> ': '->'}
1424 result = s
1425 for r in replacements:
1426 result = result.replace(r, replacements[r])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001427
Tao Baod7db5942015-01-28 10:07:51 -08001428 # Remove the space between function name and the parenthesis.
1429 result = re.sub(r'(\w+) \(', r'\1(', result)
1430 return result
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001431
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001432
Tao Baod7db5942015-01-28 10:07:51 -08001433class BlockParser(object):
1434 """A class that converts an input source file into a BlockList object."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001435
Tao Baod7db5942015-01-28 10:07:51 -08001436 def __init__(self, tokzer=None):
1437 """Initialize a block parser.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001438
Tao Baod7db5942015-01-28 10:07:51 -08001439 The input source is provided through a Tokenizer object.
1440 """
1441 self._tokzer = tokzer
1442 self._parsed = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001443
Tao Baod7db5942015-01-28 10:07:51 -08001444 @property
1445 def parsed(self):
1446 return self._parsed
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001447
Tao Baod7db5942015-01-28 10:07:51 -08001448 @staticmethod
1449 def _short_extent(extent):
1450 return '%d:%d - %d:%d' % (extent.start.line, extent.start.column,
1451 extent.end.line, extent.end.column)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001452
Tao Baod7db5942015-01-28 10:07:51 -08001453 def getBlocks(self, tokzer=None):
1454 """Return all the blocks parsed."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001455
Tao Baod7db5942015-01-28 10:07:51 -08001456 def consume_extent(i, tokens, extent=None, detect_change=False):
1457 """Return tokens that belong to the given extent.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001458
Tao Baod7db5942015-01-28 10:07:51 -08001459 It parses all the tokens that follow tokens[i], until getting out
1460 of the extent. When detect_change is True, it may terminate early
1461 when detecting preprocessing directives inside the extent.
1462 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001463
Tao Baod7db5942015-01-28 10:07:51 -08001464 result = []
1465 if extent is None:
1466 extent = tokens[i].cursor.extent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001467
Tao Baod7db5942015-01-28 10:07:51 -08001468 while i < len(tokens) and tokens[i].location in extent:
1469 t = tokens[i]
1470 if debugBlockParser:
1471 print ' ' * 2, t.id, t.kind, t.cursor.kind
1472 if (detect_change and t.cursor.extent != extent and
1473 t.cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE):
1474 break
1475 result.append(t)
1476 i += 1
1477 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001478
Tao Baod7db5942015-01-28 10:07:51 -08001479 def consume_line(i, tokens):
1480 """Return tokens that follow tokens[i] in the same line."""
1481 result = []
1482 line = tokens[i].location.line
1483 while i < len(tokens) and tokens[i].location.line == line:
1484 if tokens[i].cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1485 break
1486 result.append(tokens[i])
1487 i += 1
1488 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001489
Tao Baod7db5942015-01-28 10:07:51 -08001490 if tokzer is None:
1491 tokzer = self._tokzer
1492 tokens = tokzer.tokens
1493
1494 blocks = []
1495 buf = []
1496 i = 0
1497
1498 while i < len(tokens):
1499 t = tokens[i]
1500 cursor = t.cursor
1501
1502 if debugBlockParser:
1503 print ("%d: Processing [%s], kind=[%s], cursor=[%s], "
1504 "extent=[%s]" % (t.location.line, t.spelling, t.kind,
1505 cursor.kind,
1506 self._short_extent(cursor.extent)))
1507
1508 if cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1509 if buf:
1510 blocks.append(Block(buf))
1511 buf = []
1512
1513 j = i
1514 if j + 1 >= len(tokens):
1515 raise BadExpectedToken("### BAD TOKEN at %s" % (t.location))
1516 directive = tokens[j+1].id
1517
1518 if directive == 'define':
1519 if i+2 >= len(tokens):
1520 raise BadExpectedToken("### BAD TOKEN at %s" %
1521 (tokens[i].location))
1522
1523 # Skip '#' and 'define'.
1524 extent = tokens[i].cursor.extent
1525 i += 2
1526 id = ''
1527 # We need to separate the id from the remaining of
1528 # the line, especially for the function-like macro.
1529 if (i + 1 < len(tokens) and tokens[i+1].id == '(' and
1530 (tokens[i].location.column + len(tokens[i].spelling) ==
1531 tokens[i+1].location.column)):
1532 while i < len(tokens):
1533 id += tokens[i].id
1534 if tokens[i].spelling == ')':
1535 i += 1
1536 break
1537 i += 1
1538 else:
1539 id += tokens[i].id
1540 # Advance to the next token that follows the macro id
1541 i += 1
1542
1543 (i, ret) = consume_extent(i, tokens, extent=extent)
1544 blocks.append(Block(ret, directive=directive,
1545 lineno=t.location.line, identifier=id))
1546
1547 else:
1548 (i, ret) = consume_extent(i, tokens)
1549 blocks.append(Block(ret[2:], directive=directive,
1550 lineno=t.location.line))
1551
1552 elif cursor.kind == CursorKind.INCLUSION_DIRECTIVE:
1553 if buf:
1554 blocks.append(Block(buf))
1555 buf = []
1556 directive = tokens[i+1].id
1557 (i, ret) = consume_extent(i, tokens)
1558
1559 blocks.append(Block(ret[2:], directive=directive,
1560 lineno=t.location.line))
1561
1562 elif cursor.kind == CursorKind.VAR_DECL:
1563 if buf:
1564 blocks.append(Block(buf))
1565 buf = []
1566
1567 (i, ret) = consume_extent(i, tokens, detect_change=True)
1568 buf += ret
1569
1570 elif cursor.kind == CursorKind.FUNCTION_DECL:
1571 if buf:
1572 blocks.append(Block(buf))
1573 buf = []
1574
1575 (i, ret) = consume_extent(i, tokens, detect_change=True)
1576 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001577
1578 else:
Tao Baod7db5942015-01-28 10:07:51 -08001579 (i, ret) = consume_line(i, tokens)
1580 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001581
Tao Baod7db5942015-01-28 10:07:51 -08001582 if buf:
1583 blocks.append(Block(buf))
1584
1585 # _parsed=True indicates a successful parsing, although may result an
1586 # empty BlockList.
1587 self._parsed = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001588
1589 return BlockList(blocks)
1590
Tao Baod7db5942015-01-28 10:07:51 -08001591 def parse(self, tokzer):
1592 return self.getBlocks(tokzer)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001593
Tao Baod7db5942015-01-28 10:07:51 -08001594 def parseFile(self, path):
1595 return self.getBlocks(CppFileTokenizer(path))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001596
1597
Tao Baod7db5942015-01-28 10:07:51 -08001598def test_block_parsing(lines, expected):
1599 """Helper method to test the correctness of BlockParser.parse."""
1600 blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001601 if len(blocks) != len(expected):
Tao Baod7db5942015-01-28 10:07:51 -08001602 raise BadExpectedToken("BlockParser.parse() returned '%s' expecting "
1603 "'%s'" % (str(blocks), repr(expected)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001604 for n in range(len(blocks)):
1605 if str(blocks[n]) != expected[n]:
Tao Baod7db5942015-01-28 10:07:51 -08001606 raise BadExpectedToken("BlockParser.parse()[%d] is '%s', "
1607 "expecting '%s'" % (n, str(blocks[n]),
1608 expected[n]))
1609
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001610
1611def test_BlockParser():
Tao Baod7db5942015-01-28 10:07:51 -08001612 test_block_parsing(["#error hello"], ["#error hello"])
1613 test_block_parsing(["foo", "", "bar"], ["foo bar"])
1614
1615 # We currently cannot handle the following case with libclang properly.
1616 # Fortunately it doesn't appear in current headers.
1617 # test_block_parsing(["foo", " # ", "bar"], ["foo", "bar"])
1618
1619 test_block_parsing(["foo",
1620 " # /* ahah */ if defined(__KERNEL__) /* more */",
1621 "bar", "#endif"],
1622 ["foo", "#ifdef __KERNEL__", "bar", "#endif"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001623
1624
Tao Baod7db5942015-01-28 10:07:51 -08001625################################################################################
1626################################################################################
1627##### #####
1628##### B L O C K L I S T O P T I M I Z A T I O N #####
1629##### #####
1630################################################################################
1631################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001632
Tao Baod7db5942015-01-28 10:07:51 -08001633
1634def remove_macro_defines(blocks, excludedMacros=None):
1635 """Remove macro definitions like #define <macroName> ...."""
1636 if excludedMacros is None:
1637 excludedMacros = set()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001638 result = []
1639 for b in blocks:
1640 macroName = b.isDefine()
Tao Baod7db5942015-01-28 10:07:51 -08001641 if macroName is None or macroName not in excludedMacros:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001642 result.append(b)
1643
1644 return result
1645
Tao Baod7db5942015-01-28 10:07:51 -08001646
1647def find_matching_endif(blocks, i):
1648 """Traverse the blocks to find out the matching #endif."""
1649 n = len(blocks)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001650 depth = 1
1651 while i < n:
1652 if blocks[i].isDirective():
Tao Baod7db5942015-01-28 10:07:51 -08001653 dir_ = blocks[i].directive
1654 if dir_ in ["if", "ifndef", "ifdef"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001655 depth += 1
Tao Baod7db5942015-01-28 10:07:51 -08001656 elif depth == 1 and dir_ in ["else", "elif"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001657 return i
Tao Baod7db5942015-01-28 10:07:51 -08001658 elif dir_ == "endif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001659 depth -= 1
1660 if depth == 0:
1661 return i
1662 i += 1
1663 return i
1664
Tao Baod7db5942015-01-28 10:07:51 -08001665
1666def optimize_if01(blocks):
1667 """Remove the code between #if 0 .. #endif in a list of CppBlocks."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001668 i = 0
1669 n = len(blocks)
1670 result = []
1671 while i < n:
1672 j = i
1673 while j < n and not blocks[j].isIf():
1674 j += 1
1675 if j > i:
Tao Baod7db5942015-01-28 10:07:51 -08001676 logging.debug("appending lines %d to %d", blocks[i].lineno,
1677 blocks[j-1].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001678 result += blocks[i:j]
1679 if j >= n:
1680 break
1681 expr = blocks[j].expr
Tao Baod7db5942015-01-28 10:07:51 -08001682 r = expr.toInt()
1683 if r is None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001684 result.append(blocks[j])
1685 i = j + 1
1686 continue
1687
1688 if r == 0:
1689 # if 0 => skip everything until the corresponding #endif
Tao Baod7db5942015-01-28 10:07:51 -08001690 j = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001691 if j >= n:
1692 # unterminated #if 0, finish here
1693 break
Tao Baod7db5942015-01-28 10:07:51 -08001694 dir_ = blocks[j].directive
1695 if dir_ == "endif":
1696 logging.debug("remove 'if 0' .. 'endif' (lines %d to %d)",
1697 blocks[i].lineno, blocks[j].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001698 i = j + 1
Tao Baod7db5942015-01-28 10:07:51 -08001699 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001700 # convert 'else' into 'if 1'
Tao Baod7db5942015-01-28 10:07:51 -08001701 logging.debug("convert 'if 0' .. 'else' into 'if 1' (lines %d "
1702 "to %d)", blocks[i].lineno, blocks[j-1].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001703 blocks[j].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001704 blocks[j].expr = CppExpr(CppStringTokenizer("1").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001705 i = j
Tao Baod7db5942015-01-28 10:07:51 -08001706 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001707 # convert 'elif' into 'if'
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001708 logging.debug("convert 'if 0' .. 'elif' into 'if'")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001709 blocks[j].directive = "if"
1710 i = j
1711 continue
1712
1713 # if 1 => find corresponding endif and remove/transform them
Tao Baod7db5942015-01-28 10:07:51 -08001714 k = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001715 if k >= n:
1716 # unterminated #if 1, finish here
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001717 logging.debug("unterminated 'if 1'")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001718 result += blocks[j+1:k]
1719 break
1720
Tao Baod7db5942015-01-28 10:07:51 -08001721 dir_ = blocks[k].directive
1722 if dir_ == "endif":
1723 logging.debug("convert 'if 1' .. 'endif' (lines %d to %d)",
1724 blocks[j].lineno, blocks[k].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001725 result += optimize_if01(blocks[j+1:k])
Tao Baod7db5942015-01-28 10:07:51 -08001726 i = k + 1
1727 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001728 # convert 'else' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001729 logging.debug("convert 'if 1' .. 'else' (lines %d to %d)",
1730 blocks[j].lineno, blocks[k].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001731 result += optimize_if01(blocks[j+1:k])
1732 blocks[k].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001733 blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001734 i = k
Tao Baod7db5942015-01-28 10:07:51 -08001735 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001736 # convert 'elif' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001737 logging.debug("convert 'if 1' .. 'elif' (lines %d to %d)",
1738 blocks[j].lineno, blocks[k].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001739 result += optimize_if01(blocks[j+1:k])
Tao Baod7db5942015-01-28 10:07:51 -08001740 blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001741 i = k
1742 return result
1743
Tao Baod7db5942015-01-28 10:07:51 -08001744
1745def test_optimizeAll():
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001746 text = """\
1747#if 1
1748#define GOOD_1
1749#endif
1750#if 0
1751#define BAD_2
1752#define BAD_3
1753#endif
1754
1755#if 1
1756#define GOOD_2
1757#else
1758#define BAD_4
1759#endif
1760
1761#if 0
1762#define BAD_5
1763#else
1764#define GOOD_3
1765#endif
1766
Elliott Hughes40596aa2013-11-05 14:54:29 -08001767#if defined(__KERNEL__)
1768#define BAD_KERNEL
1769#endif
1770
1771#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
1772#define X
1773#endif
1774
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001775#ifndef SIGRTMAX
1776#define SIGRTMAX 123
1777#endif /* SIGRTMAX */
1778
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001779#if 0
1780#if 1
1781#define BAD_6
1782#endif
1783#endif\
1784"""
1785
1786 expected = """\
1787#define GOOD_1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001788#define GOOD_2
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001789#define GOOD_3
Elliott Hughes40596aa2013-11-05 14:54:29 -08001790#if !defined(__GLIBC__) || __GLIBC__ < 2
1791#define X
1792#endif
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001793#ifndef __SIGRTMAX
1794#define __SIGRTMAX 123
Tao Baod7db5942015-01-28 10:07:51 -08001795#endif\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001796"""
1797
Tao Baod7db5942015-01-28 10:07:51 -08001798 out = utils.StringOutput()
1799 blocks = BlockParser().parse(CppStringTokenizer(text))
1800 blocks.replaceTokens(kernel_token_replacements)
1801 blocks.optimizeAll({"__KERNEL__": kCppUndefinedMacro})
1802 blocks.write(out)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001803 if out.get() != expected:
Elliott Hughes40596aa2013-11-05 14:54:29 -08001804 print "[FAIL]: macro optimization failed\n"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001805 print "<<<< expecting '",
1806 print expected,
Tao Baod7db5942015-01-28 10:07:51 -08001807 print "'\n>>>> result '",
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001808 print out.get(),
1809 print "'\n----"
Elliott Hughes40596aa2013-11-05 14:54:29 -08001810 global failure_count
1811 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001812
1813
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001814def runUnitTests():
Tao Baod7db5942015-01-28 10:07:51 -08001815 """Always run all unit tests for this program."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001816 test_CppTokenizer()
1817 test_CppExpr()
1818 test_optimizeAll()
1819 test_BlockParser()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001820
Tao Baod7db5942015-01-28 10:07:51 -08001821
Elliott Hughes40596aa2013-11-05 14:54:29 -08001822failure_count = 0
1823runUnitTests()
1824if failure_count != 0:
Tao Baod7db5942015-01-28 10:07:51 -08001825 utils.panic("Unit tests failed in cpp.py.\n")