interpreterbase.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. # Copyright 2016-2017 The Meson development team
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. # Unless required by applicable law or agreed to in writing, software
  7. # distributed under the License is distributed on an "AS IS" BASIS,
  8. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. # See the License for the specific language governing permissions and
  10. # limitations under the License.
  11. # This class contains the basic functionality needed to run any interpreter
  12. # or an interpreter-based tool.
  13. from . import mparser, mesonlib, mlog
  14. from . import environment, dependencies
  15. import os, copy, re
  16. from functools import wraps
  17. # Decorators for method calls.
  18. def check_stringlist(a, msg='Arguments must be strings.'):
  19. if not isinstance(a, list):
  20. mlog.debug('Not a list:', str(a))
  21. raise InvalidArguments('Argument not a list.')
  22. if not all(isinstance(s, str) for s in a):
  23. mlog.debug('Element not a string:', str(a))
  24. raise InvalidArguments(msg)
  25. def noPosargs(f):
  26. @wraps(f)
  27. def wrapped(self, node, args, kwargs):
  28. if args:
  29. raise InvalidArguments('Function does not take positional arguments.')
  30. return f(self, node, args, kwargs)
  31. return wrapped
  32. def noKwargs(f):
  33. @wraps(f)
  34. def wrapped(self, node, args, kwargs):
  35. if kwargs:
  36. raise InvalidArguments('Function does not take keyword arguments.')
  37. return f(self, node, args, kwargs)
  38. return wrapped
  39. def stringArgs(f):
  40. @wraps(f)
  41. def wrapped(self, node, args, kwargs):
  42. assert(isinstance(args, list))
  43. check_stringlist(args)
  44. return f(self, node, args, kwargs)
  45. return wrapped
  46. class permittedKwargs:
  47. def __init__(self, permitted):
  48. self.permitted = permitted
  49. def __call__(self, f):
  50. @wraps(f)
  51. def wrapped(s, node, args, kwargs):
  52. for k in kwargs:
  53. if k not in self.permitted:
  54. mlog.warning('Passed invalid keyword argument "%s". This will become a hard error in the future.' % k)
  55. return f(s, node, args, kwargs)
  56. return wrapped
  57. class InterpreterException(mesonlib.MesonException):
  58. pass
  59. class InvalidCode(InterpreterException):
  60. pass
  61. class InvalidArguments(InterpreterException):
  62. pass
  63. class InterpreterObject:
  64. def __init__(self):
  65. self.methods = {}
  66. def method_call(self, method_name, args, kwargs):
  67. if method_name in self.methods:
  68. return self.methods[method_name](args, kwargs)
  69. raise InvalidCode('Unknown method "%s" in object.' % method_name)
  70. class MutableInterpreterObject(InterpreterObject):
  71. def __init__(self):
  72. super().__init__()
  73. class InterpreterBase:
  74. def __init__(self, source_root, subdir):
  75. self.source_root = source_root
  76. self.funcs = {}
  77. self.builtin = {}
  78. self.subdir = subdir
  79. self.variables = {}
  80. self.argument_depth = 0
  81. def load_root_meson_file(self):
  82. mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
  83. if not os.path.isfile(mesonfile):
  84. raise InvalidArguments('Missing Meson file in %s' % mesonfile)
  85. with open(mesonfile, encoding='utf8') as mf:
  86. code = mf.read()
  87. if code.isspace():
  88. raise InvalidCode('Builder file is empty.')
  89. assert(isinstance(code, str))
  90. try:
  91. self.ast = mparser.Parser(code, self.subdir).parse()
  92. except mesonlib.MesonException as me:
  93. me.file = environment.build_filename
  94. raise me
  95. def parse_project(self):
  96. """
  97. Parses project() and initializes languages, compilers etc. Do this
  98. early because we need this before we parse the rest of the AST.
  99. """
  100. self.evaluate_codeblock(self.ast, end=1)
  101. def sanity_check_ast(self):
  102. if not isinstance(self.ast, mparser.CodeBlockNode):
  103. raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.')
  104. if not self.ast.lines:
  105. raise InvalidCode('No statements in code.')
  106. first = self.ast.lines[0]
  107. if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project':
  108. raise InvalidCode('First statement must be a call to project')
  109. def run(self):
  110. # Evaluate everything after the first line, which is project() because
  111. # we already parsed that in self.parse_project()
  112. self.evaluate_codeblock(self.ast, start=1)
  113. def evaluate_codeblock(self, node, start=0, end=None):
  114. if node is None:
  115. return
  116. if not isinstance(node, mparser.CodeBlockNode):
  117. e = InvalidCode('Tried to execute a non-codeblock. Possibly a bug in the parser.')
  118. e.lineno = node.lineno
  119. e.colno = node.colno
  120. raise e
  121. statements = node.lines[start:end]
  122. i = 0
  123. while i < len(statements):
  124. cur = statements[i]
  125. try:
  126. self.evaluate_statement(cur)
  127. except Exception as e:
  128. if not(hasattr(e, 'lineno')):
  129. e.lineno = cur.lineno
  130. e.colno = cur.colno
  131. e.file = os.path.join(self.subdir, 'meson.build')
  132. raise e
  133. i += 1 # In THE FUTURE jump over blocks and stuff.
  134. def evaluate_statement(self, cur):
  135. if isinstance(cur, mparser.FunctionNode):
  136. return self.function_call(cur)
  137. elif isinstance(cur, mparser.AssignmentNode):
  138. return self.assignment(cur)
  139. elif isinstance(cur, mparser.MethodNode):
  140. return self.method_call(cur)
  141. elif isinstance(cur, mparser.StringNode):
  142. return cur.value
  143. elif isinstance(cur, mparser.BooleanNode):
  144. return cur.value
  145. elif isinstance(cur, mparser.IfClauseNode):
  146. return self.evaluate_if(cur)
  147. elif isinstance(cur, mparser.IdNode):
  148. return self.get_variable(cur.value)
  149. elif isinstance(cur, mparser.ComparisonNode):
  150. return self.evaluate_comparison(cur)
  151. elif isinstance(cur, mparser.ArrayNode):
  152. return self.evaluate_arraystatement(cur)
  153. elif isinstance(cur, mparser.NumberNode):
  154. return cur.value
  155. elif isinstance(cur, mparser.AndNode):
  156. return self.evaluate_andstatement(cur)
  157. elif isinstance(cur, mparser.OrNode):
  158. return self.evaluate_orstatement(cur)
  159. elif isinstance(cur, mparser.NotNode):
  160. return self.evaluate_notstatement(cur)
  161. elif isinstance(cur, mparser.UMinusNode):
  162. return self.evaluate_uminusstatement(cur)
  163. elif isinstance(cur, mparser.ArithmeticNode):
  164. return self.evaluate_arithmeticstatement(cur)
  165. elif isinstance(cur, mparser.ForeachClauseNode):
  166. return self.evaluate_foreach(cur)
  167. elif isinstance(cur, mparser.PlusAssignmentNode):
  168. return self.evaluate_plusassign(cur)
  169. elif isinstance(cur, mparser.IndexNode):
  170. return self.evaluate_indexing(cur)
  171. elif isinstance(cur, mparser.TernaryNode):
  172. return self.evaluate_ternary(cur)
  173. elif self.is_elementary_type(cur):
  174. return cur
  175. else:
  176. raise InvalidCode("Unknown statement.")
  177. def evaluate_arraystatement(self, cur):
  178. (arguments, kwargs) = self.reduce_arguments(cur.args)
  179. if len(kwargs) > 0:
  180. raise InvalidCode('Keyword arguments are invalid in array construction.')
  181. return arguments
  182. def evaluate_notstatement(self, cur):
  183. v = self.evaluate_statement(cur.value)
  184. if not isinstance(v, bool):
  185. raise InterpreterException('Argument to "not" is not a boolean.')
  186. return not v
  187. def evaluate_if(self, node):
  188. assert(isinstance(node, mparser.IfClauseNode))
  189. for i in node.ifs:
  190. result = self.evaluate_statement(i.condition)
  191. if not(isinstance(result, bool)):
  192. raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result))
  193. if result:
  194. self.evaluate_codeblock(i.block)
  195. return
  196. if not isinstance(node.elseblock, mparser.EmptyNode):
  197. self.evaluate_codeblock(node.elseblock)
  198. def evaluate_comparison(self, node):
  199. val1 = self.evaluate_statement(node.left)
  200. val2 = self.evaluate_statement(node.right)
  201. if node.ctype == '==':
  202. return val1 == val2
  203. elif node.ctype == '!=':
  204. return val1 != val2
  205. elif not isinstance(val1, type(val2)):
  206. raise InterpreterException(
  207. 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__,
  208. type(val2).__name__,
  209. node.ctype))
  210. elif not self.is_elementary_type(val1):
  211. raise InterpreterException('{} can only be compared for equality.'.format(node.left.value))
  212. elif not self.is_elementary_type(val2):
  213. raise InterpreterException('{} can only be compared for equality.'.format(node.right.value))
  214. elif node.ctype == '<':
  215. return val1 < val2
  216. elif node.ctype == '<=':
  217. return val1 <= val2
  218. elif node.ctype == '>':
  219. return val1 > val2
  220. elif node.ctype == '>=':
  221. return val1 >= val2
  222. else:
  223. raise InvalidCode('You broke my compare eval.')
  224. def evaluate_andstatement(self, cur):
  225. l = self.evaluate_statement(cur.left)
  226. if not isinstance(l, bool):
  227. raise InterpreterException('First argument to "and" is not a boolean.')
  228. if not l:
  229. return False
  230. r = self.evaluate_statement(cur.right)
  231. if not isinstance(r, bool):
  232. raise InterpreterException('Second argument to "and" is not a boolean.')
  233. return r
  234. def evaluate_orstatement(self, cur):
  235. l = self.evaluate_statement(cur.left)
  236. if not isinstance(l, bool):
  237. raise InterpreterException('First argument to "or" is not a boolean.')
  238. if l:
  239. return True
  240. r = self.evaluate_statement(cur.right)
  241. if not isinstance(r, bool):
  242. raise InterpreterException('Second argument to "or" is not a boolean.')
  243. return r
  244. def evaluate_uminusstatement(self, cur):
  245. v = self.evaluate_statement(cur.value)
  246. if not isinstance(v, int):
  247. raise InterpreterException('Argument to negation is not an integer.')
  248. return -v
  249. def evaluate_arithmeticstatement(self, cur):
  250. l = self.evaluate_statement(cur.left)
  251. r = self.evaluate_statement(cur.right)
  252. if cur.operation == 'add':
  253. try:
  254. return l + r
  255. except Exception as e:
  256. raise InvalidCode('Invalid use of addition: ' + str(e))
  257. elif cur.operation == 'sub':
  258. if not isinstance(l, int) or not isinstance(r, int):
  259. raise InvalidCode('Subtraction works only with integers.')
  260. return l - r
  261. elif cur.operation == 'mul':
  262. if not isinstance(l, int) or not isinstance(r, int):
  263. raise InvalidCode('Multiplication works only with integers.')
  264. return l * r
  265. elif cur.operation == 'div':
  266. if not isinstance(l, int) or not isinstance(r, int):
  267. raise InvalidCode('Division works only with integers.')
  268. return l // r
  269. elif cur.operation == 'mod':
  270. if not isinstance(l, int) or not isinstance(r, int):
  271. raise InvalidCode('Modulo works only with integers.')
  272. return l % r
  273. else:
  274. raise InvalidCode('You broke me.')
  275. def evaluate_ternary(self, node):
  276. assert(isinstance(node, mparser.TernaryNode))
  277. result = self.evaluate_statement(node.condition)
  278. if not isinstance(result, bool):
  279. raise InterpreterException('Ternary condition is not boolean.')
  280. if result:
  281. return self.evaluate_statement(node.trueblock)
  282. else:
  283. return self.evaluate_statement(node.falseblock)
  284. def evaluate_foreach(self, node):
  285. assert(isinstance(node, mparser.ForeachClauseNode))
  286. varname = node.varname.value
  287. items = self.evaluate_statement(node.items)
  288. if not isinstance(items, list):
  289. raise InvalidArguments('Items of foreach loop is not an array')
  290. for item in items:
  291. self.set_variable(varname, item)
  292. self.evaluate_codeblock(node.block)
  293. def evaluate_plusassign(self, node):
  294. assert(isinstance(node, mparser.PlusAssignmentNode))
  295. varname = node.var_name
  296. addition = self.evaluate_statement(node.value)
  297. # Remember that all variables are immutable. We must always create a
  298. # full new variable and then assign it.
  299. old_variable = self.get_variable(varname)
  300. if isinstance(old_variable, str):
  301. if not isinstance(addition, str):
  302. raise InvalidArguments('The += operator requires a string on the right hand side if the variable on the left is a string')
  303. new_value = old_variable + addition
  304. elif isinstance(old_variable, int):
  305. if not isinstance(addition, int):
  306. raise InvalidArguments('The += operator requires an int on the right hand side if the variable on the left is an int')
  307. new_value = old_variable + addition
  308. elif not isinstance(old_variable, list):
  309. raise InvalidArguments('The += operator currently only works with arrays, strings or ints ')
  310. # Add other data types here.
  311. else:
  312. if isinstance(addition, list):
  313. new_value = old_variable + addition
  314. else:
  315. new_value = old_variable + [addition]
  316. self.set_variable(varname, new_value)
  317. def evaluate_indexing(self, node):
  318. assert(isinstance(node, mparser.IndexNode))
  319. iobject = self.evaluate_statement(node.iobject)
  320. if not isinstance(iobject, list):
  321. raise InterpreterException('Tried to index a non-array object.')
  322. index = self.evaluate_statement(node.index)
  323. if not isinstance(index, int):
  324. raise InterpreterException('Index value is not an integer.')
  325. if index < -len(iobject) or index >= len(iobject):
  326. raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
  327. return iobject[index]
  328. def function_call(self, node):
  329. func_name = node.func_name
  330. (posargs, kwargs) = self.reduce_arguments(node.args)
  331. if func_name in self.funcs:
  332. return self.funcs[func_name](node, self.flatten(posargs), kwargs)
  333. else:
  334. self.unknown_function_called(func_name)
  335. def method_call(self, node):
  336. invokable = node.source_object
  337. if isinstance(invokable, mparser.IdNode):
  338. object_name = invokable.value
  339. obj = self.get_variable(object_name)
  340. else:
  341. obj = self.evaluate_statement(invokable)
  342. method_name = node.name
  343. args = node.args
  344. if isinstance(obj, str):
  345. return self.string_method_call(obj, method_name, args)
  346. if isinstance(obj, bool):
  347. return self.bool_method_call(obj, method_name, args)
  348. if isinstance(obj, int):
  349. return self.int_method_call(obj, method_name, args)
  350. if isinstance(obj, list):
  351. return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0])
  352. if isinstance(obj, mesonlib.File):
  353. raise InvalidArguments('File object "%s" is not callable.' % obj)
  354. if not isinstance(obj, InterpreterObject):
  355. raise InvalidArguments('Variable "%s" is not callable.' % object_name)
  356. (args, kwargs) = self.reduce_arguments(args)
  357. if method_name == 'extract_objects':
  358. self.validate_extraction(obj.held_object)
  359. return obj.method_call(method_name, self.flatten(args), kwargs)
  360. def bool_method_call(self, obj, method_name, args):
  361. (posargs, _) = self.reduce_arguments(args)
  362. if method_name == 'to_string':
  363. if not posargs:
  364. if obj:
  365. return 'true'
  366. else:
  367. return 'false'
  368. elif len(posargs) == 2 and isinstance(posargs[0], str) and isinstance(posargs[1], str):
  369. if obj:
  370. return posargs[0]
  371. else:
  372. return posargs[1]
  373. else:
  374. raise InterpreterException('bool.to_string() must have either no arguments or exactly two string arguments that signify what values to return for true and false.')
  375. elif method_name == 'to_int':
  376. if obj:
  377. return 1
  378. else:
  379. return 0
  380. else:
  381. raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
  382. def int_method_call(self, obj, method_name, args):
  383. (posargs, _) = self.reduce_arguments(args)
  384. if method_name == 'is_even':
  385. if not posargs:
  386. return obj % 2 == 0
  387. else:
  388. raise InterpreterException('int.is_even() must have no arguments.')
  389. elif method_name == 'is_odd':
  390. if not posargs:
  391. return obj % 2 != 0
  392. else:
  393. raise InterpreterException('int.is_odd() must have no arguments.')
  394. else:
  395. raise InterpreterException('Unknown method "%s" for an integer.' % method_name)
  396. def string_method_call(self, obj, method_name, args):
  397. (posargs, _) = self.reduce_arguments(args)
  398. if method_name == 'strip':
  399. return obj.strip()
  400. elif method_name == 'format':
  401. return self.format_string(obj, args)
  402. elif method_name == 'to_upper':
  403. return obj.upper()
  404. elif method_name == 'to_lower':
  405. return obj.lower()
  406. elif method_name == 'underscorify':
  407. return re.sub(r'[^a-zA-Z0-9]', '_', obj)
  408. elif method_name == 'split':
  409. if len(posargs) > 1:
  410. raise InterpreterException('Split() must have at most one argument.')
  411. elif len(posargs) == 1:
  412. s = posargs[0]
  413. if not isinstance(s, str):
  414. raise InterpreterException('Split() argument must be a string')
  415. return obj.split(s)
  416. else:
  417. return obj.split()
  418. elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith':
  419. s = posargs[0]
  420. if not isinstance(s, str):
  421. raise InterpreterException('Argument must be a string.')
  422. if method_name == 'startswith':
  423. return obj.startswith(s)
  424. elif method_name == 'contains':
  425. return obj.find(s) >= 0
  426. return obj.endswith(s)
  427. elif method_name == 'to_int':
  428. try:
  429. return int(obj)
  430. except Exception:
  431. raise InterpreterException('String {!r} cannot be converted to int'.format(obj))
  432. elif method_name == 'join':
  433. if len(posargs) != 1:
  434. raise InterpreterException('Join() takes exactly one argument.')
  435. strlist = posargs[0]
  436. check_stringlist(strlist)
  437. return obj.join(strlist)
  438. elif method_name == 'version_compare':
  439. if len(posargs) != 1:
  440. raise InterpreterException('Version_compare() takes exactly one argument.')
  441. cmpr = posargs[0]
  442. if not isinstance(cmpr, str):
  443. raise InterpreterException('Version_compare() argument must be a string.')
  444. return mesonlib.version_compare(obj, cmpr)
  445. raise InterpreterException('Unknown method "%s" for a string.' % method_name)
  446. def unknown_function_called(self, func_name):
  447. raise InvalidCode('Unknown function "%s".' % func_name)
  448. def array_method_call(self, obj, method_name, args):
  449. if method_name == 'contains':
  450. return self.check_contains(obj, args)
  451. elif method_name == 'length':
  452. return len(obj)
  453. elif method_name == 'get':
  454. index = args[0]
  455. fallback = None
  456. if len(args) == 2:
  457. fallback = args[1]
  458. elif len(args) > 2:
  459. m = 'Array method \'get()\' only takes two arguments: the ' \
  460. 'index and an optional fallback value if the index is ' \
  461. 'out of range.'
  462. raise InvalidArguments(m)
  463. if not isinstance(index, int):
  464. raise InvalidArguments('Array index must be a number.')
  465. if index < -len(obj) or index >= len(obj):
  466. if fallback is None:
  467. m = 'Array index {!r} is out of bounds for array of size {!r}.'
  468. raise InvalidArguments(m.format(index, len(obj)))
  469. return fallback
  470. return obj[index]
  471. m = 'Arrays do not have a method called {!r}.'
  472. raise InterpreterException(m.format(method_name))
  473. def reduce_arguments(self, args):
  474. assert(isinstance(args, mparser.ArgumentNode))
  475. if args.incorrect_order():
  476. raise InvalidArguments('All keyword arguments must be after positional arguments.')
  477. self.argument_depth += 1
  478. reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
  479. reduced_kw = {}
  480. for key in args.kwargs.keys():
  481. if not isinstance(key, str):
  482. raise InvalidArguments('Keyword argument name is not a string.')
  483. a = args.kwargs[key]
  484. reduced_kw[key] = self.evaluate_statement(a)
  485. self.argument_depth -= 1
  486. return reduced_pos, reduced_kw
  487. def flatten(self, args):
  488. if isinstance(args, mparser.StringNode):
  489. return args.value
  490. if isinstance(args, (int, str, mesonlib.File, InterpreterObject)):
  491. return args
  492. result = []
  493. for a in args:
  494. if isinstance(a, list):
  495. rest = self.flatten(a)
  496. result = result + rest
  497. elif isinstance(a, mparser.StringNode):
  498. result.append(a.value)
  499. else:
  500. result.append(a)
  501. return result
  502. def assignment(self, node):
  503. assert(isinstance(node, mparser.AssignmentNode))
  504. if self.argument_depth != 0:
  505. raise InvalidArguments('''Tried to assign values inside an argument list.
  506. To specify a keyword argument, use : instead of =.''')
  507. var_name = node.var_name
  508. if not isinstance(var_name, str):
  509. raise InvalidArguments('Tried to assign value to a non-variable.')
  510. value = self.evaluate_statement(node.value)
  511. if not self.is_assignable(value):
  512. raise InvalidCode('Tried to assign an invalid value to variable.')
  513. # For mutable objects we need to make a copy on assignment
  514. if isinstance(value, MutableInterpreterObject):
  515. value = copy.deepcopy(value)
  516. self.set_variable(var_name, value)
  517. return None
  518. def set_variable(self, varname, variable):
  519. if variable is None:
  520. raise InvalidCode('Can not assign None to variable.')
  521. if not isinstance(varname, str):
  522. raise InvalidCode('First argument to set_variable must be a string.')
  523. if not self.is_assignable(variable):
  524. raise InvalidCode('Assigned value not of assignable type.')
  525. if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None:
  526. raise InvalidCode('Invalid variable name: ' + varname)
  527. if varname in self.builtin:
  528. raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
  529. self.variables[varname] = variable
  530. def get_variable(self, varname):
  531. if varname in self.builtin:
  532. return self.builtin[varname]
  533. if varname in self.variables:
  534. return self.variables[varname]
  535. raise InvalidCode('Unknown variable "%s".' % varname)
  536. def is_assignable(self, value):
  537. return isinstance(value, (InterpreterObject, dependencies.Dependency,
  538. str, int, list, mesonlib.File))
  539. def is_elementary_type(self, v):
  540. return isinstance(v, (int, float, str, bool, list))