Пример #1
0
    def user_defined_call_semantics(self, stmt: Call, state: State,
                                    interpreter: Interpreter):
        """Forward semantics of a user-defined function/method call.

        :param stmt: call statement to be executed
        :param state: state before executing the call statement
        :return: state modified by the call statement
        """
        fname, fcfg = stmt.name, interpreter.cfgs[stmt.name]
        # add formal function parameters to the state and assign their actual values
        formal_args = interpreter.fargs[fname]
        for formal, actual in zip(formal_args, stmt.arguments):

            rhs = self.semantics(actual, state, interpreter).result
            state = state.add_variable(formal).forget_variable(formal)
            state = state.assign({formal}, rhs)

            if isinstance(actual, Call) and actual.name in interpreter.cfgs:
                _ret = VariableIdentifier(formal.typ,
                                          '{}#return'.format(actual.name))
                state = state.remove_variable(_ret)
        # add local function variables to the state
        _formal_args = set()
        for formal in formal_args:
            _formal_args.add(formal)
            # if isinstance(formal.typ, (SequenceLyraType, ContainerLyraType)):
            #     _formal_args.add(LengthIdentifier(formal))
            #     if isinstance(formal.typ, DictLyraType):
            #         _formal_args.add(KeysIdentifier(formal))
            #         _formal_args.add(ValuesIdentifier(formal))
        local_vars = set(fcfg.variables).difference(_formal_args)
        for local in local_vars:
            state = state.add_variable(local).forget_variable(local)

        fresult = interpreter.analyze(fcfg, state)  # analyze the function
        fstate = fresult.get_node_result(fcfg.out_node)[state][-1]
        state = state.bottom().join(deepcopy(fstate))

        # assign return variable
        if state.result:
            ret = VariableIdentifier(stmt.typ, '{}#return'.format(fname))
            state = state.add_variable(ret).forget_variable(ret)
            state = state.assign({ret}, state.result)
            state.result = {ret}

        # remove local function variables and formal function parameters
        for local in local_vars:
            if not local.special:
                state = state.remove_variable(local)
        for formal in formal_args:
            state = state.remove_variable(formal)

        return state
Пример #2
0
    def __init__(self, key_domain: Type[KeyWrapper],
                 precursory: FularaState,
                 scalar_vars: Set[VariableIdentifier] = None,
                 map_vars: Set[VariableIdentifier] = None,
                 k_pre_k_conv: Callable[[KeyWrapper], KeyWrapper]
                 = lambda x: x):
        """Map each program variable/dictionary segment to its liveness status.
        :param key_domain: domain for abstraction of dictionary keys,
            ranges over the scalar variables and the special key variable v_k,
            should support backward assignments with _substitute
        :param precursory: Forward analysis (Fulara analysis) result above the current statement
        :param scalar_vars: list of scalar variables, whose liveness should be abstracted
        :param map_vars: list of map variables, whose usage should be abstracted
        :param k_pre_k_conv: Conversion function to convert from key domain elements of the
                             precursory analysis to key domain elements of this analysis
                             (if the domains differ)
        """

        super().__init__(precursory)

        self._s_vars = scalar_vars or set()
        self._m_vars = map_vars or set()

        self._k_domain = key_domain

        self._scalar_liveness = StrongLivenessState(scalar_vars)

        arguments = {}
        for dv in map_vars:
            typ = dv.typ
            if isinstance(typ, DictLyraType):
                k_var = VariableIdentifier(typ.key_typ, k_name)
            elif isinstance(typ, ListLyraType):
                k_var = VariableIdentifier(IntegerLyraType(), k_name)
            else:
                raise TypeError("Map variables should be of type DictLyraType or ListLyraType")

            if typ not in arguments:
                arguments[typ] = {'key_domain': key_domain, 'value_domain': LivenessLattice,
                                  'key_d_args': {'scalar_variables': scalar_vars, 'k_var': k_var}}

        lattices = defaultdict(lambda: FularaLattice)
        self._dict_liveness = Store(map_vars, lattices, arguments)
        # start with 'dead'
        for var in map_vars:
            self._dict_liveness.store[var].empty()

        # self._length_usage

        self._k_pre_k_conv = k_pre_k_conv
Пример #3
0
    def __init__(self,
                 key_domain: Type[Union[KeyWrapper, State]],
                 scalar_vars: Set[VariableIdentifier] = None,
                 map_vars: Set[VariableIdentifier] = None):
        """Map each program variable/dictionary segment to its usage status.

        :param key_domain: domain for abstraction of dictionary keys,
            ranges over the scalar variables and the special key variable v_k
        :param scalar_vars: list of scalar variables, whose usage should be abstracted
        :param map_vars: list of map variables, whose usage should be abstracted
        """
        super().__init__()

        self._s_vars = scalar_vars or set()
        self._m_vars = map_vars or set()

        self._k_domain = key_domain

        self._scalar_usage = SimpleUsageStore(scalar_vars)

        arguments = {}
        for dv in map_vars:
            typ = dv.typ
            if isinstance(typ, DictLyraType):
                k_var = VariableIdentifier(typ.key_typ, k_name)
            elif isinstance(typ, ListLyraType):
                k_var = VariableIdentifier(IntegerLyraType(), k_name)
            else:
                raise TypeError(
                    "Map variables should be of type DictLyraType or ListLyraType"
                )

            if typ not in arguments:
                arguments[typ] = {
                    'key_domain': key_domain,
                    'value_domain': UsageLattice,
                    'key_d_args': {
                        'scalar_variables': scalar_vars,
                        'k_var': k_var
                    }
                }

        lattices = defaultdict(lambda: FularaLattice)
        self._dict_usage = Store(map_vars, lattices, arguments)
        # start with 'not used'
        for var in map_vars:
            self._dict_usage.store[var].empty()

        self._loop_flag = False
Пример #4
0
    def _cast_call_semantics(self, stmt: Call, state: State,
                             typ: LyraType) -> State:
        """Semantics of a call to 'int' or 'bool'.

        :param stmt: call to 'int' or 'bool' to be executed
        :param state: state before executing the call statement
        :return: state modified by the call statement
        """
        if len(stmt.arguments) != 1:
            error = f"Semantics for multiple arguments of {stmt.name} is not yet implemented!"
            raise NotImplementedError(error)
        argument = self.semantics(stmt.arguments[0], state).result
        result = set()
        for expression in argument:
            if isinstance(expression, Input):
                result.add(Input(typ))
            elif isinstance(expression, Literal):
                result.add(Literal(typ, expression.val))
            elif isinstance(expression, VariableIdentifier):
                result.add(VariableIdentifier(typ, expression.name))
            elif isinstance(expression, Subscription):
                pass  # TODO
            else:
                error = f"Argument of type {expression.typ} of {stmt.name} is not yet supported!"
                raise NotImplementedError(error)
        state.result = result
        return state
Пример #5
0
 def split_call_semantics(self, stmt: Call, state: State) -> State:
     if len(stmt.arguments) != 1:
         error = f"Semantics for multiple arguments of {stmt.name} is not yet implemented!"
         raise NotImplementedError(error)
     argument = self.semantics(stmt.arguments[0], state).result
     result = set()
     for arg in argument:
         assert isinstance(arg, Expression)
         if not isinstance(arg.typ, StringLyraType):
             error = f"Call to {stmt.name} of argument with unexpected type!"
             raise ValueError(error)
         typ = ListLyraType(StringLyraType())
         if isinstance(arg, Literal):  # "a b c".split() -> ["a", "b", "c"]
             items = [
                 Literal(StringLyraType(), val) for val in arg.val.split()
             ]
             result.add(ListDisplay(typ, items))
             continue
         elif isinstance(arg, VariableIdentifier):  # x.split()
             result.add(VariableIdentifier(typ, arg.name))
             continue
         elif isinstance(arg, Input):  # input().split()
             result.add(Input(typ))
             continue
         error = f"Call to {stmt.name} of unexpected argument!"
         raise ValueError(error)
     state.result = result
     return state
Пример #6
0
    def assignment_semantics(self, stmt: Assignment, state,
                             interpreter) -> State:
        """Forward semantics of an assignment.

        :param stmt: assignment statement to be executed
        :param state: state before executing the assignment
        :return: state modified by the assignment
        """
        lhs = self.semantics(stmt.left, state,
                             interpreter).result  # lhs evaluation

        ret = None
        if isinstance(stmt.right,
                      Call) and stmt.right.name in interpreter.cfgs:
            ret = VariableIdentifier(stmt.right.typ,
                                     '{}#return'.format(stmt.right.name))
            state = state.add_variable(ret).forget_variable(ret)

        rhs = self.semantics(stmt.right, state,
                             interpreter).result  # rhs evaluation
        state = state.assign(lhs, rhs)

        if isinstance(stmt.right,
                      Call) and stmt.right.name in interpreter.cfgs:
            state = state.remove_variable(ret)

        return state
Пример #7
0
 def strip_call_semantics(self, stmt: Call, state: State,
                          interpreter: Interpreter) -> State:
     if len(stmt.arguments) != 1:
         error = f"Semantics for multiple arguments of {stmt.name} is not yet implemented!"
         raise NotImplementedError(error)
     argument = self.semantics(stmt.arguments[0], state, interpreter).result
     result = set()
     for arg in argument:
         assert isinstance(arg, Expression)
         if not isinstance(arg.typ, StringLyraType):
             error = f"Call to {stmt.name} of argument with unexpected type!"
             raise ValueError(error)
         typ = StringLyraType()
         if isinstance(arg, Input):  # input().strip()
             result.add(Input(typ))
             continue
         elif isinstance(arg, VariableIdentifier):  # x.strip()
             result.add(VariableIdentifier(typ, arg.name))
             continue
         elif isinstance(arg, Subscription):  # x[i].strip()
             result.add(Subscription(typ, arg.target, arg.key))
             continue
         error = f"Call to {stmt.name} of unexpected argument {arg}!"
         raise ValueError(error)
     state.result = result
     return state
Пример #8
0
 def do(variable):
     if isinstance(variable.typ, StringLyraType):
         typ = ListLyraType(variable.typ)
         state.result = {VariableIdentifier(typ, variable.name)}
         return state
     elif isinstance(variable.typ, ListLyraType):
         state.result = {variable}
         return state
     elif isinstance(variable.typ, TupleLyraType):
         raise NotImplementedError(f"Conversion to list of {variable.typ} is not yet implemented!")
     elif isinstance(variable.typ, SetLyraType):
         typ = ListLyraType(variable.typ.typ)
         state.result = {VariableIdentifier(typ, variable.name)}
         return state
     elif isinstance(variable.typ, DictLyraType):
         typ = ListLyraType(variable.typ.key_typ)
         state.result = {VariableIdentifier(typ, variable.name)}
         return state
     raise TypeError(f"Unexpected type {variable.typ} for list conversion!")
Пример #9
0
    def __init__(self,
                 key_domain: Type[Union[KeyWrapper, State]],
                 scalar_vars: Set[VariableIdentifier] = None,
                 dict_vars: Set[VariableIdentifier] = None):
        """Map each program variable/dictionary segment to its usage status.

        :param key_domain: domain for abstraction of dictionary keys,
            ranges over the scalar variables and the special key variable v_k
        :param scalar_vars: list of scalar variables, whose usage should be abstracted
        :param dict_vars: list of dictionary variables, whose usage should be abstracted
        """
        super().__init__()

        if scalar_vars is None:
            scalar_vars = set()
        if dict_vars is None:
            dict_vars = set()

        self._s_vars = scalar_vars
        self._d_vars = dict_vars

        self._k_domain = key_domain

        self._scalar_usage = SimpleUsageStore(scalar_vars)

        arguments = {}
        for dv in dict_vars:
            typ = dv.typ
            if isinstance(typ, DictLyraType):  # should be true
                if typ not in arguments:
                    # if issubclass(key_domain, Store):  # not relational -> don't need scalar vars
                    #     key_vars = []
                    # else:
                    # key_vars = scalar_vars.copy()
                    # if issubclass(value_domain, Store):
                    #     value_vars = []
                    # else:
                    # value_vars = scalar_vars.copy()
                    k_var = VariableIdentifier(typ.key_type, k_name)

                    arguments[typ] = {
                        'key_domain': key_domain,
                        'value_domain': UsageLattice,
                        'key_d_args': {
                            'scalar_variables': scalar_vars,
                            'k_var': k_var
                        }
                    }
            else:
                raise TypeError(
                    "Dictionary variables should be of DictLyraType")

        lattices = defaultdict(lambda: FularaLattice)
        self._dict_usage = Store(dict_vars, lattices, arguments)
Пример #10
0
    def bitxor_call_semantics(self, stmt: Call, state: State,
                              interpreter: Interpreter) -> State:
        """Semantics of calls to '^' (bitwise xor).

        :param stmt: call to '^' (bitwise xor) to be executed
        :param state: state before executing the call statement
        :return: state modified by the call statement
        """
        argument = self.semantics(stmt.arguments[0], state, interpreter).result
        result = set()
        for arg in argument:
            result.add(VariableIdentifier(stmt.typ, arg.name))
        state.result = result
        return state
Пример #11
0
 def input_var(self):
     return VariableIdentifier(StringLyraType(), '.IN')
Пример #12
0
 def visit_Input(self, expr: Input):
     name = "{}.{}".format(self.pp.line, self.nonce)
     return VariableIdentifier(expr.typ, name)
Пример #13
0
 def visit_VariableIdentifier(self, expr: VariableIdentifier):
     return VariableIdentifier(expr.typ, expr.name)