Exemplo n.º 1
0
    def _control_function(self, automaton):
        """
        Generate control function. This function generates a FunctionDefinition object without a body. It is required
        to call control function within code blocks until all code blocks are translated and control function body
        can be generated.

        :param automaton: Automaton object.
        :return: FunctionDefinition object.
        """
        if str(automaton) not in self._control_functions:
            # Check that this is an aspect function or not
            if automaton in self._model_fsa:
                name = 'emg_{}'.format(automaton.process.name)
                function_objs = self._source.get_source_functions(
                    automaton.process.name)
                if len(function_objs) == 0:
                    raise ValueError(
                        "Unfortunately there is no function {!r} found by the source analysis"
                        .format(automaton.process.name))
                else:
                    # We ignore there that fact that functions can have different scopes
                    function_obj = function_objs[0]
                params = []
                for position, param in enumerate(
                        function_obj.declaration.parameters):
                    if isinstance(param, str):
                        params.append(param)
                    else:
                        params.append(
                            param.to_string('arg{}'.format(str(position)),
                                            typedef='complex_and_params'))

                if len(params) == 0:
                    param_types = ['void']
                else:
                    param_types = params

                declaration = '{0} f({1})'.format(
                    function_obj.declaration.return_value.to_string(
                        '', typedef='complex_and_params'),
                    ', '.join(param_types))
                cf = Function(name, declaration)
            else:
                name = f'emg_{automaton.process.category}_{automaton.process.name}'
                if not get_or_die(self._conf,
                                  "direct control functions calls"):
                    declaration = 'void *f(void *data)'
                else:
                    declaration = 'void f(void *data)'
                cf = Function(name, declaration)
            cf.definition_file = automaton.process.file

            self._control_functions[automaton] = cf

        return self._control_functions[automaton]
Exemplo n.º 2
0
    def __init__(self, logger, conf, source, cmodel, entry_fsa, model_fsa, event_fsa):
        raise NotImplementedError('State translator requires update to the newst API which has not been done')

        self.__state_variables = dict()
        self.__state_chains_memoization = dict()
        self.__switchers_cache = dict()

        conf.setdefault('actions composition', default_value=[])
        self.__jump_types = set([t for t in [Dispatch, Receive, Block, Subprocess]
                                 if t.__name__ not in get_or_die(conf, 'actions composition')])
        super(StateTranslator, self).__init__(logger, conf, source, cmodel, entry_fsa, model_fsa, event_fsa)
Exemplo n.º 3
0
    def __generate_calls(self, functions_collection):
        def indented_line(t, s):
            return (t * "\t") + s

        loop = get_or_die(self.conf, "infinite calls sequence")

        # Generate process
        ep = Process("main")
        ep._category = 'generic'
        ep.comment = "Call exported functions."
        ep.pretty_id = 'generic'
        ep.process = ''

        # Generate actions for all sequence
        expressions = []
        identifier = 0
        for func in functions_collection:
            for obj in functions_collection[func]:
                self.logger.info("Call function {!r} from {!r}".format(func, obj.definition_file))
                expr = self.__generate_call(ep, func, obj, identifier)
                expressions.append(expr)

        # Generate process description
        code = []
        tab = 0
        if loop:
            code.append(indented_line(tab, "while (1) {"))
            tab += 1

        code.append(indented_line(tab, "switch (ldv_undef_int()) {"))
        tab += 1
        cnt = 0
        for expr in expressions:
            # Add a break after a function call
            code.append(indented_line(tab, "case {}: ".format(cnt) + '{'))
            code.append(indented_line(tab + 1, "{}".format(expr)))
            code.append(indented_line(tab + 1, "break;"))
            code.append(indented_line(tab, "}"))
            cnt += 1
        if loop:
            code.append(indented_line(tab, "default: break;"))
        else:
            code.append(indented_line(tab, "default: ldv_assume(0);"))

        tab -= 1
        code.append(indented_line(tab, "}"))
        if loop:
            code.append("}")
            tab -= 1

        ep.add_condition('function_calls', [], code, 'Call all functions independently.')
        ep.process = "<function_calls>"

        return ep
Exemplo n.º 4
0
    def generate_environment(self):
        """
        Main function of EMG plugin.

        Plugin generates an environment model for the verification task.

        :return: None
        """
        self.logger.info("Start environment model generator {}".format(
            self.id))

        # Initialization of EMG
        self.logger.info("Import results of source analysis")
        sa = create_source_representation(self.logger, self.conf,
                                          self.abstract_task_desc)

        # Generate processes
        self.logger.info("Generate processes of an environment model")
        collection = ProcessCollection()
        reports = generate_processes(self.logger, self.conf, collection,
                                     self.abstract_task_desc, sa)

        # Send data to the server
        self.logger.info("Send data about generated instances to the server")

        report(self.logger, 'patch', {
            'identifier': self.id,
            'data': reports
        }, self.mqs['report files'], self.vals['report id'],
               get_or_die(self.conf, "main working directory"))
        self.logger.info("An intermediate environment model has been prepared")

        # Import additional aspect files
        translate_intermediate_model(self.logger, self.conf,
                                     self.abstract_task_desc, sa, collection)
        self.logger.info(
            "An environment model has been generated successfully")
Exemplo n.º 5
0
    def generate_environment(self):
        """
        Main function of EMG plugin.

        Plugin generates an environment model for the verification task.

        :return: None
        """
        self.logger.info("Start environment model generator {!r}".format(
            self.id))

        # Initialization of EMG
        self.logger.info("Import results of source analysis")
        sa = create_source_representation(self.logger, self.conf,
                                          self.abstract_task_desc)

        # Generate processes
        self.logger.info("Generate processes of an environment model")
        collection = ProcessCollection()
        generate_processes(self.logger, self.conf, collection,
                           self.abstract_task_desc, sa)
        self.logger.info("An intermediate environment model has been prepared")

        # Import additional aspect files
        program_fragment = self.abstract_task_desc['fragment']
        abstract_task = self.abstract_task_desc
        self.abstract_task_desc = list()
        used_attributed_names = set()
        data_report = {"type": "EMG", "envmodel_attrs": {}, "UDEMSes": {}}
        images = []
        for number, model in enumerate(
                decompose_intermediate_model(self.logger, self.conf,
                                             collection)):
            model.name = str(number)
            if model.attributed_name in used_attributed_names:
                raise ValueError(
                    f"The model with name '{model.attributed_name}' has been already been generated"
                )
            else:
                used_attributed_names.add(model.attributed_name)
            new_description = translate_intermediate_model(
                self.logger, self.conf, copy.deepcopy(abstract_task), sa,
                model, data_report["UDEMSes"], program_fragment, images)

            new_description["environment model attributes"] = model.attributes
            new_description["environment model pathname"] = model.name
            data_report["envmodel_attrs"][model.name] = json.dumps(
                model.attributes, ensure_ascii=True, sort_keys=True, indent=2)
            self.abstract_task_desc.append(new_description)
            self.logger.info(
                f"An environment model '{model.attributed_name}' has been generated successfully"
            )

        if len(self.abstract_task_desc) == 0:
            raise ValueError('There is no generated environment models')

        self.logger.info("Send data report to the server")
        report(self.logger, 'patch', {
            'identifier': self.id,
            'data': data_report
        }, self.mqs['report files'], self.vals['report id'],
               get_or_die(self.conf, "main working directory"))

        # Send images only for full-weight decisions. Bridge fails to accept them for lightweight decisions, but
        # they will be deleted for them anyway, so there is no sense to send them.
        if self.conf['weight'] == "0":
            self.logger.info("Send images to the server")
            for name, dot_file, image_file in images:
                report_image(self.logger, self.id, name, dot_file, image_file,
                             self.mqs['report files'], self.vals['report id'],
                             self.conf['main working directory'])
Exemplo n.º 6
0
def __add_process(logger,
                  conf,
                  interfaces,
                  process,
                  chosen,
                  category=None,
                  model=False,
                  label_map=None,
                  peer=None):
    logger.info("Add process {!r} to the model".format(process.name))
    logger.debug(
        "Make copy of process {!r} before adding it to the model".format(
            process.name))
    new = copy.copy(process)
    if not category:
        new.category = 'functions models'
        if not new.comment:
            raise KeyError(
                "You must specify manually 'comment' attribute within the description of the following "
                "function model process description: {!r}.".format(new.name))
    else:
        new.category = category
        if not new.comment:
            new.comment = get_or_die(conf, 'process comment')

    # Add comments
    comments_by_type = get_or_die(conf, 'action comments')
    for action in (a for a in new.actions.filter(include={Action})
                   if not a.comment):
        tag = type(action).__name__.lower()
        if tag in comments_by_type and isinstance(comments_by_type[tag], str):
            action.comment = comments_by_type[tag]
        elif tag in comments_by_type and isinstance(comments_by_type[tag], dict) and \
                action.name in comments_by_type[tag]:
            action.comment = comments_by_type[tag][action.name]
        elif not isinstance(action, Call):
            raise KeyError(
                "Cannot find a comment for action {0!r} of type {2!r} at new {1!r} description. You "
                "shoud either specify in the corresponding environment model specification the comment "
                "text manually or set the default comment text for all actions of the type {2!r} at EMG "
                "plugin configuration properties within 'action comments' attribute."
                .format(action.name, new.name, tag))

        # Add callback comment
        if isinstance(action, Call):
            callback_comment = get_or_die(conf,
                                          'callback comment').capitalize()
            if action.comment:
                action.comment += ' ' + callback_comment
            else:
                action.comment = callback_comment

    new.instance_number += len(chosen.models) + len(chosen.environment) + 1
    logger.info("Finally add process {!r} to the model".format(process.name))

    logger.debug("Set interfaces for given labels")
    if label_map:
        for label in label_map["matched labels"]:
            for interface in [
                    interfaces.get_or_restore_intf(name)
                    for name in label_map["matched labels"][label]
            ]:
                new.labels[label].set_interface(interface)
    else:
        for label, interface in ((l, i) for l in new.labels.values()
                                 for i in l.interfaces
                                 if not l.get_declaration(i)):
            try:
                label.set_interface(interfaces.get_or_restore_intf(interface))
            except KeyError:
                logger.warning(
                    "Process {!r} cannot be added, since it contains unknown interfaces for this "
                    "program fragment".format(str(new)))
                return None

    if model and not category:
        chosen.models[new] = new
    elif not model and category:
        chosen.environment[new] = new
    else:
        raise ValueError(
            'Provide either model or category arguments but not simultaneously'
        )

    if peer:
        logger.debug("Match signals with signals of process {!r}".format(
            str(peer)))
        new.establish_peers(peer)

    logger.info(
        "Check is there exist any dispatches or receives after process addiction to tie"
        .format(str(process)))
    __normalize_model(logger, chosen, interfaces)
    return new
Exemplo n.º 7
0
    def print_source_code(self, additional_lines):
        """
        Generate an environment model as a C code. The code is distributed across aspect addictions for original
        source files and the main environment model C code.

        :param additional_lines: Dictionary with the user-defined C code:
                                 {'file name': {'definitions': [...], 'declarations': []}}
        :return: Dictionary {'file': Path to generated file with the Code}
        """
        aspect_dir = "aspects"
        self._logger.info("Create directory for aspect files {}".format("aspects"))
        os.makedirs(aspect_dir.encode('utf8'), exist_ok=True)

        if self._conf["translation options"].get("propogate headers to instrumented files", True):
            for file in (f for f in self.files if f in additional_lines):
                self.add_headers(file, get_or_die(self._conf["translation options"], "additional headers"))

        addictions = dict()
        # Write aspects
        for file in self.files:
            lines = list()

            # Check headers
            if file == self.entry_file:
                lines += ['#include <{}>\n'.format(h)
                          for h in self._collapse_headers_sets(self._headers.get(self.entry_file, []))] + ["\n"]
            else:
                # Generate function declarations
                self._logger.info('Add aspects to a file {!r}'.format(file))

                # Add headers
                if self._headers.get(file):
                    lines += ['before: file ("$this")\n', '{\n'] + \
                             ['#include <{}>\n'.format(h) for h in self._collapse_headers_sets(self._headers[file])] + \
                             ["\n", "}\n\n"]

                # Add model itself
                lines.extend(['after: file ("$this")\n', '{\n'])

            for tp in self.types.get(file, list()):
                lines += [tp.to_string('') + " {\n"] + \
                         [("\t{};\n".format(tp.fields[field].
                                            to_string(field, typedef='complex_and_params'), scope={file}))
                          for field in list(tp.fields.keys())] + \
                         ["};\n", "\n"]

            # Add declarations
            if additional_lines.get(file, {}).get('declarations'):
                lines += ["\n", "/* EMG aliases */\n"] + additional_lines[file]['declarations']
            if file in self._function_declarations:
                lines += ["\n", "/* EMG Function declarations */\n"] + \
                         [line for func in self._function_declarations[file].keys()
                          for line in self._function_declarations[file][func]]

            if file in self._variables_declarations:
                lines += ["\n", "/* EMG variable declarations */\n"] + \
                         [decl for declarations in self._variables_declarations[file].values() for decl in declarations]

            if self._variables_initializations.get(file):
                lines += ["\n", "/* EMG variable initialization */\n"] + \
                         [i for inits in self._variables_initializations[file].values() for i in inits]

            if additional_lines.get(file, {}).get('definitions'):
                lines.extend(
                    ["\n", "/* EMG aliases for functions */\n"] +
                    additional_lines[file]['definitions']
                )

            if self._function_definitions.get(file):
                lines += ["\n", "/* EMG function definitions */\n"] + \
                         [line for defs in self._function_definitions[file].values() for line in defs + ["\n"]]

            if file != self.entry_file:
                lines.append("}\n\n")

            if self._call_aspects.get(file):
                lines += ["/* EMG kernel function models */\n"] + \
                         [line for aspect in self._call_aspects[file] for line in aspect.define() + ["\n"]]

            if file != self.entry_file:
                name = "{}.aspect".format(unique_file_name("aspects/ldv_" + os.path.splitext(os.path.basename(file))[0],
                                                           '.aspect'))
                path = os.path.relpath(name, self._workdir)
                self._logger.info("Add aspect file {!r}".format(path))
                addictions[file] = path
            else:
                name = self.entry_file
            with open(name, "w", encoding="utf8") as fh:
                fh.writelines(lines)

        return addictions
Exemplo n.º 8
0
def generate_processes(logger, conf, collection, abstract_task_desc, source):
    """
    This is the main function for generating processes of the environment model in the intermediate representation.
    From the configuration, the function reads the list of generators names and runs them one by one to obtain a final
    set of processes before translation them into C code.

    :param logger: logging.Logger plugin object.
    :param conf: EMG configuration dict.
    :param collection: ProcessCollection object.
    :param abstract_task_desc: Description dict.
    :param source: Source collection object.
    :return: ProcessCollection object.
    """
    # In a specific order start proess generators
    generator_names = (
        (e, '.vtg.emg.generators.{}'.format(e)) for e in
        [list(e.keys())[0] for e in get_or_die(conf, "generators options")])
    configurations = [
        list(e.values())[0] for e in get_or_die(conf, "generators options")
    ]
    specifications_set = conf.get('specifications set')

    # Find genererators
    modules = [(shortname, importlib.import_module(name, 'klever.core'))
               for shortname, name in generator_names]

    # Get specifications for each kind of a agenerator
    possible_locations = [root for root, *_ in os.walk(os.path.dirname(conf['specifications dir']))] + \
                         list(get_search_dirs(conf['main working directory']))

    reports = dict()
    for index, (shortname, generator_module) in enumerate(modules):
        # Set debug option
        configurations[index]['keep intermediate files'] = conf.get(
            'keep intermediate files')

        generator = generator_module.ScenarioModelgenerator(
            logger, configurations[index])
        specifications = generator.import_specifications(
            specifications_set, possible_locations)
        reports.update(
            generator.make_scenarios(abstract_task_desc, collection, source,
                                     specifications))

        # Now save specifications
        if conf.get('keep intermediate files'):
            # Save specifications
            for kind in specifications:
                file_name = "{} {}.json".format(shortname, kind)
                generator.save_specification(specifications[kind], file_name)

            # Save processes
            with open('%s intermediate model.json' % str(shortname),
                      mode='w',
                      encoding='utf8') as fp:
                json.dump(collection,
                          fp,
                          cls=CollectionEncoder,
                          sort_keys=True,
                          indent=2)

            # Save images of processes
            collection.save_digraphs('images')

    return reports
Exemplo n.º 9
0
    def __import_inits_exits(self, avt, source):
        _inits = collections.OrderedDict()
        _exits = collections.OrderedDict()
        deps = {}
        for module, dep in avt['deps'].items():
            deps[module] = list(dep)
        order = calculate_load_order(self.logger, deps)
        order_c_files = []
        for module in order:
            for module2 in avt['grps']:
                if module2['id'] != module:
                    continue
                order_c_files.extend(
                    [file['in file'] for file in module2['Extra CCs']])

        init = source.get_macro(get_or_die(self.conf, 'init'))
        if init:
            parameters = dict()
            for path in init.parameters:
                if len(init.parameters[path]) > 1:
                    raise ValueError(
                        "Cannot set two initialization functions for a file {!r}"
                        .format(path))
                elif len(init.parameters[path]) == 1:
                    parameters[path] = init.parameters[path][0][0]

            for module in (m for m in order_c_files if m in parameters):
                _inits[module] = parameters[module]
        elif not self.conf.get('kernel'):
            raise ValueError(
                'There is no module initialization function provided')

        exitt = source.get_macro(get_or_die(self.conf, 'exit'))
        if exitt:
            parameters = dict()
            for path in exitt.parameters:
                if len(exitt.parameters[path]) > 1:
                    raise KeyError(
                        "Cannot set two exit functions for a file {!r}".format(
                            path))
                elif len(exitt.parameters[path]) == 1:
                    parameters[path] = exitt.parameters[path][0][0]

            for module in (m for m in reversed(order_c_files)
                           if m in parameters):
                _exits[module] = parameters[module]
        if not exitt and not self.conf.get('kernel'):
            self.logger.warning('There is no module exit function provided')

        kernel_initializations = []
        if self.conf.get('kernel'):
            if get_or_die(self.conf, "add functions as initialization"):
                extra = get_or_die(self.conf,
                                   "add functions as initialization")
            else:
                extra = dict()

            for name in get_or_die(self.conf, 'kernel initialization'):
                mc = source.get_macro(name)

                same_list = []
                if mc:
                    for module in (m for m in order_c_files
                                   if m in mc.parameters):
                        for call in mc.parameters[module]:
                            same_list.append((module, call[0]))
                if name in extra:
                    for func in (source.get_source_function(f)
                                 for f in extra[name]
                                 if source.get_source_function(f)):
                        if func.definition_file:
                            file = func.definition_file
                        elif len(func.declaration_files) > 0:
                            file = list(func.declaration_files)[0]
                        else:
                            file = None

                        if file:
                            same_list.append((file, func.name))
                        else:
                            self.logger.warning(
                                "Cannot find file to place alias for {!r}".
                                format(func.name))
                if len(same_list) > 0:
                    kernel_initializations.append((name, same_list))

        inits = [(module, _inits[module]) for module in _inits]
        exits = [(module, _exits[module]) for module in _exits]
        return inits, exits, kernel_initializations
Exemplo n.º 10
0
def translate_intermediate_model(logger, conf, avt, source, collection,
                                 udemses, program_fragment, images):
    """
    This is the main translator function. It generates automata first for all given processes of the environment model
    and then give them to particular translator chosen by the user defined configuration. At the end it triggers
    code printing and adds necessary information to the (abstract) verification task description.

    :param logger: Logger object.
    :param conf: Configuration dictionary for the whole EMG.
    :param avt: Verification task dictionary.
    :param source: Source object.
    :param collection: ProcessCollection object.
    :param udemses: Dictionary with UDEMSes to put the new one.
    :param program_fragment: Name of program fragment for which EMG generates environment models.
    :param images: List of images to be reported to the server.
    :return: None.
    """
    # Prepare main configuration properties
    logger.info(
        f"Translate '{collection.attributed_name}' with an identifier {collection.name}"
    )
    conf['translation options'].setdefault('entry point', 'main')
    conf['translation options'].setdefault('environment model file',
                                           'environment_model.c')
    conf['translation options'].setdefault('nested automata', True)
    conf['translation options'].setdefault('direct control functions calls',
                                           True)
    conf['translation options'].setdefault('code additional aspects', list())
    conf['translation options'].setdefault('additional headers',
                                           DEFAULT_INCLUDE_HEADERS)
    conf['translation options'].setdefault('self parallel processes', False)
    conf['translation options'].setdefault('ignore missing program files',
                                           False)

    # Make a separate directory
    model_path = str(collection.name)
    assert model_path, 'Each environment model should have a unique name'
    assert not os.path.isdir(
        model_path), f"Model name '{model_path}' is used twice"
    if os.path.isdir(model_path):
        logger.info(f"Clean workdir for translation '{model_path}'")
        shutil.rmtree(model_path)
    os.makedirs(model_path)
    if collection.attributed_name != collection.name:
        os.symlink(model_path,
                   collection.attributed_name,
                   target_is_directory=True)

    # Save processes
    model_file = os.path.join(model_path, 'input model.json')
    with open(model_file, mode='w', encoding='utf-8') as fp:
        json.dump(collection, fp, cls=CollectionEncoder, indent=2)

    udems = {
        conf["specifications set"]: [{
            "fragments": [program_fragment],
            "model": collection
        }]
    }
    udemses[collection.name] = json.dumps(udems,
                                          cls=CollectionEncoder,
                                          indent=2)

    # Save images of processes
    collection.save_digraphs(os.path.join(model_path, 'images'))

    for root, _, filenames in os.walk(os.path.join(model_path, 'images')):
        for fname in filenames:
            if os.path.splitext(fname)[-1] != '.dot':
                continue
            dot_file = os.path.join(root, fname)
            image_file = os.path.join(root, fname + '.png')
            if os.path.isfile(image_file):
                images.append(('Model {0}/process "{1}"'.format(
                    collection.name,
                    os.path.splitext(os.path.basename(dot_file))[0]), dot_file,
                               image_file))
            else:
                logger.warn('Image "{0}" does not exist'.format(image_file))

    if not collection.entry:
        raise RuntimeError(
            "It is impossible to generate an environment model without main process"
        )

    if conf['translation options'].get('ignore missing function models'):
        for name in list(collection.models.keys()):
            fs = source.get_source_functions(name)
            if not fs:
                logger.info(
                    "Ignore function model {!r} since there is no such function in the code"
                    .format(name))
                del collection.models[name]

    # If necessary match peers
    if conf['translation options'].get('implicit signal peers'):
        collection.establish_peers()

    # Determine entry point file and function
    logger.info("Determine entry point file and function name")
    entry_file = os.path.join(
        model_path, conf['translation options'].get('environment model file',
                                                    'environment_model.c'))
    entry_point_name = get_or_die(conf['translation options'], 'entry point')
    files = source.c_full_paths
    if entry_file not in files:
        files.add(entry_file)
        try:
            entry_file_realpath = find_file_or_dir(
                logger, conf['main working directory'], entry_file)
        except FileNotFoundError:
            entry_file_realpath = os.path.relpath(
                entry_file, conf['main working directory'])

        # Generate new group
        avt['environment model'] = entry_file_realpath

    # First just merge all as is
    additional_code = dict()
    for process in list(collection.models.values()) + list(
            collection.environment.values()) + [collection.entry]:
        for att in ('declarations', 'definitions'):
            for file in getattr(process, att):
                additional_code.setdefault(
                    file, {
                        'declarations': sortedcontainers.SortedDict(),
                        'definitions': sortedcontainers.SortedDict()
                    })
                additional_code[file][att].update(getattr(process, att)[file])
        if process.file == 'environment model':
            process.file = entry_file

    # Initialize code representation
    cmodel = CModel(logger, conf, conf['main working directory'], files,
                    entry_point_name, entry_file)

    # Then convert into proper format
    for file in additional_code:
        additional_code[file]['declarations'] = [
            val if val.endswith('\n') else val + '\n'
            for val in additional_code[file]['declarations'].values()
        ]

        val = additional_code[file]['definitions']
        additional_code[file]['definitions'] = list()
        for name, item in val.items():
            if isinstance(item, list):
                additional_code[file]['definitions'].extend(item)
            elif isinstance(item, str):
                # Replace file contents
                pth = find_file_or_dir(logger, conf['main working directory'],
                                       item)
                with open(pth, 'r', encoding='utf-8') as fp:
                    additional_code[file]['definitions'].extend(
                        fp.readlines() + ["\n"])
            elif isinstance(item, dict):
                # Generate wrappers or do any other transformations
                func = cmodel.create_wrapper(name, item['wrapper'],
                                             item['declaration'])
                additional_code[file]['definitions'].extend(func.define() +
                                                            ["\n"])
                if isinstance(
                        additional_code['environment model']['declarations'],
                        list):
                    additional_code['environment model'][
                        'declarations'].append(
                            func.declare(extern=True)[0] + "\n")
                elif func.name not in additional_code['environment model'][
                        'declarations']:
                    additional_code['environment model']['declarations'][func.name] = \
                        func.declare(extern=True)[0] + "\n"
            else:
                raise ValueError(
                    "Expect either a list of string as a definition in intermediate model specification of"
                    " a path name but got {!r}".format(item))

    # Rename main file
    if 'environment model' in additional_code:
        additional_code[entry_file] = additional_code['environment model']
        del additional_code['environment model']

    # Add common headers provided by a user
    for file in files:
        cmodel.add_headers(
            file, get_or_die(conf['translation options'],
                             "additional headers"))

    logger.info("Generate finite state machine on each process")
    entry_fsa = Automaton(collection.entry, 1)
    identifiers = id_generator(start_from=2, cast=int)
    model_fsa = []
    main_fsa = []
    for process in collection.models.values():
        model_fsa.append(Automaton(process, next(identifiers)))
    for process in collection.environment.values():
        main_fsa.append(Automaton(process, next(identifiers)))

    # Set self parallel flag
    sp_ids = conf["translation options"].get('not self parallel processes')
    if sp_ids and isinstance(sp_ids, list):
        for amtn in (a for a in model_fsa + main_fsa + [entry_fsa]
                     if str(a.process) in sp_ids):
            amtn.self_parallelism = False

    sp_categories = conf["translation options"].get(
        "not self parallel processes from categories")
    sp_scenarios = conf["translation options"].get(
        "not self parallel processes from scenarios")
    if sp_categories and isinstance(sp_categories, list):
        for amtn in (a for a in model_fsa + main_fsa + [entry_fsa]
                     if a.process.category in sp_categories):
            amtn.self_parallelism = False
    if sp_scenarios and isinstance(sp_scenarios, list):
        for amtn in (a for a in model_fsa + main_fsa + [entry_fsa]
                     if a.process.name in sp_scenarios):
            amtn.self_parallelism = False

    # Prepare code on each automaton
    logger.info("Translate finite state machines into C code")
    if conf['translation options'].get("simple control functions calls", True):
        SimplestTranslator(logger, conf['translation options'], source,
                           collection, cmodel, entry_fsa, model_fsa, main_fsa)
    elif get_or_die(conf['translation options'], "nested automata"):
        LabelTranslator(logger, conf['translation options'], source,
                        collection, cmodel, entry_fsa, model_fsa, main_fsa)
    else:
        StateTranslator(logger, conf['translation options'], source,
                        collection, cmodel, entry_fsa, model_fsa, main_fsa)

    logger.info("Print generated source code")
    addictions = cmodel.print_source_code(model_path, additional_code)

    # Set entry point function in abstract task
    logger.info(
        "Add an entry point function name to the abstract verification task")
    avt["entry points"] = [cmodel.entry_name]
    if conf['translation options'].get("code additional aspects"):
        additional_aspects = [
            os.path.abspath(
                find_file_or_dir(logger, conf["main working directory"], f))
            for f in conf['translation options'].get("code additional aspects")
        ]
    else:
        additional_aspects = []
    for grp in avt['grps']:
        # Todo maybe this will not work with ccs with multiple ins
        logger.info('Add aspects to C files of group {!r}'.format(grp['id']))
        for cc_extra_full_desc_file in [
                f for f in grp['Extra CCs'] if 'in file' in f
        ]:
            if cc_extra_full_desc_file["in file"] in addictions:
                if 'plugin aspects' not in cc_extra_full_desc_file:
                    cc_extra_full_desc_file['plugin aspects'] = []
                cc_extra_full_desc_file['plugin aspects'].append({
                    "plugin":
                    "EMG",
                    "aspects":
                    [addictions[cc_extra_full_desc_file["in file"]]] +
                    additional_aspects
                })

    extra_c_files = {
        f
        for p in list(collection.models.values()) +
        list(collection.environment.values()) + [collection.entry]
        for f in p.cfiles
    }
    avt.setdefault('extra C files', list())
    avt['extra C files'].extend([{
        "C file":
        os.path.realpath(
            find_file_or_dir(logger, get_or_die(conf,
                                                "main working directory"), f))
    } for f in extra_c_files])
    return avt
Exemplo n.º 11
0
def translate_intermediate_model(logger, conf, avt, source, collection):
    """
    This is the main translator function. It generates automata first for all given processes of the environment model
    and then give them to particular translator chosen by the user defined configuration. At the end it triggers
    code printing and adds necessary information to the (abstract) verification task description.

    :param logger: Logger object.
    :param conf: Configuration dictionary for the whole EMG.
    :param avt: Verification task dictionary.
    :param source: Source object.
    :param collection: ProcessCollection object.
    :return: None.
    """
    # Prepare main configuration properties
    logger.info("Check necessary configuration properties to be set")
    conf['translation options'].setdefault('entry point', 'main')
    conf['translation options'].setdefault('enironment model file',
                                           'environment_model.c')
    conf['translation options'].setdefault('nested automata', True)
    conf['translation options'].setdefault('direct control functions calls',
                                           True)
    conf['translation options'].setdefault('code additional aspects', list())
    conf['translation options'].setdefault('additional headers', list())
    conf['translation options'].setdefault('self parallel processes', False)

    if not collection.entry:
        raise RuntimeError(
            "It is impossible to generate an environment model without main process"
        )

    if conf['translation options'].get('ignore missing function models'):
        for name in list(collection.models.keys()):
            fs = source.get_source_functions(name)
            if not fs:
                logger.info(
                    "Ignore function model {!r} since there is no such function in the code"
                    .format(name))
                del collection.models[name]

    # If necessary match peers
    if conf['translation options'].get('implicit signal peers'):
        process_list = list(collection.processes)
        for i, first in enumerate(process_list):
            if i + 1 < len(process_list):
                for second in process_list[i + 1:]:
                    first.establish_peers(second)

    # Determine entry point file and function
    logger.info("Determine entry point file and function name")
    entry_file = get_or_die(conf['translation options'],
                            "environment model file")
    entry_point_name = get_or_die(conf['translation options'], 'entry point')
    files = source.c_full_paths
    if entry_file not in files:
        files.add(entry_file)
        try:
            entry_file_realpath = find_file_or_dir(
                logger, conf['main working directory'], entry_file)
        except FileNotFoundError:
            entry_file_realpath = os.path.relpath(
                entry_file, conf['main working directory'])

        # Generate new group
        avt['environment model'] = entry_file_realpath

    # First just merge all as is
    additional_code = dict()
    for process in list(collection.models.values()) + list(
            collection.environment.values()) + [collection.entry]:
        for att in ('declarations', 'definitions'):
            for file in getattr(process, att):
                additional_code.setdefault(
                    file, {
                        'declarations': sortedcontainers.SortedDict(),
                        'definitions': sortedcontainers.SortedDict()
                    })
                additional_code[file][att].update(getattr(process, att)[file])
        if process.file == 'environment model':
            process.file = entry_file

    # Then convert into proper format
    for file in additional_code:
        additional_code[file]['declarations'] = [
            val if val.endswith('\n') else val + '\n'
            for val in additional_code[file]['declarations'].values()
        ]

        val = additional_code[file]['definitions']
        additional_code[file]['definitions'] = list()
        for item in val.values():
            if isinstance(item, list):
                additional_code[file]['definitions'].extend(item)
            elif isinstance(item, str):
                # Replace file contents
                pth = find_file_or_dir(logger, conf['main working directory'],
                                       item)
                with open(pth, 'r', encoding='utf8') as fp:
                    additional_code[file]['definitions'].extend(
                        fp.readlines() + ["\n"])
            else:
                raise ValueError(
                    "Expect either a list of string as a definition in intermediate model specification of"
                    " a path name but got {!r}".format(item))

    # Rename main file
    if 'environment model' in additional_code:
        additional_code[entry_file] = additional_code['environment model']
        del additional_code['environment model']

    # Initalize code representation
    cmodel = CModel(logger, conf, conf['main working directory'], files,
                    entry_point_name, entry_file)

    # Add common headers provided by a user
    for file in files:
        cmodel.add_headers(
            file, get_or_die(conf['translation options'],
                             "additional headers"))

    logger.info("Generate finite state machine on each process")
    entry_fsa = Automaton(collection.entry, 1)
    identifiers = id_generator(start_from=2, cast=int)
    model_fsa = []
    main_fsa = []
    for process in collection.models.values():
        model_fsa.append(Automaton(process, next(identifiers)))
    for process in collection.environment.values():
        main_fsa.append(Automaton(process, next(identifiers)))

    # Set self parallel flag
    sp_ids = conf["translation options"].get('not self parallel processes')
    if sp_ids and isinstance(sp_ids, list):
        for amtn in (a for a in model_fsa + main_fsa + [entry_fsa]
                     if str(a.process) in sp_ids):
            amtn.self_parallelism = False

    sp_categories = conf["translation options"].get(
        "not self parallel processes from categories")
    sp_scenarios = conf["translation options"].get(
        "not self parallel processes from scenarios")
    if sp_categories and isinstance(sp_categories, list):
        for amtn in (a for a in model_fsa + main_fsa + [entry_fsa]
                     if a.process.category in sp_categories):
            amtn.self_parallelism = False
    if sp_scenarios and isinstance(sp_scenarios, list):
        for amtn in (a for a in model_fsa + main_fsa + [entry_fsa]
                     if a.process.name in sp_scenarios):
            amtn.self_parallelism = False

    # Prepare code on each automaton
    logger.info("Translate finite state machines into C code")
    if get_or_die(conf['translation options'], "nested automata"):
        LabelTranslator(logger, conf['translation options'], source, cmodel,
                        entry_fsa, model_fsa, main_fsa)
    else:
        StateTranslator(logger, conf['translation options'], source, cmodel,
                        entry_fsa, model_fsa, main_fsa)

    logger.info("Print generated source code")
    addictions = cmodel.print_source_code(additional_code)

    # Set entry point function in abstract task
    logger.info(
        "Add an entry point function name to the abstract verification task")
    avt["entry points"] = [cmodel.entry_name]
    if conf['translation options'].get("code additional aspects"):
        additional_aspects = [
            os.path.abspath(
                find_file_or_dir(logger, conf["main working directory"], f))
            for f in conf['translation options'].get("code additional aspects")
        ]
    else:
        additional_aspects = []
    for grp in avt['grps']:
        # Todo maybe this will not work with ccs with multiple ins
        logger.info('Add aspects to C files of group {!r}'.format(grp['id']))
        for cc_extra_full_desc_file in [
                f for f in grp['Extra CCs'] if 'in file' in f
        ]:
            if cc_extra_full_desc_file["in file"] in addictions:
                if 'plugin aspects' not in cc_extra_full_desc_file:
                    cc_extra_full_desc_file['plugin aspects'] = []
                cc_extra_full_desc_file['plugin aspects'].append({
                    "plugin":
                    "EMG",
                    "aspects":
                    [addictions[cc_extra_full_desc_file["in file"]]] +
                    additional_aspects
                })

    extra_c_files = {
        f
        for p in list(collection.models.values()) +
        list(collection.environment.values()) + [collection.entry]
        for f in p.cfiles
    }
    avt.setdefault('extra C files', list())
    avt['extra C files'].extend([{
        "C file":
        os.path.realpath(
            find_file_or_dir(logger, get_or_die(conf,
                                                "main working directory"), f))
    } for f in extra_c_files])