blob: a040474423fb724408370e0f5970fdd956772b4a [file] [log] [blame]
Elliott Hughes6b586e72021-04-15 13:39:08 -07001#!/usr/bin/env python3
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.
Christopher Ferrisa2142d22021-04-20 17:30:04 -070017site.addsitedir(os.path.join(top, 'prebuilts/clang/host/linux-x86/clang-stable/lib64/python3/site-packages/'))
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 Ferrisa2142d22021-04-20 17:30:04 -070031clang.cindex.Config.set_library_file(os.path.join(top, 'prebuilts/clang/host/linux-x86/clang-stable/lib64/libclang.so'))
Tao Bao75920082015-04-22 10:37:38 -070032
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -070033from defaults import *
Tao Baod7db5942015-01-28 10:07:51 -080034
35
36debugBlockParser = False
37debugCppExpr = False
38debugOptimIf01 = False
39
40###############################################################################
41###############################################################################
42##### #####
43##### C P P T O K E N S #####
44##### #####
45###############################################################################
46###############################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080047
48# the list of supported C-preprocessor tokens
49# plus a couple of C tokens as well
Tao Baod7db5942015-01-28 10:07:51 -080050tokEOF = "\0"
51tokLN = "\n"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080052tokSTRINGIFY = "#"
Tao Baod7db5942015-01-28 10:07:51 -080053tokCONCAT = "##"
54tokLOGICAND = "&&"
55tokLOGICOR = "||"
56tokSHL = "<<"
57tokSHR = ">>"
58tokEQUAL = "=="
59tokNEQUAL = "!="
60tokLT = "<"
61tokLTE = "<="
62tokGT = ">"
63tokGTE = ">="
64tokELLIPSIS = "..."
65tokSPACE = " "
66tokDEFINED = "defined"
67tokLPAREN = "("
68tokRPAREN = ")"
69tokNOT = "!"
70tokPLUS = "+"
71tokMINUS = "-"
72tokMULTIPLY = "*"
73tokDIVIDE = "/"
74tokMODULUS = "%"
75tokBINAND = "&"
76tokBINOR = "|"
77tokBINXOR = "^"
78tokCOMMA = ","
79tokLBRACE = "{"
80tokRBRACE = "}"
81tokARROW = "->"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080082tokINCREMENT = "++"
83tokDECREMENT = "--"
Tao Baod7db5942015-01-28 10:07:51 -080084tokNUMBER = "<number>"
85tokIDENT = "<ident>"
86tokSTRING = "<string>"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080087
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080088
Tao Baod7db5942015-01-28 10:07:51 -080089class Token(clang.cindex.Token):
90 """A class that represents one token after parsing.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080091
Tao Baod7db5942015-01-28 10:07:51 -080092 It inherits the class in libclang, with an extra id property to hold the
93 new spelling of the token. The spelling property in the base class is
94 defined as read-only. New names after macro instantiation are saved in
95 their ids now. It also facilitates the renaming of directive optimizations
96 like replacing 'ifndef X' with 'if !defined(X)'.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080097
Tao Baod7db5942015-01-28 10:07:51 -080098 It also overrides the cursor property of the base class. Because the one
99 in libclang always queries based on a single token, which usually doesn't
100 hold useful information. The cursor in this class can be set by calling
101 CppTokenizer.getTokensWithCursors(). Otherwise it returns the one in the
102 base class.
103 """
104
105 def __init__(self, tu=None, group=None, int_data=None, ptr_data=None,
106 cursor=None):
107 clang.cindex.Token.__init__(self)
108 self._id = None
109 self._tu = tu
110 self._group = group
111 self._cursor = cursor
112 # self.int_data and self.ptr_data are from the base class. But
113 # self.int_data doesn't accept a None value.
114 if int_data is not None:
115 self.int_data = int_data
116 self.ptr_data = ptr_data
117
118 @property
119 def id(self):
120 """Name of the token."""
121 if self._id is None:
122 return self.spelling
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800123 else:
Tao Baod7db5942015-01-28 10:07:51 -0800124 return self._id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800125
Tao Baod7db5942015-01-28 10:07:51 -0800126 @id.setter
127 def id(self, new_id):
128 """Setting name of the token."""
129 self._id = new_id
130
131 @property
132 def cursor(self):
133 if self._cursor is None:
134 self._cursor = clang.cindex.Token.cursor
135 return self._cursor
136
137 @cursor.setter
138 def cursor(self, new_cursor):
139 self._cursor = new_cursor
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800140
141 def __repr__(self):
Tao Baod7db5942015-01-28 10:07:51 -0800142 if self.id == 'defined':
143 return self.id
144 elif self.kind == TokenKind.IDENTIFIER:
145 return "(ident %s)" % self.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800146
147 return self.id
148
149 def __str__(self):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800150 return self.id
151
Tao Baod7db5942015-01-28 10:07:51 -0800152
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800153class BadExpectedToken(Exception):
Tao Baod7db5942015-01-28 10:07:51 -0800154 """An exception that will be raised for unexpected tokens."""
155 pass
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800156
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800157
Tao Baod7db5942015-01-28 10:07:51 -0800158# The __contains__ function in libclang SourceRange class contains a bug. It
159# gives wrong result when dealing with single line range.
160# Bug filed with upstream:
161# http://llvm.org/bugs/show_bug.cgi?id=22243, http://reviews.llvm.org/D7277
162def SourceRange__contains__(self, other):
163 """Determine if a given location is inside the range."""
164 if not isinstance(other, SourceLocation):
165 return False
166 if other.file is None and self.start.file is None:
167 pass
168 elif (self.start.file.name != other.file.name or
169 other.file.name != self.end.file.name):
170 # same file name
171 return False
172 # same file, in between lines
173 if self.start.line < other.line < self.end.line:
174 return True
175 # same file, same line
176 elif self.start.line == other.line == self.end.line:
177 if self.start.column <= other.column <= self.end.column:
178 return True
179 elif self.start.line == other.line:
180 # same file first line
181 if self.start.column <= other.column:
182 return True
183 elif other.line == self.end.line:
184 # same file last line
185 if other.column <= self.end.column:
186 return True
187 return False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800188
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800189
Tao Baod7db5942015-01-28 10:07:51 -0800190SourceRange.__contains__ = SourceRange__contains__
191
192
193################################################################################
194################################################################################
195##### #####
196##### C P P T O K E N I Z E R #####
197##### #####
198################################################################################
199################################################################################
200
201
202class CppTokenizer(object):
203 """A tokenizer that converts some input text into a list of tokens.
204
205 It calls libclang's tokenizer to get the parsed tokens. In addition, it
206 updates the cursor property in each token after parsing, by calling
207 getTokensWithCursors().
208 """
209
210 clang_flags = ['-E', '-x', 'c']
211 options = TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800212
213 def __init__(self):
Tao Baod7db5942015-01-28 10:07:51 -0800214 """Initialize a new CppTokenizer object."""
215 self._indexer = clang.cindex.Index.create()
216 self._tu = None
217 self._index = 0
218 self.tokens = None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800219
Tao Baod7db5942015-01-28 10:07:51 -0800220 def _getTokensWithCursors(self):
221 """Helper method to return all tokens with their cursors.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800222
Tao Baod7db5942015-01-28 10:07:51 -0800223 The cursor property in a clang Token doesn't provide enough
224 information. Because it is queried based on single token each time
225 without any context, i.e. via calling conf.lib.clang_annotateTokens()
226 with only one token given. So we often see 'INVALID_FILE' in one
227 token's cursor. In this function it passes all the available tokens
228 to get more informative cursors.
229 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800230
Tao Baod7db5942015-01-28 10:07:51 -0800231 tokens_memory = ctypes.POINTER(clang.cindex.Token)()
232 tokens_count = ctypes.c_uint()
233
234 conf.lib.clang_tokenize(self._tu, self._tu.cursor.extent,
235 ctypes.byref(tokens_memory),
236 ctypes.byref(tokens_count))
237
238 count = int(tokens_count.value)
239
240 # If we get no tokens, no memory was allocated. Be sure not to return
241 # anything and potentially call a destructor on nothing.
242 if count < 1:
243 return
244
245 cursors = (Cursor * count)()
246 cursors_memory = ctypes.cast(cursors, ctypes.POINTER(Cursor))
247
248 conf.lib.clang_annotateTokens(self._tu, tokens_memory, count,
249 cursors_memory)
250
251 tokens_array = ctypes.cast(
252 tokens_memory,
253 ctypes.POINTER(clang.cindex.Token * count)).contents
254 token_group = TokenGroup(self._tu, tokens_memory, tokens_count)
255
256 tokens = []
Christopher Ferrisa2142d22021-04-20 17:30:04 -0700257 for i in range(0, count):
Tao Baod7db5942015-01-28 10:07:51 -0800258 token = Token(self._tu, token_group,
259 int_data=tokens_array[i].int_data,
260 ptr_data=tokens_array[i].ptr_data,
261 cursor=cursors[i])
262 # We only want non-comment tokens.
263 if token.kind != TokenKind.COMMENT:
264 tokens.append(token)
265
266 return tokens
267
268 def parseString(self, lines):
269 """Parse a list of text lines into a BlockList object."""
Elliott Hughescf346532020-07-31 10:35:03 -0700270 file_ = 'no-filename-available.c'
Tao Baod7db5942015-01-28 10:07:51 -0800271 self._tu = self._indexer.parse(file_, self.clang_flags,
272 unsaved_files=[(file_, lines)],
273 options=self.options)
274 self.tokens = self._getTokensWithCursors()
275
276 def parseFile(self, file_):
277 """Parse a file into a BlockList object."""
278 self._tu = self._indexer.parse(file_, self.clang_flags,
279 options=self.options)
280 self.tokens = self._getTokensWithCursors()
281
282 def nextToken(self):
283 """Return next token from the list."""
284 if self._index < len(self.tokens):
285 t = self.tokens[self._index]
286 self._index += 1
287 return t
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800288 else:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800289 return None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800290
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800291
Tao Baod7db5942015-01-28 10:07:51 -0800292class CppStringTokenizer(CppTokenizer):
293 """A CppTokenizer derived class that accepts a string of text as input."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800294
Tao Baod7db5942015-01-28 10:07:51 -0800295 def __init__(self, line):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800296 CppTokenizer.__init__(self)
Tao Baod7db5942015-01-28 10:07:51 -0800297 self.parseString(line)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800298
299
300class CppFileTokenizer(CppTokenizer):
Tao Baod7db5942015-01-28 10:07:51 -0800301 """A CppTokenizer derived class that accepts a file as input."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800302
Tao Baod7db5942015-01-28 10:07:51 -0800303 def __init__(self, file_):
304 CppTokenizer.__init__(self)
305 self.parseFile(file_)
306
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800307
308# Unit testing
309#
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700310class CppTokenizerTests(unittest.TestCase):
311 """CppTokenizer tests."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800312
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700313 def get_tokens(self, token_string, line_col=False):
314 tokens = CppStringTokenizer(token_string)
315 token_list = []
316 while True:
317 token = tokens.nextToken()
318 if not token:
319 break
320 if line_col:
321 token_list.append((token.id, token.location.line,
322 token.location.column))
323 else:
324 token_list.append(token.id)
325 return token_list
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800326
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700327 def test_hash(self):
328 self.assertEqual(self.get_tokens("#an/example && (01923_xy)"),
329 ["#", "an", "/", "example", tokLOGICAND, tokLPAREN,
330 "01923_xy", tokRPAREN])
Tao Baod7db5942015-01-28 10:07:51 -0800331
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700332 def test_parens(self):
333 self.assertEqual(self.get_tokens("FOO(BAR) && defined(BAZ)"),
334 ["FOO", tokLPAREN, "BAR", tokRPAREN, tokLOGICAND,
335 "defined", tokLPAREN, "BAZ", tokRPAREN])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800336
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700337 def test_comment(self):
338 self.assertEqual(self.get_tokens("/*\n#\n*/"), [])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800339
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700340 def test_line_cross(self):
341 self.assertEqual(self.get_tokens("first\nsecond"), ["first", "second"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800342
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700343 def test_line_cross_line_col(self):
344 self.assertEqual(self.get_tokens("first second\n third", True),
345 [("first", 1, 1), ("second", 1, 7), ("third", 2, 3)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800346
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700347 def test_comment_line_col(self):
348 self.assertEqual(self.get_tokens("boo /* what the\nhell */", True),
349 [("boo", 1, 1)])
Tao Baod7db5942015-01-28 10:07:51 -0800350
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700351 def test_escapes(self):
352 self.assertEqual(self.get_tokens("an \\\n example", True),
353 [("an", 1, 1), ("example", 2, 2)])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800354
355
Tao Baod7db5942015-01-28 10:07:51 -0800356################################################################################
357################################################################################
358##### #####
359##### C P P E X P R E S S I O N S #####
360##### #####
361################################################################################
362################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800363
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800364
Tao Baod7db5942015-01-28 10:07:51 -0800365class CppExpr(object):
366 """A class that models the condition of #if directives into an expr tree.
367
368 Each node in the tree is of the form (op, arg) or (op, arg1, arg2) where
369 "op" is a string describing the operation
370 """
371
372 unaries = ["!", "~"]
373 binaries = ["+", "-", "<", "<=", ">=", ">", "&&", "||", "*", "/", "%",
374 "&", "|", "^", "<<", ">>", "==", "!=", "?", ":"]
Elliott Hughes1198fd32013-11-21 11:12:34 -0800375 precedences = {
376 "?": 1, ":": 1,
377 "||": 2,
378 "&&": 3,
379 "|": 4,
380 "^": 5,
381 "&": 6,
382 "==": 7, "!=": 7,
383 "<": 8, "<=": 8, ">": 8, ">=": 8,
384 "<<": 9, ">>": 9,
385 "+": 10, "-": 10,
386 "*": 11, "/": 11, "%": 11,
387 "!": 12, "~": 12
388 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800389
390 def __init__(self, tokens):
Tao Baod7db5942015-01-28 10:07:51 -0800391 """Initialize a CppExpr. 'tokens' must be a CppToken list."""
392 self.tokens = tokens
393 self._num_tokens = len(tokens)
394 self._index = 0
395
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800396 if debugCppExpr:
Christopher Ferrisac7ec112021-04-19 13:50:16 -0700397 print("CppExpr: trying to parse %s" % repr(tokens))
Elliott Hughes40596aa2013-11-05 14:54:29 -0800398 self.expr = self.parseExpression(0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800399 if debugCppExpr:
Christopher Ferrisac7ec112021-04-19 13:50:16 -0700400 print("CppExpr: got " + repr(self.expr))
Tao Baod7db5942015-01-28 10:07:51 -0800401 if self._index != self._num_tokens:
402 self.throw(BadExpectedToken, "crap at end of input (%d != %d): %s"
403 % (self._index, self._num_tokens, repr(tokens)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800404
Elliott Hughes40596aa2013-11-05 14:54:29 -0800405 def throw(self, exception, msg):
Tao Baod7db5942015-01-28 10:07:51 -0800406 if self._index < self._num_tokens:
407 tok = self.tokens[self._index]
Christopher Ferrisac7ec112021-04-19 13:50:16 -0700408 print("%d:%d: %s" % (tok.location.line, tok.location.column, msg))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800409 else:
Christopher Ferrisac7ec112021-04-19 13:50:16 -0700410 print("EOF: %s" % msg)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800411 raise exception(msg)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800412
Elliott Hughes40596aa2013-11-05 14:54:29 -0800413 def expectId(self, id):
Tao Baod7db5942015-01-28 10:07:51 -0800414 """Check that a given token id is at the current position."""
415 token = self.tokens[self._index]
416 if self._index >= self._num_tokens or token.id != id:
417 self.throw(BadExpectedToken,
418 "### expecting '%s' in expression, got '%s'" % (
419 id, token.id))
420 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800421
422 def is_decimal(self):
Tao Baod7db5942015-01-28 10:07:51 -0800423 token = self.tokens[self._index].id
424 if token[-1] in "ULul":
425 token = token[:-1]
426 try:
427 val = int(token, 10)
428 self._index += 1
429 return ('int', val)
430 except ValueError:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800431 return None
432
Tao Baod7db5942015-01-28 10:07:51 -0800433 def is_octal(self):
434 token = self.tokens[self._index].id
435 if token[-1] in "ULul":
436 token = token[:-1]
437 if len(token) < 2 or token[0] != '0':
438 return None
439 try:
440 val = int(token, 8)
441 self._index += 1
442 return ('oct', val)
443 except ValueError:
444 return None
445
446 def is_hexadecimal(self):
447 token = self.tokens[self._index].id
448 if token[-1] in "ULul":
449 token = token[:-1]
450 if len(token) < 3 or (token[:2] != '0x' and token[:2] != '0X'):
451 return None
452 try:
453 val = int(token, 16)
454 self._index += 1
455 return ('hex', val)
456 except ValueError:
457 return None
458
459 def is_integer(self):
460 if self.tokens[self._index].kind != TokenKind.LITERAL:
461 return None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800462
Elliott Hughes40596aa2013-11-05 14:54:29 -0800463 c = self.is_hexadecimal()
Tao Baod7db5942015-01-28 10:07:51 -0800464 if c:
465 return c
466
467 c = self.is_octal()
468 if c:
469 return c
470
471 c = self.is_decimal()
472 if c:
473 return c
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800474
475 return None
476
Elliott Hughes40596aa2013-11-05 14:54:29 -0800477 def is_number(self):
Tao Baod7db5942015-01-28 10:07:51 -0800478 t = self.tokens[self._index]
479 if t.id == tokMINUS and self._index + 1 < self._num_tokens:
480 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800481 c = self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800482 if c:
Tao Baod7db5942015-01-28 10:07:51 -0800483 op, val = c
Elliott Hughes40596aa2013-11-05 14:54:29 -0800484 return (op, -val)
Tao Baod7db5942015-01-28 10:07:51 -0800485 if t.id == tokPLUS and self._index + 1 < self._num_tokens:
486 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800487 c = self.is_integer()
Tao Baod7db5942015-01-28 10:07:51 -0800488 if c:
489 return c
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800490
Elliott Hughes40596aa2013-11-05 14:54:29 -0800491 return self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800492
Elliott Hughes40596aa2013-11-05 14:54:29 -0800493 def is_defined(self):
Tao Baod7db5942015-01-28 10:07:51 -0800494 t = self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800495 if t.id != tokDEFINED:
496 return None
497
Tao Baod7db5942015-01-28 10:07:51 -0800498 # We have the defined keyword, check the rest.
499 self._index += 1
500 used_parens = False
501 if (self._index < self._num_tokens and
502 self.tokens[self._index].id == tokLPAREN):
503 used_parens = True
504 self._index += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800505
Tao Baod7db5942015-01-28 10:07:51 -0800506 if self._index >= self._num_tokens:
507 self.throw(BadExpectedToken,
508 "### 'defined' must be followed by macro name or left "
509 "paren")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800510
Tao Baod7db5942015-01-28 10:07:51 -0800511 t = self.tokens[self._index]
512 if t.kind != TokenKind.IDENTIFIER:
513 self.throw(BadExpectedToken,
514 "### 'defined' must be followed by macro name")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800515
Tao Baod7db5942015-01-28 10:07:51 -0800516 self._index += 1
Elliott Hughesfddbafd2014-05-01 10:17:27 -0700517 if used_parens:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800518 self.expectId(tokRPAREN)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800519
Tao Baod7db5942015-01-28 10:07:51 -0800520 return ("defined", t.id)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800521
Elliott Hughes40596aa2013-11-05 14:54:29 -0800522 def is_call_or_ident(self):
Tao Baod7db5942015-01-28 10:07:51 -0800523 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800524 return None
525
Tao Baod7db5942015-01-28 10:07:51 -0800526 t = self.tokens[self._index]
527 if t.kind != TokenKind.IDENTIFIER:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800528 return None
529
Tao Baod7db5942015-01-28 10:07:51 -0800530 name = t.id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800531
Tao Baod7db5942015-01-28 10:07:51 -0800532 self._index += 1
533 if (self._index >= self._num_tokens or
534 self.tokens[self._index].id != tokLPAREN):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800535 return ("ident", name)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800536
Tao Baod7db5942015-01-28 10:07:51 -0800537 params = []
538 depth = 1
539 self._index += 1
540 j = self._index
541 while self._index < self._num_tokens:
542 id = self.tokens[self._index].id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800543 if id == tokLPAREN:
544 depth += 1
545 elif depth == 1 and (id == tokCOMMA or id == tokRPAREN):
Tao Baod7db5942015-01-28 10:07:51 -0800546 k = self._index
547 param = self.tokens[j:k]
Elliott Hughes40596aa2013-11-05 14:54:29 -0800548 params.append(param)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800549 if id == tokRPAREN:
550 break
Tao Baod7db5942015-01-28 10:07:51 -0800551 j = self._index + 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800552 elif id == tokRPAREN:
553 depth -= 1
Tao Baod7db5942015-01-28 10:07:51 -0800554 self._index += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800555
Tao Baod7db5942015-01-28 10:07:51 -0800556 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800557 return None
558
Tao Baod7db5942015-01-28 10:07:51 -0800559 self._index += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800560 return ("call", (name, params))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800561
Tao Baod7db5942015-01-28 10:07:51 -0800562 # Implements the "precedence climbing" algorithm from
563 # http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm.
564 # The "classic" algorithm would be fine if we were using a tool to
565 # generate the parser, but we're not. Dijkstra's "shunting yard"
566 # algorithm hasn't been necessary yet.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800567
Elliott Hughes40596aa2013-11-05 14:54:29 -0800568 def parseExpression(self, minPrecedence):
Tao Baod7db5942015-01-28 10:07:51 -0800569 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800570 return None
571
Elliott Hughes40596aa2013-11-05 14:54:29 -0800572 node = self.parsePrimary()
Tao Baod7db5942015-01-28 10:07:51 -0800573 while (self.token() and self.isBinary(self.token()) and
574 self.precedence(self.token()) >= minPrecedence):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800575 op = self.token()
576 self.nextToken()
577 rhs = self.parseExpression(self.precedence(op) + 1)
578 node = (op.id, node, rhs)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800579
Elliott Hughes40596aa2013-11-05 14:54:29 -0800580 return node
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800581
Elliott Hughes40596aa2013-11-05 14:54:29 -0800582 def parsePrimary(self):
583 op = self.token()
584 if self.isUnary(op):
585 self.nextToken()
586 return (op.id, self.parseExpression(self.precedence(op)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800587
Elliott Hughes40596aa2013-11-05 14:54:29 -0800588 primary = None
589 if op.id == tokLPAREN:
590 self.nextToken()
591 primary = self.parseExpression(0)
592 self.expectId(tokRPAREN)
Elliott Hughes1198fd32013-11-21 11:12:34 -0800593 elif op.id == "?":
594 self.nextToken()
595 primary = self.parseExpression(0)
596 self.expectId(":")
Tao Baod7db5942015-01-28 10:07:51 -0800597 elif op.id == '+' or op.id == '-' or op.kind == TokenKind.LITERAL:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800598 primary = self.is_number()
Tao Baod7db5942015-01-28 10:07:51 -0800599 # Checking for 'defined' needs to come first now because 'defined' is
600 # recognized as IDENTIFIER.
Elliott Hughes40596aa2013-11-05 14:54:29 -0800601 elif op.id == tokDEFINED:
602 primary = self.is_defined()
Tao Baod7db5942015-01-28 10:07:51 -0800603 elif op.kind == TokenKind.IDENTIFIER:
604 primary = self.is_call_or_ident()
Elliott Hughes40596aa2013-11-05 14:54:29 -0800605 else:
Tao Baod7db5942015-01-28 10:07:51 -0800606 self.throw(BadExpectedToken,
607 "didn't expect to see a %s in factor" % (
608 self.tokens[self._index].id))
609 return primary
Elliott Hughes40596aa2013-11-05 14:54:29 -0800610
611 def isBinary(self, token):
612 return token.id in self.binaries
613
Elliott Hughes40596aa2013-11-05 14:54:29 -0800614 def isUnary(self, token):
615 return token.id in self.unaries
616
Elliott Hughes40596aa2013-11-05 14:54:29 -0800617 def precedence(self, token):
618 return self.precedences.get(token.id)
619
Elliott Hughes40596aa2013-11-05 14:54:29 -0800620 def token(self):
Tao Baod7db5942015-01-28 10:07:51 -0800621 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800622 return None
Tao Baod7db5942015-01-28 10:07:51 -0800623 return self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800624
Elliott Hughes40596aa2013-11-05 14:54:29 -0800625 def nextToken(self):
Tao Baod7db5942015-01-28 10:07:51 -0800626 self._index += 1
627 if self._index >= self._num_tokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800628 return None
Tao Baod7db5942015-01-28 10:07:51 -0800629 return self.tokens[self._index]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800630
Elliott Hughes40596aa2013-11-05 14:54:29 -0800631 def dump_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800632 op = e[0]
633 line = "(" + op
634 if op == "int":
635 line += " %d)" % e[1]
Tao Baod7db5942015-01-28 10:07:51 -0800636 elif op == "oct":
637 line += " 0%o)" % e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800638 elif op == "hex":
639 line += " 0x%x)" % e[1]
640 elif op == "ident":
641 line += " %s)" % e[1]
642 elif op == "defined":
643 line += " %s)" % e[1]
644 elif op == "call":
645 arg = e[1]
646 line += " %s [" % arg[0]
647 prefix = ""
648 for param in arg[1]:
649 par = ""
650 for tok in param:
651 par += str(tok)
652 line += "%s%s" % (prefix, par)
653 prefix = ","
654 line += "])"
655 elif op in CppExpr.unaries:
656 line += " %s)" % self.dump_node(e[1])
657 elif op in CppExpr.binaries:
658 line += " %s %s)" % (self.dump_node(e[1]), self.dump_node(e[2]))
659 else:
660 line += " ?%s)" % repr(e[1])
661
662 return line
663
664 def __repr__(self):
665 return self.dump_node(self.expr)
666
Elliott Hughes40596aa2013-11-05 14:54:29 -0800667 def source_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800668 op = e[0]
669 if op == "int":
670 return "%d" % e[1]
671 if op == "hex":
672 return "0x%x" % e[1]
Tao Baod7db5942015-01-28 10:07:51 -0800673 if op == "oct":
674 return "0%o" % e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800675 if op == "ident":
676 # XXX: should try to expand
677 return e[1]
678 if op == "defined":
679 return "defined(%s)" % e[1]
680
Tao Baod7db5942015-01-28 10:07:51 -0800681 prec = CppExpr.precedences.get(op, 1000)
682 arg = e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800683 if op in CppExpr.unaries:
684 arg_src = self.source_node(arg)
Tao Baod7db5942015-01-28 10:07:51 -0800685 arg_op = arg[0]
686 arg_prec = CppExpr.precedences.get(arg_op, 1000)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800687 if arg_prec < prec:
688 return "!(" + arg_src + ")"
689 else:
690 return "!" + arg_src
691 if op in CppExpr.binaries:
Tao Baod7db5942015-01-28 10:07:51 -0800692 arg2 = e[2]
693 arg1_op = arg[0]
694 arg2_op = arg2[0]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800695 arg1_src = self.source_node(arg)
696 arg2_src = self.source_node(arg2)
Tao Baod7db5942015-01-28 10:07:51 -0800697 if CppExpr.precedences.get(arg1_op, 1000) < prec:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800698 arg1_src = "(%s)" % arg1_src
Tao Baod7db5942015-01-28 10:07:51 -0800699 if CppExpr.precedences.get(arg2_op, 1000) < prec:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800700 arg2_src = "(%s)" % arg2_src
701
702 return "%s %s %s" % (arg1_src, op, arg2_src)
703 return "???"
704
705 def __str__(self):
706 return self.source_node(self.expr)
707
Tao Baod7db5942015-01-28 10:07:51 -0800708 @staticmethod
709 def int_node(e):
710 if e[0] in ["int", "oct", "hex"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800711 return e[1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800712 else:
713 return None
714
715 def toInt(self):
716 return self.int_node(self.expr)
717
Tao Baod7db5942015-01-28 10:07:51 -0800718 def optimize_node(self, e, macros=None):
719 if macros is None:
720 macros = {}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800721 op = e[0]
Tao Baod7db5942015-01-28 10:07:51 -0800722
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800723 if op == "defined":
Elliott Hughes40596aa2013-11-05 14:54:29 -0800724 op, name = e
Christopher Ferrisa2142d22021-04-20 17:30:04 -0700725 if name in macros:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800726 if macros[name] == kCppUndefinedMacro:
727 return ("int", 0)
728 else:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700729 try:
730 value = int(macros[name])
731 return ("int", value)
Tao Baod7db5942015-01-28 10:07:51 -0800732 except ValueError:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700733 return ("defined", macros[name])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800734
735 if kernel_remove_config_macros and name.startswith("CONFIG_"):
736 return ("int", 0)
737
Elliott Hughes40596aa2013-11-05 14:54:29 -0800738 return e
739
740 elif op == "ident":
741 op, name = e
Christopher Ferrisa2142d22021-04-20 17:30:04 -0700742 if name in macros:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800743 try:
744 value = int(macros[name])
745 expanded = ("int", value)
Tao Baod7db5942015-01-28 10:07:51 -0800746 except ValueError:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800747 expanded = ("ident", macros[name])
748 return self.optimize_node(expanded, macros)
749 return e
750
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800751 elif op == "!":
752 op, v = e
753 v = self.optimize_node(v, macros)
754 if v[0] == "int":
755 if v[1] == 0:
756 return ("int", 1)
757 else:
758 return ("int", 0)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800759 return ('!', v)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800760
761 elif op == "&&":
762 op, l, r = e
Tao Baod7db5942015-01-28 10:07:51 -0800763 l = self.optimize_node(l, macros)
764 r = self.optimize_node(r, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800765 li = self.int_node(l)
766 ri = self.int_node(r)
Tao Baod7db5942015-01-28 10:07:51 -0800767 if li is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800768 if li == 0:
769 return ("int", 0)
770 else:
771 return r
Tao Baod7db5942015-01-28 10:07:51 -0800772 elif ri is not None:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800773 if ri == 0:
774 return ("int", 0)
775 else:
776 return l
777 return (op, l, r)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800778
779 elif op == "||":
780 op, l, r = e
Tao Baod7db5942015-01-28 10:07:51 -0800781 l = self.optimize_node(l, macros)
782 r = self.optimize_node(r, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800783 li = self.int_node(l)
784 ri = self.int_node(r)
Tao Baod7db5942015-01-28 10:07:51 -0800785 if li is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800786 if li == 0:
787 return r
788 else:
789 return ("int", 1)
Tao Baod7db5942015-01-28 10:07:51 -0800790 elif ri is not None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800791 if ri == 0:
792 return l
793 else:
794 return ("int", 1)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800795 return (op, l, r)
796
797 else:
798 return e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800799
Tao Baod7db5942015-01-28 10:07:51 -0800800 def optimize(self, macros=None):
801 if macros is None:
802 macros = {}
Elliott Hughes40596aa2013-11-05 14:54:29 -0800803 self.expr = self.optimize_node(self.expr, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800804
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700805class CppExprTest(unittest.TestCase):
806 """CppExpr unit tests."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800807
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700808 def get_expr(self, expr):
809 return repr(CppExpr(CppStringTokenizer(expr).tokens))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800810
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700811 def test_cpp_expr(self):
812 self.assertEqual(self.get_expr("0"), "(int 0)")
813 self.assertEqual(self.get_expr("1"), "(int 1)")
814 self.assertEqual(self.get_expr("-5"), "(int -5)")
815 self.assertEqual(self.get_expr("+1"), "(int 1)")
816 self.assertEqual(self.get_expr("0U"), "(int 0)")
817 self.assertEqual(self.get_expr("015"), "(oct 015)")
818 self.assertEqual(self.get_expr("015l"), "(oct 015)")
819 self.assertEqual(self.get_expr("0x3e"), "(hex 0x3e)")
820 self.assertEqual(self.get_expr("(0)"), "(int 0)")
821 self.assertEqual(self.get_expr("1 && 1"), "(&& (int 1) (int 1))")
822 self.assertEqual(self.get_expr("1 && 0"), "(&& (int 1) (int 0))")
823 self.assertEqual(self.get_expr("EXAMPLE"), "(ident EXAMPLE)")
824 self.assertEqual(self.get_expr("EXAMPLE - 3"),
825 "(- (ident EXAMPLE) (int 3))")
826 self.assertEqual(self.get_expr("defined(EXAMPLE)"),
827 "(defined EXAMPLE)")
828 self.assertEqual(self.get_expr("defined ( EXAMPLE ) "),
829 "(defined EXAMPLE)")
830 self.assertEqual(self.get_expr("!defined(EXAMPLE)"),
831 "(! (defined EXAMPLE))")
832 self.assertEqual(self.get_expr("defined(ABC) || defined(BINGO)"),
833 "(|| (defined ABC) (defined BINGO))")
834 self.assertEqual(self.get_expr("FOO(BAR,5)"), "(call FOO [BAR,5])")
835 self.assertEqual(self.get_expr("A == 1 || defined(B)"),
836 "(|| (== (ident A) (int 1)) (defined B))")
Tao Baod7db5942015-01-28 10:07:51 -0800837
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700838 def get_expr_optimize(self, expr, macros=None):
839 if macros is None:
840 macros = {}
841 e = CppExpr(CppStringTokenizer(expr).tokens)
842 e.optimize(macros)
843 return repr(e)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800844
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700845 def test_cpp_expr_optimize(self):
846 self.assertEqual(self.get_expr_optimize("0"), "(int 0)")
847 self.assertEqual(self.get_expr_optimize("1"), "(int 1)")
848 self.assertEqual(self.get_expr_optimize("1 && 1"), "(int 1)")
849 self.assertEqual(self.get_expr_optimize("1 && +1"), "(int 1)")
850 self.assertEqual(self.get_expr_optimize("0x1 && 01"), "(oct 01)")
851 self.assertEqual(self.get_expr_optimize("1 && 0"), "(int 0)")
852 self.assertEqual(self.get_expr_optimize("0 && 1"), "(int 0)")
853 self.assertEqual(self.get_expr_optimize("0 && 0"), "(int 0)")
854 self.assertEqual(self.get_expr_optimize("1 || 1"), "(int 1)")
855 self.assertEqual(self.get_expr_optimize("1 || 0"), "(int 1)")
856 self.assertEqual(self.get_expr_optimize("0 || 1"), "(int 1)")
857 self.assertEqual(self.get_expr_optimize("0 || 0"), "(int 0)")
858 self.assertEqual(self.get_expr_optimize("A"), "(ident A)")
859 self.assertEqual(self.get_expr_optimize("A", {"A": 1}), "(int 1)")
860 self.assertEqual(self.get_expr_optimize("A || B", {"A": 1}), "(int 1)")
861 self.assertEqual(self.get_expr_optimize("A || B", {"B": 1}), "(int 1)")
862 self.assertEqual(self.get_expr_optimize("A && B", {"A": 1}), "(ident B)")
863 self.assertEqual(self.get_expr_optimize("A && B", {"B": 1}), "(ident A)")
864 self.assertEqual(self.get_expr_optimize("A && B"), "(&& (ident A) (ident B))")
865 self.assertEqual(self.get_expr_optimize("EXAMPLE"), "(ident EXAMPLE)")
866 self.assertEqual(self.get_expr_optimize("EXAMPLE - 3"), "(- (ident EXAMPLE) (int 3))")
867 self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)"), "(defined EXAMPLE)")
868 self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)",
869 {"EXAMPLE": "XOWOE"}),
870 "(defined XOWOE)")
871 self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)",
872 {"EXAMPLE": kCppUndefinedMacro}),
873 "(int 0)")
874 self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)"), "(! (defined EXAMPLE))")
875 self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)",
876 {"EXAMPLE": "XOWOE"}),
877 "(! (defined XOWOE))")
878 self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)",
879 {"EXAMPLE": kCppUndefinedMacro}),
880 "(int 1)")
881 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)"),
Tao Baod7db5942015-01-28 10:07:51 -0800882 "(|| (defined A) (defined B))")
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700883 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
884 {"A": "1"}),
885 "(int 1)")
886 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
887 {"B": "1"}),
888 "(int 1)")
889 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
890 {"B": kCppUndefinedMacro}),
891 "(defined A)")
892 self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
893 {"A": kCppUndefinedMacro,
894 "B": kCppUndefinedMacro}),
895 "(int 0)")
896 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)"),
897 "(&& (defined A) (defined B))")
898 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
899 {"A": "1"}),
900 "(defined B)")
901 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
902 {"B": "1"}),
903 "(defined A)")
904 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
905 {"B": kCppUndefinedMacro}),
906 "(int 0)")
907 self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
908 {"A": kCppUndefinedMacro}),
909 "(int 0)")
910 self.assertEqual(self.get_expr_optimize("A == 1 || defined(B)"),
911 "(|| (== (ident A) (int 1)) (defined B))")
912 self.assertEqual(self.get_expr_optimize(
913 "defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)",
914 {"__KERNEL__": kCppUndefinedMacro}),
915 "(|| (! (defined __GLIBC__)) (< (ident __GLIBC__) (int 2)))")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800916
Christopher Ferrisf2484aa2018-10-25 19:41:45 -0700917 def get_expr_string(self, expr):
918 return str(CppExpr(CppStringTokenizer(expr).tokens))
919
920 def test_cpp_expr_string(self):
921 self.assertEqual(self.get_expr_string("0"), "0")
922 self.assertEqual(self.get_expr_string("1"), "1")
923 self.assertEqual(self.get_expr_string("1 && 1"), "1 && 1")
924 self.assertEqual(self.get_expr_string("1 && 0"), "1 && 0")
925 self.assertEqual(self.get_expr_string("0 && 1"), "0 && 1")
926 self.assertEqual(self.get_expr_string("0 && 0"), "0 && 0")
927 self.assertEqual(self.get_expr_string("1 || 1"), "1 || 1")
928 self.assertEqual(self.get_expr_string("1 || 0"), "1 || 0")
929 self.assertEqual(self.get_expr_string("0 || 1"), "0 || 1")
930 self.assertEqual(self.get_expr_string("0 || 0"), "0 || 0")
931 self.assertEqual(self.get_expr_string("EXAMPLE"), "EXAMPLE")
932 self.assertEqual(self.get_expr_string("EXAMPLE - 3"), "EXAMPLE - 3")
933 self.assertEqual(self.get_expr_string("defined(EXAMPLE)"), "defined(EXAMPLE)")
934 self.assertEqual(self.get_expr_string("defined EXAMPLE"), "defined(EXAMPLE)")
935 self.assertEqual(self.get_expr_string("A == 1 || defined(B)"), "A == 1 || defined(B)")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800936
937
Tao Baod7db5942015-01-28 10:07:51 -0800938################################################################################
939################################################################################
940##### #####
941##### C P P B L O C K #####
942##### #####
943################################################################################
944################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800945
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800946
Tao Baod7db5942015-01-28 10:07:51 -0800947class Block(object):
948 """A class used to model a block of input source text.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800949
Tao Baod7db5942015-01-28 10:07:51 -0800950 There are two block types:
951 - directive blocks: contain the tokens of a single pre-processor
952 directive (e.g. #if)
953 - text blocks, contain the tokens of non-directive blocks
954
955 The cpp parser class below will transform an input source file into a list
956 of Block objects (grouped in a BlockList object for convenience)
957 """
958
959 def __init__(self, tokens, directive=None, lineno=0, identifier=None):
960 """Initialize a new block, if 'directive' is None, it is a text block.
961
962 NOTE: This automatically converts '#ifdef MACRO' into
963 '#if defined(MACRO)' and '#ifndef MACRO' into '#if !defined(MACRO)'.
964 """
965
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800966 if directive == "ifdef":
967 tok = Token()
Tao Baod7db5942015-01-28 10:07:51 -0800968 tok.id = tokDEFINED
969 tokens = [tok] + tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800970 directive = "if"
971
972 elif directive == "ifndef":
973 tok1 = Token()
974 tok2 = Token()
Tao Baod7db5942015-01-28 10:07:51 -0800975 tok1.id = tokNOT
976 tok2.id = tokDEFINED
977 tokens = [tok1, tok2] + tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800978 directive = "if"
979
Tao Baod7db5942015-01-28 10:07:51 -0800980 self.tokens = tokens
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800981 self.directive = directive
Tao Baod7db5942015-01-28 10:07:51 -0800982 self.define_id = identifier
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800983 if lineno > 0:
984 self.lineno = lineno
985 else:
Tao Baod7db5942015-01-28 10:07:51 -0800986 self.lineno = self.tokens[0].location.line
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800987
988 if self.isIf():
Tao Baod7db5942015-01-28 10:07:51 -0800989 self.expr = CppExpr(self.tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800990
991 def isDirective(self):
Tao Baod7db5942015-01-28 10:07:51 -0800992 """Return True iff this is a directive block."""
993 return self.directive is not None
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800994
995 def isConditional(self):
Tao Baod7db5942015-01-28 10:07:51 -0800996 """Return True iff this is a conditional directive block."""
997 return self.directive in ["if", "ifdef", "ifndef", "else", "elif",
998 "endif"]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800999
1000 def isDefine(self):
Tao Baod7db5942015-01-28 10:07:51 -08001001 """Return the macro name in a #define directive, or None otherwise."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001002 if self.directive != "define":
1003 return None
Tao Baod7db5942015-01-28 10:07:51 -08001004 return self.define_id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001005
1006 def isIf(self):
Tao Baod7db5942015-01-28 10:07:51 -08001007 """Return True iff this is an #if-like directive block."""
1008 return self.directive in ["if", "ifdef", "ifndef", "elif"]
1009
1010 def isEndif(self):
1011 """Return True iff this is an #endif directive block."""
1012 return self.directive == "endif"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001013
1014 def isInclude(self):
Tao Baod7db5942015-01-28 10:07:51 -08001015 """Check whether this is a #include directive.
1016
1017 If true, returns the corresponding file name (with brackets or
1018 double-qoutes). None otherwise.
1019 """
1020
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001021 if self.directive != "include":
1022 return None
Tao Baod7db5942015-01-28 10:07:51 -08001023 return ''.join([str(x) for x in self.tokens])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001024
Tao Baod7db5942015-01-28 10:07:51 -08001025 @staticmethod
1026 def format_blocks(tokens, indent=0):
1027 """Return the formatted lines of strings with proper indentation."""
1028 newline = True
1029 result = []
1030 buf = ''
1031 i = 0
1032 while i < len(tokens):
1033 t = tokens[i]
1034 if t.id == '{':
1035 buf += ' {'
1036 result.append(strip_space(buf))
Christopher Ferris658b16f2019-01-08 14:58:07 -08001037 # Do not indent if this is extern "C" {
1038 if i < 2 or tokens[i-2].id != 'extern' or tokens[i-1].id != '"C"':
1039 indent += 2
Tao Baod7db5942015-01-28 10:07:51 -08001040 buf = ''
1041 newline = True
1042 elif t.id == '}':
Christopher Ferris658b16f2019-01-08 14:58:07 -08001043 if indent >= 2:
1044 indent -= 2
Tao Baod7db5942015-01-28 10:07:51 -08001045 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."""
Christopher Ferrisac7ec112021-04-19 13:50:16 -07001182 print('##### BEGIN #####')
Tao Baod7db5942015-01-28 10:07:51 -08001183 for i, b in enumerate(self.blocks):
Christopher Ferrisac7ec112021-04-19 13:50:16 -07001184 print('### BLOCK %d ###' % i)
1185 print(b)
1186 print('##### END #####')
Tao Baod7db5942015-01-28 10:07:51 -08001187
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
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -07001198 def removeStructs(self, structs):
1199 """Remove structs."""
1200 for b in self.blocks:
1201 # Have to look in each block for a top-level struct definition.
1202 if b.directive:
1203 continue
1204 num_tokens = len(b.tokens)
1205 # A struct definition has at least 5 tokens:
1206 # struct
1207 # ident
1208 # {
1209 # }
1210 # ;
1211 if num_tokens < 5:
1212 continue
1213 # This is a simple struct finder, it might fail if a top-level
1214 # structure has an #if type directives that confuses the algorithm
1215 # for finding th end of the structure. Or if there is another
1216 # structure definition embedded in the structure.
1217 i = 0
1218 while i < num_tokens - 2:
1219 if (b.tokens[i].kind != TokenKind.KEYWORD or
1220 b.tokens[i].id != "struct"):
1221 i += 1
1222 continue
1223 if (b.tokens[i + 1].kind == TokenKind.IDENTIFIER and
1224 b.tokens[i + 2].kind == TokenKind.PUNCTUATION and
1225 b.tokens[i + 2].id == "{" and b.tokens[i + 1].id in structs):
1226 # Search forward for the end of the structure.
1227 # Very simple search, look for } and ; tokens. If something
1228 # more complicated is needed we can add it later.
1229 j = i + 3
1230 while j < num_tokens - 1:
1231 if (b.tokens[j].kind == TokenKind.PUNCTUATION and
1232 b.tokens[j].id == "}" and
1233 b.tokens[j + 1].kind == TokenKind.PUNCTUATION and
1234 b.tokens[j + 1].id == ";"):
1235 b.tokens = b.tokens[0:i] + b.tokens[j + 2:num_tokens]
1236 num_tokens = len(b.tokens)
1237 j = i
1238 break
1239 j += 1
1240 i = j
1241 continue
1242 i += 1
1243
Tao Baod7db5942015-01-28 10:07:51 -08001244 def optimizeAll(self, macros):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001245 self.optimizeMacros(macros)
1246 self.optimizeIf01()
1247 return
1248
1249 def findIncludes(self):
Tao Baod7db5942015-01-28 10:07:51 -08001250 """Return the list of included files in a BlockList."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001251 result = []
1252 for b in self.blocks:
1253 i = b.isInclude()
1254 if i:
1255 result.append(i)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001256 return result
1257
Tao Baod7db5942015-01-28 10:07:51 -08001258 def write(self, out):
Tao Baod7db5942015-01-28 10:07:51 -08001259 indent = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001260 for b in self.blocks:
Elliott Hughes96c1db72017-05-25 13:48:01 -07001261 indent = b.write(out, indent)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001262
Elliott Hughes64f355f2017-08-30 16:10:24 -07001263 def removeVarsAndFuncs(self, keep):
Tao Baod7db5942015-01-28 10:07:51 -08001264 """Remove variable and function declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001265
Tao Baod7db5942015-01-28 10:07:51 -08001266 All extern and static declarations corresponding to variable and
1267 function declarations are removed. We only accept typedefs and
1268 enum/structs/union declarations.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001269
Christopher Ferris658b16f2019-01-08 14:58:07 -08001270 In addition, remove any macros expanding in the headers. Usually,
1271 these macros are static inline functions, which is why they are
1272 removed.
1273
Tao Baod7db5942015-01-28 10:07:51 -08001274 However, we keep the definitions corresponding to the set of known
Elliott Hughes64f355f2017-08-30 16:10:24 -07001275 static inline functions in the set 'keep', which is useful
Tao Baod7db5942015-01-28 10:07:51 -08001276 for optimized byteorder swap functions and stuff like that.
1277 """
1278
Christopher Ferris658b16f2019-01-08 14:58:07 -08001279 # state = NORMAL => normal (i.e. LN + spaces)
1280 # state = OTHER_DECL => typedef/struct encountered, ends with ";"
1281 # state = VAR_DECL => var declaration encountered, ends with ";"
1282 # state = FUNC_DECL => func declaration encountered, ends with "}"
1283 NORMAL = 0
1284 OTHER_DECL = 1
1285 VAR_DECL = 2
1286 FUNC_DECL = 3
Tao Baod7db5942015-01-28 10:07:51 -08001287
Christopher Ferris658b16f2019-01-08 14:58:07 -08001288 state = NORMAL
Tao Baod7db5942015-01-28 10:07:51 -08001289 depth = 0
Christopher Ferris658b16f2019-01-08 14:58:07 -08001290 blocksToKeep = []
1291 blocksInProgress = []
1292 blocksOfDirectives = []
1293 ident = ""
1294 state_token = ""
1295 macros = set()
1296 for block in self.blocks:
1297 if block.isDirective():
1298 # Record all macros.
1299 if block.directive == 'define':
1300 macro_name = block.define_id
1301 paren_index = macro_name.find('(')
1302 if paren_index == -1:
1303 macros.add(macro_name)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001304 else:
Christopher Ferris658b16f2019-01-08 14:58:07 -08001305 macros.add(macro_name[0:paren_index])
1306 blocksInProgress.append(block)
1307 # If this is in a function/variable declaration, we might need
1308 # to emit the directives alone, so save them separately.
1309 blocksOfDirectives.append(block)
1310 continue
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001311
Christopher Ferris658b16f2019-01-08 14:58:07 -08001312 numTokens = len(block.tokens)
1313 lastTerminatorIndex = 0
1314 i = 0
1315 while i < numTokens:
1316 token_id = block.tokens[i].id
1317 terminator = False
1318 if token_id == '{':
1319 depth += 1
1320 if (i >= 2 and block.tokens[i-2].id == 'extern' and
1321 block.tokens[i-1].id == '"C"'):
1322 # For an extern "C" { pretend as though this is depth 0.
1323 depth -= 1
1324 elif token_id == '}':
1325 if depth > 0:
1326 depth -= 1
1327 if depth == 0:
1328 if state == OTHER_DECL:
1329 # Loop through until we hit the ';'
1330 i += 1
1331 while i < numTokens:
1332 if block.tokens[i].id == ';':
1333 token_id = ';'
1334 break
1335 i += 1
1336 # If we didn't hit the ';', just consider this the
1337 # terminator any way.
1338 terminator = True
1339 elif depth == 0:
1340 if token_id == ';':
1341 if state == NORMAL:
1342 blocksToKeep.extend(blocksInProgress)
1343 blocksInProgress = []
1344 blocksOfDirectives = []
1345 state = FUNC_DECL
1346 terminator = True
1347 elif (state == NORMAL and token_id == '(' and i >= 1 and
1348 block.tokens[i-1].kind == TokenKind.IDENTIFIER and
1349 block.tokens[i-1].id in macros):
1350 # This is a plain macro being expanded in the header
1351 # which needs to be removed.
1352 blocksToKeep.extend(blocksInProgress)
1353 if lastTerminatorIndex < i - 1:
1354 blocksToKeep.append(Block(block.tokens[lastTerminatorIndex:i-1]))
1355 blocksInProgress = []
1356 blocksOfDirectives = []
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001357
Christopher Ferris658b16f2019-01-08 14:58:07 -08001358 # Skip until we see the terminating ')'
1359 i += 1
1360 paren_depth = 1
1361 while i < numTokens:
1362 if block.tokens[i].id == ')':
1363 paren_depth -= 1
1364 if paren_depth == 0:
1365 break
1366 elif block.tokens[i].id == '(':
1367 paren_depth += 1
1368 i += 1
1369 lastTerminatorIndex = i + 1
1370 elif (state != FUNC_DECL and token_id == '(' and
1371 state_token != 'typedef'):
1372 blocksToKeep.extend(blocksInProgress)
1373 blocksInProgress = []
1374 blocksOfDirectives = []
1375 state = VAR_DECL
1376 elif state == NORMAL and token_id in ['struct', 'typedef',
1377 'enum', 'union',
1378 '__extension__']:
1379 state = OTHER_DECL
1380 state_token = token_id
1381 elif block.tokens[i].kind == TokenKind.IDENTIFIER:
1382 if state != VAR_DECL or ident == "":
1383 ident = token_id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001384
Christopher Ferris658b16f2019-01-08 14:58:07 -08001385 if terminator:
1386 if state != VAR_DECL and state != FUNC_DECL or ident in keep:
1387 blocksInProgress.append(Block(block.tokens[lastTerminatorIndex:i+1]))
1388 blocksToKeep.extend(blocksInProgress)
1389 else:
1390 # Only keep the directives found.
1391 blocksToKeep.extend(blocksOfDirectives)
1392 lastTerminatorIndex = i + 1
1393 blocksInProgress = []
1394 blocksOfDirectives = []
1395 state = NORMAL
1396 ident = ""
1397 state_token = ""
1398 i += 1
1399 if lastTerminatorIndex < numTokens:
1400 blocksInProgress.append(Block(block.tokens[lastTerminatorIndex:numTokens]))
1401 if len(blocksInProgress) > 0:
1402 blocksToKeep.extend(blocksInProgress)
1403 self.blocks = blocksToKeep
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001404
Tao Baod7db5942015-01-28 10:07:51 -08001405 def replaceTokens(self, replacements):
1406 """Replace tokens according to the given dict."""
Elliott Hughes64f355f2017-08-30 16:10:24 -07001407 extra_includes = []
Martin Storsjod32c8052010-12-08 11:38:14 +01001408 for b in self.blocks:
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001409 made_change = False
Tao Baod7db5942015-01-28 10:07:51 -08001410 if b.isInclude() is None:
Elliott Hughes64f355f2017-08-30 16:10:24 -07001411 i = 0
1412 while i < len(b.tokens):
1413 tok = b.tokens[i]
1414 if (tok.kind == TokenKind.KEYWORD and tok.id == 'struct'
1415 and (i + 2) < len(b.tokens) and b.tokens[i + 2].id == '{'):
1416 struct_name = b.tokens[i + 1].id
1417 if struct_name in kernel_struct_replacements:
1418 extra_includes.append("<bits/%s.h>" % struct_name)
1419 end = i + 2
Colin Cross68ec53e2021-06-29 14:13:02 -07001420 depth = 1
1421 while end < len(b.tokens) and depth > 0:
1422 if b.tokens[end].id == '}':
1423 depth -= 1
1424 elif b.tokens[end].id == '{':
1425 depth += 1
Elliott Hughes64f355f2017-08-30 16:10:24 -07001426 end += 1
Colin Cross68ec53e2021-06-29 14:13:02 -07001427 end += 1 # Swallow last '}'
Elliott Hughes64f355f2017-08-30 16:10:24 -07001428 while end < len(b.tokens) and b.tokens[end].id != ';':
1429 end += 1
1430 end += 1 # Swallow ';'
1431 # Remove these tokens. We'll replace them later with a #include block.
1432 b.tokens[i:end] = []
1433 made_change = True
1434 # We've just modified b.tokens, so revisit the current offset.
1435 continue
Tao Baod7db5942015-01-28 10:07:51 -08001436 if tok.kind == TokenKind.IDENTIFIER:
1437 if tok.id in replacements:
1438 tok.id = replacements[tok.id]
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001439 made_change = True
Elliott Hughes64f355f2017-08-30 16:10:24 -07001440 i += 1
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001441
Tao Baod7db5942015-01-28 10:07:51 -08001442 if b.isDefine() and b.define_id in replacements:
1443 b.define_id = replacements[b.define_id]
1444 made_change = True
1445
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001446 if made_change and b.isIf():
1447 # Keep 'expr' in sync with 'tokens'.
1448 b.expr = CppExpr(b.tokens)
Martin Storsjod32c8052010-12-08 11:38:14 +01001449
Elliott Hughes64f355f2017-08-30 16:10:24 -07001450 for extra_include in extra_includes:
1451 replacement = CppStringTokenizer(extra_include)
1452 self.blocks.insert(2, Block(replacement.tokens, directive='include'))
1453
1454
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001455
Tao Baod7db5942015-01-28 10:07:51 -08001456def strip_space(s):
1457 """Strip out redundant space in a given string."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001458
Tao Baod7db5942015-01-28 10:07:51 -08001459 # NOTE: It ought to be more clever to not destroy spaces in string tokens.
1460 replacements = {' . ': '.',
1461 ' [': '[',
1462 '[ ': '[',
1463 ' ]': ']',
1464 '( ': '(',
1465 ' )': ')',
1466 ' ,': ',',
1467 '# ': '#',
1468 ' ;': ';',
1469 '~ ': '~',
1470 ' -> ': '->'}
1471 result = s
1472 for r in replacements:
1473 result = result.replace(r, replacements[r])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001474
Tao Baod7db5942015-01-28 10:07:51 -08001475 # Remove the space between function name and the parenthesis.
1476 result = re.sub(r'(\w+) \(', r'\1(', result)
1477 return result
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001478
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001479
Tao Baod7db5942015-01-28 10:07:51 -08001480class BlockParser(object):
1481 """A class that converts an input source file into a BlockList object."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001482
Tao Baod7db5942015-01-28 10:07:51 -08001483 def __init__(self, tokzer=None):
1484 """Initialize a block parser.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001485
Tao Baod7db5942015-01-28 10:07:51 -08001486 The input source is provided through a Tokenizer object.
1487 """
1488 self._tokzer = tokzer
1489 self._parsed = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001490
Tao Baod7db5942015-01-28 10:07:51 -08001491 @property
1492 def parsed(self):
1493 return self._parsed
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001494
Tao Baod7db5942015-01-28 10:07:51 -08001495 @staticmethod
1496 def _short_extent(extent):
1497 return '%d:%d - %d:%d' % (extent.start.line, extent.start.column,
1498 extent.end.line, extent.end.column)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001499
Tao Baod7db5942015-01-28 10:07:51 -08001500 def getBlocks(self, tokzer=None):
1501 """Return all the blocks parsed."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001502
Tao Baod7db5942015-01-28 10:07:51 -08001503 def consume_extent(i, tokens, extent=None, detect_change=False):
1504 """Return tokens that belong to the given extent.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001505
Tao Baod7db5942015-01-28 10:07:51 -08001506 It parses all the tokens that follow tokens[i], until getting out
1507 of the extent. When detect_change is True, it may terminate early
1508 when detecting preprocessing directives inside the extent.
1509 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001510
Tao Baod7db5942015-01-28 10:07:51 -08001511 result = []
1512 if extent is None:
1513 extent = tokens[i].cursor.extent
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001514
Tao Baod7db5942015-01-28 10:07:51 -08001515 while i < len(tokens) and tokens[i].location in extent:
1516 t = tokens[i]
1517 if debugBlockParser:
Christopher Ferrisac7ec112021-04-19 13:50:16 -07001518 print(' ' * 2, t.id, t.kind, t.cursor.kind)
Tao Baod7db5942015-01-28 10:07:51 -08001519 if (detect_change and t.cursor.extent != extent and
1520 t.cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE):
1521 break
1522 result.append(t)
1523 i += 1
1524 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001525
Tao Baod7db5942015-01-28 10:07:51 -08001526 def consume_line(i, tokens):
1527 """Return tokens that follow tokens[i] in the same line."""
1528 result = []
1529 line = tokens[i].location.line
1530 while i < len(tokens) and tokens[i].location.line == line:
1531 if tokens[i].cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1532 break
1533 result.append(tokens[i])
1534 i += 1
1535 return (i, result)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001536
Tao Baod7db5942015-01-28 10:07:51 -08001537 if tokzer is None:
1538 tokzer = self._tokzer
1539 tokens = tokzer.tokens
1540
1541 blocks = []
1542 buf = []
1543 i = 0
1544
1545 while i < len(tokens):
1546 t = tokens[i]
1547 cursor = t.cursor
1548
1549 if debugBlockParser:
1550 print ("%d: Processing [%s], kind=[%s], cursor=[%s], "
1551 "extent=[%s]" % (t.location.line, t.spelling, t.kind,
1552 cursor.kind,
1553 self._short_extent(cursor.extent)))
1554
1555 if cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE:
1556 if buf:
1557 blocks.append(Block(buf))
1558 buf = []
1559
1560 j = i
1561 if j + 1 >= len(tokens):
1562 raise BadExpectedToken("### BAD TOKEN at %s" % (t.location))
1563 directive = tokens[j+1].id
1564
1565 if directive == 'define':
1566 if i+2 >= len(tokens):
1567 raise BadExpectedToken("### BAD TOKEN at %s" %
1568 (tokens[i].location))
1569
1570 # Skip '#' and 'define'.
1571 extent = tokens[i].cursor.extent
1572 i += 2
1573 id = ''
1574 # We need to separate the id from the remaining of
1575 # the line, especially for the function-like macro.
1576 if (i + 1 < len(tokens) and tokens[i+1].id == '(' and
1577 (tokens[i].location.column + len(tokens[i].spelling) ==
1578 tokens[i+1].location.column)):
1579 while i < len(tokens):
1580 id += tokens[i].id
1581 if tokens[i].spelling == ')':
1582 i += 1
1583 break
1584 i += 1
1585 else:
1586 id += tokens[i].id
1587 # Advance to the next token that follows the macro id
1588 i += 1
1589
1590 (i, ret) = consume_extent(i, tokens, extent=extent)
1591 blocks.append(Block(ret, directive=directive,
1592 lineno=t.location.line, identifier=id))
1593
1594 else:
1595 (i, ret) = consume_extent(i, tokens)
1596 blocks.append(Block(ret[2:], directive=directive,
1597 lineno=t.location.line))
1598
1599 elif cursor.kind == CursorKind.INCLUSION_DIRECTIVE:
1600 if buf:
1601 blocks.append(Block(buf))
1602 buf = []
1603 directive = tokens[i+1].id
1604 (i, ret) = consume_extent(i, tokens)
1605
1606 blocks.append(Block(ret[2:], directive=directive,
1607 lineno=t.location.line))
1608
1609 elif cursor.kind == CursorKind.VAR_DECL:
1610 if buf:
1611 blocks.append(Block(buf))
1612 buf = []
1613
1614 (i, ret) = consume_extent(i, tokens, detect_change=True)
1615 buf += ret
1616
1617 elif cursor.kind == CursorKind.FUNCTION_DECL:
1618 if buf:
1619 blocks.append(Block(buf))
1620 buf = []
1621
1622 (i, ret) = consume_extent(i, tokens, detect_change=True)
1623 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001624
1625 else:
Tao Baod7db5942015-01-28 10:07:51 -08001626 (i, ret) = consume_line(i, tokens)
1627 buf += ret
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001628
Tao Baod7db5942015-01-28 10:07:51 -08001629 if buf:
1630 blocks.append(Block(buf))
1631
1632 # _parsed=True indicates a successful parsing, although may result an
1633 # empty BlockList.
1634 self._parsed = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001635
1636 return BlockList(blocks)
1637
Tao Baod7db5942015-01-28 10:07:51 -08001638 def parse(self, tokzer):
1639 return self.getBlocks(tokzer)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001640
Tao Baod7db5942015-01-28 10:07:51 -08001641 def parseFile(self, path):
1642 return self.getBlocks(CppFileTokenizer(path))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001643
1644
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001645class BlockParserTests(unittest.TestCase):
1646 """BlockParser unit tests."""
Tao Baod7db5942015-01-28 10:07:51 -08001647
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001648 def get_blocks(self, lines):
1649 blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
Christopher Ferrise0cb4e12022-08-05 12:59:05 -07001650 return list(map(lambda a: str(a), blocks))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001651
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001652 def test_hash(self):
1653 self.assertEqual(self.get_blocks(["#error hello"]), ["#error hello"])
Tao Baod7db5942015-01-28 10:07:51 -08001654
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001655 def test_empty_line(self):
1656 self.assertEqual(self.get_blocks(["foo", "", "bar"]), ["foo bar"])
Tao Baod7db5942015-01-28 10:07:51 -08001657
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001658 def test_hash_with_space(self):
1659 # We currently cannot handle the following case with libclang properly.
1660 # Fortunately it doesn't appear in current headers.
1661 #self.assertEqual(self.get_blocks(["foo", " # ", "bar"]), ["foo", "bar"])
1662 pass
1663
1664 def test_with_comment(self):
1665 self.assertEqual(self.get_blocks(["foo",
1666 " # /* ahah */ if defined(__KERNEL__) /* more */",
1667 "bar", "#endif"]),
1668 ["foo", "#ifdef __KERNEL__", "bar", "#endif"])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001669
1670
Tao Baod7db5942015-01-28 10:07:51 -08001671################################################################################
1672################################################################################
1673##### #####
1674##### B L O C K L I S T O P T I M I Z A T I O N #####
1675##### #####
1676################################################################################
1677################################################################################
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001678
Tao Baod7db5942015-01-28 10:07:51 -08001679
Tao Baod7db5942015-01-28 10:07:51 -08001680def find_matching_endif(blocks, i):
1681 """Traverse the blocks to find out the matching #endif."""
1682 n = len(blocks)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001683 depth = 1
1684 while i < n:
1685 if blocks[i].isDirective():
Tao Baod7db5942015-01-28 10:07:51 -08001686 dir_ = blocks[i].directive
1687 if dir_ in ["if", "ifndef", "ifdef"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001688 depth += 1
Tao Baod7db5942015-01-28 10:07:51 -08001689 elif depth == 1 and dir_ in ["else", "elif"]:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001690 return i
Tao Baod7db5942015-01-28 10:07:51 -08001691 elif dir_ == "endif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001692 depth -= 1
1693 if depth == 0:
1694 return i
1695 i += 1
1696 return i
1697
Tao Baod7db5942015-01-28 10:07:51 -08001698
1699def optimize_if01(blocks):
1700 """Remove the code between #if 0 .. #endif in a list of CppBlocks."""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001701 i = 0
1702 n = len(blocks)
1703 result = []
1704 while i < n:
1705 j = i
1706 while j < n and not blocks[j].isIf():
1707 j += 1
1708 if j > i:
Tao Baod7db5942015-01-28 10:07:51 -08001709 logging.debug("appending lines %d to %d", blocks[i].lineno,
1710 blocks[j-1].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001711 result += blocks[i:j]
1712 if j >= n:
1713 break
1714 expr = blocks[j].expr
Tao Baod7db5942015-01-28 10:07:51 -08001715 r = expr.toInt()
1716 if r is None:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001717 result.append(blocks[j])
1718 i = j + 1
1719 continue
1720
1721 if r == 0:
1722 # if 0 => skip everything until the corresponding #endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001723 start_dir = blocks[j].directive
Tao Baod7db5942015-01-28 10:07:51 -08001724 j = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001725 if j >= n:
1726 # unterminated #if 0, finish here
1727 break
Tao Baod7db5942015-01-28 10:07:51 -08001728 dir_ = blocks[j].directive
1729 if dir_ == "endif":
1730 logging.debug("remove 'if 0' .. 'endif' (lines %d to %d)",
1731 blocks[i].lineno, blocks[j].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001732 if start_dir == "elif":
1733 # Put an endif since we started with an elif.
1734 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001735 i = j + 1
Tao Baod7db5942015-01-28 10:07:51 -08001736 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001737 # convert 'else' into 'if 1'
Tao Baod7db5942015-01-28 10:07:51 -08001738 logging.debug("convert 'if 0' .. 'else' into 'if 1' (lines %d "
1739 "to %d)", blocks[i].lineno, blocks[j-1].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001740 if start_dir == "elif":
1741 blocks[j].directive = "elif"
1742 else:
1743 blocks[j].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001744 blocks[j].expr = CppExpr(CppStringTokenizer("1").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001745 i = j
Tao Baod7db5942015-01-28 10:07:51 -08001746 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001747 # convert 'elif' into 'if'
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001748 logging.debug("convert 'if 0' .. 'elif' into 'if'")
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001749 if start_dir == "elif":
1750 blocks[j].directive = "elif"
1751 else:
1752 blocks[j].directive = "if"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001753 i = j
1754 continue
1755
1756 # if 1 => find corresponding endif and remove/transform them
Tao Baod7db5942015-01-28 10:07:51 -08001757 k = find_matching_endif(blocks, j + 1)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001758 if k >= n:
1759 # unterminated #if 1, finish here
Elliott Hughesdc1fb702014-08-20 11:16:11 -07001760 logging.debug("unterminated 'if 1'")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001761 result += blocks[j+1:k]
1762 break
1763
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001764 start_dir = blocks[j].directive
Tao Baod7db5942015-01-28 10:07:51 -08001765 dir_ = blocks[k].directive
1766 if dir_ == "endif":
1767 logging.debug("convert 'if 1' .. 'endif' (lines %d to %d)",
1768 blocks[j].lineno, blocks[k].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001769 if start_dir == "elif":
1770 # Add the elif in to the results and convert it to an elif 1.
1771 blocks[j].tokens = CppStringTokenizer("1").tokens
1772 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001773 result += optimize_if01(blocks[j+1:k])
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001774 if start_dir == "elif":
1775 # Add the endif in to the results.
1776 result += blocks[k:k+1]
Tao Baod7db5942015-01-28 10:07:51 -08001777 i = k + 1
1778 elif dir_ == "else":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001779 # convert 'else' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001780 logging.debug("convert 'if 1' .. 'else' (lines %d to %d)",
1781 blocks[j].lineno, blocks[k].lineno)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001782 if start_dir == "elif":
1783 # Add the elif in to the results and convert it to an elif 1.
1784 blocks[j].tokens = CppStringTokenizer("1").tokens
1785 result += blocks[j:j+1]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001786 result += optimize_if01(blocks[j+1:k])
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001787 if start_dir == "elif":
1788 blocks[k].directive = "elif"
1789 else:
1790 blocks[k].directive = "if"
Tao Baod7db5942015-01-28 10:07:51 -08001791 blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001792 i = k
Tao Baod7db5942015-01-28 10:07:51 -08001793 elif dir_ == "elif":
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001794 # convert 'elif' into 'if 0'
Tao Baod7db5942015-01-28 10:07:51 -08001795 logging.debug("convert 'if 1' .. 'elif' (lines %d to %d)",
1796 blocks[j].lineno, blocks[k].lineno)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001797 result += optimize_if01(blocks[j+1:k])
Tao Baod7db5942015-01-28 10:07:51 -08001798 blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001799 i = k
1800 return result
1801
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001802class OptimizerTests(unittest.TestCase):
1803 def parse(self, text, macros=None):
1804 out = utils.StringOutput()
1805 blocks = BlockParser().parse(CppStringTokenizer(text))
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001806 blocks.optimizeAll(macros)
1807 blocks.write(out)
1808 return out.get()
Tao Baod7db5942015-01-28 10:07:51 -08001809
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001810 def test_if1(self):
1811 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001812#if 1
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001813#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001814#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001815"""
1816 expected = """\
1817#define GOOD
1818"""
1819 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001820
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001821 def test_if0(self):
1822 text = """\
1823#if 0
1824#define SHOULD_SKIP1
1825#define SHOULD_SKIP2
1826#endif
1827"""
1828 expected = ""
1829 self.assertEqual(self.parse(text), expected)
1830
1831 def test_if1_else(self):
1832 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001833#if 1
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001834#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001835#else
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001836#define BAD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001837#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001838"""
1839 expected = """\
1840#define GOOD
1841"""
1842 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001843
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001844 def test_if0_else(self):
1845 text = """\
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001846#if 0
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001847#define BAD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001848#else
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001849#define GOOD
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001850#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001851"""
1852 expected = """\
1853#define GOOD
1854"""
1855 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001856
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001857 def test_if_elif1(self):
1858 text = """\
1859#if defined(something)
1860#define EXISTS
1861#elif 1
1862#define GOOD
1863#endif
1864"""
1865 expected = """\
1866#ifdef something
1867#define EXISTS
1868#elif 1
1869#define GOOD
1870#endif
1871"""
1872 self.assertEqual(self.parse(text), expected)
1873
1874 def test_if_elif1_macro(self):
1875 text = """\
1876#if defined(something)
1877#define EXISTS
1878#elif defined(WILL_BE_ONE)
1879#define GOOD
1880#endif
1881"""
1882 expected = """\
1883#ifdef something
1884#define EXISTS
1885#elif 1
1886#define GOOD
1887#endif
1888"""
1889 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1890
1891
1892 def test_if_elif1_else(self):
1893 text = """\
1894#if defined(something)
1895#define EXISTS
1896#elif 1
1897#define GOOD
1898#else
1899#define BAD
1900#endif
1901"""
1902 expected = """\
1903#ifdef something
1904#define EXISTS
1905#elif 1
1906#define GOOD
1907#endif
1908"""
1909 self.assertEqual(self.parse(text), expected)
1910
1911 def test_if_elif1_else_macro(self):
1912 text = """\
1913#if defined(something)
1914#define EXISTS
1915#elif defined(WILL_BE_ONE)
1916#define GOOD
1917#else
1918#define BAD
1919#endif
1920"""
1921 expected = """\
1922#ifdef something
1923#define EXISTS
1924#elif 1
1925#define GOOD
1926#endif
1927"""
1928 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1929
1930
1931 def test_if_elif1_else_macro(self):
1932 text = """\
1933#if defined(something)
1934#define EXISTS
1935#elif defined(WILL_BE_ONE)
1936#define GOOD
1937#else
1938#define BAD
1939#endif
1940"""
1941 expected = """\
1942#ifdef something
1943#define EXISTS
1944#elif 1
1945#define GOOD
1946#endif
1947"""
1948 self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
1949
1950 def test_macro_set_to_undefined_single(self):
1951 text = """\
Elliott Hughes40596aa2013-11-05 14:54:29 -08001952#if defined(__KERNEL__)
1953#define BAD_KERNEL
1954#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001955"""
1956 expected = ""
1957 macros = {"__KERNEL__": kCppUndefinedMacro}
1958 self.assertEqual(self.parse(text, macros), expected)
Elliott Hughes40596aa2013-11-05 14:54:29 -08001959
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001960 def test_macro_set_to_undefined_if(self):
1961 text = """\
Elliott Hughes40596aa2013-11-05 14:54:29 -08001962#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001963#define CHECK
Elliott Hughes40596aa2013-11-05 14:54:29 -08001964#endif
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001965"""
1966 expected = """\
1967#if !defined(__GLIBC__) || __GLIBC__ < 2
1968#define CHECK
1969#endif
1970"""
1971 macros = {"__KERNEL__": kCppUndefinedMacro}
1972 self.assertEqual(self.parse(text, macros), expected)
Elliott Hughes40596aa2013-11-05 14:54:29 -08001973
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001974 def test_endif_comment_removed(self):
1975 text = """\
Elliott Hughesfddbafd2014-05-01 10:17:27 -07001976#ifndef SIGRTMAX
1977#define SIGRTMAX 123
1978#endif /* SIGRTMAX */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001979"""
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001980 expected = """\
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -07001981#ifndef SIGRTMAX
1982#define SIGRTMAX 123
Elliott Hughes96c1db72017-05-25 13:48:01 -07001983#endif
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001984"""
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001985 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001986
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07001987 def test_multilevel_if0(self):
1988 text = """\
1989#if 0
1990#if 1
1991#define BAD_6
1992#endif
1993#endif
1994"""
1995 expected = ""
1996 self.assertEqual(self.parse(text), expected)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001997
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -07001998class RemoveStructsTests(unittest.TestCase):
1999 def parse(self, text, structs):
2000 out = utils.StringOutput()
2001 blocks = BlockParser().parse(CppStringTokenizer(text))
2002 blocks.removeStructs(structs)
2003 blocks.write(out)
2004 return out.get()
2005
2006 def test_remove_struct_from_start(self):
2007 text = """\
2008struct remove {
2009 int val1;
2010 int val2;
2011};
2012struct something {
2013 struct timeval val1;
2014 struct timeval val2;
2015};
2016"""
2017 expected = """\
2018struct something {
2019 struct timeval val1;
2020 struct timeval val2;
2021};
2022"""
2023 self.assertEqual(self.parse(text, set(["remove"])), expected)
2024
2025 def test_remove_struct_from_end(self):
2026 text = """\
2027struct something {
2028 struct timeval val1;
2029 struct timeval val2;
2030};
2031struct remove {
2032 int val1;
2033 int val2;
2034};
2035"""
2036 expected = """\
2037struct something {
2038 struct timeval val1;
2039 struct timeval val2;
2040};
2041"""
2042 self.assertEqual(self.parse(text, set(["remove"])), expected)
2043
2044 def test_remove_minimal_struct(self):
2045 text = """\
2046struct remove {
2047};
2048"""
2049 expected = "";
2050 self.assertEqual(self.parse(text, set(["remove"])), expected)
2051
2052 def test_remove_struct_with_struct_fields(self):
2053 text = """\
2054struct something {
2055 struct remove val1;
2056 struct remove val2;
2057};
2058struct remove {
2059 int val1;
2060 struct something val3;
2061 int val2;
2062};
2063"""
2064 expected = """\
2065struct something {
2066 struct remove val1;
2067 struct remove val2;
2068};
2069"""
2070 self.assertEqual(self.parse(text, set(["remove"])), expected)
2071
2072 def test_remove_consecutive_structs(self):
2073 text = """\
2074struct keep1 {
2075 struct timeval val1;
2076 struct timeval val2;
2077};
2078struct remove1 {
2079 int val1;
2080 int val2;
2081};
2082struct remove2 {
2083 int val1;
2084 int val2;
2085 int val3;
2086};
2087struct keep2 {
2088 struct timeval val1;
2089 struct timeval val2;
2090};
2091"""
2092 expected = """\
2093struct keep1 {
2094 struct timeval val1;
2095 struct timeval val2;
2096};
2097struct keep2 {
2098 struct timeval val1;
2099 struct timeval val2;
2100};
2101"""
2102 self.assertEqual(self.parse(text, set(["remove1", "remove2"])), expected)
2103
2104 def test_remove_multiple_structs(self):
2105 text = """\
2106struct keep1 {
2107 int val;
2108};
2109struct remove1 {
2110 int val1;
2111 int val2;
2112};
2113struct keep2 {
2114 int val;
2115};
2116struct remove2 {
2117 struct timeval val1;
2118 struct timeval val2;
2119};
2120struct keep3 {
2121 int val;
2122};
2123"""
2124 expected = """\
2125struct keep1 {
2126 int val;
2127};
2128struct keep2 {
2129 int val;
2130};
2131struct keep3 {
2132 int val;
2133};
2134"""
2135 self.assertEqual(self.parse(text, set(["remove1", "remove2"])), expected)
2136
2137
Christopher Ferris658b16f2019-01-08 14:58:07 -08002138class FullPathTest(unittest.TestCase):
2139 """Test of the full path parsing."""
2140
2141 def parse(self, text, keep=None):
2142 if not keep:
2143 keep = set()
2144 out = utils.StringOutput()
2145 blocks = BlockParser().parse(CppStringTokenizer(text))
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -07002146
2147 blocks.removeStructs(kernel_structs_to_remove)
Christopher Ferris658b16f2019-01-08 14:58:07 -08002148 blocks.removeVarsAndFuncs(keep)
2149 blocks.replaceTokens(kernel_token_replacements)
2150 blocks.optimizeAll(None)
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -07002151
Christopher Ferris658b16f2019-01-08 14:58:07 -08002152 blocks.write(out)
2153 return out.get()
2154
2155 def test_function_removed(self):
2156 text = """\
2157static inline __u64 function()
2158{
2159}
2160"""
2161 expected = ""
2162 self.assertEqual(self.parse(text), expected)
2163
2164 def test_function_removed_with_struct(self):
2165 text = """\
2166static inline struct something* function()
2167{
2168}
2169"""
2170 expected = ""
2171 self.assertEqual(self.parse(text), expected)
2172
2173 def test_function_kept(self):
2174 text = """\
2175static inline __u64 function()
2176{
2177}
2178"""
2179 expected = """\
2180static inline __u64 function() {
2181}
2182"""
2183 self.assertEqual(self.parse(text, set(["function"])), expected)
2184
2185 def test_var_removed(self):
2186 text = "__u64 variable;"
2187 expected = ""
2188 self.assertEqual(self.parse(text), expected)
2189
2190 def test_var_kept(self):
2191 text = "__u64 variable;"
2192 expected = "__u64 variable;\n"
2193 self.assertEqual(self.parse(text, set(["variable"])), expected)
2194
2195 def test_keep_function_typedef(self):
2196 text = "typedef void somefunction_t(void);"
2197 expected = "typedef void somefunction_t(void);\n"
2198 self.assertEqual(self.parse(text), expected)
2199
2200 def test_struct_keep_attribute(self):
2201 text = """\
2202struct something_s {
2203 __u32 s1;
2204 __u32 s2;
2205} __attribute__((packed));
2206"""
2207 expected = """\
2208struct something_s {
2209 __u32 s1;
2210 __u32 s2;
2211} __attribute__((packed));
2212"""
2213 self.assertEqual(self.parse(text), expected)
2214
2215 def test_function_keep_attribute_structs(self):
2216 text = """\
2217static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
2218}
2219"""
2220 expected = """\
2221static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
2222}
2223"""
2224 self.assertEqual(self.parse(text, set(["function"])), expected)
2225
2226 def test_struct_after_struct(self):
2227 text = """\
2228struct first {
2229};
2230
2231struct second {
2232 unsigned short s1;
2233#define SOMETHING 8
2234 unsigned short s2;
2235};
2236"""
2237 expected = """\
2238struct first {
2239};
2240struct second {
2241 unsigned short s1;
2242#define SOMETHING 8
2243 unsigned short s2;
2244};
2245"""
2246 self.assertEqual(self.parse(text), expected)
2247
2248 def test_other_not_removed(self):
2249 text = """\
2250typedef union {
2251 __u64 tu1;
2252 __u64 tu2;
2253} typedef_name;
2254
2255union {
2256 __u64 u1;
2257 __u64 u2;
2258};
2259
2260struct {
2261 __u64 s1;
2262 __u64 s2;
2263};
2264
2265enum {
2266 ENUM1 = 0,
2267 ENUM2,
2268};
2269
2270__extension__ typedef __signed__ long long __s64;
2271"""
2272 expected = """\
2273typedef union {
2274 __u64 tu1;
2275 __u64 tu2;
2276} typedef_name;
2277union {
2278 __u64 u1;
2279 __u64 u2;
2280};
2281struct {
2282 __u64 s1;
2283 __u64 s2;
2284};
2285enum {
2286 ENUM1 = 0,
2287 ENUM2,
2288};
2289__extension__ typedef __signed__ long long __s64;
2290"""
2291
2292 self.assertEqual(self.parse(text), expected)
2293
2294 def test_semicolon_after_function(self):
2295 text = """\
2296static inline __u64 function()
2297{
2298};
2299
2300struct should_see {
2301 __u32 field;
2302};
2303"""
2304 expected = """\
2305struct should_see {
2306 __u32 field;
2307};
2308"""
2309 self.assertEqual(self.parse(text), expected)
2310
2311 def test_define_in_middle_keep(self):
2312 text = """\
2313enum {
2314 ENUM0 = 0x10,
2315 ENUM1 = 0x20,
2316#define SOMETHING SOMETHING_ELSE
2317 ENUM2 = 0x40,
2318};
2319"""
2320 expected = """\
2321enum {
2322 ENUM0 = 0x10,
2323 ENUM1 = 0x20,
2324#define SOMETHING SOMETHING_ELSE
2325 ENUM2 = 0x40,
2326};
2327"""
2328 self.assertEqual(self.parse(text), expected)
2329
2330 def test_define_in_middle_remove(self):
2331 text = """\
2332static inline function() {
2333#define SOMETHING1 SOMETHING_ELSE1
2334 i = 0;
2335 {
2336 i = 1;
2337 }
2338#define SOMETHING2 SOMETHING_ELSE2
2339}
2340"""
2341 expected = """\
2342#define SOMETHING1 SOMETHING_ELSE1
2343#define SOMETHING2 SOMETHING_ELSE2
2344"""
2345 self.assertEqual(self.parse(text), expected)
2346
2347 def test_define_in_middle_force_keep(self):
2348 text = """\
2349static inline function() {
2350#define SOMETHING1 SOMETHING_ELSE1
2351 i = 0;
2352 {
2353 i = 1;
2354 }
2355#define SOMETHING2 SOMETHING_ELSE2
2356}
2357"""
2358 expected = """\
2359static inline function() {
2360#define SOMETHING1 SOMETHING_ELSE1
2361 i = 0;
2362 {
2363 i = 1;
2364 }
2365#define SOMETHING2 SOMETHING_ELSE2
2366}
2367"""
2368 self.assertEqual(self.parse(text, set(["function"])), expected)
2369
2370 def test_define_before_remove(self):
2371 text = """\
2372#define SHOULD_BE_KEPT NOTHING1
2373#define ANOTHER_TO_KEEP NOTHING2
2374static inline function() {
2375#define SOMETHING1 SOMETHING_ELSE1
2376 i = 0;
2377 {
2378 i = 1;
2379 }
2380#define SOMETHING2 SOMETHING_ELSE2
2381}
2382"""
2383 expected = """\
2384#define SHOULD_BE_KEPT NOTHING1
2385#define ANOTHER_TO_KEEP NOTHING2
2386#define SOMETHING1 SOMETHING_ELSE1
2387#define SOMETHING2 SOMETHING_ELSE2
2388"""
2389 self.assertEqual(self.parse(text), expected)
2390
2391 def test_extern_C(self):
2392 text = """\
2393#if defined(__cplusplus)
2394extern "C" {
2395#endif
2396
2397struct something {
2398};
2399
2400#if defined(__cplusplus)
2401}
2402#endif
2403"""
2404 expected = """\
2405#ifdef __cplusplus
2406extern "C" {
2407#endif
2408struct something {
2409};
2410#ifdef __cplusplus
2411}
2412#endif
2413"""
2414 self.assertEqual(self.parse(text), expected)
2415
2416 def test_macro_definition_removed(self):
2417 text = """\
2418#define MACRO_FUNCTION_NO_PARAMS static inline some_func() {}
2419MACRO_FUNCTION_NO_PARAMS()
2420
2421#define MACRO_FUNCTION_PARAMS(a) static inline some_func() { a; }
2422MACRO_FUNCTION_PARAMS(a = 1)
2423
2424something that should still be kept
2425MACRO_FUNCTION_PARAMS(b)
2426"""
2427 expected = """\
2428#define MACRO_FUNCTION_NO_PARAMS static inline some_func() { }
2429#define MACRO_FUNCTION_PARAMS(a) static inline some_func() { a; }
2430something that should still be kept
2431"""
2432 self.assertEqual(self.parse(text), expected)
2433
Christopher Ferrisbb9fcb42020-04-06 11:38:04 -07002434 def test_verify_timeval_itemerval(self):
2435 text = """\
2436struct __kernel_old_timeval {
2437 struct something val;
2438};
2439struct __kernel_old_itimerval {
2440 struct __kernel_old_timeval val;
2441};
2442struct fields {
2443 struct __kernel_old_timeval timeval;
2444 struct __kernel_old_itimerval itimerval;
2445};
2446"""
2447 expected = """\
2448struct fields {
2449 struct timeval timeval;
2450 struct itimerval itimerval;
2451};
2452"""
2453 self.assertEqual(self.parse(text), expected)
2454
2455 def test_token_replacement(self):
2456 text = """\
2457#define SIGRTMIN 32
2458#define SIGRTMAX _NSIG
2459"""
2460 expected = """\
2461#define __SIGRTMIN 32
2462#define __SIGRTMAX _KERNEL__NSIG
2463"""
2464 self.assertEqual(self.parse(text), expected)
2465
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08002466
Christopher Ferrisf2484aa2018-10-25 19:41:45 -07002467if __name__ == '__main__':
2468 unittest.main()