Example #1
0
def convert_args_to_waldo(method_node,sequence_local=False):
    '''
    In many cases, am not passing WaldoObjects to arguments being
    called.  (Examples: when a programmer uses a non-reference type
    that is not external.)  Currently, the compiler's invariant
    however is that all variables should be Waldo objects.  As a
    result, for each argument passed in, we check if it is a Waldo
    object.  If it is not, we turn it into one, (depending on the type
    it has).

    Additionally, method caller always passes in the reference to a
    Waldo object when the method is called with a variable.  For example,

    a = 5;
    some_method(a);

    Actually passes the reference to a into the called function.  This
    is fine if a is a reference type (list, map, user struct, function
    object) or the signature of some_method takes in an external.
    However, it is not okay for non-external value types.  These
    should be passed by value.  For each non-external value type, tell
    turn_into_waldo_var to force a copy of the variable.


    @param {bool} sequence_local --- If sequence_local is True, that
    means that the data must be copied into a peered vriable,
    regardless of whether or not they are a reference type.
    
    '''
    arg_node_index = get_arg_index_from_func_node_label(method_node.label)
    func_decl_arglist_node = method_node.children[arg_node_index]
    converted_args_string = ''

    # need to emit two sections of code, decide which to run based on
    # whether this was an endpoint call or not.  The reason that we
    # make this distinction is that non-external maps, lists, and user
    # structs passed across endpoint call boundaries are copied,
    # rather than used by reference.  (Across non-endpoint method call
    # boundaries, they're used by reference.)

    converted_args_string = 'if _context.check_and_set_from_endpoint_call_false():\n'
    converted_args_string += emit_utils.indent_str(
        convert_args_helper(func_decl_arglist_node,sequence_local,True) +
        '\npass\n')
    
    converted_args_string += '\nelse:\n'
    converted_args_string += emit_utils.indent_str(
        convert_args_helper(func_decl_arglist_node,sequence_local,False) +
        '\npass\n')

    converted_args_string += '\n'
    return converted_args_string
Example #2
0
def emit_single_endpoint(endpoint_name,ast_root,fdep_dict,emit_ctx):
    '''
    @param {String} endpoint_name --- The name of the endpoint we are
    currently emitting.

    For all other params + return, @see emit_endpoints

    Will produce something like the following:
    
def SingleSide(_waldo_classes,_host_uuid,_conn_obj):

    class _SingleSide (_waldo_classes["Endpoint"]):
        def __init__(self,_waldo_classes,_host_uuid,_conn_obj):

        ...

    return _SingleSide(_waldo_classes,_host_uuid,_conn_obj)


    The reason that we wrap the class in a function instead of just
    directly providing the class is so that we do not have to
    statically link the generated file to any library.  Can take in an
    argument that dynamically tells us how to make the Endpoint
    inherit from the hidden Waldo _Endpoint.
    '''
    endpoint_factory_func = (
        'def %s (_waldo_classes,_host_uuid,_conn_obj,*args):\n' %
        endpoint_name)
    
    endpoint_header = 'class _%s (_waldo_classes["Endpoint"]):\n' % (
        endpoint_name)

    endpoint_body = emit_endpoint_body(
        endpoint_name,ast_root,fdep_dict,emit_ctx)
    
    endpoint_class = endpoint_header + emit_utils.indent_str(endpoint_body)

    
    endpoint_factory_func += emit_utils.indent_str(endpoint_class)
    endpoint_factory_func += emit_utils.indent_str(
        'return _%s(_waldo_classes,_host_uuid,_conn_obj,*args)\n' % endpoint_name)
        
    return endpoint_factory_func
Example #3
0
def emit_message_receive(
    message_receive_node,next_to_call_node,endpoint_name,ast_root,
    fdep_dict,emit_ctx):
    '''
    @param {AstNode or None} next_to_call_node --- If AstNode, then
    has label AST_MESSAGE_RECEIVE_FUNCTION.  If None, means that this
    is the last message receive node in sequence and should tell other
    side to issue a block call with target None.
    '''

    msg_recv_node_name_node = message_receive_node.children[1]
    msg_recv_name = msg_recv_node_name_node.value
    
    emit_ctx.in_message_receive = True
    emit_ctx.message_seq_return_txt = '\nreturn '

    msg_receive_txt = emit_private_method_interface(
        message_receive_node,endpoint_name,ast_root,fdep_dict,emit_ctx,
        lib_util.partner_endpoint_msg_call_func_name)
    msg_receive_txt += '\n'
    
    emit_ctx.in_message_receive = False
    emit_ctx.message_seq_return_txt = ''

    ## Ends by telling the opposite side what to do next
    next_to_call_txt = 'None'
    if next_to_call_node != None:
        # the name of the next sequence block to execute, as it
        # appears in the source Waldo text, ie, before mangling.
        next_sequence_block_src_name = next_to_call_node.children[1].value

        # note that we must wait on receiving a response from the other
        # side before we can continue on, or the original sender may
        # return too early. We should not wait on the last message that we
        # send however, because, we do not expect any response to it.
        # (Ie, if next_to_call_txt == 'None', then do not wait.)
        
        next_to_call_txt = '"' + lib_util.partner_endpoint_msg_call_func_name(
            next_sequence_block_src_name) + '"'

    next_sequence_txt = '''

_context.hide_partner_call(
    self,_active_event,%s,False)
''' % (next_to_call_txt)

    msg_receive_txt += emit_utils.indent_str(next_sequence_txt)
    return msg_receive_txt
Example #4
0
def emit_endpoint_init(
    endpoint_name,ast_root,fdep_dict,emit_ctx):
    '''
    For params and return, @see emit_single_endpoint
    '''
    oncreate_argument_string = ''
    oncreate_node = get_oncreate_node(endpoint_name,ast_root)
    if oncreate_node != None:
        oncreate_arg_names = get_method_arg_names(oncreate_node)
        oncreate_argument_string = reduce (
            lambda x, y : x + ',' + y,
            oncreate_arg_names,'')

    init_header = (
        'def __init__(self,_waldo_classes,_host_uuid,_conn_obj%s):\n' % oncreate_argument_string)

    # create endpoint and peered variable store
    
    # should initialize variable store before entering into _Endpoint
    # super class initializer: super class initializer registers the
    # _Endpoint with connection object.  
    endpoint_global_and_peered_variable_store_load_txt = (
        emit_endpoint_global_and_peered_variable_store(
            endpoint_name,'_host_uuid',ast_root,fdep_dict,emit_ctx))

    # actually initialize super class
    init_body = '''
# a little ugly in that need to pre-initialize _host_uuid, because
# code used for initializing variable store may rely on it.  (Eg., if
# initializing nested lists.)
self._waldo_classes = _waldo_classes
self._host_uuid = _host_uuid
self._global_var_store = %s(_host_uuid)
%s
%s.__init__(self,_waldo_classes,_host_uuid,_conn_obj,self._global_var_store)

''' % (emit_utils.library_transform('VariableStore'),
       endpoint_global_and_peered_variable_store_load_txt,
       emit_utils.library_transform('Endpoint'))


    # emit call to oncreate method
    init_body += emit_oncreate_call(endpoint_name,ast_root)

    return init_header + emit_utils.indent_str(init_body)
Example #5
0
def emit_message_receive(
    message_receive_node,next_to_call_node,endpoint_name,ast_root,
    fdep_dict,emit_ctx):
    '''
    @param {AstNode or None} next_to_call_node --- If AstNode, then
    has label AST_MESSAGE_RECEIVE_FUNCTION.  If None, means that this
    is the last message receive node in sequence and should tell other
    side to issue a block call with target None.
    '''

    msg_recv_node_name_node = message_receive_node.children[1]
    msg_recv_name = msg_recv_node_name_node.value
    
    emit_ctx.in_message_receive = True
    emit_ctx.message_seq_return_txt = '\nreturn '

    msg_receive_txt = emit_private_method_interface(
        message_receive_node,endpoint_name,ast_root,fdep_dict,emit_ctx,
        lib_util.partner_endpoint_msg_call_func_name)
    msg_receive_txt += '\n'
    
    emit_ctx.in_message_receive = False
    emit_ctx.message_seq_return_txt = ''

    ## Ends by telling the opposite side what to do next
    next_to_call_txt = 'None'
    if next_to_call_node != None:
        # the name of the next sequence block to execute, as it
        # appears in the source Waldo text, ie, before mangling.
        next_sequence_block_src_name = next_to_call_node.children[1].value
        
        next_to_call_txt = '"' + lib_util.partner_endpoint_msg_call_func_name(
            next_sequence_block_src_name) + '"'
        

        
    # note that we must wait on receiving a response from the other
    # side before we can continue on, or the original sender may
    # return too early. We should not wait on the last message that we
    # send however, because, we do not expect any response to it.
    # (Ie, if next_to_call_txt == 'None', then do not wait.)
    next_sequence_txt = '''

_threadsafe_queue = %s.Queue()
_active_event.issue_partner_sequence_block_call(
    _context,%s,_threadsafe_queue,False)
# must wait on the result of the call before returning

if %s != None:
    # means that we have another sequence item to execute next

    _queue_elem = _threadsafe_queue.get()


''' % (emit_utils.library_transform('Queue'),
       next_to_call_txt,
       next_to_call_txt)



    next_sequence_txt += '''

    if isinstance(_queue_elem,%s):
        # back everything out
        raise %s()

    _context.set_to_reply_with(_queue_elem.reply_with_msg_field)

    # apply changes to sequence variables.  Note: that
    # the system has already applied deltas for global data.
    _context.sequence_local_store.incorporate_deltas(
        _active_event,_queue_elem.sequence_local_var_store_deltas)

    # send more messages
    _to_exec_next = _queue_elem.to_exec_next_name_msg_field
    if _to_exec_next != None:
        # means that we do not have any additional functions to exec
        _to_exec = getattr(self,_to_exec_next)
        _to_exec(_active_event,_context)

''' % (emit_utils.library_transform('BackoutBeforeReceiveMessageResult'),
       emit_utils.library_transform('BackoutException'))


    msg_receive_txt += emit_utils.indent_str(next_sequence_txt)
    return msg_receive_txt
Example #6
0
def emit_message_send(
    message_send_node,next_to_call_node,seq_globals_node,
    endpoint_name,ast_root, fdep_dict,emit_ctx):
    '''
    @param {AstNode} seq_globals_node --- Has label
    AST_MESSAGE_SEQUENCE_GLOBALS....In message send, we initialize the
    values of all sequence global variables.  Use this to do that.
    '''

    method_arg_names = get_method_arg_names(message_send_node)
    seq_local_init_prefix = '''
_first_msg = False
if not _context.set_msg_send_initialized_bit_true():
    # we must load all arguments into sequence local data and perform
    # initialization on sequence local data....start by loading
    # arguments into sequence local data
    # below tells the message send that it must serialize and
    # send all sequence local data.
    _first_msg = True
'''
    seq_local_init_prefix += emit_utils.indent_str(
        convert_args_to_waldo(message_send_node,True))
    # now emit the sequence global initializations and declarations
    # (this will also emit for the return nodes).
    seq_local_init_prefix += emit_utils.indent_str(emit_statement.emit_statement(
            seq_globals_node,endpoint_name,ast_root,fdep_dict,emit_ctx))    
    seq_local_init_prefix += emit_utils.indent_str('\npass\n')
    seq_local_init_prefix += '\n'

    
    # when message send ends, it must grab the sequence local data
    # requested to return.  To control for jumps, any time we jump, we
    # take whatever text is in emit_ctx's message_seq_return_txt and
    # insert it.  (For a message send function, this will return
    # sequence local data.  For a message receive function, this will
    # just be a return.)  This way, can insure that do not continue
    # executing after jump.
    return_var_name_nodes = get_message_send_return_var_names(
        message_send_node)

    msg_send_return_txt = '\nreturn '
    for counter in range(0,len(return_var_name_nodes)):
        var_name_node = return_var_name_nodes[counter]
        msg_send_return_txt += (
            '_context.sequence_local_store.get_var_if_exists("%s")' %
            var_name_node)
        if counter != (len(return_var_name_nodes) -1):
            msg_send_return_txt += ','
            

        
    emit_ctx.in_message_send = True
    emit_ctx.message_seq_return_txt = msg_send_return_txt

    # a message send function should look the same as a private
    # internal method (if we name it a little
    # differently...name_mangler arg takes care of this; and we
    # keep track of the return statement to issue on jump calls)
    msg_send_txt = emit_private_method_interface(
        message_send_node,endpoint_name,ast_root,fdep_dict,emit_ctx,
        lib_util.partner_endpoint_msg_call_func_name,seq_local_init_prefix)

    # issue call for what to call next
    msg_send_txt += '\n'
    msg_send_txt += emit_utils.indent_str(
        emit_message_node_what_to_call_next(next_to_call_node,emit_ctx))

    # takes care of fall-through return (ie, message send has
    # completed)
    msg_send_txt += emit_utils.indent_str(msg_send_return_txt)
    msg_send_txt += '\n'
    emit_ctx.in_message_send = False
    emit_ctx.message_seq_return_txt = ''
    return msg_send_txt
Example #7
0
def emit_public_method_interface(
    public_method_node,endpoint_name,ast_root,fdep_dict,emit_ctx):
    '''
    @param {AstNode} public_method_node --- An AstNode with label
    AST_PUBLIC_FUNCTION
    '''
    method_name_node = public_method_node.children[0]
    method_name = method_name_node.value

    method_arg_names = get_method_arg_names(public_method_node)
    # turns the array of argnames above into a single string of csv
    # arg names
    comma_sep_arg_names = reduce (
        lambda x, y : x + ',' + y,
        method_arg_names,'')

    public_header = '''
def %s(self%s):
''' % (method_name, comma_sep_arg_names)

    # wait until ready initialization for node has completed before
    # continuing
    public_body = '''
# ensure that both sides have completed their onCreate calls
# before continuing
self._block_ready()
'''
    
    #### Deep copy non-external args
    # non_ext_arg_names is an array of strings
    non_ext_arg_names = get_non_external_arg_names_from_func_node(
        public_method_node)
                
    # do not need to copy arguments in: each function call does so on
    # its own.

    # Each element in this list is an index for a return parameter
    # that should be de-waldo-ified before returning.  We pass this
    # argument to the internal function that the internal function
    # knows which returns need to be de-waldo-ified before being
    # returned and which do not.
    list_return_external_positions = (
        get_external_return_positions_from_func_node(public_method_node))

    #### create a root event + ctx for event, call internal, and reurn
    internal_method_name = lib_util.endpoint_call_func_name(method_name)
    public_body += '''
while True:  # FIXME: currently using infinite retry 
    _root_event = self._act_event_map.create_root_event()
    _ctx = %s(
        self._global_var_store,
        # not using sequence local store
        %s(self._host_uuid))

    # call internal function... note True as last param tells internal
    # version of function that it needs to de-waldo-ify all return
    # arguments (while inside transaction) so that this method may
    # return them....if it were false, might just get back refrences
    # to Waldo variables, and de-waldo-ifying them outside of the
    # transaction might return over-written/inconsistent values.
    _to_return = self.%s(_root_event,_ctx %s,%s)
    # try committing root event
    _root_event.request_commit()
    _commit_resp = _root_event.event_complete_queue.get()
    if isinstance(_commit_resp,%s):
        # means it isn't a backout message: we're done
        return _to_return
''' % (emit_utils.library_transform('ExecutingEventContext'),
       emit_utils.library_transform('VariableStore'),
       internal_method_name,
       comma_sep_arg_names,
       str(list_return_external_positions),
       emit_utils.library_transform('CompleteRootCallResult'))

    return public_header + emit_utils.indent_str(public_body)
Example #8
0
def emit_private_method_interface(
    method_node,endpoint_name,ast_root,fdep_dict,emit_ctx,
    name_mangler=lib_util.endpoint_call_func_name,prefix=None):
    '''
    @param {AstNode} method_node --- Either a public method node or a
    private method node.  If it's a public method, then we emit the
    internal method that gets called from the public interface of the
    method.  We emit private versions of public methods for two
    reasons:

       1: The public versions of the methods do some very basic
          bookkeeping (copy args in, check for backout and retry,
          etc.)

       2: When issuing an endpoint call, endpoint call gets issued to
          private part of function, which does most of the work.

    @param {Function} name_mangler --- Takes in a string and
    returns a string.  The compiler uses different internal names for
    functions than appear in the Waldo source text.  This is so that
    users are less likely to accidentally call into unsafe functions.
    This function should translate the name of the function from the
    source to an internal function.

    @param {String or None} prefix --- If we are emitting a message
    send function, there are some initialization operations that it
    must do.  Instead of copying the function arguments, we use the
    string provided in prefix.
    
    Also can be called to emit a message send or message receive
    function.
    '''

    name_node_index = 0
    if emit_utils.is_message_sequence_node(method_node):
        name_node_index = 1
    
    method_name_node = method_node.children[name_node_index]
    src_method_name = method_name_node.value
    internal_method_name = name_mangler(src_method_name)

    # When returning, check if it was a call from an outside-Waldo
    # function into this function... if it was (ie,
    # _returning_to_public_ext_array is not None), then we must
    # de-waldo-ify the return values before returning.  The values of
    # _returning_to_public_ext_array correspond to which return values
    # should be returned as externals (ie, we do not de-waldo-ify
    # them).
    method_arg_names = ['_returning_to_public_ext_array=None']
    if method_node.label == AST_MESSAGE_SEND_SEQUENCE_FUNCTION:
        # will fill in the default value of None in reduce
        method_arg_names = ['_returning_to_public_ext_array']
    if method_node.label != AST_MESSAGE_RECEIVE_SEQUENCE_FUNCTION:
        # message receives take no arguments
        method_arg_names = get_method_arg_names(method_node) + method_arg_names

    if method_node.label != AST_MESSAGE_SEND_SEQUENCE_FUNCTION:
        comma_sep_arg_names = reduce (
            lambda x, y : x + ',' + y,
            method_arg_names,'')
    else:
        # provide default values for all argument sequence local data.
        # That way, jumps are easier (we do not have to match # of
        # arguments when jumping).
        comma_sep_arg_names = reduce (
            lambda x, y : x + ',' + y + '=None',
            method_arg_names,'')
        
    private_header = '''
def %s(self,_active_event,_context%s):
''' % (internal_method_name, comma_sep_arg_names)

    # actually emit body of function
    private_body = '\n'

    if method_node.label != AST_MESSAGE_RECEIVE_SEQUENCE_FUNCTION:
        if prefix == None:
            private_body = convert_args_to_waldo(method_node)
        else:
            # we are in a message send function: this means that
            # instead of copying in args, we must test if we've
            # already been initialized (to handle jumps properly).
            # Similarly, we must initialize other sequence global
            # data.
            private_body = prefix
            
    method_body_node = get_method_body_node_from_method_node(method_node)
    emitted_something = False
    for statement_node in method_body_node.children:
        emitted_something = True
        private_body += emit_statement.emit_statement(
            statement_node,endpoint_name,ast_root,fdep_dict,emit_ctx)
        private_body += '\n'
        
    if private_body.strip() == '':
        # in case of empty functions
        private_body += 'pass\n'
    
    return private_header + emit_utils.indent_str(private_body)