summaryrefslogtreecommitdiff
path: root/mesonbuild/ast
diff options
context:
space:
mode:
authorVolker Weißmann <volker.weissmann@gmx.de>2025-03-20 16:37:30 +0100
committerDylan Baker <dylan@pnwbakers.com>2025-05-29 09:20:27 -0700
commit1a191d9b9569e080c55b09ab2939274d576c70f1 (patch)
treee664908631852dfe19ccb69e83f4a62404ba710e /mesonbuild/ast
parentc90385363af591d52eacb6a2576d82bfeaf57039 (diff)
downloadmeson-1a191d9b9569e080c55b09ab2939274d576c70f1.tar.gz
AstInterpreter: Replace resolve_node with node_to_runtime_value
`resolve_node` is simply a half-broken, worse implementation of `node_to_runtime_value` that we recently introduced. In the example below, the static introspection tool/rewriter now understands that the name of the executable is foo instead of bar: ``` var = 'foo' name = var var = 'bar' executable(name, 'foo.c') ```
Diffstat (limited to 'mesonbuild/ast')
-rw-r--r--mesonbuild/ast/interpreter.py104
-rw-r--r--mesonbuild/ast/introspection.py40
2 files changed, 27 insertions, 117 deletions
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index e66decf60..0d53873ca 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -40,7 +40,6 @@ from ..mparser import (
ArrayNode,
AssignmentNode,
BaseNode,
- ElementaryNode,
EmptyNode,
IdNode,
MethodNode,
@@ -76,9 +75,9 @@ def _symbol(val: str) -> SymbolNode:
# `IntrospectionDependency` is to the `IntrospectionInterpreter` what `Dependency` is to the normal `Interpreter`.
@dataclass
class IntrospectionDependency(MesonInterpreterObject):
- name: str
- required: T.Union[bool]
- version: T.List[str]
+ name: T.Union[str, UnknownValue]
+ required: T.Union[bool, UnknownValue]
+ version: T.Union[T.List[str], UnknownValue]
has_fallback: bool
conditional: bool
node: FunctionNode
@@ -612,96 +611,7 @@ class AstInterpreter(InterpreterBase):
val = self.get_cur_value(var_name)
self.dataflow_dag.add_edge(val, node)
- def resolve_node(self, node: BaseNode, include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.Optional[T.Any]:
- def quick_resolve(n: BaseNode, loop_detect: T.Optional[T.List[str]] = None) -> T.Any:
- if loop_detect is None:
- loop_detect = []
- if isinstance(n, IdNode):
- assert isinstance(n.value, str)
- if n.value in loop_detect or n.value not in self.cur_assignments:
- return []
- return quick_resolve(self.get_cur_value(n.value), loop_detect = loop_detect + [n.value])
- elif isinstance(n, ElementaryNode):
- return n.value
- else:
- return n
-
- if id_loop_detect is None:
- id_loop_detect = []
- result = None
-
- if not isinstance(node, BaseNode):
- return None
-
- assert node.ast_id
- if node.ast_id in id_loop_detect:
- return None # Loop detected
- id_loop_detect += [node.ast_id]
-
- # Try to evaluate the value of the node
- if isinstance(node, IdNode):
- result = quick_resolve(node)
-
- elif isinstance(node, ElementaryNode):
- result = node.value
-
- elif isinstance(node, NotNode):
- result = self.resolve_node(node.value, include_unknown_args, id_loop_detect)
- if isinstance(result, bool):
- result = not result
-
- elif isinstance(node, ArrayNode):
- result = node.args.arguments.copy()
-
- elif isinstance(node, ArgumentNode):
- result = node.arguments.copy()
-
- elif isinstance(node, ArithmeticNode):
- if node.operation != 'add':
- return None # Only handle string and array concats
- l = self.resolve_node(node.left, include_unknown_args, id_loop_detect)
- r = self.resolve_node(node.right, include_unknown_args, id_loop_detect)
- if isinstance(l, str) and isinstance(r, str):
- result = l + r # String concatenation detected
- else:
- result = self.flatten_args(l, include_unknown_args, id_loop_detect) + self.flatten_args(r, include_unknown_args, id_loop_detect)
-
- elif isinstance(node, MethodNode):
- src = quick_resolve(node.source_object)
- margs = self.flatten_args(node.args.arguments, include_unknown_args, id_loop_detect)
- mkwargs: T.Dict[str, TYPE_var] = {}
- method_name = node.name.value
- try:
- if isinstance(src, str):
- result = StringHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, bool):
- result = BooleanHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, int):
- result = IntegerHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, list):
- result = ArrayHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- elif isinstance(src, dict):
- result = DictHolder(src, T.cast('Interpreter', self)).method_call(method_name, margs, mkwargs)
- except mesonlib.MesonException:
- return None
-
- # Ensure that the result is fully resolved (no more nodes)
- if isinstance(result, BaseNode):
- result = self.resolve_node(result, include_unknown_args, id_loop_detect)
- elif isinstance(result, list):
- new_res: T.List[TYPE_var] = []
- for i in result:
- if isinstance(i, BaseNode):
- resolved = self.resolve_node(i, include_unknown_args, id_loop_detect)
- if resolved is not None:
- new_res += self.flatten_args(resolved, include_unknown_args, id_loop_detect)
- else:
- new_res += [i]
- result = new_res
-
- return result
-
- def flatten_args(self, args_raw: T.Union[TYPE_nvar, T.Sequence[TYPE_nvar]], include_unknown_args: bool = False, id_loop_detect: T.Optional[T.List[str]] = None) -> T.List[TYPE_var]:
+ def flatten_args(self, args_raw: T.Union[TYPE_nvar, T.Sequence[TYPE_nvar]], include_unknown_args: bool = False) -> T.List[TYPE_var]:
# Make sure we are always dealing with lists
if isinstance(args_raw, list):
args = args_raw
@@ -713,13 +623,15 @@ class AstInterpreter(InterpreterBase):
# Resolve the contents of args
for i in args:
if isinstance(i, BaseNode):
- resolved = self.resolve_node(i, include_unknown_args, id_loop_detect)
+ resolved = self.node_to_runtime_value(i)
if resolved is not None:
if not isinstance(resolved, list):
resolved = [resolved]
flattened_args += resolved
- elif isinstance(i, (str, bool, int, float)) or include_unknown_args:
+ elif isinstance(i, (str, bool, int, float, UnknownValue)) or include_unknown_args:
flattened_args += [i]
+ else:
+ raise NotImplementedError
return flattened_args
def evaluate_testcase(self, node: TestCaseClauseNode) -> Disabler | None:
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index c74a76835..7dc4281cf 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -198,18 +198,16 @@ class IntrospectionInterpreter(AstInterpreter):
if not args:
return
name = args[0]
- assert isinstance(name, str)
+ assert isinstance(name, (str, UnknownValue))
has_fallback = 'fallback' in kwargs
required = kwargs.get('required', True)
version = kwargs.get('version', [])
- if not isinstance(version, list):
- version = [version]
- assert all(isinstance(el, str) for el in version)
- version = T.cast(T.List[str], version)
- if isinstance(required, ElementaryNode):
- required = required.value
- if not isinstance(required, bool):
- required = False
+ if not isinstance(version, UnknownValue):
+ if not isinstance(version, list):
+ version = [version]
+ assert all(isinstance(el, str) for el in version)
+ version = T.cast(T.List[str], version)
+ assert isinstance(required, (bool, UnknownValue))
newdep = IntrospectionDependency(
name=name,
required=required,
@@ -219,11 +217,11 @@ class IntrospectionInterpreter(AstInterpreter):
node=node)
self.dependencies += [newdep]
- def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dict[str, TYPE_var], targetclass: T.Type[BuildTarget]) -> IntrospectionBuildTarget:
+ def build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs_raw: T.Dict[str, TYPE_var], targetclass: T.Type[BuildTarget]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
assert isinstance(node, FunctionNode)
args = self.flatten_args(args)
if not args or not isinstance(args[0], str):
- return None
+ return UnknownValue()
name = args[0]
srcqueue: T.List[BaseNode] = [node]
extra_queue = []
@@ -303,7 +301,7 @@ class IntrospectionInterpreter(AstInterpreter):
self.targets += [new_target]
return new_target
- def build_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def build_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
default_library = self.coredata.optstore.get_value_for(OptionKey('default_library'))
if default_library == 'shared':
return self.build_target(node, args, kwargs, SharedLibrary)
@@ -313,28 +311,28 @@ class IntrospectionInterpreter(AstInterpreter):
return self.build_target(node, args, kwargs, SharedLibrary)
return None
- def func_executable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_executable(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, Executable)
- def func_static_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_static_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, StaticLibrary)
- def func_shared_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_shared_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, SharedLibrary)
- def func_both_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_both_lib(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, SharedLibrary)
- def func_shared_module(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_shared_module(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, SharedModule)
- def func_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_library(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_library(node, args, kwargs)
- def func_jar(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_jar(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
return self.build_target(node, args, kwargs, Jar)
- def func_build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> IntrospectionBuildTarget:
+ def func_build_target(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str, TYPE_var]) -> T.Union[IntrospectionBuildTarget, UnknownValue]:
if 'target_type' not in kwargs:
return None
target_type = kwargs.pop('target_type')
@@ -386,7 +384,7 @@ class IntrospectionInterpreter(AstInterpreter):
flattened_kwargs = {}
for key, val in kwargs.items():
if isinstance(val, BaseNode):
- resolved = self.resolve_node(val, include_unknown_args)
+ resolved = self.node_to_runtime_value(val)
if resolved is not None:
flattened_kwargs[key] = resolved
elif isinstance(val, (str, bool, int, float)) or include_unknown_args: