/*
 * Copyright (c) 1995, 1996, 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 <assert.h>
#include <mom/c/libcast.h>
#include <mom/c/pbe.hh>

/*
 * Generate code to free the memory pointed to by a pointer variable or
 * parameter, as dictated by the deallocation semantics defined in the passed
 * `alloc' structure.
 *
 * `pexpr' is a C expression evaluating to the pointer whose memory is being
 * freed.
 *
 * `target_type' is the C type of whatever the pointer points to.  (The
 * pointer's type itself would be a CAST_TYPE_POINTER referring to that type).
 *
 * `cname' is the name of the PRES_C_INLINE_ALLOCATION_CONTEXT node that is
 * associated with this pointer.
 */
void mu_state::mu_pointer_free(cast_expr pexpr, cast_type target_type,
			       char *cname)
{
	cast_expr free_expr;
	
	/* We need to find our allocation semantics established by some parent
           INLINE_ALLOCATION_CONTEXT node. */
	mu_inline_alloc_context *iac = inline_alloc_context;
	while (iac) {
		if (strcmp(iac->name, cname) == 0)
			break;
		iac = iac->parent_context;
	}
	assert(iac);
	pres_c_allocation *palloc = iac->alloc;
	
	/* Find the appropriate allocation case, and make sure it's valid. */
	pres_c_allocation_u *ualloc = &palloc->cases[current_param_dir];
	assert(ualloc->allow != PRES_C_ALLOCATION_INVALID);
	pres_c_allocation_case *alloc = &(ualloc->pres_c_allocation_u_u.val);
	
	if (!(op & MUST_DEALLOCATE)) /* XXX */
		return;
	
	if ((alloc->flags & PRES_C_DEALLOC_EVER) == PRES_C_DEALLOC_NEVER)
		return;
	
	unsigned int len_min, len_max;
	cast_expr min_expr, max_expr, length_expr;
	mu_array_get_pres_bounds(&len_min, &min_expr, &len_max, &max_expr,
				 cname);
	length_expr = get_allocation_length(cname, palloc);
	
	/*
	 * Generate a statement to deallocate the storage pointed to by the
	 * pointer.
	 *
	 * XXX --- We ought to replace this `switch' with some kind of
	 * allocator method dispatch function.
	 */
	switch (alloc->allocator.kind) {
	case PRES_C_ALLOCATOR_STATIC:
	case PRES_C_ALLOCATOR_DONTCARE:
		/* Nothing ever needs to be done. */
		return;
	case PRES_C_ALLOCATOR_OUTOFLINE:
	case PRES_C_ALLOCATOR_NAME: {
		if( alloc->flags & PRES_C_RUN_DTOR ) {
			int is_array = !((length_expr->kind ==
					  CAST_EXPR_LIT_PRIM) &&
					 (length_expr->cast_expr_u_u.
					  lit_prim.u.kind == CAST_PRIM_INT) &&
					 (length_expr->cast_expr_u_u.lit_prim.
					  u.cast_lit_prim_u_u.i == 1));
			free_expr = cast_new_expr_op_delete(is_array, pexpr);
		}
		else {
			/* Call a function to deallocate the storage. */
			const char *name = get_deallocator(palloc);
			
			free_expr = cast_new_expr_call_1(cast_new_expr_name(name),
							 pexpr);
		}
		/* Nullify the pointer expr if this is an inout/out
		   parameter.  This is needed so the abort code won't
		   try and free something twice. */
		if( (current_param_dir == PRES_C_DIRECTION_INOUT) ||
		    (current_param_dir == PRES_C_DIRECTION_OUT) ||
		    (current_param_dir == PRES_C_DIRECTION_RETURN) )
			alloc->flags |= PRES_C_DEALLOC_NULLIFY;
		break;
	}
	
	default:
		panic(("In `mu_state::mu_pointer_free', "
		       "unknown allocator kind (%d)."),
		      alloc->allocator.kind);
	}
	
	cast_stmt free_stmt = cast_new_stmt_expr(free_expr);
	
	/* Decide if we should set the allocated length. */
	cast_stmt set_alen_stmt = 0;
	cast_expr alen_cexpr;
	cast_type alen_ctype;
	int gotarg = arglist->getargs(cname, "alloc_len",
				      &alen_cexpr, &alen_ctype);
	assert(gotarg);
	if (alen_cexpr) {
		set_alen_stmt = cast_new_stmt_expr(
			cast_new_expr_assign(alen_cexpr,
					     cast_new_expr_lit_int(0, 0)));
	}
	
	/* Decide if we should honor/set ownership (release flag). */
	cast_stmt release_stmt = 0;
	cast_expr rel_cexpr;
	cast_type rel_ctype;
	gotarg = arglist->getargs(cname, "release", &rel_cexpr, &rel_ctype);
	assert(gotarg);
	if (rel_cexpr) {
		free_stmt = cast_new_if(rel_cexpr, free_stmt, 0);
		release_stmt = cast_new_stmt_expr(
		    cast_new_expr_assign(rel_cexpr,
					 cast_new_expr_lit_int(0, 0)));
	}
	
	/* Now decide when that statement should be invoked. */
	if (alloc->flags & PRES_C_DEALLOC_ALWAYS) {
		add_stmt(free_stmt);
		if (set_alen_stmt) add_stmt(set_alen_stmt);
		if (release_stmt) add_stmt(release_stmt);
		
		/* Nullify the pointer if requested.  */
		if (alloc->flags & PRES_C_DEALLOC_NULLIFY)
			add_stmt(cast_new_stmt_expr(
				cast_new_expr_assign(
					pexpr, 
					cast_new_expr_cast(cast_new_expr_lit_int(0, 0),
							   cast_new_pointer_type(target_type)))));
	} else {
		/* We currently don't have any conditional dealloc cases.
		   This is where they would go if we did. */
		panic(("In mu_pointer_free(): "
		       "Conditional dealloc not handled!"));
	}
}

/*
 * This helper function simply returns the name of the deallocator function for
 * a given `pres_c_allocation'.
 */
const char *mu_state::get_deallocator(pres_c_allocation *palloc)
{
	/* Find the appropriate allocation case, and make sure it's valid. */
	pres_c_allocation_u *ualloc = &palloc->cases[current_param_dir];
	assert(ualloc->allow != PRES_C_ALLOCATION_INVALID);
	pres_c_allocation_case *alloc = &(ualloc->pres_c_allocation_u_u.val);
	
	if (alloc->flags & PRES_C_DEALLOC_ALWAYS)
		switch (alloc->allocator.kind) {
		case PRES_C_ALLOCATOR_DONTCARE:
		case PRES_C_ALLOCATOR_STATIC:
			/* Name of the default (stack) deallocator. */
			return "auto_flick_free";
		case PRES_C_ALLOCATOR_OUTOFLINE:
			return flick_asprintf(
				"%s_flick_free",
				alloc->allocator.pres_c_allocator_u.ool_name);
		case PRES_C_ALLOCATOR_NAME:
			return flick_asprintf(
				"%s_flick_free",
				alloc->allocator.pres_c_allocator_u.name);
		default:
			panic(("In `mu_state::get_deallocator', "
			       "unknown allocator kind (%d)."),
			      alloc->allocator.kind);
			break;
		}
	
	return "null_flick_free";
}

/*
 * This helper function simply returns the appropriate (de)allocator for
 * a given `pres_c_allocation' (as opposed to the deallocator _function_).
 */
pres_c_allocator mu_state::get_deallocator_kind(pres_c_allocation *alloc)
{
	assert(alloc->cases[current_param_dir].allow
	       == PRES_C_ALLOCATION_ALLOW);
	return alloc->cases[current_param_dir].pres_c_allocation_u_u.val.
		allocator;
}

/*
 * This helper function simply returns the appropriate deallocation flags for
 * a given `pres_c_allocation'.
 */
pres_c_alloc_flags mu_state::get_deallocator_flags(pres_c_allocation *alloc)
{
	assert(alloc->cases[current_param_dir].allow
	       == PRES_C_ALLOCATION_ALLOW);
	return alloc->cases[current_param_dir].pres_c_allocation_u_u.val.flags;
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1