interpreterbase.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  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_or_state, args, kwargs):
  52. if hasattr(s, 'subdir'):
  53. subdir = s.subdir
  54. lineno = s.current_lineno
  55. elif hasattr(node_or_state, 'subdir'):
  56. subdir = node_or_state.subdir
  57. lineno = node_or_state.current_lineno
  58. for k in kwargs:
  59. if k not in self.permitted:
  60. fname = os.path.join(subdir, environment.build_filename)
  61. mlog.warning('''Passed invalid keyword argument "%s" in %s line %d.
  62. This will become a hard error in the future.''' % (k, fname, lineno))
  63. return f(s, node_or_state, args, kwargs)
  64. return wrapped
  65. class InterpreterException(mesonlib.MesonException):
  66. pass
  67. class InvalidCode(InterpreterException):
  68. pass
  69. class InvalidArguments(InterpreterException):
  70. pass
  71. class InterpreterObject:
  72. def __init__(self):
  73. self.methods = {}
  74. def method_call(self, method_name, args, kwargs):
  75. if method_name in self.methods:
  76. return self.methods[method_name](args, kwargs)
  77. raise InvalidCode('Unknown method "%s" in object.' % method_name)
  78. class MutableInterpreterObject(InterpreterObject):
  79. def __init__(self):
  80. super().__init__()
  81. class Disabler(InterpreterObject):
  82. def __init__(self):
  83. super().__init__()
  84. self.methods.update({'found': self.found_method})
  85. def found_method(self, args, kwargs):
  86. return False
  87. def is_disabler(i):
  88. return isinstance(i, Disabler)
  89. def is_disabled(args, kwargs):
  90. for i in args:
  91. if isinstance(i, Disabler):
  92. return True
  93. for i in kwargs.values():
  94. if isinstance(i, Disabler):
  95. return True
  96. if isinstance(i, list):
  97. for j in i:
  98. if isinstance(j, Disabler):
  99. return True
  100. return False
  101. class InterpreterBase:
  102. def __init__(self, source_root, subdir):
  103. self.source_root = source_root
  104. self.funcs = {}
  105. self.builtin = {}
  106. self.subdir = subdir
  107. self.variables = {}
  108. self.argument_depth = 0
  109. self.current_lineno = -1
  110. def load_root_meson_file(self):
  111. mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
  112. if not os.path.isfile(mesonfile):
  113. raise InvalidArguments('Missing Meson file in %s' % mesonfile)
  114. with open(mesonfile, encoding='utf8') as mf:
  115. code = mf.read()
  116. if code.isspace():
  117. raise InvalidCode('Builder file is empty.')
  118. assert(isinstance(code, str))
  119. try:
  120. self.ast = mparser.Parser(code, self.subdir).parse()
  121. except mesonlib.MesonException as me:
  122. me.file = environment.build_filename
  123. raise me
  124. def parse_project(self):
  125. """
  126. Parses project() and initializes languages, compilers etc. Do this
  127. early because we need this before we parse the rest of the AST.
  128. """
  129. self.evaluate_codeblock(self.ast, end=1)
  130. def sanity_check_ast(self):
  131. if not isinstance(self.ast, mparser.CodeBlockNode):
  132. raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.')
  133. if not self.ast.lines:
  134. raise InvalidCode('No statements in code.')
  135. first = self.ast.lines[0]
  136. if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project':
  137. raise InvalidCode('First statement must be a call to project')
  138. def run(self):
  139. # Evaluate everything after the first line, which is project() because
  140. # we already parsed that in self.parse_project()
  141. self.evaluate_codeblock(self.ast, start=1)
  142. def evaluate_codeblock(self, node, start=0, end=None):
  143. if node is None:
  144. return
  145. if not isinstance(node, mparser.CodeBlockNode):
  146. e = InvalidCode('Tried to execute a non-codeblock. Possibly a bug in the parser.')
  147. e.lineno = node.lineno
  148. e.colno = node.colno
  149. raise e
  150. statements = node.lines[start:end]
  151. i = 0
  152. while i < len(statements):
  153. cur = statements[i]
  154. try:
  155. self.current_lineno = cur.lineno
  156. self.evaluate_statement(cur)
  157. except Exception as e:
  158. if not(hasattr(e, 'lineno')):
  159. e.lineno = cur.lineno
  160. e.colno = cur.colno
  161. e.file = os.path.join(self.subdir, 'meson.build')
  162. raise e
  163. i += 1 # In THE FUTURE jump over blocks and stuff.
  164. def evaluate_statement(self, cur):
  165. if isinstance(cur, mparser.FunctionNode):
  166. return self.function_call(cur)
  167. elif isinstance(cur, mparser.AssignmentNode):
  168. return self.assignment(cur)
  169. elif isinstance(cur, mparser.MethodNode):
  170. return self.method_call(cur)
  171. elif isinstance(cur, mparser.StringNode):
  172. return cur.value
  173. elif isinstance(cur, mparser.BooleanNode):
  174. return cur.value
  175. elif isinstance(cur, mparser.IfClauseNode):
  176. return self.evaluate_if(cur)
  177. elif isinstance(cur, mparser.IdNode):
  178. return self.get_variable(cur.value)
  179. elif isinstance(cur, mparser.ComparisonNode):
  180. return self.evaluate_comparison(cur)
  181. elif isinstance(cur, mparser.ArrayNode):
  182. return self.evaluate_arraystatement(cur)
  183. elif isinstance(cur, mparser.NumberNode):
  184. return cur.value
  185. elif isinstance(cur, mparser.AndNode):
  186. return self.evaluate_andstatement(cur)
  187. elif isinstance(cur, mparser.OrNode):
  188. return self.evaluate_orstatement(cur)
  189. elif isinstance(cur, mparser.NotNode):
  190. return self.evaluate_notstatement(cur)
  191. elif isinstance(cur, mparser.UMinusNode):
  192. return self.evaluate_uminusstatement(cur)
  193. elif isinstance(cur, mparser.ArithmeticNode):
  194. return self.evaluate_arithmeticstatement(cur)
  195. elif isinstance(cur, mparser.ForeachClauseNode):
  196. return self.evaluate_foreach(cur)
  197. elif isinstance(cur, mparser.PlusAssignmentNode):
  198. return self.evaluate_plusassign(cur)
  199. elif isinstance(cur, mparser.IndexNode):
  200. return self.evaluate_indexing(cur)
  201. elif isinstance(cur, mparser.TernaryNode):
  202. return self.evaluate_ternary(cur)
  203. elif self.is_elementary_type(cur):
  204. return cur
  205. else:
  206. raise InvalidCode("Unknown statement.")
  207. def evaluate_arraystatement(self, cur):
  208. (arguments, kwargs) = self.reduce_arguments(cur.args)
  209. if len(kwargs) > 0:
  210. raise InvalidCode('Keyword arguments are invalid in array construction.')
  211. return arguments
  212. def evaluate_notstatement(self, cur):
  213. v = self.evaluate_statement(cur.value)
  214. if not isinstance(v, bool):
  215. raise InterpreterException('Argument to "not" is not a boolean.')
  216. return not v
  217. def evaluate_if(self, node):
  218. assert(isinstance(node, mparser.IfClauseNode))
  219. for i in node.ifs:
  220. result = self.evaluate_statement(i.condition)
  221. if is_disabler(result):
  222. return result
  223. if not(isinstance(result, bool)):
  224. raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result))
  225. if result:
  226. self.evaluate_codeblock(i.block)
  227. return
  228. if not isinstance(node.elseblock, mparser.EmptyNode):
  229. self.evaluate_codeblock(node.elseblock)
  230. def evaluate_comparison(self, node):
  231. val1 = self.evaluate_statement(node.left)
  232. if is_disabler(val1):
  233. return val1
  234. val2 = self.evaluate_statement(node.right)
  235. if is_disabler(val2):
  236. return val2
  237. if node.ctype == '==':
  238. return val1 == val2
  239. elif node.ctype == '!=':
  240. return val1 != val2
  241. elif not isinstance(val1, type(val2)):
  242. raise InterpreterException(
  243. 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__,
  244. type(val2).__name__,
  245. node.ctype))
  246. elif not self.is_elementary_type(val1):
  247. raise InterpreterException('{} can only be compared for equality.'.format(node.left.value))
  248. elif not self.is_elementary_type(val2):
  249. raise InterpreterException('{} can only be compared for equality.'.format(node.right.value))
  250. elif node.ctype == '<':
  251. return val1 < val2
  252. elif node.ctype == '<=':
  253. return val1 <= val2
  254. elif node.ctype == '>':
  255. return val1 > val2
  256. elif node.ctype == '>=':
  257. return val1 >= val2
  258. else:
  259. raise InvalidCode('You broke my compare eval.')
  260. def evaluate_andstatement(self, cur):
  261. l = self.evaluate_statement(cur.left)
  262. if is_disabler(l):
  263. return l
  264. if not isinstance(l, bool):
  265. raise InterpreterException('First argument to "and" is not a boolean.')
  266. if not l:
  267. return False
  268. r = self.evaluate_statement(cur.right)
  269. if is_disabler(r):
  270. return r
  271. if not isinstance(r, bool):
  272. raise InterpreterException('Second argument to "and" is not a boolean.')
  273. return r
  274. def evaluate_orstatement(self, cur):
  275. l = self.evaluate_statement(cur.left)
  276. if is_disabler(l):
  277. return l
  278. if not isinstance(l, bool):
  279. raise InterpreterException('First argument to "or" is not a boolean.')
  280. if l:
  281. return True
  282. r = self.evaluate_statement(cur.right)
  283. if is_disabler(r):
  284. return r
  285. if not isinstance(r, bool):
  286. raise InterpreterException('Second argument to "or" is not a boolean.')
  287. return r
  288. def evaluate_uminusstatement(self, cur):
  289. v = self.evaluate_statement(cur.value)
  290. if is_disabler(v):
  291. return v
  292. if not isinstance(v, int):
  293. raise InterpreterException('Argument to negation is not an integer.')
  294. return -v
  295. def evaluate_arithmeticstatement(self, cur):
  296. l = self.evaluate_statement(cur.left)
  297. if is_disabler(l):
  298. return l
  299. r = self.evaluate_statement(cur.right)
  300. if is_disabler(r):
  301. return r
  302. if cur.operation == 'add':
  303. try:
  304. return l + r
  305. except Exception as e:
  306. raise InvalidCode('Invalid use of addition: ' + str(e))
  307. elif cur.operation == 'sub':
  308. if not isinstance(l, int) or not isinstance(r, int):
  309. raise InvalidCode('Subtraction works only with integers.')
  310. return l - r
  311. elif cur.operation == 'mul':
  312. if not isinstance(l, int) or not isinstance(r, int):
  313. raise InvalidCode('Multiplication works only with integers.')
  314. return l * r
  315. elif cur.operation == 'div':
  316. if not isinstance(l, int) or not isinstance(r, int):
  317. raise InvalidCode('Division works only with integers.')
  318. return l // r
  319. elif cur.operation == 'mod':
  320. if not isinstance(l, int) or not isinstance(r, int):
  321. raise InvalidCode('Modulo works only with integers.')
  322. return l % r
  323. else:
  324. raise InvalidCode('You broke me.')
  325. def evaluate_ternary(self, node):
  326. assert(isinstance(node, mparser.TernaryNode))
  327. result = self.evaluate_statement(node.condition)
  328. if is_disabler(result):
  329. return result
  330. if not isinstance(result, bool):
  331. raise InterpreterException('Ternary condition is not boolean.')
  332. if result:
  333. return self.evaluate_statement(node.trueblock)
  334. else:
  335. return self.evaluate_statement(node.falseblock)
  336. def evaluate_foreach(self, node):
  337. assert(isinstance(node, mparser.ForeachClauseNode))
  338. varname = node.varname.value
  339. items = self.evaluate_statement(node.items)
  340. if is_disabler(items):
  341. return items
  342. if not isinstance(items, list):
  343. raise InvalidArguments('Items of foreach loop is not an array')
  344. for item in items:
  345. self.set_variable(varname, item)
  346. self.evaluate_codeblock(node.block)
  347. def evaluate_plusassign(self, node):
  348. assert(isinstance(node, mparser.PlusAssignmentNode))
  349. varname = node.var_name
  350. addition = self.evaluate_statement(node.value)
  351. if is_disabler(addition):
  352. set_variable(varname, addition)
  353. return
  354. # Remember that all variables are immutable. We must always create a
  355. # full new variable and then assign it.
  356. old_variable = self.get_variable(varname)
  357. if isinstance(old_variable, str):
  358. if not isinstance(addition, str):
  359. raise InvalidArguments('The += operator requires a string on the right hand side if the variable on the left is a string')
  360. new_value = old_variable + addition
  361. elif isinstance(old_variable, int):
  362. if not isinstance(addition, int):
  363. raise InvalidArguments('The += operator requires an int on the right hand side if the variable on the left is an int')
  364. new_value = old_variable + addition
  365. elif not isinstance(old_variable, list):
  366. raise InvalidArguments('The += operator currently only works with arrays, strings or ints ')
  367. # Add other data types here.
  368. else:
  369. if isinstance(addition, list):
  370. new_value = old_variable + addition
  371. else:
  372. new_value = old_variable + [addition]
  373. self.set_variable(varname, new_value)
  374. def evaluate_indexing(self, node):
  375. assert(isinstance(node, mparser.IndexNode))
  376. iobject = self.evaluate_statement(node.iobject)
  377. if is_disabler(iobject):
  378. return iobject
  379. if not hasattr(iobject, '__getitem__'):
  380. raise InterpreterException(
  381. 'Tried to index an object that doesn\'t support indexing.')
  382. index = self.evaluate_statement(node.index)
  383. if not isinstance(index, int):
  384. raise InterpreterException('Index value is not an integer.')
  385. try:
  386. return iobject[index]
  387. except IndexError:
  388. raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
  389. def function_call(self, node):
  390. func_name = node.func_name
  391. (posargs, kwargs) = self.reduce_arguments(node.args)
  392. if is_disabled(posargs, kwargs):
  393. return Disabler()
  394. if func_name in self.funcs:
  395. return self.funcs[func_name](node, self.flatten(posargs), kwargs)
  396. else:
  397. self.unknown_function_called(func_name)
  398. def method_call(self, node):
  399. invokable = node.source_object
  400. if isinstance(invokable, mparser.IdNode):
  401. object_name = invokable.value
  402. obj = self.get_variable(object_name)
  403. else:
  404. obj = self.evaluate_statement(invokable)
  405. method_name = node.name
  406. args = node.args
  407. if isinstance(obj, str):
  408. return self.string_method_call(obj, method_name, args)
  409. if isinstance(obj, bool):
  410. return self.bool_method_call(obj, method_name, args)
  411. if isinstance(obj, int):
  412. return self.int_method_call(obj, method_name, args)
  413. if isinstance(obj, list):
  414. return self.array_method_call(obj, method_name, args)
  415. if isinstance(obj, mesonlib.File):
  416. raise InvalidArguments('File object "%s" is not callable.' % obj)
  417. if not isinstance(obj, InterpreterObject):
  418. raise InvalidArguments('Variable "%s" is not callable.' % object_name)
  419. (args, kwargs) = self.reduce_arguments(args)
  420. # Special case. This is the only thing you can do with a disabler
  421. # object. Every other use immediately returns the disabler object.
  422. if isinstance(obj, Disabler) and method_name == 'found':
  423. return False
  424. if is_disabled(args, kwargs):
  425. return Disabler()
  426. if method_name == 'extract_objects':
  427. self.validate_extraction(obj.held_object)
  428. return obj.method_call(method_name, self.flatten(args), kwargs)
  429. def bool_method_call(self, obj, method_name, args):
  430. (posargs, kwargs) = self.reduce_arguments(args)
  431. if is_disabled(posargs, kwargs):
  432. return Disabler()
  433. if method_name == 'to_string':
  434. if not posargs:
  435. if obj:
  436. return 'true'
  437. else:
  438. return 'false'
  439. elif len(posargs) == 2 and isinstance(posargs[0], str) and isinstance(posargs[1], str):
  440. if obj:
  441. return posargs[0]
  442. else:
  443. return posargs[1]
  444. else:
  445. 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.')
  446. elif method_name == 'to_int':
  447. if obj:
  448. return 1
  449. else:
  450. return 0
  451. else:
  452. raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
  453. def int_method_call(self, obj, method_name, args):
  454. (posargs, kwargs) = self.reduce_arguments(args)
  455. if is_disabled(posargs, kwargs):
  456. return Disabler()
  457. if method_name == 'is_even':
  458. if not posargs:
  459. return obj % 2 == 0
  460. else:
  461. raise InterpreterException('int.is_even() must have no arguments.')
  462. elif method_name == 'is_odd':
  463. if not posargs:
  464. return obj % 2 != 0
  465. else:
  466. raise InterpreterException('int.is_odd() must have no arguments.')
  467. elif method_name == 'to_string':
  468. if not posargs:
  469. return str(obj)
  470. else:
  471. raise InterpreterException('int.to_string() must have no arguments.')
  472. else:
  473. raise InterpreterException('Unknown method "%s" for an integer.' % method_name)
  474. @staticmethod
  475. def _get_one_string_posarg(posargs, method_name):
  476. if len(posargs) > 1:
  477. m = '{}() must have zero or one arguments'
  478. raise InterpreterException(m.format(method_name))
  479. elif len(posargs) == 1:
  480. s = posargs[0]
  481. if not isinstance(s, str):
  482. m = '{}() argument must be a string'
  483. raise InterpreterException(m.format(method_name))
  484. return s
  485. return None
  486. def string_method_call(self, obj, method_name, args):
  487. (posargs, kwargs) = self.reduce_arguments(args)
  488. if is_disabled(posargs, kwargs):
  489. return Disabler()
  490. if method_name == 'strip':
  491. s = self._get_one_string_posarg(posargs, 'strip')
  492. if s is not None:
  493. return obj.strip(s)
  494. return obj.strip()
  495. elif method_name == 'format':
  496. return self.format_string(obj, args)
  497. elif method_name == 'to_upper':
  498. return obj.upper()
  499. elif method_name == 'to_lower':
  500. return obj.lower()
  501. elif method_name == 'underscorify':
  502. return re.sub(r'[^a-zA-Z0-9]', '_', obj)
  503. elif method_name == 'split':
  504. s = self._get_one_string_posarg(posargs, 'split')
  505. if s is not None:
  506. return obj.split(s)
  507. return obj.split()
  508. elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith':
  509. s = posargs[0]
  510. if not isinstance(s, str):
  511. raise InterpreterException('Argument must be a string.')
  512. if method_name == 'startswith':
  513. return obj.startswith(s)
  514. elif method_name == 'contains':
  515. return obj.find(s) >= 0
  516. return obj.endswith(s)
  517. elif method_name == 'to_int':
  518. try:
  519. return int(obj)
  520. except Exception:
  521. raise InterpreterException('String {!r} cannot be converted to int'.format(obj))
  522. elif method_name == 'join':
  523. if len(posargs) != 1:
  524. raise InterpreterException('Join() takes exactly one argument.')
  525. strlist = posargs[0]
  526. check_stringlist(strlist)
  527. return obj.join(strlist)
  528. elif method_name == 'version_compare':
  529. if len(posargs) != 1:
  530. raise InterpreterException('Version_compare() takes exactly one argument.')
  531. cmpr = posargs[0]
  532. if not isinstance(cmpr, str):
  533. raise InterpreterException('Version_compare() argument must be a string.')
  534. return mesonlib.version_compare(obj, cmpr)
  535. raise InterpreterException('Unknown method "%s" for a string.' % method_name)
  536. def unknown_function_called(self, func_name):
  537. raise InvalidCode('Unknown function "%s".' % func_name)
  538. def array_method_call(self, obj, method_name, args):
  539. (posargs, kwargs) = self.reduce_arguments(args)
  540. if is_disabled(posargs, kwargs):
  541. return Disabler()
  542. if method_name == 'contains':
  543. return self.check_contains(obj, posargs)
  544. elif method_name == 'length':
  545. return len(obj)
  546. elif method_name == 'get':
  547. index = posargs[0]
  548. fallback = None
  549. if len(posargs) == 2:
  550. fallback = posargs[1]
  551. elif len(posargs) > 2:
  552. m = 'Array method \'get()\' only takes two arguments: the ' \
  553. 'index and an optional fallback value if the index is ' \
  554. 'out of range.'
  555. raise InvalidArguments(m)
  556. if not isinstance(index, int):
  557. raise InvalidArguments('Array index must be a number.')
  558. if index < -len(obj) or index >= len(obj):
  559. if fallback is None:
  560. m = 'Array index {!r} is out of bounds for array of size {!r}.'
  561. raise InvalidArguments(m.format(index, len(obj)))
  562. return fallback
  563. return obj[index]
  564. m = 'Arrays do not have a method called {!r}.'
  565. raise InterpreterException(m.format(method_name))
  566. def reduce_arguments(self, args):
  567. assert(isinstance(args, mparser.ArgumentNode))
  568. if args.incorrect_order():
  569. raise InvalidArguments('All keyword arguments must be after positional arguments.')
  570. self.argument_depth += 1
  571. reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
  572. reduced_kw = {}
  573. for key in args.kwargs.keys():
  574. if not isinstance(key, str):
  575. raise InvalidArguments('Keyword argument name is not a string.')
  576. a = args.kwargs[key]
  577. reduced_kw[key] = self.evaluate_statement(a)
  578. self.argument_depth -= 1
  579. return reduced_pos, reduced_kw
  580. def flatten(self, args):
  581. if isinstance(args, mparser.StringNode):
  582. return args.value
  583. if isinstance(args, (int, str, mesonlib.File, InterpreterObject)):
  584. return args
  585. result = []
  586. for a in args:
  587. if isinstance(a, list):
  588. rest = self.flatten(a)
  589. result = result + rest
  590. elif isinstance(a, mparser.StringNode):
  591. result.append(a.value)
  592. else:
  593. result.append(a)
  594. return result
  595. def assignment(self, node):
  596. assert(isinstance(node, mparser.AssignmentNode))
  597. if self.argument_depth != 0:
  598. raise InvalidArguments('''Tried to assign values inside an argument list.
  599. To specify a keyword argument, use : instead of =.''')
  600. var_name = node.var_name
  601. if not isinstance(var_name, str):
  602. raise InvalidArguments('Tried to assign value to a non-variable.')
  603. value = self.evaluate_statement(node.value)
  604. if not self.is_assignable(value):
  605. raise InvalidCode('Tried to assign an invalid value to variable.')
  606. # For mutable objects we need to make a copy on assignment
  607. if isinstance(value, MutableInterpreterObject):
  608. value = copy.deepcopy(value)
  609. self.set_variable(var_name, value)
  610. return None
  611. def set_variable(self, varname, variable):
  612. if variable is None:
  613. raise InvalidCode('Can not assign None to variable.')
  614. if not isinstance(varname, str):
  615. raise InvalidCode('First argument to set_variable must be a string.')
  616. if not self.is_assignable(variable):
  617. raise InvalidCode('Assigned value not of assignable type.')
  618. if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None:
  619. raise InvalidCode('Invalid variable name: ' + varname)
  620. if varname in self.builtin:
  621. raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
  622. self.variables[varname] = variable
  623. def get_variable(self, varname):
  624. if varname in self.builtin:
  625. return self.builtin[varname]
  626. if varname in self.variables:
  627. return self.variables[varname]
  628. raise InvalidCode('Unknown variable "%s".' % varname)
  629. def is_assignable(self, value):
  630. return isinstance(value, (InterpreterObject, dependencies.Dependency,
  631. str, int, list, mesonlib.File))
  632. def is_elementary_type(self, v):
  633. return isinstance(v, (int, float, str, bool, list))