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

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


/*
  This functor is used to build the switch statement.
  mu_inline_virtual_union has already read/written the discriminator.
*/
struct mu_inline_virtual_union_functor : public functor
{
	virtual void func(mu_state *must);
	mu_inline_virtual_union_functor(inline_state *ist_in,
					mint_ref union_itype_in,
					pres_c_inline inl) :
		ist(ist_in),
		union_itype(union_itype_in)
	{
		assert (inl->kind == PRES_C_INLINE_VIRTUAL_UNION);
		vuinl = &(inl->pres_c_inline_u_u.virtual_union);
		discrim_expr = 0;
		discrim_type = 0;
	}
	
	inline_state *ist;
	mint_ref union_itype;
	pres_c_inline_virtual_union *vuinl;
	cast_expr discrim_expr;
	cast_type discrim_type;
};

// This functor is used to build the contents of a case statement,
struct vu_case_functor : public functor
{
	virtual void func(mu_state *must);
	vu_case_functor() {}
	
	mint_ref slot_itype;
	pres_c_inline slot_inl;
	inline_state *ist;
};

void vu_case_functor::func(mu_state *must)
{
	must->mu_inline(ist, slot_itype, slot_inl);
	// Break the open chunk
	must->break_chunk();
}

struct mu_inline_virtual_union_error_functor : public functor
{
	virtual void func(mu_state *must);
};

void mu_inline_virtual_union_error_functor::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_VIRTUAL_UNION));
	/* Break any chunk that may be open at this point. */
	must->break_chunk();
}

static void do_vu_case(pres_c_inline_virtual_union_case c,
		       mint_ref itype,
		       inline_state *ist,
		       mu_state *must)
{
	vu_case_functor case_functor;
	case_functor.slot_itype = itype;
	case_functor.slot_inl = c;
	case_functor.ist = ist;
	
	must->mu_union_case(&case_functor);
}
		

void
mu_inline_virtual_union_functor::func(mu_state *must) 
{
	int i;
	mint_ref val_itype;
	mint_const discrim_val;
	mint_union_def *udef;
	mu_memory_allocator_state *enter_case_state;
	cast_stmt saved_c_block, switch_body_c_block, case_c_block;
	
	/* Since there is no other code that can execute alongside a
	   virtual union we don't need to rollback through each of the
	   cases, but we do need to make separate blocks for each case... */
	struct mu_abort_block *mab_par, *mab_con, *mab_thr;
	
	assert(must->pres->mint.defs.defs_val[union_itype].kind == MINT_UNION);
	udef = &(must->
		 pres->mint.defs.defs_val[union_itype].mint_def_u.union_def);
	
	// Break the chunk, we'll be dealing with data of different sizes
	// Save the state so we can restore for each case possible
	must->break_chunk();
	enter_case_state = must->memory_allocator_state();
	
	mab_par = must->abort_block;
	mab_con = new mu_abort_block;
	mab_con->set_kind(MABK_CONTROL);
	mab_con->begin();
	
	mu_msg_span *union_span = 0, *parent_span = 0;
	// Save the current block of code
	// we'll add a 'switch' statement after we've built our cases
	saved_c_block = must->c_block;
	must->c_block = cast_new_block(0,0);
	if( must->current_span ) {
		union_span = new mu_msg_span;
		union_span->set_kind(MSK_UNION);
		union_span->set_block(must->c_block);
		union_span->set_abort(must->abort_block);
		union_span->begin();
		parent_span = must->current_span;
	}
	for (i = 0; i < (signed int)vuinl->cases.cases_len; i++) {
		mab_thr = new mu_abort_block();
		must->abort_block = mab_thr;
		mab_thr->set_kind(MABK_THREAD);
		mab_thr->begin();
		
		/* Add a span for each case */
		if( must->current_span ) {
			must->current_span = new mu_msg_span;
			must->current_span->set_kind(MSK_SEQUENTIAL);
			must->current_span->set_block(must->c_block);
			must->current_span->set_abort(must->abort_block);
			must->current_span->begin();
		}
		// Save the body of the 'switch' statment we've built so far
		switch_body_c_block = must->c_block;
		
		// Start a new block for this case
		must->c_block = cast_new_block(0,0);
		
		// Reset the memory state to
		// the one we saved before we starte the cases
		must->set_memory_allocator_state(enter_case_state);
		
		// Get the itype for the value selected
		val_itype = udef->cases.cases_val[i].var;
		
		// Create a CAST version of the selector for this case.
		discrim_val = udef->cases.cases_val[i].val;
		cast_expr case_value_expr = make_case_value_expr(discrim_val);
		
		// now build the individual 'case' statement for this case
		do_vu_case(vuinl->cases.cases_val[i], val_itype, ist, must);
		
		// Append a break & add it to the switch statement body
		must->add_stmt(cast_new_break());
		mab_thr->end();
		mab_con->add_child(mab_thr, 0);
		must->add_stmt(mab_thr->get_block_label());
		
		if( parent_span ) {
			must->current_span->end();
			union_span->add_child(must->current_span);
		}
		case_c_block = must->c_block;
		must->c_block = switch_body_c_block;
		
		must->add_stmt(cast_new_case(case_value_expr, case_c_block));
	}
	
	// Handle the default case
	if( must->current_span ) {
		must->current_span = new mu_msg_span;
		must->current_span->set_kind(MSK_SEQUENTIAL);
		must->current_span->set_block(must->c_block);
		must->current_span->set_abort(must->abort_block);
		must->current_span->begin();
	}
	switch_body_c_block = must->c_block;
	must->c_block = cast_new_block(0,0);
	mab_thr = new mu_abort_block();
	must->abort_block = mab_thr;
	mab_thr->set_kind(MABK_THREAD);
	mab_thr->begin();
	must->set_memory_allocator_state(enter_case_state);
	
	if (vuinl->dfault) {
		assert(udef->dfault != -1);
		
		// Get the itype for the value selected
		val_itype = udef->dfault;
		
		// now build the individual 'case' statement for this case
		// - XXX - notice the borrowing of val
		do_vu_case(vuinl->dfault, val_itype, ist, must);
	} else {
		// No default case - it's an error -
		// XXX - notice the borrowing of val
		mu_inline_virtual_union_error_functor error_functor;
		must->mu_union_case(&error_functor);
	}
		
	// add the default to the switch statement body
	must->add_stmt(cast_new_break());
	if( parent_span ) {
		must->current_span->end();
		union_span->add_child(must->current_span);
		union_span->end();
		parent_span->add_child(union_span);
		must->current_span = parent_span;
	}
	
	mab_thr->end();
	mab_con->add_child(mab_thr, 0);
	must->add_stmt(mab_thr->get_block_label());
	
	mab_con->end();
	mab_par->add_child(mab_con, 0);
	must->abort_block = mab_par;
	case_c_block = must->c_block;
	must->c_block = switch_body_c_block;
	must->add_stmt(cast_new_default(case_c_block));
	
	// Finally produce the completed switch statement
	switch_body_c_block = must->c_block;
	must->c_block = saved_c_block;
	must->add_stmt(cast_new_switch(discrim_expr, switch_body_c_block));
	
	delete enter_case_state;
}

/***************************************************************************/
/* Finally, the `mu_state' method that sets the code generation in motion. */

void mu_state::mu_inline_virtual_union(inline_state *ist,
				       mint_ref union_itype,
				       pres_c_inline inl)
{
	pres_c_inline_virtual_union *vuinl;
	mint_union_def *udef;
	
	mu_inline_virtual_union_functor f(ist, union_itype, inl);
	
	/*****/
	
	assert(inl->kind == PRES_C_INLINE_VIRTUAL_UNION);
	vuinl = &(inl->pres_c_inline_u_u.virtual_union);
	
	/* Find the itype and make sure it matches. */
	assert(pres->mint.defs.defs_val[union_itype].kind == MINT_UNION);
	udef = &(pres->mint.defs.defs_val[union_itype].mint_def_u.union_def);
	
	/* Marshal or unmarshal the discriminator.
	   We also have to find and save the discriminator expression. */
	mu_state_arglist *oldlist = arglist;
	arglist = new mu_state_arglist(vuinl->arglist_name, oldlist);
	arglist->add(vuinl->arglist_name, "discrim");
	mu_inline(ist, udef->discrim, vuinl->discrim);
	int gotarg = arglist->getargs(vuinl->arglist_name, "discrim",
				      &f.discrim_expr, &f.discrim_type);
	assert(gotarg);
	assert(f.discrim_expr);
	assert(f.discrim_type);
	delete arglist;
	arglist = oldlist;
	
	/* Finally, let `mu_union' and our functor generate the big `switch'
	   statement. */
	mu_union(&f);
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1