/*
 * Copyright (c) 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <assert.h>

#include <mom/compiler.h>
#include <mom/c/libcast.h>
#include <mom/c/pbe.hh>

/*
 * This function is a helper for `mu_state::mu_mapping_param_root' below.
 *
 * Briefly: `insert_actual_param' makes an actual-for-formal parameter
 * substitution in an expression that describes the invocation of a function.
 *
 * Not so briefly: `insert_actual_param' inserts `actual_param_cexpr', a CAST
 * expression describing an actual function parameter value, into `actual_func_
 * invocation_cexpr', which is a C expression describing a function invocation.
 * The `actual_param_cexpr' is inserted into `actual_func_invocation_cexpr' at
 * the point corresponding to the formal parameter `formal_param_cexpr', which
 * must be a CAST name.  The position of `formal_param_cexpr' in `formal_func_
 * invocation_cexpr' determines the corresponding position at which the actual
 * value `actual_param_cexpr' will be placed in `actual_func_invocation_cexpr'.
 *
 * The `formal_func_invocation_cexpr' and `actual_func_invocation_cexpr' are
 * supposed to be identical, except that `actual_func_invocation_cexpr' will
 * contain ``holes'' (null CAST expressions) in places at which no actual-for-
 * formal substitution has yet been made.
 */
static void insert_actual_param(
	cast_expr formal_func_invocation_cexpr,
	cast_expr actual_func_invocation_cexpr,
	cast_expr formal_param_cexpr,
	cast_expr actual_param_cexpr)
{
	cast_scoped_name	*formal_param_name;
	
	cast_expr		*formal_lvalue;
	cast_expr		formal_funcall;
	cast_expr		formal_func_cexpr;
	cast_expr_array		*formal_params;
	
	cast_expr		*actual_lvalue;
	cast_expr		actual_funcall;
	cast_expr		actual_func_cexpr;
	cast_expr_array		*actual_params;
	
	unsigned int i;
	int found;
	
	/*****/
	
	/*
	 * Sanity checks.
	 */
	if (formal_param_cexpr->kind != CAST_EXPR_NAME)
		panic("In `insert_actual_param', bad formal parameter name.");
	formal_param_name = &(formal_param_cexpr->cast_expr_u_u.name);
	
	if (!formal_func_invocation_cexpr)
		panic("In `insert_actual_param', "
		      "the formal function invocation expression is null.");
	if (!actual_func_invocation_cexpr)
		panic("In `insert_actual_param', "
		      "the actual function invocation expression is null.");
	
	if (formal_func_invocation_cexpr->kind
	    != actual_func_invocation_cexpr->kind)
		panic("In `insert_actual_param', "
		      "the actual and formal CAST expressions are of "
		      "different kinds.");
	
	/*
	 * Locate the components of the formal and actual invocation cexprs.
	 */
	switch (formal_func_invocation_cexpr->kind) {
	case CAST_EXPR_BINARY:
		/*
		 * _return = work_func(param, param, ...);
		 */
		formal_lvalue  = &(formal_func_invocation_cexpr->
				   cast_expr_u_u.binary.expr[0]);
		formal_funcall = formal_func_invocation_cexpr->
				 cast_expr_u_u.binary.expr[1];
		actual_lvalue  = &(actual_func_invocation_cexpr->
				   cast_expr_u_u.binary.expr[0]);
		actual_funcall = actual_func_invocation_cexpr->
				 cast_expr_u_u.binary.expr[1];
		
		break;
		
	case CAST_EXPR_CALL:
		/*
		 * work_func(param, param, ...);
		 */
		formal_lvalue  = 0;
		formal_funcall = formal_func_invocation_cexpr;
		actual_lvalue  = 0;
		actual_funcall = actual_func_invocation_cexpr;
		break;
		
	default:
		panic("In `insert_actual_param', "
		      "don't know how to handle invocation expression.");
		break;
	}
	
	/*
	 * More sanity checks.
	 */
	if (formal_lvalue
	    && ((*formal_lvalue)->kind != CAST_EXPR_NAME))
		panic("In `insert_actual_param', "
		      "the formal lvalue CAST expression is not a name.");
	
	if (formal_funcall->kind != CAST_EXPR_CALL)
		panic("In `insert_actual_param', "
		      "the formal funcall CAST expression is not a funcall.");
	
	if (actual_funcall->kind != CAST_EXPR_CALL)
		panic("In `insert_actual_param', "
		      "the actual funcall CAST expression is not a funcall.");
	
	/*
	 * Special case: we're making a substitution of the return value.
	 */
	if (formal_lvalue) {
		if (cast_cmp_scoped_names(
			formal_param_name,
			&((*formal_lvalue)->cast_expr_u_u.name))
		    != 0) {
			/* Our formal ``parameter'' is really the retval. */
			if (*actual_lvalue != 0)
				panic("In `insert_actual_param', "
				      "attempted to set an already-set actual "
				      "return value.");
			*actual_lvalue = actual_param_cexpr;
			return;
		}
	}
	
	/*
	 * Special case: we're making a substitution for the invoked object.
	 *
	 * XXX --- Our expression parsing should be more robust, and should be
	 * probably be handled in a separate function.  We currently handle
	 * only `obj.method' and `obj->method'.
	 */
	formal_func_cexpr = formal_funcall->cast_expr_u_u.call.func;
	actual_func_cexpr = actual_funcall->cast_expr_u_u.call.func;
	
	if (formal_func_cexpr->kind == CAST_EXPR_SEL) {
		found = 0;
		switch (formal_func_cexpr->cast_expr_u_u.sel.var->kind) {
		case CAST_EXPR_NAME:
			if (/* The object name matches our formal name... */
			    (cast_cmp_scoped_names(
				    formal_param_name,
				    &(formal_func_cexpr->
				      cast_expr_u_u.sel.var->
				      cast_expr_u_u.name))
			     != 0)
			    &&
			    /* ...and it's an actual hole. */
			    (actual_func_cexpr->cast_expr_u_u.sel.var == 0)
				) {
				/*
				 * Our formal ``parameter'' is really the
				 * object reference.
				 */
				actual_func_cexpr->cast_expr_u_u.sel.var
					= actual_param_cexpr;
				found = 1;
			}
			break;
			
		case CAST_EXPR_UNARY:
			if (/* The unary operand is a name... */
			    (formal_func_cexpr->
			     cast_expr_u_u.sel.var->
			     cast_expr_u_u.unary.expr->kind
			     == CAST_EXPR_NAME)
			    &&
			    /* ...and the name matches our formal name... */
			    (cast_cmp_scoped_names(
				    formal_param_name,
				    &(formal_func_cexpr->
				      cast_expr_u_u.sel.var->
				      cast_expr_u_u.unary.expr->
				      cast_expr_u_u.name))
			     != 0)
			    &&
			    /* ...and it's an actual hole. */
			    (actual_func_cexpr->
			     cast_expr_u_u.sel.var->
			     cast_expr_u_u.unary.expr
			     == 0)
				) {
				/*
				 * Our formal ``parameter'' is really the
				 * object reference.
				 */
				actual_func_cexpr->
					cast_expr_u_u.sel.var->
					cast_expr_u_u.unary.expr
					= actual_param_cexpr;
				found = 1;
			}
			break;
			
		default:
			panic("In `insert_actual_param', "
			      "can't parse fancy method invocation.");
			break;
		}
		
		/* If we made the actual-for-formal match, we're done. */
		if (found)
			return;
	}
	
	/*
	 * Regular case: search the formal parameters.
	 */
	formal_params = &(formal_funcall->cast_expr_u_u.call.params);
	actual_params = &(actual_funcall->cast_expr_u_u.call.params);
	
	if (formal_params->cast_expr_array_len
	    != actual_params->cast_expr_array_len)
		panic("In `insert_actual_param', "
		      "the formal and actual funcalls have arglists of "
		      "different lengths.");
	
	found = 0;
	for (i = 0; i < formal_params->cast_expr_array_len; ++i) {
		cast_expr this_param = formal_params->cast_expr_array_val[i];
		
		if (this_param->kind != CAST_EXPR_NAME)
			panic("In `insert_actual_param', "
			      "a formal parameter is not a CAST name.");
		
		if (cast_cmp_scoped_names(formal_param_name,
					  &(this_param->cast_expr_u_u.name))
		    != 0) {
			/* We've found the formal; fill in the actual. */
			if (actual_params->cast_expr_array_val[i] != 0)
				panic("In `insert_actual_param', "
				      "attempted to set an already-set actual "
				      "parameter.");
			
			actual_params->cast_expr_array_val[i]
				= actual_param_cexpr;
			found = 1;
		}
	}
	
	if (!found)
		panic("In `insert_actual_param', "
		      "the corresponding formal parameter was not found.");
	
	return;
}


/*****************************************************************************/

/*
 * A `pres_c_mapping_param_root' node indicates that the current CAST
 * expression and CAST type describe a formal parameter to a client stub or
 * server work function: i.e., the name and type of a parameter in the function
 * type signature.
 *
 * When the BE is generating some request-unmarshaling code within a server
 * dispatch function, a `pres_c_mapping_param_root' node tells the back end to
 * allocate the ``root'' of an actual parameter that will be given to the
 * server work function.  The type of the formal parameter may be transformed
 * in order to create a more efficient actual parameter: for instance, formal
 * array types may be transformed into actual pointer-to-element types in order
 * to allow for unmarshaling optimizations.
 *
 * When the BE is generating a client stub, a `pres_c_mapping_param_root' node
 * currently has no effect.
 *
 * XXX --- The initial implementation of this method simply replaces logic that
 * used to happen in the `mu_state::mu_server_func' auxiliary functions `whack_
 * on_server_cfunct' and `mu_state::mu_server_func_alloc_param'.  But soon,
 * this method will be enhanced to handle new kinds of parameter allocations,
 * i.e., C++ references and constrcuted objects.
 */

/*
 * XXX --- Below is the comment from `whack_on_server_work_cfunct', which has
 * been replaced by this method.  The comments that are still germane should be
 * folded into the comments above.
 */
/*
 * XXX --- The following function is a hack to change the names of the server
 * work function parameters and their types (to remove CAST_TYPE_QUALIFIERs,
 * and to change array types into pointer-to-element types).
 *
 * + Name changes are good because they help us avoid name clashes in the
 *   generated code (e.g., between the name of a type and the name of a local
 *   variable).
 *
 * + The removal of type qualifiers is necessary so that we don't try to
 *   allocate locals with `const' types!
 *
 * + Changing array types into pointer types helps `mu_server_func' avoid
 *   declaring unnecessary array locals.  Using a pointer helps the back end be
 *   smart, e.g., point into the message buffer when the encoded array is
 *   bitwise identical to the presented array.  This transformation works only
 *   because of the ``array slice hack'' that was implemented in `mu_state::
 *   mu_mapping_stub_inline', which allows us to associate pointer-to-element
 *   CAST types with the marshal/unmarshal stubs for the corresponding array
 *   CAST types.
 *
 * But really, we should do all of these things in a more principled way.
 */

#define LOCAL_PREFIX "_local_"

void mu_state::mu_mapping_param_root(
	cast_expr cexpr,
	cast_type ctype,
	mint_ref itype,
	pres_c_mapping_param_root *root_map)
{
	cast_scoped_name *formal_scoped_name;
	char *formal_simple_name;
	
	char *actual_simple_name;
	cast_expr actual_cexpr;
	cast_type actual_ctype;
	
	cast_type internal_ctype;
	
	/*****/
	
	if (cexpr->kind != CAST_EXPR_NAME)
		panic("In `mu_state::mu_mapping_param_root', "
		      "the parameter CAST expression is not a name.");
	if (cast_scoped_name_is_empty(&(cexpr->cast_expr_u_u.name)))
		panic("In `mu_state::mu_mapping_param_root', "
		      "the parameter name is empty.");
	
	formal_scoped_name = &(cexpr->cast_expr_u_u.name);
	formal_simple_name
		= (formal_scoped_name->
		   cast_scoped_name_val[
			   formal_scoped_name->cast_scoped_name_len - 1].
		   name);
	
	if (   !strcmp(get_which_stub(), "client")
	    || !strcmp(get_which_stub(), "send")
	    || !strcmp(get_which_stub(), "msg")) {
		/*
		 * Simple: use the formal parameter name.  If we were told to
		 * view the parameter as an instance of a specific type, do so;
		 * otherwise, just use the formal parameter type.
		 */
		actual_cexpr = cexpr;
		if (root_map->ctype != 0)
			actual_ctype = root_map->ctype;
		else
			actual_ctype = ctype;
		
	} else if (!strcmp(get_which_stub(), "server")) {
		/**************************************************************
		 *
		 * Logic stolen from `whack_on_server_work_cfunct', which has
		 * been replaced by this method.
		 */
		/*
		 * Determine the new name.  Ultimately, this name will be used
		 * to declare a local variable in the server dispatch function.
		 */
		if (formal_simple_name[0] == '_') {
			/*
			 * Don't munge the name if it starts with an `_'.  Some
			 * PG/BE code thinks that it knows the names of these
			 * parameters (e.g., that `_ev' is the environment.).
			 */
			actual_simple_name = formal_simple_name;
		} else {
			actual_simple_name
				= ((char *)
				   mustmalloc(sizeof(LOCAL_PREFIX)
					      + strlen(formal_simple_name)));
			strcpy(actual_simple_name, LOCAL_PREFIX);
			strcat(actual_simple_name, formal_simple_name);
		}
		
		actual_cexpr
			= cast_new_expr_scoped_name(
				cast_new_scoped_name(actual_simple_name,
						     NULL));
		
		/*
		 * Determine the new type.
		 *
		 * We strip away qualifiers and transform array types into
		 * pointer-to-element types.  By transforming array types into
		 * pointer types, we make it easier for `mu_server_func' to be
		 * intelligent.  (We don't want `mu_server_func' to declare a
		 * local array if it's not necessary!)
		 */
		if (root_map->ctype != 0)
			actual_ctype = root_map->ctype;
		else
			actual_ctype = ctype;
		
		while (actual_ctype->kind == CAST_TYPE_QUALIFIED)
			actual_ctype = actual_ctype->cast_type_u_u.qualified.
				       actual;
		
		if ((actual_ctype->kind == CAST_TYPE_POINTER)
		    && (actual_ctype->cast_type_u_u.pointer_type.target->kind
			== CAST_TYPE_QUALIFIED)
			) {
			/*
			 * Strip away modifiers from the target type, too!
			 */
			cast_type target_ctype = actual_ctype->cast_type_u_u.
						 pointer_type.target;
			
			while (target_ctype->kind == CAST_TYPE_QUALIFIED)
				target_ctype = target_ctype->cast_type_u_u.
					       qualified.actual;
			
			actual_ctype = cast_new_pointer_type(target_ctype);
		}
		
		if (actual_ctype->kind == CAST_TYPE_REFERENCE) {
			/*
			 * Strip away modifiers from the target type, too!
			 */
			cast_type target_ctype = actual_ctype->cast_type_u_u.
						 reference_type.target;
			
			while (target_ctype->kind == CAST_TYPE_QUALIFIED)
				target_ctype = target_ctype->cast_type_u_u.
					       qualified.actual;
			
			actual_ctype = cast_new_reference_type(target_ctype);
		}
		
		/*
		 * Transform array types into pointer-to-element types.  Note
		 * that reference types are not dereferenced here --- see the
		 * comments below.
		 */
		switch (actual_ctype->kind) {
		default:
			/*
			 * General case: the parameter has a type that is not
			 * an interesting type.
			 */
			break;
			
		case CAST_TYPE_ARRAY:
			/*
			 * Easy case: the parameter type is an explicit array
			 * type.
			 */
			actual_ctype =
				cast_new_pointer_type(actual_ctype->
						      cast_type_u_u.array_type.
						      element_type);
			break;
			
		case CAST_TYPE_NAME:
			/*
			 * Difficult case: the parameter type is a named type
			 * that we must resolve in order to see if it is an
			 * array or reference type.
			 */
			internal_ctype = cast_find_typedef_type(&(pres->cast),
								actual_ctype);
			
			if (!internal_ctype) {
				/* We hit an unresolvable name.  Punt. */
				
			} else if (internal_ctype->kind == CAST_TYPE_ARRAY) {
				/* We resolved to an array! */
				actual_ctype
					= cast_new_pointer_type(
						internal_ctype->
						cast_type_u_u.array_type.
						element_type);
				
			} else {
				/* What we finally found wasn't interesting. */
			}
			break;
		}
		
		/*************************************************************/
		
		if (op & MUST_ALLOCATE) {
			/*
			 * Allocate a local variable to act as our parameter
			 * root.  Logic stolen from `mu_state::mu_server_func
			 * _alloc_param', which has been replaced by the code
			 * here.
			 *
			 * If the parameter is a reference type, allocate the
			 * referent type instead of the `actual_ctype'.  We do
			 * *not* remove the reference from the `actual_ctype',
			 * however: that must be done by a subsequent MAPPING_
			 * VAR_REFERENCE node.
			 *
			 * XXX --- Technically, in the case of reference types,
			 * the `actual_ctype' and `actual_cexpr' that we pass
			 * to `mu_mapping' won't quite correspond.  This isn't
			 * a real problem, however.  The benefit of handling
			 * references in this way is that it makes the handling
			 * of references more uniform: references are always
			 * ``dereferenced'' by a MAPPING_VAR_REFERENCE node.
			 */
			if (actual_ctype->kind == CAST_TYPE_REFERENCE)
				add_var(actual_simple_name,
					(actual_ctype->cast_type_u_u.
					 reference_type.target),
					root_map->init,
					CAST_SC_NONE);
			else
				add_var(actual_simple_name,
					actual_ctype,
					root_map->init,
					CAST_SC_NONE);
			
			/*
			 * Adapted from `whack_...' again.
			 */
			insert_actual_param(formal_func_invocation_cexpr,
					    actual_func_invocation_cexpr,
					    cexpr,
					    actual_cexpr);
		}
		
	} else
		/* Handling neither client nor server?  Panic! */
		panic("In `mu_state::mu_mapping_param_root', "
		      "handling neither client stub nor server function.");
	
	/*********************************************************************/
	
	/* Finally, handle the parameter data. */
	mu_mapping(actual_cexpr, actual_ctype, itype, root_map->map);
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1