Source code for m2isar.metamodel.utils.function_throws

# SPDX-License-Identifier: Apache-2.0
#
# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R
#
# Copyright (C) 2022
# Chair of Electrical Design Automation
# Technical University of Munich

"""Tranformation functions to determine whether a function throws an exception."""

from functools import reduce
from functools import singledispatchmethod
from operator import or_
from typing import Any

from ...metamodel import arch, behav
from .ExprVisitor import ExprVisitor

# pylint: disable=unused-argument

[docs] class FunctionThrowsVisitor(ExprVisitor): """Visitor that determines whether behavior expression trees can throw exceptions.""" @singledispatchmethod
[docs] def generate(self, expr: behav.BaseNode, context=None): raise NotImplementedError(f"No visit method implemented for type {type(expr).__name__} in {type(expr).__name__}")
@generate.register
[docs] def _(self, expr: behav.Operation, context): statements = [] for stmt in expr.statements: temp = self.generate(stmt, context) if isinstance(temp, list): statements.extend(temp) else: statements.append(temp) return reduce(or_, statements, arch.FunctionThrows.NO)
@generate.register def _(self, expr: behav.Block, context): stmts = [self.generate(x, context) for x in expr.statements] return reduce(or_, stmts, arch.FunctionThrows.NO) @generate.register def _(self, expr: behav.BinaryOperation, context): left = self.generate(expr.left, context) right = self.generate(expr.right, context) return reduce(or_, [left, right]) @generate.register def _(self, expr: behav.SliceOperation, context): expr_result = self.generate(expr.expr, context) left = self.generate(expr.left, context) right = self.generate(expr.right, context) return reduce(or_, [expr_result, left, right]) @generate.register def _(self, expr: behav.ConcatOperation, context): left = self.generate(expr.left, context) right = self.generate(expr.right, context) return reduce(or_, [left, right]) @generate.register def _(self, expr: behav.NumberLiteral, context): return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.IntLiteral, context): return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.StringLiteral, context): return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.ScalarDefinition, context): return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.Break, context): return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.Assignment, context): target = self.generate(expr.target, context) expr_result = self.generate(expr.expr, context) return reduce(or_, [target, expr_result]) @generate.register def _(self, expr: behav.Conditional, context): conds = [self.generate(x, context) for x in expr.conds] stmts = [self.generate(x, context) for x in expr.stmts] conds.extend(stmts) return arch.FunctionThrows.MAYBE if reduce(or_, conds) else arch.FunctionThrows.NO @generate.register def _(self, expr: behav.Loop, context): cond = self.generate(expr.cond, context) stmts = [self.generate(x, context) for x in expr.stmts] stmts.append(cond) return reduce(or_, stmts) @generate.register def _(self, expr: behav.Ternary, context): cond = self.generate(expr.cond, context) then_expr = self.generate(expr.then_expr, context) else_expr = self.generate(expr.else_expr, context) return reduce(or_, [cond, then_expr, else_expr]) @generate.register def _(self, expr: behav.Return, context): if expr.expr is not None: return self.generate(expr.expr, context) return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.UnaryOperation, context): right = self.generate(expr.right, context) return right @generate.register def _(self, expr: behav.NamedReference, context): if isinstance(expr.reference, arch.Memory) and arch.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: return arch.FunctionThrows.YES return arch.FunctionThrows.NO @generate.register def _(self, expr: behav.IndexedReference, context): if isinstance(expr.reference, arch.Memory) and arch.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: return arch.FunctionThrows.YES return self.generate(expr.index, context) @generate.register def _(self, expr: behav.TypeConv, context): expr_result = self.generate(expr.expr, context) return expr_result @generate.register def _(self, expr: behav.Callable, context): args = [self.generate(arg, context) for arg in expr.args] throws = getattr(expr.ref_or_name, "throws", arch.FunctionThrows.NO) args.append(throws if isinstance(throws, arch.FunctionThrows) else cast_to_throws(throws)) return reduce(or_, args) @generate.register def _(self, expr: behav.ProcedureCall, context): args = [self.generate(arg, context) for arg in expr.args] throws = getattr(expr.ref_or_name, "throws", arch.FunctionThrows.NO) args.append(throws if isinstance(throws, arch.FunctionThrows) else cast_to_throws(throws)) return reduce(or_, args) @generate.register def _(self, expr: behav.Group, context): expr_result = self.generate(expr.expr, context) return expr_result
[docs] def cast_to_throws(throws: Any) -> arch.FunctionThrows: """Cast unknown throws values into FunctionThrows for robust visitor dispatch.""" if isinstance(throws, bool): return arch.FunctionThrows.YES if throws else arch.FunctionThrows.NO return arch.FunctionThrows(throws)