/*
 * 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/compiler.h>
#include <mom/libmint.h>
#include <mom/c/libcast.h>
#include <mom/c/libpres_c.h>
#include <mom/c/pbe.hh>


/* This class is called from within a case branch
 * of the top level switch statement.
 * It should deal with the branch of the union that it receives
 */
struct void_u_inl_union_case_functor : public functor
{
	void_u_inl_union_case_functor(functor *func):
		func_to_call(func) {}
	
	virtual void func(mu_state *must) {
		must->mu_union_case(func_to_call);
	} ;
	
	functor *func_to_call;
};
  
/*
 * This class is called from within a case branch to set the discriminator
 * to a literal value.  This functor is only used when the discriminator
 * uses a PRES_C_MAPPING_ELSEWHERE and needs to be set statically.
 */
struct void_u_mu_discriminator_functor : public functor
{
	void_u_mu_discriminator_functor(
		cast_expr discrim_expr,
		cast_expr cast_case_value,
		mint_const mint_case_value,
		mint_ref discrim_itype):
		cexpr(discrim_expr), cvalue(cast_case_value),
		mvalue(mint_case_value), itype(discrim_itype) {}
	
	virtual void func(mu_state *must) {
		if (must->op & MUST_DECODE) {
			/* Assign the discriminator the value
			   of the given branch */
			assert(cexpr);
			assert(cvalue);
			must->add_stmt(
				cast_new_stmt_expr(
					cast_new_expr_assign(cexpr, cvalue)));
		} else {
			must->mu_encode_const(mvalue, itype);
		}
	}
	
	cast_expr cexpr, cvalue;
	mint_const mvalue;
	mint_ref itype;
}; 

/*
 * This class is called from within a case branch from inside mu_union_case
 * of the top level switch statement.
 */
struct void_u_inl_success_functor : public functor
{
	void_u_inl_success_functor(inline_state *ist_in,
				   mint_ref case_itype_in,
				   pres_c_inline_void_union_case case_inl,
				   cast_expr void_expr,
				   functor *optional_func):
		ist(ist_in), itype(case_itype_in), inl(case_inl),
		vexpr(void_expr), opt_func(optional_func) {}
	
	virtual void func(mu_state *must) {
		// Call the optional functor if it exists.
		if (opt_func)
			opt_func->func(must);
		
		// We need to type cast the mapping to the correct type.
		cast_expr typed = cast_new_expr(CAST_EXPR_CAST);
		typed->cast_expr_u_u.cast.expr = vexpr;
		typed->cast_expr_u_u.cast.type = inl.type;
		must->mu_mapping(typed, inl.type, itype, inl.mapping);
	}

	inline_state *ist;
	mint_ref itype;
	pres_c_inline_void_union_case inl;
	cast_expr vexpr;
	functor *opt_func;
};

/***/

/* The class of the `functor' that is invoked in order to generate the code
   that appears within the `case' corresponding to a successful decode of the
   value contained in the collapsed union.  A failure functor emits a statement
   that indicates a decoding error. */
   
struct void_u_inl_failure_functor : public functor {
	virtual void func(mu_state *must) {
		/* NOTE: Breaking a glob here would cause an excessive
		         number of globs in the common case.  It is not
			 necessary since the error macros have *no* 
			 assumptions about the marshal/unmarshal state. */
		
		must->add_stmt(must->make_error(FLICK_ERROR_VOID_UNION));
	};
};


/***/

/* The class of the `functor' that is invoked by `mu_union' in order to
   generate discriminating code. */
   
struct void_u_inl_discriminate_functor : public functor
{
	void_u_inl_discriminate_functor(inline_state *istate,
					mint_ref type,
					pres_c_inline _inline) {
		ist = istate;
		itype = type;
		inl = _inline;
	}
	virtual void func(mu_state *must);
	
	inline_state *ist;
	mint_ref itype;
	pres_c_inline inl;
};

void void_u_inl_discriminate_functor::func(mu_state *must)
{
	functor **success_array;
	void_u_inl_failure_functor failure;
	mint_const *discrim_vals;
	cast_type discrim_ctype;
	cast_expr discrim_cexpr, void_expr;
	int count;
	// used to set the discriminator's value in each branch.
	functor *opt_func = 0; 
	
	// See how many cases we have
	mint_union_def *udef = &(must->pres->mint.defs.defs_val[itype].
				 mint_def_u.union_def);
	count = udef->cases.cases_len;
	
	// Allocate space for the functors & the union values
	discrim_vals = (mint_const *)mustcalloc(sizeof(mint_const) * count);
	success_array = (functor **)mustcalloc(sizeof(functor *) * count);
	
	// Get the expression (we don't need the type) of the void * element
	ist->slot_access(inl->pres_c_inline_u_u.void_union.void_index, &void_expr, &discrim_ctype);
	// We could assert that the type is a void *, but who cares? - XXX
	
	// Get the C type and expression to get to the discriminator
	ist->slot_access(inl->pres_c_inline_u_u.void_union.discrim.index, &discrim_cexpr, &discrim_ctype);
	
	/* mu_discriminate and hash_const() take care of memory management */
	
	// Build the arrays of functors and values
	for (int i = 0; i < count; i++) {
		discrim_vals[i] = udef->cases.cases_val[i].val;
		
		/* assign the discriminator's value if not assigned yet */
		if (inl->pres_c_inline_u_u.void_union.discrim.mapping->kind
		    == PRES_C_MAPPING_ELSEWHERE) {
			if (inl->pres_c_inline_u_u.void_union.
			    cases.cases_val[i].case_value) {
				opt_func = new void_u_mu_discriminator_functor(
					discrim_cexpr,
					inl->pres_c_inline_u_u.void_union.
					cases.cases_val[i].case_value,
				        discrim_vals[i],
					udef->discrim);
				assert(opt_func);
			} else {
				/*
				 * XXX - discriminator is never initialized
				 * upon decoding
				 */
#if 1
				assert(opt_func == 0); /* inconsistencies? */
#else
				opt_func = 0;
#endif
			}
		}
		
		success_array[i] = new void_u_inl_union_case_functor(
  			new void_u_inl_success_functor(
  				ist, udef->cases.cases_val[i].var,
  				inl->pres_c_inline_u_u.void_union.cases.
  				cases_val[i],
				void_expr, opt_func));
		assert(success_array[i]);
	}
	
	if ((opt_func) && (must->op & MUST_DECODE)) {
		/*
		 * The discriminator hasn't been
		 * unmarshaled yet, so make mu_discriminate()
		 * discriminate piece by piece.
		 */
		discrim_ctype = 0;
		discrim_cexpr = 0;
	}
		
	/* `mu_discriminate' will generate the `switch', 
	   call the functors, and thereby generate code.
	   NOTE: This code used to call mu_var_discriminate. */
	must->mu_discriminate(discrim_vals,
			      must->pres->mint.defs.defs_val[itype].
			      mint_def_u.union_def.discrim,
			      count, success_array,
			      new void_u_inl_union_case_functor(&failure),
			      discrim_ctype, discrim_cexpr);
}

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

void mu_state::mu_inline_void_union(inline_state *ist, mint_ref itype,
				    pres_c_inline inl)
{
	cast_expr discrim_expr;
	cast_type discrim_ctype;
	assert(itype >= 0);
	assert(itype < (signed int)pres->mint.defs.defs_len);
	
	mint_def *def = &pres->mint.defs.defs_val[itype];
	mint_union_def *udef = &def->mint_def_u.union_def;
	assert(def->kind == MINT_UNION);
	pres_c_inline_void_union *inlvu;
	
	// Build the functor that we're using
	void_u_inl_discriminate_functor discrim_functor(ist, itype, inl);
	
	assert(inl->kind == PRES_C_INLINE_VOID_UNION);
	inlvu = &(inl->pres_c_inline_u_u.void_union);
	
	// marshal or unmarshal the discriminator
	ist->slot_access(inlvu->discrim.index, &discrim_expr, &discrim_ctype);
	mu_mapping(discrim_expr, discrim_ctype,
		   udef->discrim, inlvu->discrim.mapping);
	
	// Now deal with the union
	mu_union(&discrim_functor);
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1