def compose_entry_point(self, given_body): """ Generate an entry point function for the environment model. :param given_body: Body of the main function provided by a translator. :return: List of C statements of the generated function body. """ ep = Function(self.entry_name, "int {}(void)".format(self.entry_name)) ep.definition_file = self.entry_file body = ['/* EMG_ACTION {' + '"thread": 1, "type": "CONTROL_FUNCTION_BEGIN", "comment": "Entry point \'{0}\'", ' '"function": "{0}"'.format(self.entry_name) + '} */'] # Init external allocated pointers cnt = 0 functions = [] if len(self.__external_allocated.keys()) > 0: for file, ext_vars in ((f, v) for f, v in self.__external_allocated.items() if v): func = Function('emg_allocate_external_{}'.format(cnt), "void emg_allocate_external_{}(void)".format(cnt)) func.declaration_files.add(file) func.definition_file = file init = ["{} = {}();".format(var.name, 'external_allocated_data') for var in ext_vars] func.body = init self.add_function_definition(func) self.add_function_declaration(self.entry_file, func, extern=True) functions.append(func) cnt += 1 gl_init = Function('emg_initialize_external_data', 'void emg_initialize_external_data(void)') gl_init.declaration_files.add(self.entry_file) gl_init.definition_file = self.entry_file init_body = ['{}();'.format(func.name) for func in functions] gl_init.body = init_body self.add_function_definition(gl_init) body += [ '/* Initialize external data */', 'emg_initialize_external_data();' ] if self._conf.get("initialize requirements", True): body.append('ldv_initialize();') comment_data = {'action': 'scenarios'} body += [model_comment('ACTION_BEGIN', 'Begin Environment model scenarios', comment_data)] + given_body + \ [model_comment('ACTION_END', other=comment_data)] if self._conf.get("check final state", True): body.append('ldv_check_final_state();') body += ['return 0;', '/* EMG_ACTION {' + '"comment": "Exit entry point \'{0}\'", "type": "CONTROL_FUNCTION_END", "function": "{0}"'. format(self.entry_name) + '} */'] ep.body = body self.add_function_definition(ep) return body
def source_preset(): cfiles = ['main.c', 'lib.c'] source = Source(cfiles, [], dict()) main_functions = { 'f1': "static int f1(struct test *)", 'f2': "static void f2(struct test *)", 'f3': "static void f3(struct test *)", 'f4': "static int f4(struct validation *)", 'f5': "static void f4(void)" } external_functions = { "register_c1": "int register_c1(struct test *)", "deregister_c1": "void deregister_c1(struct test *)", "register_c2": "int register_c2(struct validation *)", "deregister_c2": "void deregister_c2(struct validation *)" } for name, declaration_str in main_functions.items(): new = Function(name, declaration_str) new.definition_file = cfiles[0] source.set_source_function(new, cfiles[0]) for name, declaration_str in external_functions.items(): new = Function(name, declaration_str) new.definition_file = cfiles[1] source.set_source_function(new, cfiles[1]) return source
def add_function(self, func, scope, fs, deps, cfiles): fs_desc = fs[scope][func] if scope == 'unknown': key = list(fs_desc['declarations'].keys())[0] signature = fs_desc['declarations'][key]['signature'] func_intf = Function(func, signature) # Do not set definition file since it is out of scope of the target program fragment else: signature = fs_desc.get('signature') func_intf = Function(func, signature) func_intf.definition_file = scope # Set static if fs_desc.get('type') == "static": func_intf.static = True else: func_intf.static = False # Add declarations files = sortedcontainers.SortedSet() if func_intf.definition_file: files.add(func_intf.definition_file) if fs_desc['declarations']: files.update({ f for f in fs_desc['declarations'] if f != 'unknown' and f in deps }) for file in files: if file not in cfiles and file not in func_intf.header_files: func_intf.header_files.append(file) for cfile in deps[file]: self.set_source_function(func_intf, cfile) func_intf.declaration_files.add(cfile)
def __state_switch(self, states): key = ''.join(sorted([str(i) for i in states])) if key in self.__switchers_cache: return self.__switchers_cache[key]['call'] # Generate switch function name = 'emg_switch_{}'.format(len(list(self.__switchers_cache.keys()))) func = Function(name, 'int f(void)') # todo: Incorrect file func.definition_file = self._cmodel.entry_file # Generate switch body code = list() code.append('switch (ldv_undef_int()) {') for index in range(len(states)): code.append('\tcase {}: '.format(index) + '{') code.append('\t\treturn {};'.format(states[index])) code.append('\t\tbreak;') code.append('\t}') code.append('\tdefault: ldv_assume(0);') code.append('}') func.body.extend(code) # Add function self._cmodel.add_function_definition(func) invoke = '{}()'.format(name) self.__switchers_cache[key] = { 'call': invoke, 'function': func } return invoke
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]
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: return super(SimplestTranslator, self)._control_function(automaton) else: name = f'emg_{automaton.process.category}_{automaton.process.name}' receives = [ r for r in automaton.process.actions.filter( include={Receive}) if r.replicative ] if len(receives) == 0: # This is the main process declaration = f'void f(void)' elif len(receives) > 1: raise RuntimeError( f'Process {str(automaton.process)} has more than the one receive signal which' f'is not supported by the translator. Choose an another one.' ) else: action = receives.pop() param_declarations = [] for index, param in enumerate(action.parameters): receiver_access = automaton.process.resolve_access( param) var = automaton.determine_variable( receiver_access.label) param_declarations.append( var.declaration.to_string( '', typedef='complex_and_params', scope={automaton.process.file})) args = ', '.join(param_declarations) declaration = f'void f({args})' cf = Function(name, declaration) cf.definition_file = automaton.process.file self._control_functions[automaton] = cf return self._control_functions[automaton]
def _model_factory(model_class): """The function allows to build a model with provided processes.""" files = ['test.c'] functions = { 'f1': "static int f1(struct test *)", 'f2': "static void f2(struct test *)" } source = Source(files, [], dict()) for name, declaration_str in functions.items(): new = Function(name, declaration_str) new.definition_file = files[0] source.set_source_function(new, files[0]) spec = { "functions models": { "f1": model_class.f1_model, "f2": model_class.f2_model, }, "environment processes": model_class.environment_models, "main process": model_class.entry } collection = CollectionDecoder(logging, dict()).parse_event_specification(source, json.loads(json.dumps(spec)), ProcessCollection()) return collection
def _dispatch(self, action, automaton): """ Generate a code block for a dispatch action of the process for which the automaton is generated. A dispatch code block is always generated in a fixed form: as a function call of auxiliary function. Such a function contains switch or if operator to choose one of available optional receivers to send the signal. Implementation of particular dispatch to particular receiver is configurable and can be implemented differently in various translation. :param action: Action object. :param automaton: Automaton object which contains the dispatch. :return: [list of strings with lines of C code statements of the code block], [list of strings with new local variable declarations required for the block], [list of strings with boolean conditional expressions which guard code block entering], [list of strings with model comments which embrace the code block] """ code, v_code, conditions, comments = list(), list(), list(), list() # Determine peers to receive the signal automata_peers = sortedcontainers.SortedDict() if len(action.peers) > 0: # Do call only if model which can be called will not hang extract_relevant_automata( self._logger, self._event_fsa + self._model_fsa + [self._entry_fsa], automata_peers, action.peers, Receive) else: # Generate comment code.append( "/* Dispatch {!r} is not expected by any process, skipping the action */" .format(action.name)) # Make comments if len(automata_peers) > 0: category = list(automata_peers.values() )[0]['automaton'].process.category.upper() comment = action.comment.format(category) else: comment = 'Skip the action, since no peers has been found.' comments.append(action_model_comment(action, comment, begin=True)) comments.append(action_model_comment(action, None, begin=False)) # Add given conditions from a spec conditions = [] if action.condition and len(action.condition) > 0: for statement in action.condition: cn = self._cmodel.text_processor(automaton, statement) conditions.extend(cn) if len(automata_peers) > 0: # Add conditions on base of dispatches checks = self._relevant_checks(automata_peers) if len(checks) > 0: if automaton in self._model_fsa: conditions.append("({})".format(' || '.join(checks))) else: # Convert conditions into assume, because according to signals semantics process could not proceed # until it sends a signal and condition describes precondition to prevent signal sending to a # wrong process. if len(checks) > 0: code.append('ldv_assume({});'.format( ' || '.join(checks))) # Generate artificial function body = [] if not self._conf.get('direct control functions calls'): body = ['int ret;'] # Check dispatch type replicative = False for a_peer in automata_peers: for act in automata_peers[a_peer]['actions']: if act.replicative: replicative = True break # Determine parameters df_parameters = [] function_parameters = [] # Add parameters for index in range(len(action.parameters)): # Determine dispatcher parameter # We expect strictly one dispatcher_access = automaton.process.resolve_access( action.parameters[index]) variable = automaton.determine_variable( dispatcher_access.label) function_parameters.append(variable.declaration) df_parameters.append(variable.name) # Generate blocks on each receive to another process # You can implement your own translation with different implementations of the function pre, blocks, post = self._dispatch_blocks(action, automaton, function_parameters, automata_peers, replicative) if len(blocks) > 0: body += pre # Print body of a dispatching function if action.broadcast: for block in blocks: body += block else: imply_signals = self._conf.get('do not skip signals') if len(blocks) > 2 or (len(blocks) == 2 and not imply_signals): body.append('switch (ldv_undef_int()) {') for index in range(len(blocks)): body.extend( ['\tcase {}: '.format(index) + '{'] + \ ['\t\t' + stm for stm in blocks[index]] + \ ['\t\tbreak;', '\t};'] ) if imply_signals: body.append('\tdefault: ldv_assume(0);') body.append('};') elif len(blocks) == 2 and imply_signals: body.append('if (ldv_undef_int()) {') body.extend(['\t' + stm for stm in blocks[0]]) body.extend(['}', 'else {']) body.extend(['\t' + stm for stm in blocks[1]]) body.extend(['}']) elif len(blocks) == 1 and not imply_signals: body.append('if (ldv_undef_int()) {') body.extend(['\t' + stm for stm in blocks[0]]) body.extend(['}']) else: body.extend(blocks[0]) if len(function_parameters) > 0: df = Function( "emg_dispatch_{}_{}".format(str(action), str(automaton)), "void f({})".format(', '.join([ function_parameters[index].to_string( 'arg{}'.format(index), typedef='complex_and_params') for index in range(len(function_parameters)) ]))) else: df = Function( "emg_dispatch_{}_{}".format(str(action), str(automaton)), "void f(void)") df.definition_file = automaton.process.file body.extend(post) body.append('return;') df.body.extend(body) # Add function definition self._cmodel.add_function_definition(df) code.extend( ['{}({});'.format(df.name, ', '.join(df_parameters))]) else: # This is becouse translation can have specific restrictions self._logger.debug( f'No block to implement signal receive of actioon {str(action)} in {str(automaton)}' ) code.append( '/* Skip the dispatch because there is no process to receive the signal */' ) else: self._logger.debug( f'No peers to implement signal receive of actioon {str(action)} in {str(automaton)}' ) code.append( '/* Skip the dispatch because there is no process to receive the signal */' ) return code, v_code, conditions, comments