blob: 1ada59e091fdac79d60d39af2dbdb32213515696 [file] [log] [blame]
Stephen Crane77bb5642017-08-31 15:08:26 -07001#!/usr/bin/env python
Tao Baod7db5942015-01-28 10:07:51 -08002"""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
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07009import unittest
Tao Baod7db5942015-01-28 10:07:51 -080010import utils
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080011
Tao Baod7db5942015-01-28 10:07:51 -080012top = os.getenv('ANDROID_BUILD_TOP')
13if top is None:
14 utils.panic('ANDROID_BUILD_TOP not set.\n')
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080015
Tao Baod7db5942015-01-28 10:07:51 -080016# Set up the env vars for libclang.
17site.addsitedir(os.path.join(top, 'external/clang/bindings/python'))
Tao Baod7db5942015-01-28 10:07:51 -080018
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
Tao Bao75920082015-04-22 10:37:38 -070029# Set up LD_LIBRARY_PATH to include libclang.so, libLLVM.so, and etc.
30# Note that setting LD_LIBRARY_PATH with os.putenv() sometimes doesn't help.
Christopher Ferris1308ad32017-11-14 17:32:13 -080031clang.cindex.Config.set_library_file(os.path.join(top, 'prebuilts/sdk/tools/linux/lib64/libclang_android.so'))
Tao Bao75920082015-04-22 10:37:38 -070032
Tao Baod7db5942015-01-28 10:07:51 -080033from defaults import kCppUndefinedMacro
34from defaults import kernel_remove_config_macros
Elliott Hughes64f355f2017-08-30 16:10:24 -070035from defaults import kernel_struct_replacements
Tao Baod7db5942015-01-28 10:07:51 -080036from defaults import kernel_token_replacements
37
38
39debugBlockParser = False
40debugCppExpr = False
41debugOptimIf01 = False
42
43###############################################################################
44###############################################################################
45##### #####
46##### C P P T O K E N S #####
47##### #####
48###############################################################################
49###############################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080050
51# the list of supported C-preprocessor tokens
52# plus a couple of C tokens as well
Tao Baod7db5942015-01-28 10:07:51 -080053tokEOF = "\0"
54tokLN = "\n"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080055tokSTRINGIFY = "#"
Tao Baod7db5942015-01-28 10:07:51 -080056tokCONCAT = "##"
57tokLOGICAND = "&&"
58tokLOGICOR = "||"
59tokSHL = "<<"
60tokSHR = ">>"
61tokEQUAL = "=="
62tokNEQUAL = "!="
63tokLT = "<"
64tokLTE = "<="
65tokGT = ">"
66tokGTE = ">="
67tokELLIPSIS = "..."
68tokSPACE = " "
69tokDEFINED = "defined"
70tokLPAREN = "("
71tokRPAREN = ")"
72tokNOT = "!"
73tokPLUS = "+"
74tokMINUS = "-"
75tokMULTIPLY = "*"
76tokDIVIDE = "/"
77tokMODULUS = "%"
78tokBINAND = "&"
79tokBINOR = "|"
80tokBINXOR = "^"
81tokCOMMA = ","
82tokLBRACE = "{"
83tokRBRACE = "}"
84tokARROW = "->"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080085tokINCREMENT = "++"
86tokDECREMENT = "--"
Tao Baod7db5942015-01-28 10:07:51 -080087tokNUMBER = "<number>"
88tokIDENT = "<ident>"
89tokSTRING = "<string>"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080090
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080091
Tao Baod7db5942015-01-28 10:07:51 -080092class Token(clang.cindex.Token):
93 """A class that represents one token after parsing.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080094
Tao Baod7db5942015-01-28 10:07:51 -080095 It inherits the class in libclang, with an extra id property to hold the
96 new spelling of the token. The spelling property in the base class is
97 defined as read-only. New names after macro instantiation are saved in
98 their ids now. It also facilitates the renaming of directive optimizations
99 like replacing 'ifndef X' with 'if !defined(X)'.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800100
Tao Baod7db5942015-01-28 10:07:51 -0800101 It also overrides the cursor property of the base class. Because the one
102 in libclang always queries based on a single token, which usually doesn't
103 hold useful information. The cursor in this class can be set by calling
104 CppTokenizer.getTokensWithCursors(). Otherwise it returns the one in the
105 base class.
106 """
107
108 def __init__(self, tu=None, group=None, int_data=None, ptr_data=None,
109 cursor=None):
110 clang.cindex.Token.__init__(self)
111 self._id = None
112 self._tu = tu
113 self._group = group
114 self._cursor = cursor
115 # self.int_data and self.ptr_data are from the base class. But
116 # self.int_data doesn't accept a None value.
117 if int_data is not None:
118 self.int_data = int_data
119 self.ptr_data = ptr_data
120
121 @property
122 def id(self):
123 """Name of the token."""
124 if self._id is None:
125 return self.spelling
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800126 else:
Tao Baod7db5942015-01-28 10:07:51 -0800127 return self._id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800128
Tao Baod7db5942015-01-28 10:07:51 -0800129 @id.setter
130 def id(self, new_id):
131 """Setting name of the token."""
132 self._id = new_id
133
134 @property
135 def cursor(self):
136 if self._cursor is None:
137 self._cursor = clang.cindex.Token.cursor
138 return self._cursor
139
140 @cursor.setter
141 def cursor(self, new_cursor):
142 self._cursor = new_cursor
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800143
144 def __repr__(self):
Tao Baod7db5942015-01-28 10:07:51 -0800145 if self.id == 'defined':
146 return self.id
147 elif self.kind == TokenKind.IDENTIFIER:
148 return "(ident %s)" % self.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800149
150 return self.id
151
152 def __str__(self):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800153 return self.id
154
Tao Baod7db5942015-01-28 10:07:51 -0800155
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800156class BadExpectedToken(Exception):
Tao Baod7db5942015-01-28 10:07:51 -0800157 """An exception that will be raised for unexpected tokens."""
158 pass
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800159
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800160
Tao Baod7db5942015-01-28 10:07:51 -0800161# The __contains__ function in libclang SourceRange class contains a bug. It
162# gives wrong result when dealing with single line range.
163# Bug filed with upstream:
164# http://llvm.org/bugs/show_bug.cgi?id=22243, http://reviews.llvm.org/D7277
165def SourceRange__contains__(self, other):
166 """Determine if a given location is inside the range."""
167 if not isinstance(other, SourceLocation):
168 return False
169 if other.file is None and self.start.file is None:
170 pass
171 elif (self.start.file.name != other.file.name or
172 other.file.name != self.end.file.name):
173 # same file name
174 return False
175 # same file, in between lines
176 if self.start.line < other.line < self.end.line:
177 return True
178 # same file, same line
179 elif self.start.line == other.line == self.end.line:
180 if self.start.column <= other.column <= self.end.column:
181 return True
182 elif self.start.line == other.line:
183 # same file first line
184 if self.start.column <= other.column:
185 return True
186 elif other.line == self.end.line:
187 # same file last line
188 if other.column <= self.end.column:
189 return True
190 return False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800191
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800192
Tao Baod7db5942015-01-28 10:07:51 -0800193SourceRange.__contains__ = SourceRange__contains__
194
195
196################################################################################
197################################################################################
198##### #####
199##### C P P T O K E N I Z E R #####
200##### #####
201################################################################################
202################################################################################
203
204
205class CppTokenizer(object):
206 """A tokenizer that converts some input text into a list of tokens.
207
208 It calls libclang's tokenizer to get the parsed tokens. In addition, it
209 updates the cursor property in each token after parsing, by calling
210 getTokensWithCursors().
211 """
212
213 clang_flags = ['-E', '-x', 'c']
214 options = TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800215
216 def __init__(self):
Tao Baod7db5942015-01-28 10:07:51 -0800217 """Initialize a new CppTokenizer object."""
218 self._indexer = clang.cindex.Index.create()
219 self._tu = None
220 self._index = 0
221 self.tokens = None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800222
Tao Baod7db5942015-01-28 10:07:51 -0800223 def _getTokensWithCursors(self):
224 """Helper method to return all tokens with their cursors.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800225
Tao Baod7db5942015-01-28 10:07:51 -0800226 The cursor property in a clang Token doesn't provide enough
227 information. Because it is queried based on single token each time
228 without any context, i.e. via calling conf.lib.clang_annotateTokens()
229 with only one token given. So we often see 'INVALID_FILE' in one
230 token's cursor. In this function it passes all the available tokens
231 to get more informative cursors.
232 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800233
Tao Baod7db5942015-01-28 10:07:51 -0800234 tokens_memory = ctypes.POINTER(clang.cindex.Token)()
235 tokens_count = ctypes.c_uint()
236
237 conf.lib.clang_tokenize(self._tu, self._tu.cursor.extent,
238 ctypes.byref(tokens_memory),
239 ctypes.byref(tokens_count))
240
241 count = int(tokens_count.value)
242
243 # If we get no tokens, no memory was allocated. Be sure not to return
244 # anything and potentially call a destructor on nothing.
245 if count < 1:
246 return
247
248 cursors = (Cursor * count)()
249 cursors_memory = ctypes.cast(cursors, ctypes.POINTER(Cursor))
250
251 conf.lib.clang_annotateTokens(self._tu, tokens_memory, count,
252 cursors_memory)
253
254 tokens_array = ctypes.cast(
255 tokens_memory,
256 ctypes.POINTER(clang.cindex.Token * count)).contents
257 token_group = TokenGroup(self._tu, tokens_memory, tokens_count)
258
259 tokens = []
260 for i in xrange(0, count):
261 token = Token(self._tu, token_group,
262 int_data=tokens_array[i].int_data,
263 ptr_data=tokens_array[i].ptr_data,
264 cursor=cursors[i])
265 # We only want non-comment tokens.
266 if token.kind != TokenKind.COMMENT:
267 tokens.append(token)
268
269 return tokens
270
271 def parseString(self, lines):
272 """Parse a list of text lines into a BlockList object."""
273 file_ = 'dummy.c'
274 self._tu = self._indexer.parse(file_, self.clang_flags,
275 unsaved_files=[(file_, lines)],
276 options=self.options)
277 self.tokens = self._getTokensWithCursors()
278
279 def parseFile(self, file_):
280 """Parse a file into a BlockList object."""
281 self._tu = self._indexer.parse(file_, self.clang_flags,
282 options=self.options)
283 self.tokens = self._getTokensWithCursors()
284
285 def nextToken(self):
286 """Return next token from the list."""
287 if self._index < len(self.tokens):
288 t = self.tokens[self._index]
289 self._index += 1
290 return t
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800291 else:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800292 return None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800293
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800294
Tao Baod7db5942015-01-28 10:07:51 -0800295class CppStringTokenizer(CppTokenizer):
296 """A CppTokenizer derived class that accepts a string of text as input."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800297
Tao Baod7db5942015-01-28 10:07:51 -0800298 def __init__(self, line):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800299 CppTokenizer.__init__(self)
Tao Baod7db5942015-01-28 10:07:51 -0800300 self.parseString(line)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800301
302
303class CppFileTokenizer(CppTokenizer):
Tao Baod7db5942015-01-28 10:07:51 -0800304 """A CppTokenizer derived class that accepts a file as input."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800305
Tao Baod7db5942015-01-28 10:07:51 -0800306 def __init__(self, file_):
307 CppTokenizer.__init__(self)
308 self.parseFile(file_)
309
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800310
311# Unit testing
312#
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700313class CppTokenizerTests(unittest.TestCase):
314 """CppTokenizer tests."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800315
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700316 def get_tokens(self, token_string, line_col=False):
317 tokens = CppStringTokenizer(token_string)
318 token_list = []
319 while True:
320 token = tokens.nextToken()
321 if not token:
322 break
323 if line_col:
324 token_list.append((token.id, token.location.line,
325 token.location.column))
326 else:
327 token_list.append(token.id)
328 return token_list
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800329
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700330 def test_hash(self):
331 self.assertEqual(self.get_tokens("#an/example && (01923_xy)"),
332 ["#", "an", "/", "example", tokLOGICAND, tokLPAREN,
333 "01923_xy", tokRPAREN])
Tao Baod7db5942015-01-28 10:07:51 -0800334
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700335 def test_parens(self):
336 self.assertEqual(self.get_tokens("FOO(BAR) && defined(BAZ)"),
337 ["FOO", tokLPAREN, "BAR", tokRPAREN, tokLOGICAND,
338 "defined", tokLPAREN, "BAZ", tokRPAREN])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800339
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700340 def test_comment(self):
341 self.assertEqual(self.get_tokens("/*\n#\n*/"), [])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800342
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700343 def test_line_cross(self):
344 self.assertEqual(self.get_tokens("first\nsecond"), ["first", "second"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800345
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700346 def test_line_cross_line_col(self):
347 self.assertEqual(self.get_tokens("first second\n third", True),
348 [("first", 1, 1), ("second", 1, 7), ("third", 2, 3)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800349
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700350 def test_comment_line_col(self):
351 self.assertEqual(self.get_tokens("boo /* what the\nhell */", True),
352 [("boo", 1, 1)])
Tao Baod7db5942015-01-28 10:07:51 -0800353
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700354 def test_escapes(self):
355 self.assertEqual(self.get_tokens("an \\\n example", True),
356 [("an", 1, 1), ("example", 2, 2)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800357
358
Tao Baod7db5942015-01-28 10:07:51 -0800359################################################################################
360################################################################################
361##### #####
362##### C P P E X P R E S S I O N S #####
363##### #####
364################################################################################
365################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800366
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800367
Tao Baod7db5942015-01-28 10:07:51 -0800368class CppExpr(object):
369 """A class that models the condition of #if directives into an expr tree.
370
371 Each node in the tree is of the form (op, arg) or (op, arg1, arg2) where
372 "op" is a string describing the operation
373 """
374
375 unaries = ["!", "~"]
376 binaries = ["+", "-", "<", "<=", ">=", ">", "&&", "||", "*", "/", "%",
377 "&", "|", "^", "<<", ">>", "==", "!=", "?", ":"]
Elliott Hughes1198fd32013-11-21 11:12:34 -0800378 precedences = {
379 "?": 1, ":": 1,
380 "||": 2,
381 "&&": 3,
382 "|": 4,
383 "^": 5,
384 "&": 6,
385 "==": 7, "!=": 7,
386 "<": 8, "<=": 8, ">": 8, ">=": 8,
387 "<<": 9, ">>": 9,
388 "+": 10, "-": 10,
389 "*": 11, "/": 11, "%": 11,
390 "!": 12, "~": 12
391 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800392
393 def __init__(self, tokens):
Tao Baod7db5942015-01-28 10:07:51 -0800394 """Initialize a CppExpr. 'tokens' must be a CppToken list."""
395 self.tokens = tokens
396 self._num_tokens = len(tokens)
397 self._index = 0
398
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800399 if debugCppExpr:
400 print "CppExpr: trying to parse %s" % repr(tokens)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800401 self.expr = self.parseExpression(0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800402 if debugCppExpr:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800403 print "CppExpr: got " + repr(self.expr)
Tao Baod7db5942015-01-28 10:07:51 -0800404 if self._index != self._num_tokens:
405 self.throw(BadExpectedToken, "crap at end of input (%d != %d): %s"
406 % (self._index, self._num_tokens, repr(tokens)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800407
Elliott Hughes40596aa2013-11-05 14:54:29 -0800408 def throw(self, exception, msg):
Tao Baod7db5942015-01-28 10:07:51 -0800409 if self._index < self._num_tokens:
410 tok = self.tokens[self._index]
411 print "%d:%d: %s" % (tok.location.line, tok.location.column, msg)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800412 else:
413 print "EOF: %s" % msg
Elliott Hughes40596aa2013-11-05 14:54:29 -0800414 raise exception(msg)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800415
Elliott Hughes40596aa2013-11-05 14:54:29 -0800416 def expectId(self, id):
Tao Baod7db5942015-01-28 10:07:51 -0800417 """Check that a given token id is at the current position."""
418 token = self.tokens[self._index]
419 if self._index >= self._num_tokens or token.id != id:
420 self.throw(BadExpectedToken,
421 "### expecting '%s' in expression, got '%s'" % (
422 id, token.id))
423 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800424
425 def is_decimal(self):
Tao Baod7db5942015-01-28 10:07:51 -0800426 token = self.tokens[self._index].id
427 if token[-1] in "ULul":
428 token = token[:-1]
429 try:
430 val = int(token, 10)
431 self._index += 1
432 return ('int', val)
433 except ValueError:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800434 return None
435
Tao Baod7db5942015-01-28 10:07:51 -0800436 def is_octal(self):
437 token = self.tokens[self._index].id
438 if token[-1] in "ULul":
439 token = token[:-1]
440 if len(token) < 2 or token[0] != '0':
441 return None
442 try:
443 val = int(token, 8)
444 self._index += 1
445 return ('oct', val)
446 except ValueError:
447 return None
448
449 def is_hexadecimal(self):
450 token = self.tokens[self._index].id
451 if token[-1] in "ULul":
452 token = token[:-1]
453 if len(token) < 3 or (token[:2] != '0x' and token[:2] != '0X'):
454 return None
455 try:
456 val = int(token, 16)
457 self._index += 1
458 return ('hex', val)
459 except ValueError:
460 return None
461
462 def is_integer(self):
463 if self.tokens[self._index].kind != TokenKind.LITERAL:
464 return None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800465
Elliott Hughes40596aa2013-11-05 14:54:29 -0800466 c = self.is_hexadecimal()
Tao Baod7db5942015-01-28 10:07:51 -0800467 if c:
468 return c
469
470 c = self.is_octal()
471 if c:
472 return c
473
474 c = self.is_decimal()
475 if c:
476 return c
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800477
478 return None
479
Elliott Hughes40596aa2013-11-05 14:54:29 -0800480 def is_number(self):
Tao Baod7db5942015-01-28 10:07:51 -0800481 t = self.tokens[self._index]
482 if t.id == tokMINUS and self._index + 1 < self._num_tokens:
483 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800484 c = self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800485 if c:
Tao Baod7db5942015-01-28 10:07:51 -0800486 op, val = c
Elliott Hughes40596aa2013-11-05 14:54:29 -0800487 return (op, -val)
Tao Baod7db5942015-01-28 10:07:51 -0800488 if t.id == tokPLUS and self._index + 1 < self._num_tokens:
489 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800490 c = self.is_integer()
Tao Baod7db5942015-01-28 10:07:51 -0800491 if c:
492 return c
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800493
Elliott Hughes40596aa2013-11-05 14:54:29 -0800494 return self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800495
Elliott Hughes40596aa2013-11-05 14:54:29 -0800496 def is_defined(self):
Tao Baod7db5942015-01-28 10:07:51 -0800497 t = self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800498 if t.id != tokDEFINED:
499 return None
500
Tao Baod7db5942015-01-28 10:07:51 -0800501 # We have the defined keyword, check the rest.
502 self._index += 1
503 used_parens = False
504 if (self._index < self._num_tokens and
505 self.tokens[self._index].id == tokLPAREN):
506 used_parens = True
507 self._index += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800508
Tao Baod7db5942015-01-28 10:07:51 -0800509 if self._index >= self._num_tokens:
510 self.throw(BadExpectedToken,
511 "### 'defined' must be followed by macro name or left "
512 "paren")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800513
Tao Baod7db5942015-01-28 10:07:51 -0800514 t = self.tokens[self._index]
515 if t.kind != TokenKind.IDENTIFIER:
516 self.throw(BadExpectedToken,
517 "### 'defined' must be followed by macro name")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800518
Tao Baod7db5942015-01-28 10:07:51 -0800519 self._index += 1
Elliott Hughesfddbafd2014-05-01 10:17:27 -0700520 if used_parens:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800521 self.expectId(tokRPAREN)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800522
Tao Baod7db5942015-01-28 10:07:51 -0800523 return ("defined", t.id)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800524
Elliott Hughes40596aa2013-11-05 14:54:29 -0800525 def is_call_or_ident(self):
Tao Baod7db5942015-01-28 10:07:51 -0800526 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800527 return None
528
Tao Baod7db5942015-01-28 10:07:51 -0800529 t = self.tokens[self._index]
530 if t.kind != TokenKind.IDENTIFIER:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800531 return None
532
Tao Baod7db5942015-01-28 10:07:51 -0800533 name = t.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800534
Tao Baod7db5942015-01-28 10:07:51 -0800535 self._index += 1
536 if (self._index >= self._num_tokens or
537 self.tokens[self._index].id != tokLPAREN):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800538 return ("ident", name)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800539
Tao Baod7db5942015-01-28 10:07:51 -0800540 params = []
541 depth = 1
542 self._index += 1
543 j = self._index
544 while self._index < self._num_tokens:
545 id = self.tokens[self._index].id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800546 if id == tokLPAREN:
547 depth += 1
548 elif depth == 1 and (id == tokCOMMA or id == tokRPAREN):
Tao Baod7db5942015-01-28 10:07:51 -0800549 k = self._index
550 param = self.tokens[j:k]
Elliott Hughes40596aa2013-11-05 14:54:29 -0800551 params.append(param)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800552 if id == tokRPAREN:
553 break
Tao Baod7db5942015-01-28 10:07:51 -0800554 j = self._index + 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800555 elif id == tokRPAREN:
556 depth -= 1
Tao Baod7db5942015-01-28 10:07:51 -0800557 self._index += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800558
Tao Baod7db5942015-01-28 10:07:51 -0800559 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800560 return None
561
Tao Baod7db5942015-01-28 10:07:51 -0800562 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800563 return ("call", (name, params))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800564
Tao Baod7db5942015-01-28 10:07:51 -0800565 # Implements the "precedence climbing" algorithm from
566 # http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm.
567 # The "classic" algorithm would be fine if we were using a tool to
568 # generate the parser, but we're not. Dijkstra's "shunting yard"
569 # algorithm hasn't been necessary yet.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800570
Elliott Hughes40596aa2013-11-05 14:54:29 -0800571 def parseExpression(self, minPrecedence):
Tao Baod7db5942015-01-28 10:07:51 -0800572 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800573 return None
574
Elliott Hughes40596aa2013-11-05 14:54:29 -0800575 node = self.parsePrimary()
Tao Baod7db5942015-01-28 10:07:51 -0800576 while (self.token() and self.isBinary(self.token()) and
577 self.precedence(self.token()) >= minPrecedence):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800578 op = self.token()
579 self.nextToken()
580 rhs = self.parseExpression(self.precedence(op) + 1)
581 node = (op.id, node, rhs)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800582
Elliott Hughes40596aa2013-11-05 14:54:29 -0800583 return node
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800584
Elliott Hughes40596aa2013-11-05 14:54:29 -0800585 def parsePrimary(self):
586 op = self.token()
587 if self.isUnary(op):
588 self.nextToken()
589 return (op.id, self.parseExpression(self.precedence(op)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800590
Elliott Hughes40596aa2013-11-05 14:54:29 -0800591 primary = None
592 if op.id == tokLPAREN:
593 self.nextToken()
594 primary = self.parseExpression(0)
595 self.expectId(tokRPAREN)
Elliott Hughes1198fd32013-11-21 11:12:34 -0800596 elif op.id == "?":
597 self.nextToken()
598 primary = self.parseExpression(0)
599 self.expectId(":")
Tao Baod7db5942015-01-28 10:07:51 -0800600 elif op.id == '+' or op.id == '-' or op.kind == TokenKind.LITERAL:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800601 primary = self.is_number()
Tao Baod7db5942015-01-28 10:07:51 -0800602 # Checking for 'defined' needs to come first now because 'defined' is
603 # recognized as IDENTIFIER.
Elliott Hughes40596aa2013-11-05 14:54:29 -0800604 elif op.id == tokDEFINED:
605 primary = self.is_defined()
Tao Baod7db5942015-01-28 10:07:51 -0800606 elif op.kind == TokenKind.IDENTIFIER:
607 primary = self.is_call_or_ident()
Elliott Hughes40596aa2013-11-05 14:54:29 -0800608 else:
Tao Baod7db5942015-01-28 10:07:51 -0800609 self.throw(BadExpectedToken,
610 "didn't expect to see a %s in factor" % (
611 self.tokens[self._index].id))
612 return primary
Elliott Hughes40596aa2013-11-05 14:54:29 -0800613
614 def isBinary(self, token):
615 return token.id in self.binaries
616
Elliott Hughes40596aa2013-11-05 14:54:29 -0800617 def isUnary(self, token):
618 return token.id in self.unaries
619
Elliott Hughes40596aa2013-11-05 14:54:29 -0800620 def precedence(self, token):
621 return self.precedences.get(token.id)
622
Elliott Hughes40596aa2013-11-05 14:54:29 -0800623 def token(self):
Tao Baod7db5942015-01-28 10:07:51 -0800624 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800625 return None
Tao Baod7db5942015-01-28 10:07:51 -0800626 return self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800627
Elliott Hughes40596aa2013-11-05 14:54:29 -0800628 def nextToken(self):
Tao Baod7db5942015-01-28 10:07:51 -0800629 self._index += 1
630 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800631 return None
Tao Baod7db5942015-01-28 10:07:51 -0800632 return self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800633
Elliott Hughes40596aa2013-11-05 14:54:29 -0800634 def dump_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800635 op = e[0]
636 line = "(" + op
637 if op == "int":
638 line += " %d)" % e[1]
Tao Baod7db5942015-01-28 10:07:51 -0800639 elif op == "oct":
640 line += " 0%o)" % e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800641 elif op == "hex":
642 line += " 0x%x)" % e[1]
643 elif op == "ident":
644 line += " %s)" % e[1]
645 elif op == "defined":
646 line += " %s)" % e[1]
647 elif op == "call":
648 arg = e[1]
649 line += " %s [" % arg[0]
650 prefix = ""
651 for param in arg[1]:
652 par = ""
653 for tok in param:
654 par += str(tok)
655 line += "%s%s" % (prefix, par)
656 prefix = ","
657 line += "])"
658 elif op in CppExpr.unaries:
659 line += " %s)" % self.dump_node(e[1])
660 elif op in CppExpr.binaries:
661 line += " %s %s)" % (self.dump_node(e[1]), self.dump_node(e[2]))
662 else:
663 line += " ?%s)" % repr(e[1])
664
665 return line
666
667 def __repr__(self):
668 return self.dump_node(self.expr)
669
Elliott Hughes40596aa2013-11-05 14:54:29 -0800670 def source_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800671 op = e[0]
672 if op == "int":
673 return "%d" % e[1]
674 if op == "hex":
675 return "0x%x" % e[1]
Tao Baod7db5942015-01-28 10:07:51 -0800676 if op == "oct":
677 return "0%o" % e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800678 if op == "ident":
679 # XXX: should try to expand
680 return e[1]
681 if op == "defined":
682 return "defined(%s)" % e[1]
683
Tao Baod7db5942015-01-28 10:07:51 -0800684 prec = CppExpr.precedences.get(op, 1000)
685 arg = e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800686 if op in CppExpr.unaries:
687 arg_src = self.source_node(arg)
Tao Baod7db5942015-01-28 10:07:51 -0800688 arg_op = arg[0]
689 arg_prec = CppExpr.precedences.get(arg_op, 1000)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800690 if arg_prec < prec:
691 return "!(" + arg_src + ")"
692 else:
693 return "!" + arg_src
694 if op in CppExpr.binaries:
Tao Baod7db5942015-01-28 10:07:51 -0800695 arg2 = e[2]
696 arg1_op = arg[0]
697 arg2_op = arg2[0]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800698 arg1_src = self.source_node(arg)
699 arg2_src = self.source_node(arg2)
Tao Baod7db5942015-01-28 10:07:51 -0800700 if CppExpr.precedences.get(arg1_op, 1000) < prec:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800701 arg1_src = "(%s)" % arg1_src
Tao Baod7db5942015-01-28 10:07:51 -0800702 if CppExpr.precedences.get(arg2_op, 1000) < prec:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800703 arg2_src = "(%s)" % arg2_src
704
705 return "%s %s %s" % (arg1_src, op, arg2_src)
706 return "???"
707
708 def __str__(self):
709 return self.source_node(self.expr)
710
Tao Baod7db5942015-01-28 10:07:51 -0800711 @staticmethod
712 def int_node(e):
713 if e[0] in ["int", "oct", "hex"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800714 return e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800715 else:
716 return None
717
718 def toInt(self):
719 return self.int_node(self.expr)
720
Tao Baod7db5942015-01-28 10:07:51 -0800721 def optimize_node(self, e, macros=None):
722 if macros is None:
723 macros = {}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800724 op = e[0]
Tao Baod7db5942015-01-28 10:07:51 -0800725
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800726 if op == "defined":
Elliott Hughes40596aa2013-11-05 14:54:29 -0800727 op, name = e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800728 if macros.has_key(name):
729 if macros[name] == kCppUndefinedMacro:
730 return ("int", 0)
731 else:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700732 try:
733 value = int(macros[name])
734 return ("int", value)
Tao Baod7db5942015-01-28 10:07:51 -0800735 except ValueError:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700736 return ("defined", macros[name])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800737
738 if kernel_remove_config_macros and name.startswith("CONFIG_"):
739 return ("int", 0)
740
Elliott Hughes40596aa2013-11-05 14:54:29 -0800741 return e
742
743 elif op == "ident":
744 op, name = e
745 if macros.has_key(name):
746 try:
747 value = int(macros[name])
748 expanded = ("int", value)
Tao Baod7db5942015-01-28 10:07:51 -0800749 except ValueError:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800750 expanded = ("ident", macros[name])
751 return self.optimize_node(expanded, macros)
752 return e
753
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800754 elif op == "!":
755 op, v = e
756 v = self.optimize_node(v, macros)
757 if v[0] == "int":
758 if v[1] == 0:
759 return ("int", 1)
760 else:
761 return ("int", 0)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800762 return ('!', v)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800763
764 elif op == "&&":
765 op, l, r = e
Tao Baod7db5942015-01-28 10:07:51 -0800766 l = self.optimize_node(l, macros)
767 r = self.optimize_node(r, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800768 li = self.int_node(l)
769 ri = self.int_node(r)
Tao Baod7db5942015-01-28 10:07:51 -0800770 if li is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800771 if li == 0:
772 return ("int", 0)
773 else:
774 return r
Tao Baod7db5942015-01-28 10:07:51 -0800775 elif ri is not None:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800776 if ri == 0:
777 return ("int", 0)
778 else:
779 return l
780 return (op, l, r)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800781
782 elif op == "||":
783 op, l, r = e
Tao Baod7db5942015-01-28 10:07:51 -0800784 l = self.optimize_node(l, macros)
785 r = self.optimize_node(r, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800786 li = self.int_node(l)
787 ri = self.int_node(r)
Tao Baod7db5942015-01-28 10:07:51 -0800788 if li is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800789 if li == 0:
790 return r
791 else:
792 return ("int", 1)
Tao Baod7db5942015-01-28 10:07:51 -0800793 elif ri is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800794 if ri == 0:
795 return l
796 else:
797 return ("int", 1)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800798 return (op, l, r)
799
800 else:
801 return e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800802
Tao Baod7db5942015-01-28 10:07:51 -0800803 def optimize(self, macros=None):
804 if macros is None:
805 macros = {}
Elliott Hughes40596aa2013-11-05 14:54:29 -0800806 self.expr = self.optimize_node(self.expr, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800807
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700808class CppExprTest(unittest.TestCase):
809 """CppExpr unit tests."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800810
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700811 def get_expr(self, expr):
812 return repr(CppExpr(CppStringTokenizer(expr).tokens))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800813
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700814 def test_cpp_expr(self):
815 self.assertEqual(self.get_expr("0"), "(int 0)")
816 self.assertEqual(self.get_expr("1"), "(int 1)")
817 self.assertEqual(self.get_expr("-5"), "(int -5)")
818 self.assertEqual(self.get_expr("+1"), "(int 1)")
819 self.assertEqual(self.get_expr("0U"), "(int 0)")
820 self.assertEqual(self.get_expr("015"), "(oct 015)")
821 self.assertEqual(self.get_expr("015l"), "(oct 015)")
822 self.assertEqual(self.get_expr("0x3e"), "(hex 0x3e)")
823 self.assertEqual(self.get_expr("(0)"), "(int 0)")
824 self.assertEqual(self.get_expr("1 && 1"), "(&& (int 1) (int 1))")
825 self.assertEqual(self.get_expr("1 && 0"), "(&& (int 1) (int 0))")
826 self.assertEqual(self.get_expr("EXAMPLE"), "(ident EXAMPLE)")
827 self.assertEqual(self.get_expr("EXAMPLE - 3"),
828 "(- (ident EXAMPLE) (int 3))")
829 self.assertEqual(self.get_expr("defined(EXAMPLE)"),
830 "(defined EXAMPLE)")
831 self.assertEqual(self.get_expr("defined ( EXAMPLE ) "),
832 "(defined EXAMPLE)")
833 self.assertEqual(self.get_expr("!defined(EXAMPLE)"),
834 "(! (defined EXAMPLE))")
835 self.assertEqual(self.get_expr("defined(ABC) || defined(BINGO)"),
836 "(|| (defined ABC) (defined BINGO))")
837 self.assertEqual(self.get_expr("FOO(BAR,5)"), "(call FOO [BAR,5])")
838 self.assertEqual(self.get_expr("A == 1 || defined(B)"),
839 "(|| (== (ident A) (int 1)) (defined B))")
Tao Baod7db5942015-01-28 10:07:51 -0800840
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700841 def get_expr_optimize(self, expr, macros=None):
842 if macros is None:
843 macros = {}
844 e = CppExpr(CppStringTokenizer(expr).tokens)
845 e.optimize(macros)
846 return repr(e)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800847
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700848 def test_cpp_expr_optimize(self):
849 self.assertEqual(self.get_expr_optimize("0"), "(int 0)")
850 self.assertEqual(self.get_expr_optimize("1"), "(int 1)")
851 self.assertEqual(self.get_expr_optimize("1 && 1"), "(int 1)")
852 self.assertEqual(self.get_expr_optimize("1 && +1"), "(int 1)")
853 self.assertEqual(self.get_expr_optimize("0x1 && 01"), "(oct 01)")
854 self.assertEqual(self.get_expr_optimize("1 && 0"), "(int 0)")
855 self.assertEqual(self.get_expr_optimize("0 && 1"), "(int 0)")
856 self.assertEqual(self.get_expr_optimize("0 && 0"), "(int 0)")
857 self.assertEqual(self.get_expr_optimize("1 || 1"), "(int 1)")
858 self.assertEqual(self.get_expr_optimize("1 || 0"), "(int 1)")
859 self.assertEqual(self.get_expr_optimize("0 || 1"), "(int 1)")
860 self.assertEqual(self.get_expr_optimize("0 || 0"), "(int 0)")
861 self.assertEqual(self.get_expr_optimize("A"), "(ident A)")
862 self.assertEqual(self.get_expr_optimize("A", {"A": 1}), "(int 1)")
863 self.assertEqual(self.get_expr_optimize("A || B", {"A": 1}), "(int 1)")
864 self.assertEqual(self.get_expr_optimize("A || B", {"B": 1}), "(int 1)")
865 self.assertEqual(self.get_expr_optimize("A && B", {"A": 1}), "(ident B)")
866 self.assertEqual(self.get_expr_optimize("A && B", {"B": 1}), "(ident A)")
867 self.assertEqual(self.get_expr_optimize("A && B"), "(&& (ident A) (ident B))")
868 self.assertEqual(self.get_expr_optimize("EXAMPLE"), "(ident EXAMPLE)")
869 self.assertEqual(self.get_expr_optimize("EXAMPLE - 3"), "(- (ident EXAMPLE) (int 3))")
870 self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)"), "(defined EXAMPLE)")
871 self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)",
872 {"EXAMPLE": "XOWOE"}),
873 "(defined XOWOE)")
874 self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)",
875 {"EXAMPLE": kCppUndefinedMacro}),
876 "(int 0)")
877 self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)"), "(! (defined EXAMPLE))")
878 self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)",
879 {"EXAMPLE": "XOWOE"}),
880 "(! (defined XOWOE))")
881 self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)",
882 {"EXAMPLE": kCppUndefinedMacro}),
883 "(int 1)")
884 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)"),
Tao Baod7db5942015-01-28 10:07:51 -0800885 "(|| (defined A) (defined B))")
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700886 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
887 {"A": "1"}),
888 "(int 1)")
889 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
890 {"B": "1"}),
891 "(int 1)")
892 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
893 {"B": kCppUndefinedMacro}),
894 "(defined A)")
895 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
896 {"A": kCppUndefinedMacro,
897 "B": kCppUndefinedMacro}),
898 "(int 0)")
899 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)"),
900 "(&& (defined A) (defined B))")
901 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
902 {"A": "1"}),
903 "(defined B)")
904 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
905 {"B": "1"}),
906 "(defined A)")
907 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
908 {"B": kCppUndefinedMacro}),
909 "(int 0)")
910 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
911 {"A": kCppUndefinedMacro}),
912 "(int 0)")
913 self.assertEqual(self.get_expr_optimize("A == 1 || defined(B)"),
914 "(|| (== (ident A) (int 1)) (defined B))")
915 self.assertEqual(self.get_expr_optimize(
916 "defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)",
917 {"__KERNEL__": kCppUndefinedMacro}),
918 "(|| (! (defined __GLIBC__)) (< (ident __GLIBC__) (int 2)))")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800919
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700920 def get_expr_string(self, expr):
921 return str(CppExpr(CppStringTokenizer(expr).tokens))
922
923 def test_cpp_expr_string(self):
924 self.assertEqual(self.get_expr_string("0"), "0")
925 self.assertEqual(self.get_expr_string("1"), "1")
926 self.assertEqual(self.get_expr_string("1 && 1"), "1 && 1")
927 self.assertEqual(self.get_expr_string("1 && 0"), "1 && 0")
928 self.assertEqual(self.get_expr_string("0 && 1"), "0 && 1")
929 self.assertEqual(self.get_expr_string("0 && 0"), "0 && 0")
930 self.assertEqual(self.get_expr_string("1 || 1"), "1 || 1")
931 self.assertEqual(self.get_expr_string("1 || 0"), "1 || 0")
932 self.assertEqual(self.get_expr_string("0 || 1"), "0 || 1")
933 self.assertEqual(self.get_expr_string("0 || 0"), "0 || 0")
934 self.assertEqual(self.get_expr_string("EXAMPLE"), "EXAMPLE")
935 self.assertEqual(self.get_expr_string("EXAMPLE - 3"), "EXAMPLE - 3")
936 self.assertEqual(self.get_expr_string("defined(EXAMPLE)"), "defined(EXAMPLE)")
937 self.assertEqual(self.get_expr_string("defined EXAMPLE"), "defined(EXAMPLE)")
938 self.assertEqual(self.get_expr_string("A == 1 || defined(B)"), "A == 1 || defined(B)")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800939
940
Tao Baod7db5942015-01-28 10:07:51 -0800941################################################################################
942################################################################################
943##### #####
944##### C P P B L O C K #####
945##### #####
946################################################################################
947################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800948
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800949
Tao Baod7db5942015-01-28 10:07:51 -0800950class Block(object):
951 """A class used to model a block of input source text.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800952
Tao Baod7db5942015-01-28 10:07:51 -0800953 There are two block types:
954 - directive blocks: contain the tokens of a single pre-processor
955 directive (e.g. #if)
956 - text blocks, contain the tokens of non-directive blocks
957
958 The cpp parser class below will transform an input source file into a list
959 of Block objects (grouped in a BlockList object for convenience)
960 """
961
962 def __init__(self, tokens, directive=None, lineno=0, identifier=None):
963 """Initialize a new block, if 'directive' is None, it is a text block.
964
965 NOTE: This automatically converts '#ifdef MACRO' into
966 '#if defined(MACRO)' and '#ifndef MACRO' into '#if !defined(MACRO)'.
967 """
968
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800969 if directive == "ifdef":
970 tok = Token()
Tao Baod7db5942015-01-28 10:07:51 -0800971 tok.id = tokDEFINED
972 tokens = [tok] + tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800973 directive = "if"
974
975 elif directive == "ifndef":
976 tok1 = Token()
977 tok2 = Token()
Tao Baod7db5942015-01-28 10:07:51 -0800978 tok1.id = tokNOT
979 tok2.id = tokDEFINED
980 tokens = [tok1, tok2] + tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800981 directive = "if"
982
Tao Baod7db5942015-01-28 10:07:51 -0800983 self.tokens = tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800984 self.directive = directive
Tao Baod7db5942015-01-28 10:07:51 -0800985 self.define_id = identifier
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800986 if lineno > 0:
987 self.lineno = lineno
988 else:
Tao Baod7db5942015-01-28 10:07:51 -0800989 self.lineno = self.tokens[0].location.line
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800990
991 if self.isIf():
Tao Baod7db5942015-01-28 10:07:51 -0800992 self.expr = CppExpr(self.tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800993
994 def isDirective(self):
Tao Baod7db5942015-01-28 10:07:51 -0800995 """Return True iff this is a directive block."""
996 return self.directive is not None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800997
998 def isConditional(self):
Tao Baod7db5942015-01-28 10:07:51 -0800999 """Return True iff this is a conditional directive block."""
1000 return self.directive in ["if", "ifdef", "ifndef", "else", "elif",
1001 "endif"]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001002
1003 def isDefine(self):
Tao Baod7db5942015-01-28 10:07:51 -08001004 """Return the macro name in a #define directive, or None otherwise."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001005 if self.directive != "define":
1006 return None
Tao Baod7db5942015-01-28 10:07:51 -08001007 return self.define_id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001008
1009 def isIf(self):
Tao Baod7db5942015-01-28 10:07:51 -08001010 """Return True iff this is an #if-like directive block."""
1011 return self.directive in ["if", "ifdef", "ifndef", "elif"]
1012
1013 def isEndif(self):
1014 """Return True iff this is an #endif directive block."""
1015 return self.directive == "endif"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001016
1017 def isInclude(self):
Tao Baod7db5942015-01-28 10:07:51 -08001018 """Check whether this is a #include directive.
1019
1020 If true, returns the corresponding file name (with brackets or
1021 double-qoutes). None otherwise.
1022 """
1023
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001024 if self.directive != "include":
1025 return None
Tao Baod7db5942015-01-28 10:07:51 -08001026 return ''.join([str(x) for x in self.tokens])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001027
Tao Baod7db5942015-01-28 10:07:51 -08001028 @staticmethod
1029 def format_blocks(tokens, indent=0):
1030 """Return the formatted lines of strings with proper indentation."""
1031 newline = True
1032 result = []
1033 buf = ''
1034 i = 0
1035 while i < len(tokens):
1036 t = tokens[i]
1037 if t.id == '{':
1038 buf += ' {'
1039 result.append(strip_space(buf))
Christopher Ferris658b16f2019-01-08 14:58:07 -08001040 # Do not indent if this is extern "C" {
1041 if i < 2 or tokens[i-2].id != 'extern' or tokens[i-1].id != '"C"':
1042 indent += 2
Tao Baod7db5942015-01-28 10:07:51 -08001043 buf = ''
1044 newline = True
1045 elif t.id == '}':
Christopher Ferris658b16f2019-01-08 14:58:07 -08001046 if indent >= 2:
1047 indent -= 2
Tao Baod7db5942015-01-28 10:07:51 -08001048 if not newline:
1049 result.append(strip_space(buf))
1050 # Look ahead to determine if it's the end of line.
1051 if (i + 1 < len(tokens) and
1052 (tokens[i+1].id == ';' or
1053 tokens[i+1].id in ['else', '__attribute__',
1054 '__attribute', '__packed'] or
1055 tokens[i+1].kind == TokenKind.IDENTIFIER)):
1056 buf = ' ' * indent + '}'
1057 newline = False
1058 else:
1059 result.append(' ' * indent + '}')
1060 buf = ''
1061 newline = True
1062 elif t.id == ';':
1063 result.append(strip_space(buf) + ';')
1064 buf = ''
1065 newline = True
1066 # We prefer a new line for each constant in enum.
1067 elif t.id == ',' and t.cursor.kind == CursorKind.ENUM_DECL:
1068 result.append(strip_space(buf) + ',')
1069 buf = ''
1070 newline = True
1071 else:
1072 if newline:
1073 buf += ' ' * indent + str(t)
1074 else:
1075 buf += ' ' + str(t)
1076 newline = False
1077 i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001078
Tao Baod7db5942015-01-28 10:07:51 -08001079 if buf:
1080 result.append(strip_space(buf))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001081
Tao Baod7db5942015-01-28 10:07:51 -08001082 return result, indent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001083
Elliott Hughes96c1db72017-05-25 13:48:01 -07001084 def write(self, out, indent):
1085 """Dump the current block."""
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001086 # removeWhiteSpace() will sometimes creates non-directive blocks
1087 # without any tokens. These come from blocks that only contained
1088 # empty lines and spaces. They should not be printed in the final
1089 # output, and then should not be counted for this operation.
1090 #
Tao Baod7db5942015-01-28 10:07:51 -08001091 if self.directive is None and not self.tokens:
Elliott Hughes96c1db72017-05-25 13:48:01 -07001092 return indent
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001093
1094 if self.directive:
Tao Baod7db5942015-01-28 10:07:51 -08001095 out.write(str(self) + '\n')
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001096 else:
Tao Baod7db5942015-01-28 10:07:51 -08001097 lines, indent = self.format_blocks(self.tokens, indent)
1098 for line in lines:
1099 out.write(line + '\n')
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001100
Elliott Hughes96c1db72017-05-25 13:48:01 -07001101 return indent
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001102
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001103 def __repr__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001104 """Generate the representation of a given block."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001105 if self.directive:
1106 result = "#%s " % self.directive
1107 if self.isIf():
1108 result += repr(self.expr)
1109 else:
1110 for tok in self.tokens:
1111 result += repr(tok)
1112 else:
1113 result = ""
1114 for tok in self.tokens:
1115 result += repr(tok)
1116
1117 return result
1118
1119 def __str__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001120 """Generate the string representation of a given block."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001121 if self.directive:
Tao Baod7db5942015-01-28 10:07:51 -08001122 # "#if"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001123 if self.directive == "if":
1124 # small optimization to re-generate #ifdef and #ifndef
1125 e = self.expr.expr
1126 op = e[0]
1127 if op == "defined":
1128 result = "#ifdef %s" % e[1]
1129 elif op == "!" and e[1][0] == "defined":
1130 result = "#ifndef %s" % e[1][1]
1131 else:
1132 result = "#if " + str(self.expr)
Tao Baod7db5942015-01-28 10:07:51 -08001133
1134 # "#define"
1135 elif self.isDefine():
1136 result = "#%s %s" % (self.directive, self.define_id)
1137 if self.tokens:
1138 result += " "
1139 expr = strip_space(' '.join([tok.id for tok in self.tokens]))
1140 # remove the space between name and '(' in function call
1141 result += re.sub(r'(\w+) \(', r'\1(', expr)
1142
1143 # "#error"
1144 # Concatenating tokens with a space separator, because they may
1145 # not be quoted and broken into several tokens
1146 elif self.directive == "error":
1147 result = "#error %s" % ' '.join([tok.id for tok in self.tokens])
1148
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001149 else:
1150 result = "#%s" % self.directive
Tao Baod7db5942015-01-28 10:07:51 -08001151 if self.tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001152 result += " "
Tao Baod7db5942015-01-28 10:07:51 -08001153 result += ''.join([tok.id for tok in self.tokens])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001154 else:
Tao Baod7db5942015-01-28 10:07:51 -08001155 lines, _ = self.format_blocks(self.tokens)
1156 result = '\n'.join(lines)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001157
1158 return result
1159
Tao Baod7db5942015-01-28 10:07:51 -08001160
1161class BlockList(object):
1162 """A convenience class used to hold and process a list of blocks.
1163
1164 It calls the cpp parser to get the blocks.
1165 """
1166
1167 def __init__(self, blocks):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001168 self.blocks = blocks
1169
1170 def __len__(self):
1171 return len(self.blocks)
1172
Tao Baod7db5942015-01-28 10:07:51 -08001173 def __getitem__(self, n):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001174 return self.blocks[n]
1175
1176 def __repr__(self):
1177 return repr(self.blocks)
1178
1179 def __str__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001180 result = '\n'.join([str(b) for b in self.blocks])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001181 return result
1182
Tao Baod7db5942015-01-28 10:07:51 -08001183 def dump(self):
1184 """Dump all the blocks in current BlockList."""
1185 print '##### BEGIN #####'
1186 for i, b in enumerate(self.blocks):
1187 print '### BLOCK %d ###' % i
1188 print b
1189 print '##### END #####'
1190
1191 def optimizeIf01(self):
1192 """Remove the code between #if 0 .. #endif in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001193 self.blocks = optimize_if01(self.blocks)
1194
1195 def optimizeMacros(self, macros):
Tao Baod7db5942015-01-28 10:07:51 -08001196 """Remove known defined and undefined macros from a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001197 for b in self.blocks:
1198 if b.isIf():
1199 b.expr.optimize(macros)
1200
Tao Baod7db5942015-01-28 10:07:51 -08001201 def optimizeAll(self, macros):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001202 self.optimizeMacros(macros)
1203 self.optimizeIf01()
1204 return
1205
1206 def findIncludes(self):
Tao Baod7db5942015-01-28 10:07:51 -08001207 """Return the list of included files in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001208 result = []
1209 for b in self.blocks:
1210 i = b.isInclude()
1211 if i:
1212 result.append(i)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001213 return result
1214
Tao Baod7db5942015-01-28 10:07:51 -08001215 def write(self, out):
Tao Baod7db5942015-01-28 10:07:51 -08001216 indent = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001217 for b in self.blocks:
Elliott Hughes96c1db72017-05-25 13:48:01 -07001218 indent = b.write(out, indent)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001219
Elliott Hughes64f355f2017-08-30 16:10:24 -07001220 def removeVarsAndFuncs(self, keep):
Tao Baod7db5942015-01-28 10:07:51 -08001221 """Remove variable and function declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001222
Tao Baod7db5942015-01-28 10:07:51 -08001223 All extern and static declarations corresponding to variable and
1224 function declarations are removed. We only accept typedefs and
1225 enum/structs/union declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001226
Christopher Ferris658b16f2019-01-08 14:58:07 -08001227 In addition, remove any macros expanding in the headers. Usually,
1228 these macros are static inline functions, which is why they are
1229 removed.
1230
Tao Baod7db5942015-01-28 10:07:51 -08001231 However, we keep the definitions corresponding to the set of known
Elliott Hughes64f355f2017-08-30 16:10:24 -07001232 static inline functions in the set 'keep', which is useful
Tao Baod7db5942015-01-28 10:07:51 -08001233 for optimized byteorder swap functions and stuff like that.
1234 """
1235
Christopher Ferris658b16f2019-01-08 14:58:07 -08001236 # state = NORMAL => normal (i.e. LN + spaces)
1237 # state = OTHER_DECL => typedef/struct encountered, ends with ";"
1238 # state = VAR_DECL => var declaration encountered, ends with ";"
1239 # state = FUNC_DECL => func declaration encountered, ends with "}"
1240 NORMAL = 0
1241 OTHER_DECL = 1
1242 VAR_DECL = 2
1243 FUNC_DECL = 3
Tao Baod7db5942015-01-28 10:07:51 -08001244
Christopher Ferris658b16f2019-01-08 14:58:07 -08001245 state = NORMAL
Tao Baod7db5942015-01-28 10:07:51 -08001246 depth = 0
Christopher Ferris658b16f2019-01-08 14:58:07 -08001247 blocksToKeep = []
1248 blocksInProgress = []
1249 blocksOfDirectives = []
1250 ident = ""
1251 state_token = ""
1252 macros = set()
1253 for block in self.blocks:
1254 if block.isDirective():
1255 # Record all macros.
1256 if block.directive == 'define':
1257 macro_name = block.define_id
1258 paren_index = macro_name.find('(')
1259 if paren_index == -1:
1260 macros.add(macro_name)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001261 else:
Christopher Ferris658b16f2019-01-08 14:58:07 -08001262 macros.add(macro_name[0:paren_index])
1263 blocksInProgress.append(block)
1264 # If this is in a function/variable declaration, we might need
1265 # to emit the directives alone, so save them separately.
1266 blocksOfDirectives.append(block)
1267 continue
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001268
Christopher Ferris658b16f2019-01-08 14:58:07 -08001269 numTokens = len(block.tokens)
1270 lastTerminatorIndex = 0
1271 i = 0
1272 while i < numTokens:
1273 token_id = block.tokens[i].id
1274 terminator = False
1275 if token_id == '{':
1276 depth += 1
1277 if (i >= 2 and block.tokens[i-2].id == 'extern' and
1278 block.tokens[i-1].id == '"C"'):
1279 # For an extern "C" { pretend as though this is depth 0.
1280 depth -= 1
1281 elif token_id == '}':
1282 if depth > 0:
1283 depth -= 1
1284 if depth == 0:
1285 if state == OTHER_DECL:
1286 # Loop through until we hit the ';'
1287 i += 1
1288 while i < numTokens:
1289 if block.tokens[i].id == ';':
1290 token_id = ';'
1291 break
1292 i += 1
1293 # If we didn't hit the ';', just consider this the
1294 # terminator any way.
1295 terminator = True
1296 elif depth == 0:
1297 if token_id == ';':
1298 if state == NORMAL:
1299 blocksToKeep.extend(blocksInProgress)
1300 blocksInProgress = []
1301 blocksOfDirectives = []
1302 state = FUNC_DECL
1303 terminator = True
1304 elif (state == NORMAL and token_id == '(' and i >= 1 and
1305 block.tokens[i-1].kind == TokenKind.IDENTIFIER and
1306 block.tokens[i-1].id in macros):
1307 # This is a plain macro being expanded in the header
1308 # which needs to be removed.
1309 blocksToKeep.extend(blocksInProgress)
1310 if lastTerminatorIndex < i - 1:
1311 blocksToKeep.append(Block(block.tokens[lastTerminatorIndex:i-1]))
1312 blocksInProgress = []
1313 blocksOfDirectives = []
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001314
Christopher Ferris658b16f2019-01-08 14:58:07 -08001315 # Skip until we see the terminating ')'
1316 i += 1
1317 paren_depth = 1
1318 while i < numTokens:
1319 if block.tokens[i].id == ')':
1320 paren_depth -= 1
1321 if paren_depth == 0:
1322 break
1323 elif block.tokens[i].id == '(':
1324 paren_depth += 1
1325 i += 1
1326 lastTerminatorIndex = i + 1
1327 elif (state != FUNC_DECL and token_id == '(' and
1328 state_token != 'typedef'):
1329 blocksToKeep.extend(blocksInProgress)
1330 blocksInProgress = []
1331 blocksOfDirectives = []
1332 state = VAR_DECL
1333 elif state == NORMAL and token_id in ['struct', 'typedef',
1334 'enum', 'union',
1335 '__extension__']:
1336 state = OTHER_DECL
1337 state_token = token_id
1338 elif block.tokens[i].kind == TokenKind.IDENTIFIER:
1339 if state != VAR_DECL or ident == "":
1340 ident = token_id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001341
Christopher Ferris658b16f2019-01-08 14:58:07 -08001342 if terminator:
1343 if state != VAR_DECL and state != FUNC_DECL or ident in keep:
1344 blocksInProgress.append(Block(block.tokens[lastTerminatorIndex:i+1]))
1345 blocksToKeep.extend(blocksInProgress)
1346 else:
1347 # Only keep the directives found.
1348 blocksToKeep.extend(blocksOfDirectives)
1349 lastTerminatorIndex = i + 1
1350 blocksInProgress = []
1351 blocksOfDirectives = []
1352 state = NORMAL
1353 ident = ""
1354 state_token = ""
1355 i += 1
1356 if lastTerminatorIndex < numTokens:
1357 blocksInProgress.append(Block(block.tokens[lastTerminatorIndex:numTokens]))
1358 if len(blocksInProgress) > 0:
1359 blocksToKeep.extend(blocksInProgress)
1360 self.blocks = blocksToKeep
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001361
Tao Baod7db5942015-01-28 10:07:51 -08001362 def replaceTokens(self, replacements):
1363 """Replace tokens according to the given dict."""
Elliott Hughes64f355f2017-08-30 16:10:24 -07001364 extra_includes = []
Martin Storsjod32c8052010-12-08 11:38:14 +01001365 for b in self.blocks:
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001366 made_change = False
Tao Baod7db5942015-01-28 10:07:51 -08001367 if b.isInclude() is None:
Elliott Hughes64f355f2017-08-30 16:10:24 -07001368 i = 0
1369 while i < len(b.tokens):
1370 tok = b.tokens[i]
1371 if (tok.kind == TokenKind.KEYWORD and tok.id == 'struct'
1372 and (i + 2) < len(b.tokens) and b.tokens[i + 2].id == '{'):
1373 struct_name = b.tokens[i + 1].id
1374 if struct_name in kernel_struct_replacements:
1375 extra_includes.append("<bits/%s.h>" % struct_name)
1376 end = i + 2
1377 while end < len(b.tokens) and b.tokens[end].id != '}':
1378 end += 1
1379 end += 1 # Swallow '}'
1380 while end < len(b.tokens) and b.tokens[end].id != ';':
1381 end += 1
1382 end += 1 # Swallow ';'
1383 # Remove these tokens. We'll replace them later with a #include block.
1384 b.tokens[i:end] = []
1385 made_change = True
1386 # We've just modified b.tokens, so revisit the current offset.
1387 continue
Tao Baod7db5942015-01-28 10:07:51 -08001388 if tok.kind == TokenKind.IDENTIFIER:
1389 if tok.id in replacements:
1390 tok.id = replacements[tok.id]
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001391 made_change = True
Elliott Hughes64f355f2017-08-30 16:10:24 -07001392 i += 1
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001393
Tao Baod7db5942015-01-28 10:07:51 -08001394 if b.isDefine() and b.define_id in replacements:
1395 b.define_id = replacements[b.define_id]
1396 made_change = True
1397
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001398 if made_change and b.isIf():
1399 # Keep 'expr' in sync with 'tokens'.
1400 b.expr = CppExpr(b.tokens)
Martin Storsjod32c8052010-12-08 11:38:14 +01001401
Elliott Hughes64f355f2017-08-30 16:10:24 -07001402 for extra_include in extra_includes:
1403 replacement = CppStringTokenizer(extra_include)
1404 self.blocks.insert(2, Block(replacement.tokens, directive='include'))
1405
1406
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001407
Tao Baod7db5942015-01-28 10:07:51 -08001408def strip_space(s):
1409 """Strip out redundant space in a given string."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001410
Tao Baod7db5942015-01-28 10:07:51 -08001411 # NOTE: It ought to be more clever to not destroy spaces in string tokens.
1412 replacements = {' . ': '.',
1413 ' [': '[',
1414 '[ ': '[',
1415 ' ]': ']',
1416 '( ': '(',
1417 ' )': ')',
1418 ' ,': ',',
1419 '# ': '#',
1420 ' ;': ';',
1421 '~ ': '~',
1422 ' -> ': '->'}
1423 result = s
1424 for r in replacements:
1425 result = result.replace(r, replacements[r])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001426
Tao Baod7db5942015-01-28 10:07:51 -08001427 # Remove the space between function name and the parenthesis.
1428 result = re.sub(r'(\w+) \(', r'\1(', result)
1429 return result
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001430
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001431
Tao Baod7db5942015-01-28 10:07:51 -08001432class BlockParser(object):
1433 """A class that converts an input source file into a BlockList object."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001434
Tao Baod7db5942015-01-28 10:07:51 -08001435 def __init__(self, tokzer=None):
1436 """Initialize a block parser.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001437
Tao Baod7db5942015-01-28 10:07:51 -08001438 The input source is provided through a Tokenizer object.
1439 """
1440 self._tokzer = tokzer
1441 self._parsed = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001442
Tao Baod7db5942015-01-28 10:07:51 -08001443 @property
1444 def parsed(self):
1445 return self._parsed
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001446
Tao Baod7db5942015-01-28 10:07:51 -08001447 @staticmethod
1448 def _short_extent(extent):
1449 return '%d:%d - %d:%d' % (extent.start.line, extent.start.column,
1450 extent.end.line, extent.end.column)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001451
Tao Baod7db5942015-01-28 10:07:51 -08001452 def getBlocks(self, tokzer=None):
1453 """Return all the blocks parsed."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001454
Tao Baod7db5942015-01-28 10:07:51 -08001455 def consume_extent(i, tokens, extent=None, detect_change=False):
1456 """Return tokens that belong to the given extent.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001457
Tao Baod7db5942015-01-28 10:07:51 -08001458 It parses all the tokens that follow tokens[i], until getting out
1459 of the extent. When detect_change is True, it may terminate early
1460 when detecting preprocessing directives inside the extent.
1461 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001462
Tao Baod7db5942015-01-28 10:07:51 -08001463 result = []
1464 if extent is None:
1465 extent = tokens[i].cursor.extent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001466
Tao Baod7db5942015-01-28 10:07:51 -08001467 while i < len(tokens) and tokens[i].location in extent:
1468 t = tokens[i]
1469 if debugBlockParser:
1470 print ' ' * 2, t.id, t.kind, t.cursor.kind
1471 if (detect_change and t.cursor.extent != extent and
1472 t.cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE):
1473 break
1474 result.append(t)
1475 i += 1
1476 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001477
Tao Baod7db5942015-01-28 10:07:51 -08001478 def consume_line(i, tokens):
1479 """Return tokens that follow tokens[i] in the same line."""
1480 result = []
1481 line = tokens[i].location.line
1482 while i < len(tokens) and tokens[i].location.line == line:
1483 if tokens[i].cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1484 break
1485 result.append(tokens[i])
1486 i += 1
1487 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001488
Tao Baod7db5942015-01-28 10:07:51 -08001489 if tokzer is None:
1490 tokzer = self._tokzer
1491 tokens = tokzer.tokens
1492
1493 blocks = []
1494 buf = []
1495 i = 0
1496
1497 while i < len(tokens):
1498 t = tokens[i]
1499 cursor = t.cursor
1500
1501 if debugBlockParser:
1502 print ("%d: Processing [%s], kind=[%s], cursor=[%s], "
1503 "extent=[%s]" % (t.location.line, t.spelling, t.kind,
1504 cursor.kind,
1505 self._short_extent(cursor.extent)))
1506
1507 if cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1508 if buf:
1509 blocks.append(Block(buf))
1510 buf = []
1511
1512 j = i
1513 if j + 1 >= len(tokens):
1514 raise BadExpectedToken("### BAD TOKEN at %s" % (t.location))
1515 directive = tokens[j+1].id
1516
1517 if directive == 'define':
1518 if i+2 >= len(tokens):
1519 raise BadExpectedToken("### BAD TOKEN at %s" %
1520 (tokens[i].location))
1521
1522 # Skip '#' and 'define'.
1523 extent = tokens[i].cursor.extent
1524 i += 2
1525 id = ''
1526 # We need to separate the id from the remaining of
1527 # the line, especially for the function-like macro.
1528 if (i + 1 < len(tokens) and tokens[i+1].id == '(' and
1529 (tokens[i].location.column + len(tokens[i].spelling) ==
1530 tokens[i+1].location.column)):
1531 while i < len(tokens):
1532 id += tokens[i].id
1533 if tokens[i].spelling == ')':
1534 i += 1
1535 break
1536 i += 1
1537 else:
1538 id += tokens[i].id
1539 # Advance to the next token that follows the macro id
1540 i += 1
1541
1542 (i, ret) = consume_extent(i, tokens, extent=extent)
1543 blocks.append(Block(ret, directive=directive,
1544 lineno=t.location.line, identifier=id))
1545
1546 else:
1547 (i, ret) = consume_extent(i, tokens)
1548 blocks.append(Block(ret[2:], directive=directive,
1549 lineno=t.location.line))
1550
1551 elif cursor.kind == CursorKind.INCLUSION_DIRECTIVE:
1552 if buf:
1553 blocks.append(Block(buf))
1554 buf = []
1555 directive = tokens[i+1].id
1556 (i, ret) = consume_extent(i, tokens)
1557
1558 blocks.append(Block(ret[2:], directive=directive,
1559 lineno=t.location.line))
1560
1561 elif cursor.kind == CursorKind.VAR_DECL:
1562 if buf:
1563 blocks.append(Block(buf))
1564 buf = []
1565
1566 (i, ret) = consume_extent(i, tokens, detect_change=True)
1567 buf += ret
1568
1569 elif cursor.kind == CursorKind.FUNCTION_DECL:
1570 if buf:
1571 blocks.append(Block(buf))
1572 buf = []
1573
1574 (i, ret) = consume_extent(i, tokens, detect_change=True)
1575 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001576
1577 else:
Tao Baod7db5942015-01-28 10:07:51 -08001578 (i, ret) = consume_line(i, tokens)
1579 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001580
Tao Baod7db5942015-01-28 10:07:51 -08001581 if buf:
1582 blocks.append(Block(buf))
1583
1584 # _parsed=True indicates a successful parsing, although may result an
1585 # empty BlockList.
1586 self._parsed = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001587
1588 return BlockList(blocks)
1589
Tao Baod7db5942015-01-28 10:07:51 -08001590 def parse(self, tokzer):
1591 return self.getBlocks(tokzer)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001592
Tao Baod7db5942015-01-28 10:07:51 -08001593 def parseFile(self, path):
1594 return self.getBlocks(CppFileTokenizer(path))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001595
1596
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001597class BlockParserTests(unittest.TestCase):
1598 """BlockParser unit tests."""
Tao Baod7db5942015-01-28 10:07:51 -08001599
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001600 def get_blocks(self, lines):
1601 blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
1602 return map(lambda a: str(a), blocks)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001603
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001604 def test_hash(self):
1605 self.assertEqual(self.get_blocks(["#error hello"]), ["#error hello"])
Tao Baod7db5942015-01-28 10:07:51 -08001606
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001607 def test_empty_line(self):
1608 self.assertEqual(self.get_blocks(["foo", "", "bar"]), ["foo bar"])
Tao Baod7db5942015-01-28 10:07:51 -08001609
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001610 def test_hash_with_space(self):
1611 # We currently cannot handle the following case with libclang properly.
1612 # Fortunately it doesn't appear in current headers.
1613 #self.assertEqual(self.get_blocks(["foo", " # ", "bar"]), ["foo", "bar"])
1614 pass
1615
1616 def test_with_comment(self):
1617 self.assertEqual(self.get_blocks(["foo",
1618 " # /* ahah */ if defined(__KERNEL__) /* more */",
1619 "bar", "#endif"]),
1620 ["foo", "#ifdef __KERNEL__", "bar", "#endif"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001621
1622
Tao Baod7db5942015-01-28 10:07:51 -08001623################################################################################
1624################################################################################
1625##### #####
1626##### B L O C K L I S T O P T I M I Z A T I O N #####
1627##### #####
1628################################################################################
1629################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001630
Tao Baod7db5942015-01-28 10:07:51 -08001631
Tao Baod7db5942015-01-28 10:07:51 -08001632def find_matching_endif(blocks, i):
1633 """Traverse the blocks to find out the matching #endif."""
1634 n = len(blocks)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001635 depth = 1
1636 while i < n:
1637 if blocks[i].isDirective():
Tao Baod7db5942015-01-28 10:07:51 -08001638 dir_ = blocks[i].directive
1639 if dir_ in ["if", "ifndef", "ifdef"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001640 depth += 1
Tao Baod7db5942015-01-28 10:07:51 -08001641 elif depth == 1 and dir_ in ["else", "elif"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001642 return i
Tao Baod7db5942015-01-28 10:07:51 -08001643 elif dir_ == "endif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001644 depth -= 1
1645 if depth == 0:
1646 return i
1647 i += 1
1648 return i
1649
Tao Baod7db5942015-01-28 10:07:51 -08001650
1651def optimize_if01(blocks):
1652 """Remove the code between #if 0 .. #endif in a list of CppBlocks."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001653 i = 0
1654 n = len(blocks)
1655 result = []
1656 while i < n:
1657 j = i
1658 while j < n and not blocks[j].isIf():
1659 j += 1
1660 if j > i:
Tao Baod7db5942015-01-28 10:07:51 -08001661 logging.debug("appending lines %d to %d", blocks[i].lineno,
1662 blocks[j-1].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001663 result += blocks[i:j]
1664 if j >= n:
1665 break
1666 expr = blocks[j].expr
Tao Baod7db5942015-01-28 10:07:51 -08001667 r = expr.toInt()
1668 if r is None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001669 result.append(blocks[j])
1670 i = j + 1
1671 continue
1672
1673 if r == 0:
1674 # if 0 => skip everything until the corresponding #endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001675 start_dir = blocks[j].directive
Tao Baod7db5942015-01-28 10:07:51 -08001676 j = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001677 if j >= n:
1678 # unterminated #if 0, finish here
1679 break
Tao Baod7db5942015-01-28 10:07:51 -08001680 dir_ = blocks[j].directive
1681 if dir_ == "endif":
1682 logging.debug("remove 'if 0' .. 'endif' (lines %d to %d)",
1683 blocks[i].lineno, blocks[j].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001684 if start_dir == "elif":
1685 # Put an endif since we started with an elif.
1686 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001687 i = j + 1
Tao Baod7db5942015-01-28 10:07:51 -08001688 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001689 # convert 'else' into 'if 1'
Tao Baod7db5942015-01-28 10:07:51 -08001690 logging.debug("convert 'if 0' .. 'else' into 'if 1' (lines %d "
1691 "to %d)", blocks[i].lineno, blocks[j-1].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001692 if start_dir == "elif":
1693 blocks[j].directive = "elif"
1694 else:
1695 blocks[j].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001696 blocks[j].expr = CppExpr(CppStringTokenizer("1").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001697 i = j
Tao Baod7db5942015-01-28 10:07:51 -08001698 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001699 # convert 'elif' into 'if'
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001700 logging.debug("convert 'if 0' .. 'elif' into 'if'")
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001701 if start_dir == "elif":
1702 blocks[j].directive = "elif"
1703 else:
1704 blocks[j].directive = "if"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001705 i = j
1706 continue
1707
1708 # if 1 => find corresponding endif and remove/transform them
Tao Baod7db5942015-01-28 10:07:51 -08001709 k = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001710 if k >= n:
1711 # unterminated #if 1, finish here
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001712 logging.debug("unterminated 'if 1'")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001713 result += blocks[j+1:k]
1714 break
1715
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001716 start_dir = blocks[j].directive
Tao Baod7db5942015-01-28 10:07:51 -08001717 dir_ = blocks[k].directive
1718 if dir_ == "endif":
1719 logging.debug("convert 'if 1' .. 'endif' (lines %d to %d)",
1720 blocks[j].lineno, blocks[k].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001721 if start_dir == "elif":
1722 # Add the elif in to the results and convert it to an elif 1.
1723 blocks[j].tokens = CppStringTokenizer("1").tokens
1724 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001725 result += optimize_if01(blocks[j+1:k])
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001726 if start_dir == "elif":
1727 # Add the endif in to the results.
1728 result += blocks[k:k+1]
Tao Baod7db5942015-01-28 10:07:51 -08001729 i = k + 1
1730 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001731 # convert 'else' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001732 logging.debug("convert 'if 1' .. 'else' (lines %d to %d)",
1733 blocks[j].lineno, blocks[k].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001734 if start_dir == "elif":
1735 # Add the elif in to the results and convert it to an elif 1.
1736 blocks[j].tokens = CppStringTokenizer("1").tokens
1737 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001738 result += optimize_if01(blocks[j+1:k])
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001739 if start_dir == "elif":
1740 blocks[k].directive = "elif"
1741 else:
1742 blocks[k].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001743 blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001744 i = k
Tao Baod7db5942015-01-28 10:07:51 -08001745 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001746 # convert 'elif' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001747 logging.debug("convert 'if 1' .. 'elif' (lines %d to %d)",
1748 blocks[j].lineno, blocks[k].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001749 result += optimize_if01(blocks[j+1:k])
Tao Baod7db5942015-01-28 10:07:51 -08001750 blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001751 i = k
1752 return result
1753
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001754class OptimizerTests(unittest.TestCase):
1755 def parse(self, text, macros=None):
1756 out = utils.StringOutput()
1757 blocks = BlockParser().parse(CppStringTokenizer(text))
1758 blocks.replaceTokens(kernel_token_replacements)
1759 blocks.optimizeAll(macros)
1760 blocks.write(out)
1761 return out.get()
Tao Baod7db5942015-01-28 10:07:51 -08001762
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001763 def test_if1(self):
1764 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001765#if 1
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001766#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001767#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001768"""
1769 expected = """\
1770#define GOOD
1771"""
1772 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001773
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001774 def test_if0(self):
1775 text = """\
1776#if 0
1777#define SHOULD_SKIP1
1778#define SHOULD_SKIP2
1779#endif
1780"""
1781 expected = ""
1782 self.assertEqual(self.parse(text), expected)
1783
1784 def test_if1_else(self):
1785 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001786#if 1
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001787#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001788#else
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001789#define BAD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001790#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001791"""
1792 expected = """\
1793#define GOOD
1794"""
1795 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001796
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001797 def test_if0_else(self):
1798 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001799#if 0
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001800#define BAD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001801#else
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001802#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001803#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001804"""
1805 expected = """\
1806#define GOOD
1807"""
1808 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001809
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001810 def test_if_elif1(self):
1811 text = """\
1812#if defined(something)
1813#define EXISTS
1814#elif 1
1815#define GOOD
1816#endif
1817"""
1818 expected = """\
1819#ifdef something
1820#define EXISTS
1821#elif 1
1822#define GOOD
1823#endif
1824"""
1825 self.assertEqual(self.parse(text), expected)
1826
1827 def test_if_elif1_macro(self):
1828 text = """\
1829#if defined(something)
1830#define EXISTS
1831#elif defined(WILL_BE_ONE)
1832#define GOOD
1833#endif
1834"""
1835 expected = """\
1836#ifdef something
1837#define EXISTS
1838#elif 1
1839#define GOOD
1840#endif
1841"""
1842 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1843
1844
1845 def test_if_elif1_else(self):
1846 text = """\
1847#if defined(something)
1848#define EXISTS
1849#elif 1
1850#define GOOD
1851#else
1852#define BAD
1853#endif
1854"""
1855 expected = """\
1856#ifdef something
1857#define EXISTS
1858#elif 1
1859#define GOOD
1860#endif
1861"""
1862 self.assertEqual(self.parse(text), expected)
1863
1864 def test_if_elif1_else_macro(self):
1865 text = """\
1866#if defined(something)
1867#define EXISTS
1868#elif defined(WILL_BE_ONE)
1869#define GOOD
1870#else
1871#define BAD
1872#endif
1873"""
1874 expected = """\
1875#ifdef something
1876#define EXISTS
1877#elif 1
1878#define GOOD
1879#endif
1880"""
1881 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1882
1883
1884 def test_if_elif1_else_macro(self):
1885 text = """\
1886#if defined(something)
1887#define EXISTS
1888#elif defined(WILL_BE_ONE)
1889#define GOOD
1890#else
1891#define BAD
1892#endif
1893"""
1894 expected = """\
1895#ifdef something
1896#define EXISTS
1897#elif 1
1898#define GOOD
1899#endif
1900"""
1901 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1902
1903 def test_macro_set_to_undefined_single(self):
1904 text = """\
Elliott Hughes40596aa2013-11-05 14:54:29 -08001905#if defined(__KERNEL__)
1906#define BAD_KERNEL
1907#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001908"""
1909 expected = ""
1910 macros = {"__KERNEL__": kCppUndefinedMacro}
1911 self.assertEqual(self.parse(text, macros), expected)
Elliott Hughes40596aa2013-11-05 14:54:29 -08001912
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001913 def test_macro_set_to_undefined_if(self):
1914 text = """\
Elliott Hughes40596aa2013-11-05 14:54:29 -08001915#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001916#define CHECK
Elliott Hughes40596aa2013-11-05 14:54:29 -08001917#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001918"""
1919 expected = """\
1920#if !defined(__GLIBC__) || __GLIBC__ < 2
1921#define CHECK
1922#endif
1923"""
1924 macros = {"__KERNEL__": kCppUndefinedMacro}
1925 self.assertEqual(self.parse(text, macros), expected)
Elliott Hughes40596aa2013-11-05 14:54:29 -08001926
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001927 def test_endif_comment_removed(self):
1928 text = """\
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001929#ifndef SIGRTMAX
1930#define SIGRTMAX 123
1931#endif /* SIGRTMAX */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001932"""
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001933 expected = """\
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001934#ifndef __SIGRTMAX
1935#define __SIGRTMAX 123
Elliott Hughes96c1db72017-05-25 13:48:01 -07001936#endif
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001937"""
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001938 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001939
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001940 def test_multilevel_if0(self):
1941 text = """\
1942#if 0
1943#if 1
1944#define BAD_6
1945#endif
1946#endif
1947"""
1948 expected = ""
1949 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001950
Christopher Ferris658b16f2019-01-08 14:58:07 -08001951class FullPathTest(unittest.TestCase):
1952 """Test of the full path parsing."""
1953
1954 def parse(self, text, keep=None):
1955 if not keep:
1956 keep = set()
1957 out = utils.StringOutput()
1958 blocks = BlockParser().parse(CppStringTokenizer(text))
1959 blocks.removeVarsAndFuncs(keep)
1960 blocks.replaceTokens(kernel_token_replacements)
1961 blocks.optimizeAll(None)
1962 blocks.write(out)
1963 return out.get()
1964
1965 def test_function_removed(self):
1966 text = """\
1967static inline __u64 function()
1968{
1969}
1970"""
1971 expected = ""
1972 self.assertEqual(self.parse(text), expected)
1973
1974 def test_function_removed_with_struct(self):
1975 text = """\
1976static inline struct something* function()
1977{
1978}
1979"""
1980 expected = ""
1981 self.assertEqual(self.parse(text), expected)
1982
1983 def test_function_kept(self):
1984 text = """\
1985static inline __u64 function()
1986{
1987}
1988"""
1989 expected = """\
1990static inline __u64 function() {
1991}
1992"""
1993 self.assertEqual(self.parse(text, set(["function"])), expected)
1994
1995 def test_var_removed(self):
1996 text = "__u64 variable;"
1997 expected = ""
1998 self.assertEqual(self.parse(text), expected)
1999
2000 def test_var_kept(self):
2001 text = "__u64 variable;"
2002 expected = "__u64 variable;\n"
2003 self.assertEqual(self.parse(text, set(["variable"])), expected)
2004
2005 def test_keep_function_typedef(self):
2006 text = "typedef void somefunction_t(void);"
2007 expected = "typedef void somefunction_t(void);\n"
2008 self.assertEqual(self.parse(text), expected)
2009
2010 def test_struct_keep_attribute(self):
2011 text = """\
2012struct something_s {
2013 __u32 s1;
2014 __u32 s2;
2015} __attribute__((packed));
2016"""
2017 expected = """\
2018struct something_s {
2019 __u32 s1;
2020 __u32 s2;
2021} __attribute__((packed));
2022"""
2023 self.assertEqual(self.parse(text), expected)
2024
2025 def test_function_keep_attribute_structs(self):
2026 text = """\
2027static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
2028}
2029"""
2030 expected = """\
2031static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
2032}
2033"""
2034 self.assertEqual(self.parse(text, set(["function"])), expected)
2035
2036 def test_struct_after_struct(self):
2037 text = """\
2038struct first {
2039};
2040
2041struct second {
2042 unsigned short s1;
2043#define SOMETHING 8
2044 unsigned short s2;
2045};
2046"""
2047 expected = """\
2048struct first {
2049};
2050struct second {
2051 unsigned short s1;
2052#define SOMETHING 8
2053 unsigned short s2;
2054};
2055"""
2056 self.assertEqual(self.parse(text), expected)
2057
2058 def test_other_not_removed(self):
2059 text = """\
2060typedef union {
2061 __u64 tu1;
2062 __u64 tu2;
2063} typedef_name;
2064
2065union {
2066 __u64 u1;
2067 __u64 u2;
2068};
2069
2070struct {
2071 __u64 s1;
2072 __u64 s2;
2073};
2074
2075enum {
2076 ENUM1 = 0,
2077 ENUM2,
2078};
2079
2080__extension__ typedef __signed__ long long __s64;
2081"""
2082 expected = """\
2083typedef union {
2084 __u64 tu1;
2085 __u64 tu2;
2086} typedef_name;
2087union {
2088 __u64 u1;
2089 __u64 u2;
2090};
2091struct {
2092 __u64 s1;
2093 __u64 s2;
2094};
2095enum {
2096 ENUM1 = 0,
2097 ENUM2,
2098};
2099__extension__ typedef __signed__ long long __s64;
2100"""
2101
2102 self.assertEqual(self.parse(text), expected)
2103
2104 def test_semicolon_after_function(self):
2105 text = """\
2106static inline __u64 function()
2107{
2108};
2109
2110struct should_see {
2111 __u32 field;
2112};
2113"""
2114 expected = """\
2115struct should_see {
2116 __u32 field;
2117};
2118"""
2119 self.assertEqual(self.parse(text), expected)
2120
2121 def test_define_in_middle_keep(self):
2122 text = """\
2123enum {
2124 ENUM0 = 0x10,
2125 ENUM1 = 0x20,
2126#define SOMETHING SOMETHING_ELSE
2127 ENUM2 = 0x40,
2128};
2129"""
2130 expected = """\
2131enum {
2132 ENUM0 = 0x10,
2133 ENUM1 = 0x20,
2134#define SOMETHING SOMETHING_ELSE
2135 ENUM2 = 0x40,
2136};
2137"""
2138 self.assertEqual(self.parse(text), expected)
2139
2140 def test_define_in_middle_remove(self):
2141 text = """\
2142static inline function() {
2143#define SOMETHING1 SOMETHING_ELSE1
2144 i = 0;
2145 {
2146 i = 1;
2147 }
2148#define SOMETHING2 SOMETHING_ELSE2
2149}
2150"""
2151 expected = """\
2152#define SOMETHING1 SOMETHING_ELSE1
2153#define SOMETHING2 SOMETHING_ELSE2
2154"""
2155 self.assertEqual(self.parse(text), expected)
2156
2157 def test_define_in_middle_force_keep(self):
2158 text = """\
2159static inline function() {
2160#define SOMETHING1 SOMETHING_ELSE1
2161 i = 0;
2162 {
2163 i = 1;
2164 }
2165#define SOMETHING2 SOMETHING_ELSE2
2166}
2167"""
2168 expected = """\
2169static inline function() {
2170#define SOMETHING1 SOMETHING_ELSE1
2171 i = 0;
2172 {
2173 i = 1;
2174 }
2175#define SOMETHING2 SOMETHING_ELSE2
2176}
2177"""
2178 self.assertEqual(self.parse(text, set(["function"])), expected)
2179
2180 def test_define_before_remove(self):
2181 text = """\
2182#define SHOULD_BE_KEPT NOTHING1
2183#define ANOTHER_TO_KEEP NOTHING2
2184static inline function() {
2185#define SOMETHING1 SOMETHING_ELSE1
2186 i = 0;
2187 {
2188 i = 1;
2189 }
2190#define SOMETHING2 SOMETHING_ELSE2
2191}
2192"""
2193 expected = """\
2194#define SHOULD_BE_KEPT NOTHING1
2195#define ANOTHER_TO_KEEP NOTHING2
2196#define SOMETHING1 SOMETHING_ELSE1
2197#define SOMETHING2 SOMETHING_ELSE2
2198"""
2199 self.assertEqual(self.parse(text), expected)
2200
2201 def test_extern_C(self):
2202 text = """\
2203#if defined(__cplusplus)
2204extern "C" {
2205#endif
2206
2207struct something {
2208};
2209
2210#if defined(__cplusplus)
2211}
2212#endif
2213"""
2214 expected = """\
2215#ifdef __cplusplus
2216extern "C" {
2217#endif
2218struct something {
2219};
2220#ifdef __cplusplus
2221}
2222#endif
2223"""
2224 self.assertEqual(self.parse(text), expected)
2225
2226 def test_macro_definition_removed(self):
2227 text = """\
2228#define MACRO_FUNCTION_NO_PARAMS static inline some_func() {}
2229MACRO_FUNCTION_NO_PARAMS()
2230
2231#define MACRO_FUNCTION_PARAMS(a) static inline some_func() { a; }
2232MACRO_FUNCTION_PARAMS(a = 1)
2233
2234something that should still be kept
2235MACRO_FUNCTION_PARAMS(b)
2236"""
2237 expected = """\
2238#define MACRO_FUNCTION_NO_PARAMS static inline some_func() { }
2239#define MACRO_FUNCTION_PARAMS(a) static inline some_func() { a; }
2240something that should still be kept
2241"""
2242 self.assertEqual(self.parse(text), expected)
2243
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08002244
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07002245if __name__ == '__main__':
2246 unittest.main()