/*
 * Copyright (c) 1997, 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 <string.h>

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

/*
 * Two auxiliary functions: one to manufacture a call to `memset', and one to
 * determine if a CAST expression is a call to `memset'.
 */
static cast_expr make_memset(cast_expr expr, cast_type ctype)
{
	return cast_new_expr_call_3(cast_new_expr_name("memset"),
				    ((ctype->kind == CAST_TYPE_ARRAY) ?
				     expr :
				     cast_new_unary_expr(CAST_UNARY_ADDR,
							 expr)),
				    cast_new_expr_lit_int(0, 0),
				    cast_new_expr_sizeof_type(ctype));
}

static int is_memset(cast_expr expr)
{
	return ((expr->kind == CAST_EXPR_CALL)
		&& (expr->cast_expr_u_u.call.func->kind == CAST_EXPR_NAME)
		&& (!strcmp(expr->cast_expr_u_u.call.func->cast_expr_u_u.name.
			    cast_scoped_name_val[0].name,
			    "memset"))
		);
}

/*
 * Create and return a CAST expression that sets `expr' to zero.  The resultant
 * expression may be an assignment, a procedure call (to `memset'), or a comma
 * expression that combines multiple assignments and/or `memset' calls.
 */
cast_expr cast_new_expr_assign_to_zero(cast_expr expr,
				       cast_type ctype,
				       cast_scope *scope)
{
	cast_expr assign_expr = 0;
	cast_expr zero_expr;
	unsigned int i;
			
	/*****/
	
	switch (ctype->kind) {
	case CAST_TYPE_PRIMITIVE:
		/*
		 * Create an assignment with the right kind of zero.
		 */
		switch (ctype->cast_type_u_u.primitive_type.kind) {
		case CAST_PRIM_CHAR:
			zero_expr = cast_new_expr_lit_char(0, 0);
			break;
		case CAST_PRIM_INT:
			zero_expr = cast_new_expr_lit_int(0, 0);
			break;
		case CAST_PRIM_FLOAT:
			zero_expr = cast_new_expr_lit_float(0.0);
			break;
		case CAST_PRIM_DOUBLE:
			zero_expr = cast_new_expr_lit_double(0.0, 0);
			break;
		default:
			panic("In `cast_new_expr_assign_to_zero', "
			      "unrecognized CAST primitive type.");
			break;
		}
		
		assign_expr = cast_new_expr_assign(expr, zero_expr);
		break;
		
	case CAST_TYPE_POINTER:
	case CAST_TYPE_FUNCTION: /* XXX? */
		assign_expr
			= cast_new_expr_assign(expr,
					       cast_new_expr_lit_int(0, 0));
		break;
		
	case CAST_TYPE_ENUM:
	case CAST_TYPE_ENUM_NAME: {
		/*
		 * Find the zero-valued member of the enumeration, or failing
		 * that, any member of the enumeration.
		 */
		cast_enum_type *enum_type = cast_find_enum_type(scope, ctype);
		
		if (!enum_type)
			/* We couldn't find the enumeration definition. */
			zero_expr = cast_new_expr_lit_int(0, 0);
		else {
			/* Find the zero-valued member of the enumeration. */
			for (i = 0; i < enum_type->slots.slots_len; ++i)
				if ((enum_type->slots.slots_val[i].val->kind
				     == CAST_EXPR_LIT_PRIM)
				    &&
				    (enum_type->slots.slots_val[i].val->
				     cast_expr_u_u.lit_prim.u.kind
				     == CAST_PRIM_INT)
				    &&
				    (enum_type->slots.slots_val[i].val->
				     cast_expr_u_u.lit_prim.u.
				     cast_lit_prim_u_u.i
				     == 0))
					break;
			
			if (i >= enum_type->slots.slots_len) {
				/*
				 * We didn't find a zero value, so we'll just
				 * use the first member of the enumeration.
				 */
				if (enum_type->slots.slots_len > 0)
					i = 0;
				else
					panic("Empty enumeration!");
			}
			
			zero_expr = cast_new_expr_name(enum_type->slots.
						       slots_val[i].name);
		}
		
		assign_expr = cast_new_expr_assign(expr, zero_expr);
		break;
	}
	
	case CAST_TYPE_AGGREGATE:
	case CAST_TYPE_STRUCT_NAME: {
		/*
		 * Make a comma expression to zero the structure members.
		 */
		cast_aggregate_type *agg_type =
			cast_find_struct_type(scope,
					      ctype);
		int will_memset;
		
		if (!agg_type || (agg_type->kind == CAST_AGGREGATE_UNION)) {
			/* We couldn't find the structure definition. */
			assign_expr = make_memset(expr, ctype);
			break;
		}
		/* Iterate through the structure slots. */
		will_memset = 0;
		for (i = 0;
		     i < agg_type->scope.cast_scope_len;
		     ++i) {
			switch(agg_type->scope.
			       cast_scope_val[i].u.kind) {
			case CAST_VAR_DEF:
			case CAST_VAR_DECL:
				zero_expr = cast_new_expr_assign_to_zero(
					cast_new_expr_sel(
						expr,
						agg_type->scope.
						cast_scope_val[i].
						name),
					(agg_type->scope.
					 cast_scope_val[i].u.kind ==
					 CAST_VAR_DEF) ?
					(agg_type->scope.cast_scope_val[i].
					 u.cast_def_u_u.var_def.type) :
					(agg_type->scope.
					 cast_scope_val[i].u.cast_def_u_u.
					 var_type),
					scope);
				
				if (is_memset(zero_expr)) {
					/*
					 * If we have to `memset' any member,
					 * we might as well `memset' the entire
					 * structure.
					 */
					assign_expr = make_memset(expr,
								  ctype);
					will_memset = 1;
					break;
				}
				
				if (!assign_expr)
					assign_expr = zero_expr;
				else
					assign_expr =
						cast_new_binary_expr(
							CAST_BINARY_COMMA,
							assign_expr,
							zero_expr);
				break;
			default:
				break;
			}
			if (will_memset)
				/* We're done iterating over the slots. */
				break;
		}
		if( !assign_expr ) {
			assign_expr = make_memset(expr, ctype);
		}
		break;
	}
	
	case CAST_TYPE_CLASS_NAME:
	case CAST_TYPE_UNION_NAME:
	case CAST_TYPE_ARRAY:
		/*
		 * Use `memset' to zero the expression.
		 */
		assign_expr = make_memset(expr, ctype);
		break;
		
	case CAST_TYPE_NAME: {
		/*
		 * Dereference the named type and call this function again.
		 */
		cast_type real_ctype = cast_find_typedef_type(scope, ctype);
		
		if (!real_ctype)
			/* We couldn't find the type's definition.  Punt. */
			assign_expr = make_memset(expr, ctype);
		else {
			assign_expr = cast_new_expr_assign_to_zero(expr,
								   real_ctype,
								   scope);
			if (is_memset(assign_expr))
				/*
				 * After finding the real C type, we still made
				 * a call to `memset'.  For pretty code, we
				 * should use the given `ctype' in the call,
				 * not the `real_ctype'.
				 */
				assign_expr = make_memset(expr, ctype);
		}
		break;
	}
	
	case CAST_TYPE_QUALIFIED:
		/*
		 * Dig through the qualifiers and call this function again.
		 */
		while (ctype->kind == CAST_TYPE_QUALIFIED)
			ctype = ctype->cast_type_u_u.qualified.actual;
		
		assign_expr = cast_new_expr_assign_to_zero(expr, ctype, scope);
		break;
		
	case CAST_TYPE_VOID:
		panic("In `cast_new_expr_assign_to_zero', "
		      "cannot assign a void-typed expression to zero.");
		break;
		
	default:
		panic("In `cast_new_expr_assign_to_zero', "
		      "unrecognized CAST type %d.", ctype->kind);
		break;
	}
	
	return assign_expr;
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1