summaryrefslogtreecommitdiff
path: root/mesonbuild/interpreterbase/baseobjects.py
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2025-01-08 12:26:07 +0100
committerJussi Pakkanen <jussi.pakkanen@mailbox.org>2025-06-17 12:29:56 +0300
commit2f47d0b4b15e27aa62f439c4b65282771149cc7c (patch)
treed50002095ec899f5db9d037ff658e575e86c490d /mesonbuild/interpreterbase/baseobjects.py
parentf3366a7e543f69844bd3658db65c28295912db79 (diff)
downloadmeson-2f47d0b4b15e27aa62f439c4b65282771149cc7c.tar.gz
interpreter: make operators per-class
Do not call update() and Enum.__hash__ a gazillion times; operators are the same for every instance of the class. In order to access the class for non-trivial operators, the operators are first marked using a decorator, and then OPERATORS is built via __init_subclass__. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'mesonbuild/interpreterbase/baseobjects.py')
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py40
1 files changed, 30 insertions, 10 deletions
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py
index 928d03861..ce961bdfb 100644
--- a/mesonbuild/interpreterbase/baseobjects.py
+++ b/mesonbuild/interpreterbase/baseobjects.py
@@ -43,37 +43,55 @@ class InterpreterObject:
]
] = {}
+ OPERATORS: T.Dict[MesonOperator, TYPE_op_func] = {}
+
def __init_subclass__(cls: T.Type[InterpreterObject], **kwargs: T.Any) -> None:
super().__init_subclass__(**kwargs)
saved_trivial_operators = cls.TRIVIAL_OPERATORS
+
+ cls.OPERATORS = {}
cls.TRIVIAL_OPERATORS = {}
# Compute inherited operators according to the Python resolution order
# Reverse the result of mro() because update() will overwrite operators
# that are set by the superclass with those that are set by the subclass
for superclass in reversed(cls.mro()[1:]):
- if issubclass(superclass, InterpreterObject):
+ 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.OPERATORS.update(superclass.OPERATORS)
cls.TRIVIAL_OPERATORS.update(superclass.TRIVIAL_OPERATORS)
+ for name, method in cls.__dict__.items():
+ if hasattr(method, 'meson_operator'):
+ cls.OPERATORS[method.meson_operator] = method
cls.TRIVIAL_OPERATORS.update(saved_trivial_operators)
+ @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, TYPE_op_func] = {}
# 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.__class__.op_equals,
- MesonOperator.NOT_EQUALS: self.__class__.op_not_equals,
- })
-
# The type of the object that can be printed to the user
def display_name(self) -> str:
return type(self).__name__
@@ -101,8 +119,8 @@ class InterpreterObject:
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](self, other)
- if operator in self.operators:
- return self.operators[operator](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.')
@@ -165,12 +183,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, '!=')