blob: 336a9c8354a3a00f22179ce13eadd959074f9d1f [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))
1040 indent += 2
1041 buf = ''
1042 newline = True
1043 elif t.id == '}':
1044 indent -= 2
1045 if not newline:
1046 result.append(strip_space(buf))
1047 # Look ahead to determine if it's the end of line.
1048 if (i + 1 < len(tokens) and
1049 (tokens[i+1].id == ';' or
1050 tokens[i+1].id in ['else', '__attribute__',
1051 '__attribute', '__packed'] or
1052 tokens[i+1].kind == TokenKind.IDENTIFIER)):
1053 buf = ' ' * indent + '}'
1054 newline = False
1055 else:
1056 result.append(' ' * indent + '}')
1057 buf = ''
1058 newline = True
1059 elif t.id == ';':
1060 result.append(strip_space(buf) + ';')
1061 buf = ''
1062 newline = True
1063 # We prefer a new line for each constant in enum.
1064 elif t.id == ',' and t.cursor.kind == CursorKind.ENUM_DECL:
1065 result.append(strip_space(buf) + ',')
1066 buf = ''
1067 newline = True
1068 else:
1069 if newline:
1070 buf += ' ' * indent + str(t)
1071 else:
1072 buf += ' ' + str(t)
1073 newline = False
1074 i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001075
Tao Baod7db5942015-01-28 10:07:51 -08001076 if buf:
1077 result.append(strip_space(buf))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001078
Tao Baod7db5942015-01-28 10:07:51 -08001079 return result, indent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001080
Elliott Hughes96c1db72017-05-25 13:48:01 -07001081 def write(self, out, indent):
1082 """Dump the current block."""
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001083 # removeWhiteSpace() will sometimes creates non-directive blocks
1084 # without any tokens. These come from blocks that only contained
1085 # empty lines and spaces. They should not be printed in the final
1086 # output, and then should not be counted for this operation.
1087 #
Tao Baod7db5942015-01-28 10:07:51 -08001088 if self.directive is None and not self.tokens:
Elliott Hughes96c1db72017-05-25 13:48:01 -07001089 return indent
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001090
1091 if self.directive:
Tao Baod7db5942015-01-28 10:07:51 -08001092 out.write(str(self) + '\n')
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001093 else:
Tao Baod7db5942015-01-28 10:07:51 -08001094 lines, indent = self.format_blocks(self.tokens, indent)
1095 for line in lines:
1096 out.write(line + '\n')
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001097
Elliott Hughes96c1db72017-05-25 13:48:01 -07001098 return indent
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001099
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001100 def __repr__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001101 """Generate the representation of a given block."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001102 if self.directive:
1103 result = "#%s " % self.directive
1104 if self.isIf():
1105 result += repr(self.expr)
1106 else:
1107 for tok in self.tokens:
1108 result += repr(tok)
1109 else:
1110 result = ""
1111 for tok in self.tokens:
1112 result += repr(tok)
1113
1114 return result
1115
1116 def __str__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001117 """Generate the string representation of a given block."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001118 if self.directive:
Tao Baod7db5942015-01-28 10:07:51 -08001119 # "#if"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001120 if self.directive == "if":
1121 # small optimization to re-generate #ifdef and #ifndef
1122 e = self.expr.expr
1123 op = e[0]
1124 if op == "defined":
1125 result = "#ifdef %s" % e[1]
1126 elif op == "!" and e[1][0] == "defined":
1127 result = "#ifndef %s" % e[1][1]
1128 else:
1129 result = "#if " + str(self.expr)
Tao Baod7db5942015-01-28 10:07:51 -08001130
1131 # "#define"
1132 elif self.isDefine():
1133 result = "#%s %s" % (self.directive, self.define_id)
1134 if self.tokens:
1135 result += " "
1136 expr = strip_space(' '.join([tok.id for tok in self.tokens]))
1137 # remove the space between name and '(' in function call
1138 result += re.sub(r'(\w+) \(', r'\1(', expr)
1139
1140 # "#error"
1141 # Concatenating tokens with a space separator, because they may
1142 # not be quoted and broken into several tokens
1143 elif self.directive == "error":
1144 result = "#error %s" % ' '.join([tok.id for tok in self.tokens])
1145
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001146 else:
1147 result = "#%s" % self.directive
Tao Baod7db5942015-01-28 10:07:51 -08001148 if self.tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001149 result += " "
Tao Baod7db5942015-01-28 10:07:51 -08001150 result += ''.join([tok.id for tok in self.tokens])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001151 else:
Tao Baod7db5942015-01-28 10:07:51 -08001152 lines, _ = self.format_blocks(self.tokens)
1153 result = '\n'.join(lines)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001154
1155 return result
1156
Tao Baod7db5942015-01-28 10:07:51 -08001157
1158class BlockList(object):
1159 """A convenience class used to hold and process a list of blocks.
1160
1161 It calls the cpp parser to get the blocks.
1162 """
1163
1164 def __init__(self, blocks):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001165 self.blocks = blocks
1166
1167 def __len__(self):
1168 return len(self.blocks)
1169
Tao Baod7db5942015-01-28 10:07:51 -08001170 def __getitem__(self, n):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001171 return self.blocks[n]
1172
1173 def __repr__(self):
1174 return repr(self.blocks)
1175
1176 def __str__(self):
Tao Baod7db5942015-01-28 10:07:51 -08001177 result = '\n'.join([str(b) for b in self.blocks])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001178 return result
1179
Tao Baod7db5942015-01-28 10:07:51 -08001180 def dump(self):
1181 """Dump all the blocks in current BlockList."""
1182 print '##### BEGIN #####'
1183 for i, b in enumerate(self.blocks):
1184 print '### BLOCK %d ###' % i
1185 print b
1186 print '##### END #####'
1187
1188 def optimizeIf01(self):
1189 """Remove the code between #if 0 .. #endif in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001190 self.blocks = optimize_if01(self.blocks)
1191
1192 def optimizeMacros(self, macros):
Tao Baod7db5942015-01-28 10:07:51 -08001193 """Remove known defined and undefined macros from a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001194 for b in self.blocks:
1195 if b.isIf():
1196 b.expr.optimize(macros)
1197
Tao Baod7db5942015-01-28 10:07:51 -08001198 def optimizeAll(self, macros):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001199 self.optimizeMacros(macros)
1200 self.optimizeIf01()
1201 return
1202
1203 def findIncludes(self):
Tao Baod7db5942015-01-28 10:07:51 -08001204 """Return the list of included files in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001205 result = []
1206 for b in self.blocks:
1207 i = b.isInclude()
1208 if i:
1209 result.append(i)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001210 return result
1211
Tao Baod7db5942015-01-28 10:07:51 -08001212 def write(self, out):
Tao Baod7db5942015-01-28 10:07:51 -08001213 indent = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001214 for b in self.blocks:
Elliott Hughes96c1db72017-05-25 13:48:01 -07001215 indent = b.write(out, indent)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001216
Elliott Hughes64f355f2017-08-30 16:10:24 -07001217 def removeVarsAndFuncs(self, keep):
Tao Baod7db5942015-01-28 10:07:51 -08001218 """Remove variable and function declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001219
Tao Baod7db5942015-01-28 10:07:51 -08001220 All extern and static declarations corresponding to variable and
1221 function declarations are removed. We only accept typedefs and
1222 enum/structs/union declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001223
Tao Baod7db5942015-01-28 10:07:51 -08001224 However, we keep the definitions corresponding to the set of known
Elliott Hughes64f355f2017-08-30 16:10:24 -07001225 static inline functions in the set 'keep', which is useful
Tao Baod7db5942015-01-28 10:07:51 -08001226 for optimized byteorder swap functions and stuff like that.
1227 """
1228
1229 # NOTE: It's also removing function-like macros, such as __SYSCALL(...)
1230 # in uapi/asm-generic/unistd.h, or KEY_FIELD(...) in linux/bcache.h.
1231 # It could be problematic when we have function-like macros but without
1232 # '}' following them. It will skip all the tokens/blocks until seeing a
1233 # '}' as the function end. Fortunately we don't have such cases in the
1234 # current kernel headers.
1235
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001236 # state = 0 => normal (i.e. LN + spaces)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001237 # state = 1 => typedef/struct encountered, ends with ";"
1238 # state = 2 => var declaration encountered, ends with ";"
1239 # state = 3 => func declaration encountered, ends with "}"
Tao Baod7db5942015-01-28 10:07:51 -08001240
Tao Baod7db5942015-01-28 10:07:51 -08001241 state = 0
1242 depth = 0
1243 blocks2 = []
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001244 skipTokens = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001245 for b in self.blocks:
1246 if b.isDirective():
1247 blocks2.append(b)
1248 else:
Tao Baod7db5942015-01-28 10:07:51 -08001249 n = len(b.tokens)
1250 i = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001251 if skipTokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001252 first = n
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001253 else:
1254 first = 0
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001255 while i < n:
1256 tok = b.tokens[i]
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001257 tokid = tok.id
1258 # If we are not looking for the start of a new
1259 # type/var/func, then skip over tokens until
1260 # we find our terminator, managing the depth of
1261 # accolades as we go.
1262 if state > 0:
1263 terminator = False
1264 if tokid == '{':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001265 depth += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001266 elif tokid == '}':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001267 if depth > 0:
1268 depth -= 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001269 if (depth == 0) and (state == 3):
1270 terminator = True
1271 elif tokid == ';' and depth == 0:
1272 terminator = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001273
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001274 if terminator:
1275 # we found the terminator
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001276 state = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001277 if skipTokens:
1278 skipTokens = False
Tao Baod7db5942015-01-28 10:07:51 -08001279 first = i + 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001280
Tao Baod7db5942015-01-28 10:07:51 -08001281 i += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001282 continue
1283
1284 # Is it a new type definition, then start recording it
Tao Baod7db5942015-01-28 10:07:51 -08001285 if tok.id in ['struct', 'typedef', 'enum', 'union',
1286 '__extension__']:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001287 state = 1
Tao Baod7db5942015-01-28 10:07:51 -08001288 i += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001289 continue
1290
1291 # Is it a variable or function definition. If so, first
1292 # try to determine which type it is, and also extract
1293 # its name.
1294 #
1295 # We're going to parse the next tokens of the same block
Elliott Hughes64f355f2017-08-30 16:10:24 -07001296 # until we find a semicolon or a left parenthesis.
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001297 #
Elliott Hughes64f355f2017-08-30 16:10:24 -07001298 # The semicolon corresponds to a variable definition,
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001299 # the left-parenthesis to a function definition.
1300 #
1301 # We also assume that the var/func name is the last
1302 # identifier before the terminator.
1303 #
Tao Baod7db5942015-01-28 10:07:51 -08001304 j = i + 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001305 ident = ""
1306 while j < n:
1307 tokid = b.tokens[j].id
1308 if tokid == '(': # a function declaration
1309 state = 3
1310 break
Tao Baod7db5942015-01-28 10:07:51 -08001311 elif tokid == ';': # a variable declaration
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001312 state = 2
1313 break
Tao Baod7db5942015-01-28 10:07:51 -08001314 if b.tokens[j].kind == TokenKind.IDENTIFIER:
1315 ident = b.tokens[j].id
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001316 j += 1
1317
1318 if j >= n:
1319 # This can only happen when the declaration
1320 # does not end on the current block (e.g. with
1321 # a directive mixed inside it.
1322 #
1323 # We will treat it as malformed because
1324 # it's very hard to recover from this case
1325 # without making our parser much more
1326 # complex.
1327 #
Tao Baod7db5942015-01-28 10:07:51 -08001328 logging.debug("### skip unterminated static '%s'",
1329 ident)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001330 break
1331
Elliott Hughes64f355f2017-08-30 16:10:24 -07001332 if ident in keep:
Tao Baod7db5942015-01-28 10:07:51 -08001333 logging.debug("### keep var/func '%s': %s", ident,
1334 repr(b.tokens[i:j]))
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001335 else:
1336 # We're going to skip the tokens for this declaration
Tao Baod7db5942015-01-28 10:07:51 -08001337 logging.debug("### skip var/func '%s': %s", ident,
1338 repr(b.tokens[i:j]))
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001339 if i > first:
Tao Baod7db5942015-01-28 10:07:51 -08001340 blocks2.append(Block(b.tokens[first:i]))
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001341 skipTokens = True
Tao Baod7db5942015-01-28 10:07:51 -08001342 first = n
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001343
Tao Baod7db5942015-01-28 10:07:51 -08001344 i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001345
1346 if i > first:
Elliott Hughes64f355f2017-08-30 16:10:24 -07001347 #print "### final '%s'" % repr(b.tokens[first:i])
Tao Baod7db5942015-01-28 10:07:51 -08001348 blocks2.append(Block(b.tokens[first:i]))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001349
1350 self.blocks = blocks2
1351
Tao Baod7db5942015-01-28 10:07:51 -08001352 def replaceTokens(self, replacements):
1353 """Replace tokens according to the given dict."""
Elliott Hughes64f355f2017-08-30 16:10:24 -07001354 extra_includes = []
Martin Storsjod32c8052010-12-08 11:38:14 +01001355 for b in self.blocks:
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001356 made_change = False
Tao Baod7db5942015-01-28 10:07:51 -08001357 if b.isInclude() is None:
Elliott Hughes64f355f2017-08-30 16:10:24 -07001358 i = 0
1359 while i < len(b.tokens):
1360 tok = b.tokens[i]
1361 if (tok.kind == TokenKind.KEYWORD and tok.id == 'struct'
1362 and (i + 2) < len(b.tokens) and b.tokens[i + 2].id == '{'):
1363 struct_name = b.tokens[i + 1].id
1364 if struct_name in kernel_struct_replacements:
1365 extra_includes.append("<bits/%s.h>" % struct_name)
1366 end = i + 2
1367 while end < len(b.tokens) and b.tokens[end].id != '}':
1368 end += 1
1369 end += 1 # Swallow '}'
1370 while end < len(b.tokens) and b.tokens[end].id != ';':
1371 end += 1
1372 end += 1 # Swallow ';'
1373 # Remove these tokens. We'll replace them later with a #include block.
1374 b.tokens[i:end] = []
1375 made_change = True
1376 # We've just modified b.tokens, so revisit the current offset.
1377 continue
Tao Baod7db5942015-01-28 10:07:51 -08001378 if tok.kind == TokenKind.IDENTIFIER:
1379 if tok.id in replacements:
1380 tok.id = replacements[tok.id]
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001381 made_change = True
Elliott Hughes64f355f2017-08-30 16:10:24 -07001382 i += 1
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001383
Tao Baod7db5942015-01-28 10:07:51 -08001384 if b.isDefine() and b.define_id in replacements:
1385 b.define_id = replacements[b.define_id]
1386 made_change = True
1387
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001388 if made_change and b.isIf():
1389 # Keep 'expr' in sync with 'tokens'.
1390 b.expr = CppExpr(b.tokens)
Martin Storsjod32c8052010-12-08 11:38:14 +01001391
Elliott Hughes64f355f2017-08-30 16:10:24 -07001392 for extra_include in extra_includes:
1393 replacement = CppStringTokenizer(extra_include)
1394 self.blocks.insert(2, Block(replacement.tokens, directive='include'))
1395
1396
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001397
Tao Baod7db5942015-01-28 10:07:51 -08001398def strip_space(s):
1399 """Strip out redundant space in a given string."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001400
Tao Baod7db5942015-01-28 10:07:51 -08001401 # NOTE: It ought to be more clever to not destroy spaces in string tokens.
1402 replacements = {' . ': '.',
1403 ' [': '[',
1404 '[ ': '[',
1405 ' ]': ']',
1406 '( ': '(',
1407 ' )': ')',
1408 ' ,': ',',
1409 '# ': '#',
1410 ' ;': ';',
1411 '~ ': '~',
1412 ' -> ': '->'}
1413 result = s
1414 for r in replacements:
1415 result = result.replace(r, replacements[r])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001416
Tao Baod7db5942015-01-28 10:07:51 -08001417 # Remove the space between function name and the parenthesis.
1418 result = re.sub(r'(\w+) \(', r'\1(', result)
1419 return result
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001420
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001421
Tao Baod7db5942015-01-28 10:07:51 -08001422class BlockParser(object):
1423 """A class that converts an input source file into a BlockList object."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001424
Tao Baod7db5942015-01-28 10:07:51 -08001425 def __init__(self, tokzer=None):
1426 """Initialize a block parser.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001427
Tao Baod7db5942015-01-28 10:07:51 -08001428 The input source is provided through a Tokenizer object.
1429 """
1430 self._tokzer = tokzer
1431 self._parsed = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001432
Tao Baod7db5942015-01-28 10:07:51 -08001433 @property
1434 def parsed(self):
1435 return self._parsed
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001436
Tao Baod7db5942015-01-28 10:07:51 -08001437 @staticmethod
1438 def _short_extent(extent):
1439 return '%d:%d - %d:%d' % (extent.start.line, extent.start.column,
1440 extent.end.line, extent.end.column)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001441
Tao Baod7db5942015-01-28 10:07:51 -08001442 def getBlocks(self, tokzer=None):
1443 """Return all the blocks parsed."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001444
Tao Baod7db5942015-01-28 10:07:51 -08001445 def consume_extent(i, tokens, extent=None, detect_change=False):
1446 """Return tokens that belong to the given extent.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001447
Tao Baod7db5942015-01-28 10:07:51 -08001448 It parses all the tokens that follow tokens[i], until getting out
1449 of the extent. When detect_change is True, it may terminate early
1450 when detecting preprocessing directives inside the extent.
1451 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001452
Tao Baod7db5942015-01-28 10:07:51 -08001453 result = []
1454 if extent is None:
1455 extent = tokens[i].cursor.extent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001456
Tao Baod7db5942015-01-28 10:07:51 -08001457 while i < len(tokens) and tokens[i].location in extent:
1458 t = tokens[i]
1459 if debugBlockParser:
1460 print ' ' * 2, t.id, t.kind, t.cursor.kind
1461 if (detect_change and t.cursor.extent != extent and
1462 t.cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE):
1463 break
1464 result.append(t)
1465 i += 1
1466 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001467
Tao Baod7db5942015-01-28 10:07:51 -08001468 def consume_line(i, tokens):
1469 """Return tokens that follow tokens[i] in the same line."""
1470 result = []
1471 line = tokens[i].location.line
1472 while i < len(tokens) and tokens[i].location.line == line:
1473 if tokens[i].cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1474 break
1475 result.append(tokens[i])
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 if tokzer is None:
1480 tokzer = self._tokzer
1481 tokens = tokzer.tokens
1482
1483 blocks = []
1484 buf = []
1485 i = 0
1486
1487 while i < len(tokens):
1488 t = tokens[i]
1489 cursor = t.cursor
1490
1491 if debugBlockParser:
1492 print ("%d: Processing [%s], kind=[%s], cursor=[%s], "
1493 "extent=[%s]" % (t.location.line, t.spelling, t.kind,
1494 cursor.kind,
1495 self._short_extent(cursor.extent)))
1496
1497 if cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1498 if buf:
1499 blocks.append(Block(buf))
1500 buf = []
1501
1502 j = i
1503 if j + 1 >= len(tokens):
1504 raise BadExpectedToken("### BAD TOKEN at %s" % (t.location))
1505 directive = tokens[j+1].id
1506
1507 if directive == 'define':
1508 if i+2 >= len(tokens):
1509 raise BadExpectedToken("### BAD TOKEN at %s" %
1510 (tokens[i].location))
1511
1512 # Skip '#' and 'define'.
1513 extent = tokens[i].cursor.extent
1514 i += 2
1515 id = ''
1516 # We need to separate the id from the remaining of
1517 # the line, especially for the function-like macro.
1518 if (i + 1 < len(tokens) and tokens[i+1].id == '(' and
1519 (tokens[i].location.column + len(tokens[i].spelling) ==
1520 tokens[i+1].location.column)):
1521 while i < len(tokens):
1522 id += tokens[i].id
1523 if tokens[i].spelling == ')':
1524 i += 1
1525 break
1526 i += 1
1527 else:
1528 id += tokens[i].id
1529 # Advance to the next token that follows the macro id
1530 i += 1
1531
1532 (i, ret) = consume_extent(i, tokens, extent=extent)
1533 blocks.append(Block(ret, directive=directive,
1534 lineno=t.location.line, identifier=id))
1535
1536 else:
1537 (i, ret) = consume_extent(i, tokens)
1538 blocks.append(Block(ret[2:], directive=directive,
1539 lineno=t.location.line))
1540
1541 elif cursor.kind == CursorKind.INCLUSION_DIRECTIVE:
1542 if buf:
1543 blocks.append(Block(buf))
1544 buf = []
1545 directive = tokens[i+1].id
1546 (i, ret) = consume_extent(i, tokens)
1547
1548 blocks.append(Block(ret[2:], directive=directive,
1549 lineno=t.location.line))
1550
1551 elif cursor.kind == CursorKind.VAR_DECL:
1552 if buf:
1553 blocks.append(Block(buf))
1554 buf = []
1555
1556 (i, ret) = consume_extent(i, tokens, detect_change=True)
1557 buf += ret
1558
1559 elif cursor.kind == CursorKind.FUNCTION_DECL:
1560 if buf:
1561 blocks.append(Block(buf))
1562 buf = []
1563
1564 (i, ret) = consume_extent(i, tokens, detect_change=True)
1565 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001566
1567 else:
Tao Baod7db5942015-01-28 10:07:51 -08001568 (i, ret) = consume_line(i, tokens)
1569 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001570
Tao Baod7db5942015-01-28 10:07:51 -08001571 if buf:
1572 blocks.append(Block(buf))
1573
1574 # _parsed=True indicates a successful parsing, although may result an
1575 # empty BlockList.
1576 self._parsed = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001577
1578 return BlockList(blocks)
1579
Tao Baod7db5942015-01-28 10:07:51 -08001580 def parse(self, tokzer):
1581 return self.getBlocks(tokzer)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001582
Tao Baod7db5942015-01-28 10:07:51 -08001583 def parseFile(self, path):
1584 return self.getBlocks(CppFileTokenizer(path))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001585
1586
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001587class BlockParserTests(unittest.TestCase):
1588 """BlockParser unit tests."""
Tao Baod7db5942015-01-28 10:07:51 -08001589
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001590 def get_blocks(self, lines):
1591 blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
1592 return map(lambda a: str(a), blocks)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001593
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001594 def test_hash(self):
1595 self.assertEqual(self.get_blocks(["#error hello"]), ["#error hello"])
Tao Baod7db5942015-01-28 10:07:51 -08001596
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001597 def test_empty_line(self):
1598 self.assertEqual(self.get_blocks(["foo", "", "bar"]), ["foo bar"])
Tao Baod7db5942015-01-28 10:07:51 -08001599
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001600 def test_hash_with_space(self):
1601 # We currently cannot handle the following case with libclang properly.
1602 # Fortunately it doesn't appear in current headers.
1603 #self.assertEqual(self.get_blocks(["foo", " # ", "bar"]), ["foo", "bar"])
1604 pass
1605
1606 def test_with_comment(self):
1607 self.assertEqual(self.get_blocks(["foo",
1608 " # /* ahah */ if defined(__KERNEL__) /* more */",
1609 "bar", "#endif"]),
1610 ["foo", "#ifdef __KERNEL__", "bar", "#endif"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001611
1612
Tao Baod7db5942015-01-28 10:07:51 -08001613################################################################################
1614################################################################################
1615##### #####
1616##### B L O C K L I S T O P T I M I Z A T I O N #####
1617##### #####
1618################################################################################
1619################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001620
Tao Baod7db5942015-01-28 10:07:51 -08001621
Tao Baod7db5942015-01-28 10:07:51 -08001622def find_matching_endif(blocks, i):
1623 """Traverse the blocks to find out the matching #endif."""
1624 n = len(blocks)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001625 depth = 1
1626 while i < n:
1627 if blocks[i].isDirective():
Tao Baod7db5942015-01-28 10:07:51 -08001628 dir_ = blocks[i].directive
1629 if dir_ in ["if", "ifndef", "ifdef"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001630 depth += 1
Tao Baod7db5942015-01-28 10:07:51 -08001631 elif depth == 1 and dir_ in ["else", "elif"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001632 return i
Tao Baod7db5942015-01-28 10:07:51 -08001633 elif dir_ == "endif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001634 depth -= 1
1635 if depth == 0:
1636 return i
1637 i += 1
1638 return i
1639
Tao Baod7db5942015-01-28 10:07:51 -08001640
1641def optimize_if01(blocks):
1642 """Remove the code between #if 0 .. #endif in a list of CppBlocks."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001643 i = 0
1644 n = len(blocks)
1645 result = []
1646 while i < n:
1647 j = i
1648 while j < n and not blocks[j].isIf():
1649 j += 1
1650 if j > i:
Tao Baod7db5942015-01-28 10:07:51 -08001651 logging.debug("appending lines %d to %d", blocks[i].lineno,
1652 blocks[j-1].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001653 result += blocks[i:j]
1654 if j >= n:
1655 break
1656 expr = blocks[j].expr
Tao Baod7db5942015-01-28 10:07:51 -08001657 r = expr.toInt()
1658 if r is None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001659 result.append(blocks[j])
1660 i = j + 1
1661 continue
1662
1663 if r == 0:
1664 # if 0 => skip everything until the corresponding #endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001665 start_dir = blocks[j].directive
Tao Baod7db5942015-01-28 10:07:51 -08001666 j = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001667 if j >= n:
1668 # unterminated #if 0, finish here
1669 break
Tao Baod7db5942015-01-28 10:07:51 -08001670 dir_ = blocks[j].directive
1671 if dir_ == "endif":
1672 logging.debug("remove 'if 0' .. 'endif' (lines %d to %d)",
1673 blocks[i].lineno, blocks[j].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001674 if start_dir == "elif":
1675 # Put an endif since we started with an elif.
1676 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001677 i = j + 1
Tao Baod7db5942015-01-28 10:07:51 -08001678 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001679 # convert 'else' into 'if 1'
Tao Baod7db5942015-01-28 10:07:51 -08001680 logging.debug("convert 'if 0' .. 'else' into 'if 1' (lines %d "
1681 "to %d)", blocks[i].lineno, blocks[j-1].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001682 if start_dir == "elif":
1683 blocks[j].directive = "elif"
1684 else:
1685 blocks[j].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001686 blocks[j].expr = CppExpr(CppStringTokenizer("1").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001687 i = j
Tao Baod7db5942015-01-28 10:07:51 -08001688 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001689 # convert 'elif' into 'if'
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001690 logging.debug("convert 'if 0' .. 'elif' into 'if'")
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001691 if start_dir == "elif":
1692 blocks[j].directive = "elif"
1693 else:
1694 blocks[j].directive = "if"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001695 i = j
1696 continue
1697
1698 # if 1 => find corresponding endif and remove/transform them
Tao Baod7db5942015-01-28 10:07:51 -08001699 k = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001700 if k >= n:
1701 # unterminated #if 1, finish here
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001702 logging.debug("unterminated 'if 1'")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001703 result += blocks[j+1:k]
1704 break
1705
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001706 start_dir = blocks[j].directive
Tao Baod7db5942015-01-28 10:07:51 -08001707 dir_ = blocks[k].directive
1708 if dir_ == "endif":
1709 logging.debug("convert 'if 1' .. 'endif' (lines %d to %d)",
1710 blocks[j].lineno, blocks[k].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001711 if start_dir == "elif":
1712 # Add the elif in to the results and convert it to an elif 1.
1713 blocks[j].tokens = CppStringTokenizer("1").tokens
1714 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001715 result += optimize_if01(blocks[j+1:k])
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001716 if start_dir == "elif":
1717 # Add the endif in to the results.
1718 result += blocks[k:k+1]
Tao Baod7db5942015-01-28 10:07:51 -08001719 i = k + 1
1720 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001721 # convert 'else' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001722 logging.debug("convert 'if 1' .. 'else' (lines %d to %d)",
1723 blocks[j].lineno, blocks[k].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001724 if start_dir == "elif":
1725 # Add the elif in to the results and convert it to an elif 1.
1726 blocks[j].tokens = CppStringTokenizer("1").tokens
1727 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001728 result += optimize_if01(blocks[j+1:k])
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001729 if start_dir == "elif":
1730 blocks[k].directive = "elif"
1731 else:
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
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001744class OptimizerTests(unittest.TestCase):
1745 def parse(self, text, macros=None):
1746 out = utils.StringOutput()
1747 blocks = BlockParser().parse(CppStringTokenizer(text))
1748 blocks.replaceTokens(kernel_token_replacements)
1749 blocks.optimizeAll(macros)
1750 blocks.write(out)
1751 return out.get()
Tao Baod7db5942015-01-28 10:07:51 -08001752
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001753 def test_if1(self):
1754 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001755#if 1
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001756#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001757#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001758"""
1759 expected = """\
1760#define GOOD
1761"""
1762 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001763
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001764 def test_if0(self):
1765 text = """\
1766#if 0
1767#define SHOULD_SKIP1
1768#define SHOULD_SKIP2
1769#endif
1770"""
1771 expected = ""
1772 self.assertEqual(self.parse(text), expected)
1773
1774 def test_if1_else(self):
1775 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001776#if 1
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001777#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001778#else
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001779#define BAD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001780#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001781"""
1782 expected = """\
1783#define GOOD
1784"""
1785 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001786
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001787 def test_if0_else(self):
1788 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001789#if 0
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001790#define BAD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001791#else
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001792#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001793#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001794"""
1795 expected = """\
1796#define GOOD
1797"""
1798 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001799
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001800 def test_if_elif1(self):
1801 text = """\
1802#if defined(something)
1803#define EXISTS
1804#elif 1
1805#define GOOD
1806#endif
1807"""
1808 expected = """\
1809#ifdef something
1810#define EXISTS
1811#elif 1
1812#define GOOD
1813#endif
1814"""
1815 self.assertEqual(self.parse(text), expected)
1816
1817 def test_if_elif1_macro(self):
1818 text = """\
1819#if defined(something)
1820#define EXISTS
1821#elif defined(WILL_BE_ONE)
1822#define GOOD
1823#endif
1824"""
1825 expected = """\
1826#ifdef something
1827#define EXISTS
1828#elif 1
1829#define GOOD
1830#endif
1831"""
1832 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1833
1834
1835 def test_if_elif1_else(self):
1836 text = """\
1837#if defined(something)
1838#define EXISTS
1839#elif 1
1840#define GOOD
1841#else
1842#define BAD
1843#endif
1844"""
1845 expected = """\
1846#ifdef something
1847#define EXISTS
1848#elif 1
1849#define GOOD
1850#endif
1851"""
1852 self.assertEqual(self.parse(text), expected)
1853
1854 def test_if_elif1_else_macro(self):
1855 text = """\
1856#if defined(something)
1857#define EXISTS
1858#elif defined(WILL_BE_ONE)
1859#define GOOD
1860#else
1861#define BAD
1862#endif
1863"""
1864 expected = """\
1865#ifdef something
1866#define EXISTS
1867#elif 1
1868#define GOOD
1869#endif
1870"""
1871 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1872
1873
1874 def test_if_elif1_else_macro(self):
1875 text = """\
1876#if defined(something)
1877#define EXISTS
1878#elif defined(WILL_BE_ONE)
1879#define GOOD
1880#else
1881#define BAD
1882#endif
1883"""
1884 expected = """\
1885#ifdef something
1886#define EXISTS
1887#elif 1
1888#define GOOD
1889#endif
1890"""
1891 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1892
1893 def test_macro_set_to_undefined_single(self):
1894 text = """\
Elliott Hughes40596aa2013-11-05 14:54:29 -08001895#if defined(__KERNEL__)
1896#define BAD_KERNEL
1897#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001898"""
1899 expected = ""
1900 macros = {"__KERNEL__": kCppUndefinedMacro}
1901 self.assertEqual(self.parse(text, macros), expected)
Elliott Hughes40596aa2013-11-05 14:54:29 -08001902
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001903 def test_macro_set_to_undefined_if(self):
1904 text = """\
Elliott Hughes40596aa2013-11-05 14:54:29 -08001905#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001906#define CHECK
Elliott Hughes40596aa2013-11-05 14:54:29 -08001907#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001908"""
1909 expected = """\
1910#if !defined(__GLIBC__) || __GLIBC__ < 2
1911#define CHECK
1912#endif
1913"""
1914 macros = {"__KERNEL__": kCppUndefinedMacro}
1915 self.assertEqual(self.parse(text, macros), expected)
Elliott Hughes40596aa2013-11-05 14:54:29 -08001916
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001917 def test_endif_comment_removed(self):
1918 text = """\
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001919#ifndef SIGRTMAX
1920#define SIGRTMAX 123
1921#endif /* SIGRTMAX */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001922"""
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001923 expected = """\
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001924#ifndef __SIGRTMAX
1925#define __SIGRTMAX 123
Elliott Hughes96c1db72017-05-25 13:48:01 -07001926#endif
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001927"""
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001928 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001929
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001930 def test_multilevel_if0(self):
1931 text = """\
1932#if 0
1933#if 1
1934#define BAD_6
1935#endif
1936#endif
1937"""
1938 expected = ""
1939 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001940
1941
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001942if __name__ == '__main__':
1943 unittest.main()