1# Copyright 2013-2021 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from .. import mparser 16from .exceptions import InvalidCode, InvalidArguments 17from .helpers import flatten, resolve_second_level_holders 18from .operator import MesonOperator 19from ..mesonlib import HoldableObject, MesonBugException 20import textwrap 21 22import typing as T 23from abc import ABCMeta 24 25if T.TYPE_CHECKING: 26 # Object holders need the actual interpreter 27 from ..interpreter import Interpreter 28 29TV_fw_var = T.Union[str, int, bool, list, dict, 'InterpreterObject'] 30TV_fw_args = T.List[T.Union[mparser.BaseNode, TV_fw_var]] 31TV_fw_kwargs = T.Dict[str, T.Union[mparser.BaseNode, TV_fw_var]] 32 33TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any]) 34 35TYPE_elementary = T.Union[str, int, bool, T.List[T.Any], T.Dict[str, T.Any]] 36TYPE_var = T.Union[TYPE_elementary, HoldableObject, 'MesonInterpreterObject'] 37TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode] 38TYPE_kwargs = T.Dict[str, TYPE_var] 39TYPE_nkwargs = T.Dict[str, TYPE_nvar] 40TYPE_key_resolver = T.Callable[[mparser.BaseNode], str] 41 42if T.TYPE_CHECKING: 43 from typing_extensions import Protocol 44 __T = T.TypeVar('__T', bound=TYPE_var, contravariant=True) 45 46 class OperatorCall(Protocol[__T]): 47 def __call__(self, other: __T) -> TYPE_var: ... 48 49class InterpreterObject: 50 def __init__(self, *, subproject: T.Optional[str] = None) -> None: 51 self.methods: T.Dict[ 52 str, 53 T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var] 54 ] = {} 55 self.operators: T.Dict[MesonOperator, 'OperatorCall'] = {} 56 self.trivial_operators: T.Dict[ 57 MesonOperator, 58 T.Tuple[ 59 T.Union[T.Type, T.Tuple[T.Type, ...]], 60 'OperatorCall' 61 ] 62 ] = {} 63 # Current node set during a method call. This can be used as location 64 # when printing a warning message during a method call. 65 self.current_node: mparser.BaseNode = None 66 self.subproject: str = subproject or '' 67 68 # Some default operators supported by all objects 69 self.operators.update({ 70 MesonOperator.EQUALS: self.op_equals, 71 MesonOperator.NOT_EQUALS: self.op_not_equals, 72 }) 73 74 # The type of the object that can be printed to the user 75 def display_name(self) -> str: 76 return type(self).__name__ 77 78 def method_call( 79 self, 80 method_name: str, 81 args: T.List[TYPE_var], 82 kwargs: TYPE_kwargs 83 ) -> TYPE_var: 84 if method_name in self.methods: 85 method = self.methods[method_name] 86 if not getattr(method, 'no-args-flattening', False): 87 args = flatten(args) 88 if not getattr(method, 'no-second-level-holder-flattening', False): 89 args, kwargs = resolve_second_level_holders(args, kwargs) 90 return method(args, kwargs) 91 raise InvalidCode(f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.') 92 93 def operator_call(self, operator: MesonOperator, other: TYPE_var) -> TYPE_var: 94 if operator in self.trivial_operators: 95 op = self.trivial_operators[operator] 96 if op[0] is None and other is not None: 97 raise MesonBugException(f'The unary operator `{operator.value}` of {self.display_name()} was passed the object {other} of type {type(other).__name__}') 98 if op[0] is not None and not isinstance(other, op[0]): 99 raise InvalidArguments(f'The `{operator.value}` operator of {self.display_name()} does not accept objects of type {type(other).__name__} ({other})') 100 return op[1](other) 101 if operator in self.operators: 102 return self.operators[operator](other) 103 raise InvalidCode(f'Object {self} of type {self.display_name()} does not support the `{operator.value}` operator.') 104 105 # Default comparison operator support 106 def _throw_comp_exception(self, other: TYPE_var, opt_type: str) -> T.NoReturn: 107 raise InvalidArguments(textwrap.dedent( 108 f''' 109 Trying to compare values of different types ({self.display_name()}, {type(other).__name__}) using {opt_type}. 110 This was deprecated and undefined behavior previously and is as of 0.60.0 a hard error. 111 ''' 112 )) 113 114 def op_equals(self, other: TYPE_var) -> bool: 115 # We use `type(...) == type(...)` here to enforce an *exact* match for comparison. We 116 # don't want comparisons to be possible where `isinstance(derived_obj, type(base_obj))` 117 # would pass because this comparison must never be true: `derived_obj == base_obj` 118 if type(self) != type(other): 119 self._throw_comp_exception(other, '==') 120 return self == other 121 122 def op_not_equals(self, other: TYPE_var) -> bool: 123 if type(self) != type(other): 124 self._throw_comp_exception(other, '!=') 125 return self != other 126 127class MesonInterpreterObject(InterpreterObject): 128 ''' All non-elementary objects and non-object-holders should be derived from this ''' 129 130class MutableInterpreterObject: 131 ''' Dummy class to mark the object type as mutable ''' 132 133HoldableTypes = (HoldableObject, int, bool, str, list, dict) 134TYPE_HoldableTypes = T.Union[TYPE_elementary, HoldableObject] 135InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=TYPE_HoldableTypes) 136 137class ObjectHolder(InterpreterObject, T.Generic[InterpreterObjectTypeVar]): 138 def __init__(self, obj: InterpreterObjectTypeVar, interpreter: 'Interpreter') -> None: 139 super().__init__(subproject=interpreter.subproject) 140 # This causes some type checkers to assume that obj is a base 141 # HoldableObject, not the specialized type, so only do this assert in 142 # non-type checking situations 143 if not T.TYPE_CHECKING: 144 assert isinstance(obj, HoldableTypes), f'This is a bug: Trying to hold object of type `{type(obj).__name__}` that is not in `{HoldableTypes}`' 145 self.held_object = obj 146 self.interpreter = interpreter 147 self.env = self.interpreter.environment 148 149 # Hide the object holder abstraction from the user 150 def display_name(self) -> str: 151 return type(self.held_object).__name__ 152 153 # Override default comparison operators for the held object 154 def op_equals(self, other: TYPE_var) -> bool: 155 # See the comment from InterpreterObject why we are using `type()` here. 156 if type(self.held_object) != type(other): 157 self._throw_comp_exception(other, '==') 158 return self.held_object == other 159 160 def op_not_equals(self, other: TYPE_var) -> bool: 161 if type(self.held_object) != type(other): 162 self._throw_comp_exception(other, '!=') 163 return self.held_object != other 164 165 def __repr__(self) -> str: 166 return f'<[{type(self).__name__}] holds [{type(self.held_object).__name__}]: {self.held_object!r}>' 167 168class IterableObject(metaclass=ABCMeta): 169 '''Base class for all objects that can be iterated over in a foreach loop''' 170 171 def iter_tuple_size(self) -> T.Optional[int]: 172 '''Return the size of the tuple for each iteration. Returns None if only a single value is returned.''' 173 raise MesonBugException(f'iter_tuple_size not implemented for {self.__class__.__name__}') 174 175 def iter_self(self) -> T.Iterator[T.Union[TYPE_var, T.Tuple[TYPE_var, ...]]]: 176 raise MesonBugException(f'iter not implemented for {self.__class__.__name__}') 177 178 def size(self) -> int: 179 raise MesonBugException(f'size not implemented for {self.__class__.__name__}') 180