Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2753,7 +2753,7 @@ def is_subproject(self) -> bool:
@noSecondLevelHolderResolving
def func_set_variable(self, node: mparser.BaseNode, args: T.Tuple[str, object], kwargs: 'TYPE_kwargs') -> None:
varname, value = args
self.set_variable(varname, value, holderify=True)
self.set_variable(varname, self._holderify(value))

@typed_pos_args('get_variable', (str, Disabler), optargs=[object])
@noKwargs
Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/interpreterbase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
'Disabler',
'is_disabled',

'VoidObject',

'InterpreterException',
'InvalidCode',
'InvalidArguments',
Expand Down Expand Up @@ -129,3 +131,4 @@
from .helpers import default_resolve_key, flatten, resolve_second_level_holders
from .interpreterbase import InterpreterBase
from .operator import MesonOperator
from .void import VoidObject
9 changes: 7 additions & 2 deletions mesonbuild/interpreterbase/baseobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any])

TYPE_elementary = T.Union[str, int, bool, T.List[T.Any], T.Dict[str, T.Any]]
TYPE_elementary = T.Union[str, int, bool, T.List[T.Any], T.Dict[str, T.Any], None]
TYPE_var = T.Union[TYPE_elementary, HoldableObject, 'MesonInterpreterObject']
TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
TYPE_kwargs = T.Dict[str, TYPE_var]
Expand Down Expand Up @@ -75,6 +75,11 @@ def __init__(self, *, subproject: T.Optional[str] = None) -> None:
def display_name(self) -> str:
return type(self).__name__

@property
def is_assignable(self) -> bool:
''' Property used to indicate whether an object can be used at all '''
return True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

properties are really slow, you generally want to avoid using them if at all possible, I'd make this a class variable instead, set the base class to True, and have the VoidHolder class override it in its class definition.


def method_call(
self,
method_name: str,
Expand Down Expand Up @@ -130,7 +135,7 @@ class MesonInterpreterObject(InterpreterObject):
class MutableInterpreterObject:
''' Dummy class to mark the object type as mutable '''

HoldableTypes = (HoldableObject, int, bool, str, list, dict)
HoldableTypes = (HoldableObject, int, bool, str, list, dict, type(None))
TYPE_HoldableTypes = T.Union[TYPE_elementary, HoldableObject]
InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=TYPE_HoldableTypes)

Expand Down
55 changes: 28 additions & 27 deletions mesonbuild/interpreterbase/interpreterbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

from .decorators import FeatureNew
from .disabler import Disabler, is_disabled
from .void import VoidObject
from .helpers import default_resolve_key, flatten, resolve_second_level_holders
from .operator import MesonOperator
from ._unholder import _unholder
Expand All @@ -62,6 +63,7 @@
T.Type[str],
T.Type[list],
T.Type[dict],
T.Type[None],
],
# For some reason, this has to be a callable and can't just be ObjectHolder[InterpreterObjectTypeVar]
T.Callable[[InterpreterObjectTypeVar, 'Interpreter'], ObjectHolder[InterpreterObjectTypeVar]]
Expand Down Expand Up @@ -95,6 +97,10 @@ def __init__(self, source_root: str, subdir: str, subproject: str):
# current meson version target within that if-block.
self.tmp_meson_version = None # type: T.Optional[str]

self.holder_map.update({
type(None): VoidObject,
})

def load_root_meson_file(self) -> None:
mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
if not os.path.isfile(mesonfile):
Expand Down Expand Up @@ -173,7 +179,7 @@ def evaluate_codeblock(self, node: mparser.CodeBlockNode, start: int = 0, end: T
raise e
i += 1 # In THE FUTURE jump over blocks and stuff.

def evaluate_statement(self, cur: mparser.BaseNode) -> T.Optional[InterpreterObject]:
def evaluate_statement(self, cur: mparser.BaseNode) -> InterpreterObject:
self.current_node = cur
if isinstance(cur, mparser.FunctionNode):
return self.function_call(cur)
Expand All @@ -186,7 +192,7 @@ def evaluate_statement(self, cur: mparser.BaseNode) -> T.Optional[InterpreterObj
elif isinstance(cur, mparser.BooleanNode):
return self._holderify(cur.value)
elif isinstance(cur, mparser.IfClauseNode):
return self.evaluate_if(cur)
self.evaluate_if(cur)
elif isinstance(cur, mparser.IdNode):
return self.get_variable(cur.value)
elif isinstance(cur, mparser.ComparisonNode):
Expand Down Expand Up @@ -223,7 +229,7 @@ def evaluate_statement(self, cur: mparser.BaseNode) -> T.Optional[InterpreterObj
raise BreakRequest()
else:
raise InvalidCode("Unknown statement.")
return None
return self._holderify(None)

def evaluate_arraystatement(self, cur: mparser.ArrayNode) -> InterpreterObject:
(arguments, kwargs) = self.reduce_arguments(cur.args)
Expand All @@ -250,15 +256,15 @@ def evaluate_notstatement(self, cur: mparser.NotNode) -> InterpreterObject:
return v
return self._holderify(v.operator_call(MesonOperator.NOT, None))

def evaluate_if(self, node: mparser.IfClauseNode) -> T.Optional[Disabler]:
def evaluate_if(self, node: mparser.IfClauseNode) -> None:
assert isinstance(node, mparser.IfClauseNode)
for i in node.ifs:
# Reset self.tmp_meson_version to know if it gets set during this
# statement evaluation.
self.tmp_meson_version = None
result = self.evaluate_statement(i.condition)
if isinstance(result, Disabler):
return result
return None
if not isinstance(result, InterpreterObject):
raise mesonlib.MesonBugException(f'Argument to not ({result}) is not an InterpreterObject but {type(result).__name__}.')
res = result.operator_call(MesonOperator.BOOL, None)
Expand All @@ -275,7 +281,6 @@ def evaluate_if(self, node: mparser.IfClauseNode) -> T.Optional[Disabler]:
return None
if not isinstance(node.elseblock, mparser.EmptyNode):
self.evaluate_codeblock(node.elseblock)
return None

def evaluate_comparison(self, node: mparser.ComparisonNode) -> InterpreterObject:
val1 = self.evaluate_statement(node.left)
Expand Down Expand Up @@ -354,7 +359,7 @@ def evaluate_arithmeticstatement(self, cur: mparser.ArithmeticNode) -> Interpret
res = l.operator_call(mapping[cur.operation], _unholder(r))
return self._holderify(res)

def evaluate_ternary(self, node: mparser.TernaryNode) -> T.Optional[InterpreterObject]:
def evaluate_ternary(self, node: mparser.TernaryNode) -> InterpreterObject:
assert isinstance(node, mparser.TernaryNode)
result = self.evaluate_statement(node.condition)
if isinstance(result, Disabler):
Expand Down Expand Up @@ -438,7 +443,7 @@ def evaluate_indexing(self, node: mparser.IndexNode) -> InterpreterObject:
iobject.current_node = node
return self._holderify(iobject.operator_call(MesonOperator.INDEX, index))

def function_call(self, node: mparser.FunctionNode) -> T.Optional[InterpreterObject]:
def function_call(self, node: mparser.FunctionNode) -> InterpreterObject:
func_name = node.func_name
(h_posargs, h_kwargs) = self.reduce_arguments(node.args)
(posargs, kwargs) = self._unholder_args(h_posargs, h_kwargs)
Expand All @@ -452,12 +457,12 @@ def function_call(self, node: mparser.FunctionNode) -> T.Optional[InterpreterObj
if not getattr(func, 'no-second-level-holder-flattening', False):
func_args, kwargs = resolve_second_level_holders(func_args, kwargs)
res = func(node, func_args, kwargs)
return self._holderify(res) if res is not None else None
return self._holderify(res)
else:
self.unknown_function_called(func_name)
return None
return self._holderify(None)

def method_call(self, node: mparser.MethodNode) -> T.Optional[InterpreterObject]:
def method_call(self, node: mparser.MethodNode) -> InterpreterObject:
invokable = node.source_object
obj: T.Optional[InterpreterObject]
if isinstance(invokable, mparser.IdNode):
Expand All @@ -480,7 +485,7 @@ def method_call(self, node: mparser.MethodNode) -> T.Optional[InterpreterObject]
raise InvalidArguments(f'Invalid operation "extract_objects" on variable "{object_name}" of type {type(obj).__name__}')
obj.current_node = node
res = obj.method_call(method_name, args, kwargs)
return self._holderify(res) if res is not None else None
return self._holderify(res)

def _holderify(self, res: T.Union[TYPE_var, InterpreterObject]) -> InterpreterObject:
if isinstance(res, HoldableTypes):
Expand Down Expand Up @@ -523,23 +528,23 @@ def reduce_arguments(
raise InvalidArguments('All keyword arguments must be after positional arguments.')
self.argument_depth += 1
reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
if any(x is None for x in reduced_pos):
raise InvalidArguments(f'At least one value in the arguments is void.')
if any(not x.is_assignable for x in reduced_pos):
raise InvalidArguments(f'At least one value in the arguments is not assignable.')
reduced_kw: T.Dict[str, InterpreterObject] = {}
for key, val in args.kwargs.items():
reduced_key = key_resolver(key)
assert isinstance(val, mparser.BaseNode)
reduced_val = self.evaluate_statement(val)
if reduced_val is None:
raise InvalidArguments(f'Value of key {reduced_key} is void.')
if not reduced_val.is_assignable:
raise InvalidArguments(f'Value of key {reduced_key} is not assignable.')
if duplicate_key_error and reduced_key in reduced_kw:
raise InvalidArguments(duplicate_key_error.format(reduced_key))
reduced_kw[reduced_key] = reduced_val
self.argument_depth -= 1
final_kw = self.expand_default_kwargs(reduced_kw)
return reduced_pos, final_kw

def expand_default_kwargs(self, kwargs: T.Dict[str, T.Optional[InterpreterObject]]) -> T.Dict[str, T.Optional[InterpreterObject]]:
def expand_default_kwargs(self, kwargs: T.Dict[str, InterpreterObject]) -> T.Dict[str, InterpreterObject]:
if 'kwargs' not in kwargs:
return kwargs
to_expand = _unholder(kwargs.pop('kwargs'))
Expand Down Expand Up @@ -568,17 +573,13 @@ def assignment(self, node: mparser.AssignmentNode) -> None:
if isinstance(value, MutableInterpreterObject):
value = copy.deepcopy(value)
self.set_variable(var_name, value)
return None

def set_variable(self, varname: str, variable: T.Union[TYPE_var, InterpreterObject], *, holderify: bool = False) -> None:
if variable is None:
raise InvalidCode('Can not assign None to variable.')
if holderify:
variable = self._holderify(variable)
else:
# Ensure that we are always storing ObjectHolders
if not isinstance(variable, InterpreterObject):
raise mesonlib.MesonBugException(f'set_variable in InterpreterBase called with a non InterpreterObject {variable} of type {type(variable).__name__}')
def set_variable(self, varname: str, variable: InterpreterObject) -> None:
if not variable.is_assignable:
raise InvalidCode(f'Can not assign object of type {variable.display_name()} to variable {varname}.')
# Ensure that we are always storing ObjectHolders
if not isinstance(variable, InterpreterObject):
raise mesonlib.MesonBugException(f'set_variable in InterpreterBase called with a non InterpreterObject {variable} of type {type(variable).__name__}')
if not isinstance(varname, str):
raise InvalidCode('First argument to set_variable must be a string.')
if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None:
Expand Down
16 changes: 16 additions & 0 deletions mesonbuild/interpreterbase/void.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2021 The Meson development team
# SPDX-license-identifier: Apache-2.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# SPDX-license-identifier: Apache-2.0
# SPDX-License-Identifier: Apache-2.0


from .baseobjects import ObjectHolder, TYPE_var, TYPE_kwargs, InvalidCode, MesonOperator
import typing as T

class VoidObject(ObjectHolder[None]):
@property
def is_assignable(self) -> bool:
return False

def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var:
raise InvalidCode(f'Tried to call method {method_name} on void.')

def operator_call(self, operator: MesonOperator, other: TYPE_var) -> TYPE_var:
raise InvalidCode(f'Tried use operator {operator.value} on void.')