interpreterbase.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  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, types
  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 _get_callee_args(wrapped_args):
  26. # Functions have 4 positional args and methods have 3.
  27. s = wrapped_args[0]
  28. if len(wrapped_args) == 3:
  29. node_or_state = None
  30. args = wrapped_args[1]
  31. kwargs = wrapped_args[2]
  32. elif len(wrapped_args) == 4:
  33. node_or_state = wrapped_args[1]
  34. args = wrapped_args[2]
  35. kwargs = wrapped_args[3]
  36. else:
  37. raise InvalidArguments('Expecting 3 or 4 args, got {}'.format(len(wrapped_args)))
  38. # Sometimes interpreter methods are called internally with None instead of
  39. # empty list/dict
  40. args = args if args is not None else []
  41. kwargs = kwargs if kwargs is not None else {}
  42. return s, node_or_state, args, kwargs
  43. def flatten(args):
  44. if isinstance(args, mparser.StringNode):
  45. return args.value
  46. if isinstance(args, (int, str, mesonlib.File, InterpreterObject)):
  47. return args
  48. result = []
  49. for a in args:
  50. if isinstance(a, list):
  51. rest = flatten(a)
  52. result = result + rest
  53. elif isinstance(a, mparser.StringNode):
  54. result.append(a.value)
  55. else:
  56. result.append(a)
  57. return result
  58. def noPosargs(f):
  59. @wraps(f)
  60. def wrapped(*wrapped_args, **wrapped_kwargs):
  61. args = _get_callee_args(wrapped_args)[2]
  62. if args:
  63. raise InvalidArguments('Function does not take positional arguments.')
  64. return f(*wrapped_args, **wrapped_kwargs)
  65. return wrapped
  66. def noKwargs(f):
  67. @wraps(f)
  68. def wrapped(*wrapped_args, **wrapped_kwargs):
  69. kwargs = _get_callee_args(wrapped_args)[3]
  70. if kwargs:
  71. raise InvalidArguments('Function does not take keyword arguments.')
  72. return f(*wrapped_args, **wrapped_kwargs)
  73. return wrapped
  74. def stringArgs(f):
  75. @wraps(f)
  76. def wrapped(*wrapped_args, **wrapped_kwargs):
  77. args = _get_callee_args(wrapped_args)[2]
  78. assert(isinstance(args, list))
  79. check_stringlist(args)
  80. return f(*wrapped_args, **wrapped_kwargs)
  81. return wrapped
  82. def noArgsFlattening(f):
  83. setattr(f, 'no-args-flattening', True)
  84. return f
  85. class permittedKwargs:
  86. def __init__(self, permitted):
  87. self.permitted = permitted
  88. def __call__(self, f):
  89. @wraps(f)
  90. def wrapped(*wrapped_args, **wrapped_kwargs):
  91. s, node_or_state, args, kwargs = _get_callee_args(wrapped_args)
  92. loc = types.SimpleNamespace()
  93. if hasattr(s, 'subdir'):
  94. loc.subdir = s.subdir
  95. loc.lineno = s.current_lineno
  96. elif node_or_state and hasattr(node_or_state, 'subdir'):
  97. loc.subdir = node_or_state.subdir
  98. loc.lineno = node_or_state.current_lineno
  99. else:
  100. loc = None
  101. for k in kwargs:
  102. if k not in self.permitted:
  103. mlog.warning('''Passed invalid keyword argument "{}".'''.format(k), location=loc)
  104. mlog.warning('This will become a hard error in the future.')
  105. return f(*wrapped_args, **wrapped_kwargs)
  106. return wrapped
  107. class FeatureNew:
  108. """Checks for new features"""
  109. # Shared across all instances
  110. feature_versions = dict()
  111. def __init__(self, feature_name, version):
  112. self.feature_name = feature_name
  113. self.feature_version = version
  114. def add_called_feature(self):
  115. if self.feature_version not in self.feature_versions:
  116. self.feature_versions[self.feature_version] = set()
  117. self.feature_versions[self.feature_version].add(self.feature_name)
  118. def called_features_report():
  119. fv = FeatureNew.feature_versions
  120. if fv:
  121. print('Minimum version of features used:')
  122. for version in sorted(fv.keys()):
  123. print('{}: {}'.format(version, fv[version]))
  124. def use(self):
  125. self.add_called_feature()
  126. tv = mesonlib.target_version
  127. if tv == '':
  128. return
  129. if not mesonlib.version_compare_condition_with_min(tv, self.feature_version):
  130. mlog.warning(
  131. '''Project targetting \'{}\' but tried to use feature introduced in \'{}\': {}'''
  132. .format(tv, self.feature_version, self.feature_name))
  133. def __call__(self, f):
  134. @wraps(f)
  135. def wrapped(*wrapped_args, **wrapped_kwargs):
  136. self.use()
  137. return f(*wrapped_args, **wrapped_kwargs)
  138. return wrapped
  139. class FeatureDeprecated:
  140. """Checks for deprecated features"""
  141. # Shared across all instances
  142. feature_versions = dict()
  143. def __init__(self, feature_name, version):
  144. self.feature_name = feature_name
  145. self.feature_version = version
  146. def add_called_feature(self):
  147. if self.feature_version not in self.feature_versions:
  148. self.feature_versions[self.feature_version] = set()
  149. self.feature_versions[self.feature_version].add(self.feature_name)
  150. def called_features_report():
  151. fv = FeatureDeprecated.feature_versions
  152. if fv:
  153. print('Deprecated features used:')
  154. for version in sorted(fv.keys()):
  155. print('{}: {}'.format(version, fv[version]))
  156. def use(self):
  157. self.add_called_feature()
  158. tv = mesonlib.target_version
  159. if tv == '':
  160. return
  161. if not mesonlib.version_compare_condition_with_max(tv, self.feature_version):
  162. mlog.warning(
  163. '''Project targetting \'{}\' but tried to use feature deprecated since \'{}\': {}'''
  164. .format(tv, self.feature_version, self.feature_name))
  165. def __call__(self, f):
  166. @wraps(f)
  167. def wrapped(*wrapped_args, **wrapped_kwargs):
  168. self.use()
  169. return f(*wrapped_args, **wrapped_kwargs)
  170. return wrapped
  171. class FeatureNewKwargs:
  172. def __init__(self, feature_name, feature_version, kwargs):
  173. self.feature_name = feature_name
  174. self.feature_version = feature_version
  175. self.kwargs = kwargs
  176. def __call__(self, f):
  177. @wraps(f)
  178. def wrapped(*wrapped_args, **wrapped_kwargs):
  179. for arg in self.kwargs:
  180. if arg in wrapped_kwargs:
  181. FeatureNew(arg + ' arg in ' + self.feature_name, self.feature_version).use()
  182. return f(*wrapped_args, **wrapped_kwargs)
  183. return wrapped
  184. class FeatureDeprecatedKwargs:
  185. def __init__(self, feature_name, feature_version, kwargs):
  186. self.feature_name = feature_name
  187. self.feature_version = feature_version
  188. self.kwargs = kwargs
  189. def __call__(self, f):
  190. @wraps(f)
  191. def wrapped(*wrapped_args, **wrapped_kwargs):
  192. for arg in self.kwargs:
  193. if arg in wrapped_kwargs:
  194. FeatureDeprecated(arg + ' arg in ' + self.feature_name, self.feature_version).use()
  195. return f(*wrapped_args, **wrapped_kwargs)
  196. return wrapped
  197. class InterpreterException(mesonlib.MesonException):
  198. pass
  199. class InvalidCode(InterpreterException):
  200. pass
  201. class InvalidArguments(InterpreterException):
  202. pass
  203. class SubdirDoneRequest(BaseException):
  204. pass
  205. class InterpreterObject:
  206. def __init__(self):
  207. self.methods = {}
  208. def method_call(self, method_name, args, kwargs):
  209. if method_name in self.methods:
  210. method = self.methods[method_name]
  211. if not getattr(method, 'no-args-flattening', False):
  212. args = flatten(args)
  213. return method(args, kwargs)
  214. raise InvalidCode('Unknown method "%s" in object.' % method_name)
  215. class MutableInterpreterObject(InterpreterObject):
  216. def __init__(self):
  217. super().__init__()
  218. class Disabler(InterpreterObject):
  219. def __init__(self):
  220. super().__init__()
  221. self.methods.update({'found': self.found_method})
  222. def found_method(self, args, kwargs):
  223. return False
  224. def is_disabler(i):
  225. return isinstance(i, Disabler)
  226. def is_disabled(args, kwargs):
  227. for i in args:
  228. if isinstance(i, Disabler):
  229. return True
  230. for i in kwargs.values():
  231. if isinstance(i, Disabler):
  232. return True
  233. if isinstance(i, list):
  234. for j in i:
  235. if isinstance(j, Disabler):
  236. return True
  237. return False
  238. class InterpreterBase:
  239. def __init__(self, source_root, subdir):
  240. self.source_root = source_root
  241. self.funcs = {}
  242. self.builtin = {}
  243. self.subdir = subdir
  244. self.variables = {}
  245. self.argument_depth = 0
  246. self.current_lineno = -1
  247. def load_root_meson_file(self):
  248. mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
  249. if not os.path.isfile(mesonfile):
  250. raise InvalidArguments('Missing Meson file in %s' % mesonfile)
  251. with open(mesonfile, encoding='utf8') as mf:
  252. code = mf.read()
  253. if code.isspace():
  254. raise InvalidCode('Builder file is empty.')
  255. assert(isinstance(code, str))
  256. try:
  257. self.ast = mparser.Parser(code, self.subdir).parse()
  258. except mesonlib.MesonException as me:
  259. me.file = environment.build_filename
  260. raise me
  261. def parse_project(self):
  262. """
  263. Parses project() and initializes languages, compilers etc. Do this
  264. early because we need this before we parse the rest of the AST.
  265. """
  266. self.evaluate_codeblock(self.ast, end=1)
  267. def sanity_check_ast(self):
  268. if not isinstance(self.ast, mparser.CodeBlockNode):
  269. raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.')
  270. if not self.ast.lines:
  271. raise InvalidCode('No statements in code.')
  272. first = self.ast.lines[0]
  273. if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project':
  274. raise InvalidCode('First statement must be a call to project')
  275. def run(self):
  276. # Evaluate everything after the first line, which is project() because
  277. # we already parsed that in self.parse_project()
  278. try:
  279. self.evaluate_codeblock(self.ast, start=1)
  280. except SubdirDoneRequest:
  281. pass
  282. def evaluate_codeblock(self, node, start=0, end=None):
  283. if node is None:
  284. return
  285. if not isinstance(node, mparser.CodeBlockNode):
  286. e = InvalidCode('Tried to execute a non-codeblock. Possibly a bug in the parser.')
  287. e.lineno = node.lineno
  288. e.colno = node.colno
  289. raise e
  290. statements = node.lines[start:end]
  291. i = 0
  292. while i < len(statements):
  293. cur = statements[i]
  294. try:
  295. self.current_lineno = cur.lineno
  296. self.evaluate_statement(cur)
  297. except Exception as e:
  298. if not(hasattr(e, 'lineno')):
  299. e.lineno = cur.lineno
  300. e.colno = cur.colno
  301. e.file = os.path.join(self.subdir, 'meson.build')
  302. raise e
  303. i += 1 # In THE FUTURE jump over blocks and stuff.
  304. def evaluate_statement(self, cur):
  305. if isinstance(cur, mparser.FunctionNode):
  306. return self.function_call(cur)
  307. elif isinstance(cur, mparser.AssignmentNode):
  308. return self.assignment(cur)
  309. elif isinstance(cur, mparser.MethodNode):
  310. return self.method_call(cur)
  311. elif isinstance(cur, mparser.StringNode):
  312. return cur.value
  313. elif isinstance(cur, mparser.BooleanNode):
  314. return cur.value
  315. elif isinstance(cur, mparser.IfClauseNode):
  316. return self.evaluate_if(cur)
  317. elif isinstance(cur, mparser.IdNode):
  318. return self.get_variable(cur.value)
  319. elif isinstance(cur, mparser.ComparisonNode):
  320. return self.evaluate_comparison(cur)
  321. elif isinstance(cur, mparser.ArrayNode):
  322. return self.evaluate_arraystatement(cur)
  323. elif isinstance(cur, mparser.DictNode):
  324. return self.evaluate_dictstatement(cur)
  325. elif isinstance(cur, mparser.NumberNode):
  326. return cur.value
  327. elif isinstance(cur, mparser.AndNode):
  328. return self.evaluate_andstatement(cur)
  329. elif isinstance(cur, mparser.OrNode):
  330. return self.evaluate_orstatement(cur)
  331. elif isinstance(cur, mparser.NotNode):
  332. return self.evaluate_notstatement(cur)
  333. elif isinstance(cur, mparser.UMinusNode):
  334. return self.evaluate_uminusstatement(cur)
  335. elif isinstance(cur, mparser.ArithmeticNode):
  336. return self.evaluate_arithmeticstatement(cur)
  337. elif isinstance(cur, mparser.ForeachClauseNode):
  338. return self.evaluate_foreach(cur)
  339. elif isinstance(cur, mparser.PlusAssignmentNode):
  340. return self.evaluate_plusassign(cur)
  341. elif isinstance(cur, mparser.IndexNode):
  342. return self.evaluate_indexing(cur)
  343. elif isinstance(cur, mparser.TernaryNode):
  344. return self.evaluate_ternary(cur)
  345. elif self.is_elementary_type(cur):
  346. return cur
  347. else:
  348. raise InvalidCode("Unknown statement.")
  349. def evaluate_arraystatement(self, cur):
  350. (arguments, kwargs) = self.reduce_arguments(cur.args)
  351. if len(kwargs) > 0:
  352. raise InvalidCode('Keyword arguments are invalid in array construction.')
  353. return arguments
  354. @FeatureNew('dict', '0.47.0')
  355. def evaluate_dictstatement(self, cur):
  356. (arguments, kwargs) = self.reduce_arguments(cur.args)
  357. assert (not arguments)
  358. return kwargs
  359. def evaluate_notstatement(self, cur):
  360. v = self.evaluate_statement(cur.value)
  361. if not isinstance(v, bool):
  362. raise InterpreterException('Argument to "not" is not a boolean.')
  363. return not v
  364. def evaluate_if(self, node):
  365. assert(isinstance(node, mparser.IfClauseNode))
  366. for i in node.ifs:
  367. result = self.evaluate_statement(i.condition)
  368. if is_disabler(result):
  369. return result
  370. if not(isinstance(result, bool)):
  371. raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result))
  372. if result:
  373. self.evaluate_codeblock(i.block)
  374. return
  375. if not isinstance(node.elseblock, mparser.EmptyNode):
  376. self.evaluate_codeblock(node.elseblock)
  377. def validate_comparison_types(self, val1, val2):
  378. if type(val1) != type(val2):
  379. return False
  380. return True
  381. def evaluate_comparison(self, node):
  382. val1 = self.evaluate_statement(node.left)
  383. if is_disabler(val1):
  384. return val1
  385. val2 = self.evaluate_statement(node.right)
  386. if is_disabler(val2):
  387. return val2
  388. valid = self.validate_comparison_types(val1, val2)
  389. # Ordering comparisons of different types isn't allowed since PR #1810
  390. # (0.41.0). Since PR #2884 we also warn about equality comparisons of
  391. # different types, which will one day become an error.
  392. if not valid and (node.ctype == '==' or node.ctype == '!='):
  393. mlog.warning('''Trying to compare values of different types ({}, {}) using {}.
  394. The result of this is undefined and will become a hard error in a future Meson release.'''
  395. .format(type(val1).__name__, type(val2).__name__, node.ctype), location=node)
  396. if node.ctype == '==':
  397. return val1 == val2
  398. elif node.ctype == '!=':
  399. return val1 != val2
  400. elif not valid:
  401. raise InterpreterException(
  402. 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__,
  403. type(val2).__name__,
  404. node.ctype))
  405. elif not self.is_elementary_type(val1):
  406. raise InterpreterException('{} can only be compared for equality.'.format(node.left.value))
  407. elif not self.is_elementary_type(val2):
  408. raise InterpreterException('{} can only be compared for equality.'.format(node.right.value))
  409. elif node.ctype == '<':
  410. return val1 < val2
  411. elif node.ctype == '<=':
  412. return val1 <= val2
  413. elif node.ctype == '>':
  414. return val1 > val2
  415. elif node.ctype == '>=':
  416. return val1 >= val2
  417. else:
  418. raise InvalidCode('You broke my compare eval.')
  419. def evaluate_andstatement(self, cur):
  420. l = self.evaluate_statement(cur.left)
  421. if is_disabler(l):
  422. return l
  423. if not isinstance(l, bool):
  424. raise InterpreterException('First argument to "and" is not a boolean.')
  425. if not l:
  426. return False
  427. r = self.evaluate_statement(cur.right)
  428. if is_disabler(r):
  429. return r
  430. if not isinstance(r, bool):
  431. raise InterpreterException('Second argument to "and" is not a boolean.')
  432. return r
  433. def evaluate_orstatement(self, cur):
  434. l = self.evaluate_statement(cur.left)
  435. if is_disabler(l):
  436. return l
  437. if not isinstance(l, bool):
  438. raise InterpreterException('First argument to "or" is not a boolean.')
  439. if l:
  440. return True
  441. r = self.evaluate_statement(cur.right)
  442. if is_disabler(r):
  443. return r
  444. if not isinstance(r, bool):
  445. raise InterpreterException('Second argument to "or" is not a boolean.')
  446. return r
  447. def evaluate_uminusstatement(self, cur):
  448. v = self.evaluate_statement(cur.value)
  449. if is_disabler(v):
  450. return v
  451. if not isinstance(v, int):
  452. raise InterpreterException('Argument to negation is not an integer.')
  453. return -v
  454. def evaluate_arithmeticstatement(self, cur):
  455. l = self.evaluate_statement(cur.left)
  456. if is_disabler(l):
  457. return l
  458. r = self.evaluate_statement(cur.right)
  459. if is_disabler(r):
  460. return r
  461. if cur.operation == 'add':
  462. try:
  463. return l + r
  464. except Exception as e:
  465. raise InvalidCode('Invalid use of addition: ' + str(e))
  466. elif cur.operation == 'sub':
  467. if not isinstance(l, int) or not isinstance(r, int):
  468. raise InvalidCode('Subtraction works only with integers.')
  469. return l - r
  470. elif cur.operation == 'mul':
  471. if not isinstance(l, int) or not isinstance(r, int):
  472. raise InvalidCode('Multiplication works only with integers.')
  473. return l * r
  474. elif cur.operation == 'div':
  475. if not isinstance(l, int) or not isinstance(r, int):
  476. raise InvalidCode('Division works only with integers.')
  477. return l // r
  478. elif cur.operation == 'mod':
  479. if not isinstance(l, int) or not isinstance(r, int):
  480. raise InvalidCode('Modulo works only with integers.')
  481. return l % r
  482. else:
  483. raise InvalidCode('You broke me.')
  484. def evaluate_ternary(self, node):
  485. assert(isinstance(node, mparser.TernaryNode))
  486. result = self.evaluate_statement(node.condition)
  487. if is_disabler(result):
  488. return result
  489. if not isinstance(result, bool):
  490. raise InterpreterException('Ternary condition is not boolean.')
  491. if result:
  492. return self.evaluate_statement(node.trueblock)
  493. else:
  494. return self.evaluate_statement(node.falseblock)
  495. def evaluate_foreach(self, node):
  496. assert(isinstance(node, mparser.ForeachClauseNode))
  497. items = self.evaluate_statement(node.items)
  498. if isinstance(items, list):
  499. if len(node.varnames) != 1:
  500. raise InvalidArguments('Foreach on array does not unpack')
  501. varname = node.varnames[0].value
  502. if is_disabler(items):
  503. return items
  504. for item in items:
  505. self.set_variable(varname, item)
  506. self.evaluate_codeblock(node.block)
  507. elif isinstance(items, dict):
  508. if len(node.varnames) != 2:
  509. raise InvalidArguments('Foreach on dict unpacks key and value')
  510. if is_disabler(items):
  511. return items
  512. for key, value in items.items():
  513. self.set_variable(node.varnames[0].value, key)
  514. self.set_variable(node.varnames[1].value, value)
  515. self.evaluate_codeblock(node.block)
  516. else:
  517. raise InvalidArguments('Items of foreach loop must be an array or a dict')
  518. def evaluate_plusassign(self, node):
  519. assert(isinstance(node, mparser.PlusAssignmentNode))
  520. varname = node.var_name
  521. addition = self.evaluate_statement(node.value)
  522. if is_disabler(addition):
  523. self.set_variable(varname, addition)
  524. return
  525. # Remember that all variables are immutable. We must always create a
  526. # full new variable and then assign it.
  527. old_variable = self.get_variable(varname)
  528. if isinstance(old_variable, str):
  529. if not isinstance(addition, str):
  530. raise InvalidArguments('The += operator requires a string on the right hand side if the variable on the left is a string')
  531. new_value = old_variable + addition
  532. elif isinstance(old_variable, int):
  533. if not isinstance(addition, int):
  534. raise InvalidArguments('The += operator requires an int on the right hand side if the variable on the left is an int')
  535. new_value = old_variable + addition
  536. elif not isinstance(old_variable, list):
  537. raise InvalidArguments('The += operator currently only works with arrays, strings or ints ')
  538. # Add other data types here.
  539. else:
  540. if isinstance(addition, list):
  541. new_value = old_variable + addition
  542. else:
  543. new_value = old_variable + [addition]
  544. self.set_variable(varname, new_value)
  545. def evaluate_indexing(self, node):
  546. assert(isinstance(node, mparser.IndexNode))
  547. iobject = self.evaluate_statement(node.iobject)
  548. if is_disabler(iobject):
  549. return iobject
  550. if not hasattr(iobject, '__getitem__'):
  551. raise InterpreterException(
  552. 'Tried to index an object that doesn\'t support indexing.')
  553. index = self.evaluate_statement(node.index)
  554. if isinstance(iobject, dict):
  555. if not isinstance(index, str):
  556. raise InterpreterException('Key is not a string')
  557. try:
  558. return iobject[index]
  559. except KeyError:
  560. raise InterpreterException('Key %s is not in dict' % index)
  561. else:
  562. if not isinstance(index, int):
  563. raise InterpreterException('Index value is not an integer.')
  564. try:
  565. return iobject[index]
  566. except IndexError:
  567. raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
  568. def function_call(self, node):
  569. func_name = node.func_name
  570. (posargs, kwargs) = self.reduce_arguments(node.args)
  571. if is_disabled(posargs, kwargs):
  572. return Disabler()
  573. if func_name in self.funcs:
  574. func = self.funcs[func_name]
  575. if not getattr(func, 'no-args-flattening', False):
  576. posargs = flatten(posargs)
  577. return func(node, posargs, kwargs)
  578. else:
  579. self.unknown_function_called(func_name)
  580. def method_call(self, node):
  581. invokable = node.source_object
  582. if isinstance(invokable, mparser.IdNode):
  583. object_name = invokable.value
  584. obj = self.get_variable(object_name)
  585. else:
  586. obj = self.evaluate_statement(invokable)
  587. method_name = node.name
  588. args = node.args
  589. if isinstance(obj, str):
  590. return self.string_method_call(obj, method_name, args)
  591. if isinstance(obj, bool):
  592. return self.bool_method_call(obj, method_name, args)
  593. if isinstance(obj, int):
  594. return self.int_method_call(obj, method_name, args)
  595. if isinstance(obj, list):
  596. return self.array_method_call(obj, method_name, args)
  597. if isinstance(obj, dict):
  598. return self.dict_method_call(obj, method_name, args)
  599. if isinstance(obj, mesonlib.File):
  600. raise InvalidArguments('File object "%s" is not callable.' % obj)
  601. if not isinstance(obj, InterpreterObject):
  602. raise InvalidArguments('Variable "%s" is not callable.' % object_name)
  603. (args, kwargs) = self.reduce_arguments(args)
  604. # Special case. This is the only thing you can do with a disabler
  605. # object. Every other use immediately returns the disabler object.
  606. if isinstance(obj, Disabler) and method_name == 'found':
  607. return False
  608. if is_disabled(args, kwargs):
  609. return Disabler()
  610. if method_name == 'extract_objects':
  611. self.validate_extraction(obj.held_object)
  612. return obj.method_call(method_name, args, kwargs)
  613. def bool_method_call(self, obj, method_name, args):
  614. (posargs, kwargs) = self.reduce_arguments(args)
  615. if is_disabled(posargs, kwargs):
  616. return Disabler()
  617. if method_name == 'to_string':
  618. if not posargs:
  619. if obj:
  620. return 'true'
  621. else:
  622. return 'false'
  623. elif len(posargs) == 2 and isinstance(posargs[0], str) and isinstance(posargs[1], str):
  624. if obj:
  625. return posargs[0]
  626. else:
  627. return posargs[1]
  628. else:
  629. 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.')
  630. elif method_name == 'to_int':
  631. if obj:
  632. return 1
  633. else:
  634. return 0
  635. else:
  636. raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
  637. def int_method_call(self, obj, method_name, args):
  638. (posargs, kwargs) = self.reduce_arguments(args)
  639. if is_disabled(posargs, kwargs):
  640. return Disabler()
  641. if method_name == 'is_even':
  642. if not posargs:
  643. return obj % 2 == 0
  644. else:
  645. raise InterpreterException('int.is_even() must have no arguments.')
  646. elif method_name == 'is_odd':
  647. if not posargs:
  648. return obj % 2 != 0
  649. else:
  650. raise InterpreterException('int.is_odd() must have no arguments.')
  651. elif method_name == 'to_string':
  652. if not posargs:
  653. return str(obj)
  654. else:
  655. raise InterpreterException('int.to_string() must have no arguments.')
  656. else:
  657. raise InterpreterException('Unknown method "%s" for an integer.' % method_name)
  658. @staticmethod
  659. def _get_one_string_posarg(posargs, method_name):
  660. if len(posargs) > 1:
  661. m = '{}() must have zero or one arguments'
  662. raise InterpreterException(m.format(method_name))
  663. elif len(posargs) == 1:
  664. s = posargs[0]
  665. if not isinstance(s, str):
  666. m = '{}() argument must be a string'
  667. raise InterpreterException(m.format(method_name))
  668. return s
  669. return None
  670. def string_method_call(self, obj, method_name, args):
  671. (posargs, kwargs) = self.reduce_arguments(args)
  672. if is_disabled(posargs, kwargs):
  673. return Disabler()
  674. if method_name == 'strip':
  675. s = self._get_one_string_posarg(posargs, 'strip')
  676. if s is not None:
  677. return obj.strip(s)
  678. return obj.strip()
  679. elif method_name == 'format':
  680. return self.format_string(obj, args)
  681. elif method_name == 'to_upper':
  682. return obj.upper()
  683. elif method_name == 'to_lower':
  684. return obj.lower()
  685. elif method_name == 'underscorify':
  686. return re.sub(r'[^a-zA-Z0-9]', '_', obj)
  687. elif method_name == 'split':
  688. s = self._get_one_string_posarg(posargs, 'split')
  689. if s is not None:
  690. return obj.split(s)
  691. return obj.split()
  692. elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith':
  693. s = posargs[0]
  694. if not isinstance(s, str):
  695. raise InterpreterException('Argument must be a string.')
  696. if method_name == 'startswith':
  697. return obj.startswith(s)
  698. elif method_name == 'contains':
  699. return obj.find(s) >= 0
  700. return obj.endswith(s)
  701. elif method_name == 'to_int':
  702. try:
  703. return int(obj)
  704. except Exception:
  705. raise InterpreterException('String {!r} cannot be converted to int'.format(obj))
  706. elif method_name == 'join':
  707. if len(posargs) != 1:
  708. raise InterpreterException('Join() takes exactly one argument.')
  709. strlist = posargs[0]
  710. check_stringlist(strlist)
  711. return obj.join(strlist)
  712. elif method_name == 'version_compare':
  713. if len(posargs) != 1:
  714. raise InterpreterException('Version_compare() takes exactly one argument.')
  715. cmpr = posargs[0]
  716. if not isinstance(cmpr, str):
  717. raise InterpreterException('Version_compare() argument must be a string.')
  718. return mesonlib.version_compare(obj, cmpr)
  719. raise InterpreterException('Unknown method "%s" for a string.' % method_name)
  720. def unknown_function_called(self, func_name):
  721. raise InvalidCode('Unknown function "%s".' % func_name)
  722. def array_method_call(self, obj, method_name, args):
  723. (posargs, kwargs) = self.reduce_arguments(args)
  724. if is_disabled(posargs, kwargs):
  725. return Disabler()
  726. if method_name == 'contains':
  727. return self.check_contains(obj, posargs)
  728. elif method_name == 'length':
  729. return len(obj)
  730. elif method_name == 'get':
  731. index = posargs[0]
  732. fallback = None
  733. if len(posargs) == 2:
  734. fallback = posargs[1]
  735. elif len(posargs) > 2:
  736. m = 'Array method \'get()\' only takes two arguments: the ' \
  737. 'index and an optional fallback value if the index is ' \
  738. 'out of range.'
  739. raise InvalidArguments(m)
  740. if not isinstance(index, int):
  741. raise InvalidArguments('Array index must be a number.')
  742. if index < -len(obj) or index >= len(obj):
  743. if fallback is None:
  744. m = 'Array index {!r} is out of bounds for array of size {!r}.'
  745. raise InvalidArguments(m.format(index, len(obj)))
  746. return fallback
  747. return obj[index]
  748. m = 'Arrays do not have a method called {!r}.'
  749. raise InterpreterException(m.format(method_name))
  750. def dict_method_call(self, obj, method_name, args):
  751. (posargs, kwargs) = self.reduce_arguments(args)
  752. if is_disabled(posargs, kwargs):
  753. return Disabler()
  754. if method_name in ('has_key', 'get'):
  755. if method_name == 'has_key':
  756. if len(posargs) != 1:
  757. raise InterpreterException('has_key() takes exactly one argument.')
  758. else:
  759. if len(posargs) not in (1, 2):
  760. raise InterpreterException('get() takes one or two arguments.')
  761. key = posargs[0]
  762. if not isinstance(key, (str)):
  763. raise InvalidArguments('Dictionary key must be a string.')
  764. has_key = key in obj
  765. if method_name == 'has_key':
  766. return has_key
  767. if has_key:
  768. return obj[key]
  769. if len(posargs) == 2:
  770. return posargs[1]
  771. raise InterpreterException('Key {!r} is not in the dictionary.'.format(key))
  772. if method_name == 'keys':
  773. if len(posargs) != 0:
  774. raise InterpreterException('keys() takes no arguments.')
  775. return list(obj.keys())
  776. raise InterpreterException('Dictionaries do not have a method called "%s".' % method_name)
  777. def reduce_arguments(self, args):
  778. assert(isinstance(args, mparser.ArgumentNode))
  779. if args.incorrect_order():
  780. raise InvalidArguments('All keyword arguments must be after positional arguments.')
  781. self.argument_depth += 1
  782. reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
  783. reduced_kw = {}
  784. for key in args.kwargs.keys():
  785. if not isinstance(key, str):
  786. raise InvalidArguments('Keyword argument name is not a string.')
  787. a = args.kwargs[key]
  788. reduced_kw[key] = self.evaluate_statement(a)
  789. if args.kwargs_dict is not None:
  790. reduced_dict = self.evaluate_statement(args.kwargs_dict)
  791. if not isinstance(reduced_dict, dict):
  792. raise InvalidArguments("Keyword argument expansion is not a dictionary.")
  793. reduced_kw.update(reduced_dict)
  794. self.argument_depth -= 1
  795. return reduced_pos, reduced_kw
  796. def assignment(self, node):
  797. assert(isinstance(node, mparser.AssignmentNode))
  798. if self.argument_depth != 0:
  799. raise InvalidArguments('''Tried to assign values inside an argument list.
  800. To specify a keyword argument, use : instead of =.''')
  801. var_name = node.var_name
  802. if not isinstance(var_name, str):
  803. raise InvalidArguments('Tried to assign value to a non-variable.')
  804. value = self.evaluate_statement(node.value)
  805. if not self.is_assignable(value):
  806. raise InvalidCode('Tried to assign an invalid value to variable.')
  807. # For mutable objects we need to make a copy on assignment
  808. if isinstance(value, MutableInterpreterObject):
  809. value = copy.deepcopy(value)
  810. self.set_variable(var_name, value)
  811. return None
  812. def set_variable(self, varname, variable):
  813. if variable is None:
  814. raise InvalidCode('Can not assign None to variable.')
  815. if not isinstance(varname, str):
  816. raise InvalidCode('First argument to set_variable must be a string.')
  817. if not self.is_assignable(variable):
  818. raise InvalidCode('Assigned value not of assignable type.')
  819. if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None:
  820. raise InvalidCode('Invalid variable name: ' + varname)
  821. if varname in self.builtin:
  822. raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
  823. self.variables[varname] = variable
  824. def get_variable(self, varname):
  825. if varname in self.builtin:
  826. return self.builtin[varname]
  827. if varname in self.variables:
  828. return self.variables[varname]
  829. raise InvalidCode('Unknown variable "%s".' % varname)
  830. def is_assignable(self, value):
  831. return isinstance(value, (InterpreterObject, dependencies.Dependency,
  832. str, int, list, dict, mesonlib.File))
  833. def is_elementary_type(self, v):
  834. return isinstance(v, (int, float, str, bool, list))