aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreterbase/baseobjects.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/interpreterbase/baseobjects.py')
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py123
1 files changed, 91 insertions, 32 deletions
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py
index a5cccce..c756761 100644
--- a/mesonbuild/interpreterbase/baseobjects.py
+++ b/mesonbuild/interpreterbase/baseobjects.py
@@ -15,16 +15,11 @@ from abc import ABCMeta
from contextlib import AbstractContextManager
if T.TYPE_CHECKING:
- from typing_extensions import Protocol, TypeAlias
+ from typing_extensions import TypeAlias
# Object holders need the actual interpreter
from ..interpreter import Interpreter
- __T = T.TypeVar('__T', bound='TYPE_var', contravariant=True)
-
- class OperatorCall(Protocol[__T]):
- def __call__(self, other: __T) -> 'TYPE_var': ...
-
TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any])
@@ -34,34 +29,85 @@ TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
TYPE_kwargs = T.Dict[str, TYPE_var]
TYPE_nkwargs = T.Dict[str, TYPE_nvar]
TYPE_key_resolver = T.Callable[[mparser.BaseNode], str]
+TYPE_op_arg = T.TypeVar('TYPE_op_arg', bound='TYPE_var', contravariant=True)
+TYPE_op_func = T.Callable[[TYPE_op_arg, TYPE_op_arg], TYPE_var]
+TYPE_method_func = T.Callable[['InterpreterObject', T.List[TYPE_var], TYPE_kwargs], TYPE_var]
+
SubProject = T.NewType('SubProject', str)
class InterpreterObject:
+ TRIVIAL_OPERATORS: T.Dict[
+ MesonOperator,
+ T.Tuple[
+ T.Union[T.Type, T.Tuple[T.Type, ...]],
+ TYPE_op_func
+ ]
+ ] = {}
+
+ OPERATORS: T.Dict[MesonOperator, TYPE_op_func] = {}
+
+ METHODS: T.Dict[
+ str,
+ TYPE_method_func,
+ ] = {}
+
+ def __init_subclass__(cls: T.Type[InterpreterObject], **kwargs: T.Any) -> None:
+ super().__init_subclass__(**kwargs)
+ saved_trivial_operators = cls.TRIVIAL_OPERATORS
+
+ cls.METHODS = {}
+ cls.OPERATORS = {}
+ cls.TRIVIAL_OPERATORS = {}
+
+ # Compute inherited operators and methods according to the Python resolution
+ # order. Reverse the result of mro() because update() will overwrite entries
+ # that are set by the superclass with those that are set by the subclass.
+ for superclass in reversed(cls.mro()[1:]):
+ if superclass is InterpreterObject:
+ # InterpreterObject cannot use @InterpreterObject.operator because
+ # __init_subclass__ does not operate on InterpreterObject itself
+ cls.OPERATORS.update({
+ MesonOperator.EQUALS: InterpreterObject.op_equals,
+ MesonOperator.NOT_EQUALS: InterpreterObject.op_not_equals
+ })
+
+ elif issubclass(superclass, InterpreterObject):
+ cls.METHODS.update(superclass.METHODS)
+ cls.OPERATORS.update(superclass.OPERATORS)
+ cls.TRIVIAL_OPERATORS.update(superclass.TRIVIAL_OPERATORS)
+
+ for name, method in cls.__dict__.items():
+ if hasattr(method, 'meson_method'):
+ cls.METHODS[method.meson_method] = method
+ if hasattr(method, 'meson_operator'):
+ cls.OPERATORS[method.meson_operator] = method
+ cls.TRIVIAL_OPERATORS.update(saved_trivial_operators)
+
+ @staticmethod
+ def method(name: str) -> T.Callable[[TV_func], TV_func]:
+ '''Decorator that tags a Python method as the implementation of a method
+ for the Meson interpreter'''
+ def decorator(f: TV_func) -> TV_func:
+ f.meson_method = name # type: ignore[attr-defined]
+ return f
+ return decorator
+
+ @staticmethod
+ def operator(op: MesonOperator) -> T.Callable[[TV_func], TV_func]:
+ '''Decorator that tags a method as the implementation of an operator
+ for the Meson interpreter'''
+ def decorator(f: TV_func) -> TV_func:
+ f.meson_operator = op # type: ignore[attr-defined]
+ return f
+ return decorator
+
def __init__(self, *, subproject: T.Optional['SubProject'] = None) -> None:
- self.methods: T.Dict[
- str,
- T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var]
- ] = {}
- self.operators: T.Dict[MesonOperator, 'OperatorCall'] = {}
- self.trivial_operators: T.Dict[
- MesonOperator,
- T.Tuple[
- T.Union[T.Type, T.Tuple[T.Type, ...]],
- 'OperatorCall'
- ]
- ] = {}
# Current node set during a method call. This can be used as location
# when printing a warning message during a method call.
self.current_node: mparser.BaseNode = None
self.subproject = subproject or SubProject('')
- # Some default operators supported by all objects
- self.operators.update({
- MesonOperator.EQUALS: self.op_equals,
- MesonOperator.NOT_EQUALS: self.op_not_equals,
- })
-
# The type of the object that can be printed to the user
def display_name(self) -> str:
return type(self).__name__
@@ -72,25 +118,26 @@ class InterpreterObject:
args: T.List[TYPE_var],
kwargs: TYPE_kwargs
) -> TYPE_var:
- if method_name in self.methods:
- method = self.methods[method_name]
+ if method_name in self.METHODS:
+ method = self.METHODS[method_name]
if not getattr(method, 'no-args-flattening', False):
args = flatten(args)
if not getattr(method, 'no-second-level-holder-flattening', False):
args, kwargs = resolve_second_level_holders(args, kwargs)
- return method(args, kwargs)
+ return method(self, args, kwargs)
raise InvalidCode(f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.')
def operator_call(self, operator: MesonOperator, other: TYPE_var) -> TYPE_var:
- if operator in self.trivial_operators:
- op = self.trivial_operators[operator]
+ if operator in self.TRIVIAL_OPERATORS:
+ op = self.TRIVIAL_OPERATORS[operator]
if op[0] is None and other is not None:
raise MesonBugException(f'The unary operator `{operator.value}` of {self.display_name()} was passed the object {other} of type {type(other).__name__}')
if op[0] is not None and not isinstance(other, op[0]):
raise InvalidArguments(f'The `{operator.value}` operator of {self.display_name()} does not accept objects of type {type(other).__name__} ({other})')
- return op[1](other)
- if operator in self.operators:
- return self.operators[operator](other)
+ return op[1](self, other)
+ if operator in self.OPERATORS:
+ return self.OPERATORS[operator](self, other)
+
raise InvalidCode(f'Object {self} of type {self.display_name()} does not support the `{operator.value}` operator.')
# Default comparison operator support
@@ -121,6 +168,16 @@ class MesonInterpreterObject(InterpreterObject):
class MutableInterpreterObject:
''' Dummy class to mark the object type as mutable '''
+class UnknownValue(MesonInterpreterObject):
+ '''This class is only used for the rewriter/static introspection tool and
+ indicates that a value cannot be determined statically, either because of
+ limitations in our code or because the value differs from machine to
+ machine.'''
+
+class UndefinedVariable(MesonInterpreterObject):
+ '''This class is only used for the rewriter/static introspection tool and
+ represents the `value` a meson-variable has if it was never written to.'''
+
HoldableTypes = (HoldableObject, int, bool, str, list, dict)
TYPE_HoldableTypes = T.Union[TYPE_var, HoldableObject]
InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=TYPE_HoldableTypes)
@@ -142,12 +199,14 @@ class ObjectHolder(InterpreterObject, T.Generic[InterpreterObjectTypeVar]):
return type(self.held_object).__name__
# Override default comparison operators for the held object
+ @InterpreterObject.operator(MesonOperator.EQUALS)
def op_equals(self, other: TYPE_var) -> bool:
# See the comment from InterpreterObject why we are using `type()` here.
if type(self.held_object) is not type(other):
self._throw_comp_exception(other, '==')
return self.held_object == other
+ @InterpreterObject.operator(MesonOperator.NOT_EQUALS)
def op_not_equals(self, other: TYPE_var) -> bool:
if type(self.held_object) is not type(other):
self._throw_comp_exception(other, '!=')