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

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

/* This functor is called by `mu_union_case' to generate the code that will be
   executed when an optional datum is present.  This code marshals/unmarshals
   the datum. */

struct mu_mapping_optional_pointer_then_functor : public functor
{
	virtual void func(mu_state *must);
	
	mu_mapping_optional_pointer_then_functor() {}
	
	/*****/
	
	mint_array_def *adef;
	cast_expr len_var_expr;
	
	cast_expr ptr_expr;
	cast_type ptr_ctype;
	pres_c_mapping_optional_pointer *ptr_map;
};

void mu_mapping_optional_pointer_then_functor::func(mu_state *must)
{
	/* Find the type of the pointer target. */
	assert(ptr_ctype->kind == CAST_TYPE_POINTER);
	cast_type target_ctype = ptr_ctype->cast_type_u_u.pointer_type.target;
	
	/* Allocate space */
	assert(ptr_ctype->kind == CAST_TYPE_POINTER);
	must->mu_pointer_alloc(ptr_expr, target_ctype, ptr_map->arglist_name);
	
	must->mu_mapping(cast_new_unary_expr(CAST_UNARY_DEREF, ptr_expr),
			 target_ctype, adef->element_type, ptr_map->target);
	
	/* Deallocate space */
	assert(ptr_ctype->kind == CAST_TYPE_POINTER);
	must->mu_pointer_free(ptr_expr, target_ctype, ptr_map->arglist_name);
}

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

/* This functor is called by `mu_union_case' in order to generate the code
   to be executed when the optional data is not present.  When marshaling,
   there is nothing to do (the zero "count" has already been marshaled).  When
   unmarshaling, however, we must set the CAST pointer to NULL. */

struct mu_mapping_optional_pointer_else_functor : public functor
{
	virtual void func(mu_state *must);
	
	mu_mapping_optional_pointer_else_functor() {}
	
	/*****/
	
	cast_expr ptr_expr;
};

void mu_mapping_optional_pointer_else_functor::func(mu_state *must)
{
	if (must->op & MUST_DECODE)
		must->add_stmt(cast_new_stmt_expr(cast_new_expr_assign(
			ptr_expr,
			cast_new_expr_lit_int(0, 0)
			)));
	must->break_chunk();
}

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

/* This functor is called by `mu_union' in order to generate the code that
   marshals/unmarshals the "variants" of an optional pointer --- i.e., the
   optional datum if present, or nothing.  The "discriminator" of the optional
   pointer is handled separately, by `mu_mapping_optional_pointer', before this
   functor is invoked. */

struct mu_mapping_optional_pointer_functor : public functor
{
	virtual void func(mu_state *must);
	
	mu_mapping_optional_pointer_functor() {}
	
	/*****/
	
	cast_expr len_var_expr;
	
	mu_mapping_optional_pointer_then_functor then_functor;
	mu_mapping_optional_pointer_else_functor else_functor;
};

/* This method produces the C `if' statement that processes the "variants" of
   the optional pointer.
   */
void mu_mapping_optional_pointer_functor::func(mu_state *must)
{
	mu_memory_allocator_state *initial_memory_state;
	cast_stmt saved_block, then_block, else_block;
	mu_msg_span *union_span = 0, *parent_span = 0;
	char *skip_label = must->add_label();
	
	/* At this point we want to generate an `if' statement:
	   
	       `if (<len_var_expr>) { ... } else { ... };'
	   
	   Unfortunately, most of the functions that generate code don't
	   *return* code --- instead, they call `add_stmt()' to append code to
	   the current block.  Because we need the code generated by the
	   following function calls to go into blocks within an `if', we must
	   save the current value of `must->c_block', call the functions, and
	   then fix up the code.  Ugh.
	   
	   Moreover, because we're generating a code branch, we must remember
	   the initial state of the glob/chunk memory allocator so that we can
	   restore it before generating each branch.
	   */
	
	/* Prepare to generate the `if' statement: Take a snapshot of the
	   current memory allocator, and save off the current `c_block'. */
	initial_memory_state = must->memory_allocator_state();
	saved_block = must->c_block;
	
	/* An optional pointer adds an 'if' so we need to replicate this
	   in the abort code. */
	struct mu_abort_block *mab_par, *mab_con, *mab_thr;
	
	mab_par = must->abort_block;
	mab_con = new mu_abort_block();
	mab_con->set_kind(MABK_CONTROL_IF_ELSE);
	mab_con->begin();
	mab_con->add_stmt(cast_new_stmt_expr(cast_new_expr_assign(
		len_var_expr,
		cast_new_binary_expr(CAST_BINARY_NE,
				     else_functor.ptr_expr,
				     cast_new_expr_lit_int(0, 0)))));
	/* A variable length message means we need to
	   add a span for each case. */
	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;
		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();
	}
	mab_thr = new mu_abort_block();
	must->abort_block = mab_thr;
	mab_thr->set_kind(MABK_THREAD);
	mab_thr->set_expr(len_var_expr);
	mab_thr->begin();
	
	/* Generate and collect the code for the `then' arm. */
	must->c_block = cast_new_block(0, 0);
	must->mu_union_case(&then_functor);
	then_block = must->c_block;
	
	/* Since there is no way to break out of an if statement
	   like a loop or switch, we have to manually do a goto. */
	must->add_stmt(cast_new_goto(skip_label));
	mab_thr->end();
	mab_con->add_child(mab_thr, MABF_OUT_OF_LINE);
	must->add_stmt(mab_thr->get_block_label());
	if( parent_span ) {
		must->current_span->end();
		union_span->add_child(must->current_span);
	}
	
	/* Handle span and abort stuff for the else case */
	mab_thr = new mu_abort_block();
	must->abort_block = mab_thr;
	mab_thr->set_kind(MABK_THREAD);
	mab_thr->set_expr(0);
	mab_thr->begin();
	if( parent_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();
	}
	
	/* Generate and collect the code for the `else' arm. */
	must->c_block = cast_new_block(0, 0);
	must->set_memory_allocator_state(initial_memory_state);
	must->mu_union_case(&else_functor);
	else_block = must->c_block;
	must->add_stmt(cast_new_goto(skip_label));
	mab_thr->end();
	mab_con->add_child(mab_thr, MABF_OUT_OF_LINE);
	must->add_stmt(mab_thr->get_block_label());
	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;
	}
	must->abort_block = mab_par;
	mab_con->end();
	mab_par->add_child(mab_con, MABF_OUT_OF_LINE);
	
	/* Finally, output the `if'. */
	must->c_block = saved_block;
	must->add_stmt(cast_new_if(len_var_expr, then_block, else_block));
	
	/* During a normal run we need to jump past the abort code inside
	   of the if/then and to the next part of the regular code. */
	cast_stmt clabel;
	
	must->add_stmt(mab_con->get_block_label());
	
	clabel = cast_new_label(skip_label, cast_new_stmt(CAST_STMT_NULL));
	clabel->cast_stmt_u_u.s_label.users = 2;
	must->add_stmt(clabel);
}

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

/* `mu_state::mu_optional_pointer_mapping' is the method that handles the
   PRES_C_MAPPING_OPTIONAL_POINTER mapping.
   
   This mapping embodies the following presentation semantics: An optional
   datum is present if a certain C pointer points to it.  If the optional datum
   is not present, the C pointer is null.  In MINT, an optional pointer
   corresponds to a counted array with zero or one elements.

   On encoding, the given C pointer is examined.  If it is null, a counted
   array with zero elements is marshaled into our message.  If the pointer is
   non-null, a counted array with one element is marshaled.  The single element
   is the datum that the pointer points at.
   
   On decoding, the counted array in the message is examined.  If it contains
   zero elements, the C pointer is set to null.  Otherwise, the first element
   of the array is unmarshaled, and the C pointer is set to point to it.
   
   A `pres_c_mapping_optional_pointer' is unlike a `pres_c_mapping_pointer' in
   that an optional pointer doesn't just "eat up" one level of C pointer
   indirection.  An optional pointer is a presentation mechanism that embodies
   semantics --- the value of the pointer (NULL or non-NULL) is determined by
   the presence of the optional datum.  A simple `pres_c_mapping_pointer', on
   the other hand, doesn't have any semantics to speak of --- it's simply a way
   of presenting data.
   */
void mu_state::mu_mapping_optional_pointer(
	cast_expr ptr_expr,
	cast_type ptr_ctype,
	mint_ref itype,
	pres_c_mapping_optional_pointer *ptr_map)
{
	mu_mapping_optional_pointer_functor f;
	
	assert(itype >= 0);
	assert(itype < (signed int) pres->mint.defs.defs_len);
	
	mu_array_data old_array_data = array_data;
	mint_def *def = &pres->mint.defs.defs_val[itype];
	assert(def->kind == MINT_ARRAY);
	mint_array_def *adef = &def->mint_def_u.array_def;
	mint_get_array_len(&pres->mint, itype,
			   &array_data.mint_len_min,
			   &array_data.mint_len_max);
	array_data.is_valid = 1;
	
	mu_inline_alloc_context *iac = inline_alloc_context;
	while (iac) {
		if (strcmp(iac->name, ptr_map->arglist_name) == 0)
			break;
		iac = iac->parent_context;
	}
	if (!iac)
		panic(("In `mu_state::mu_mapping_optional_pointer', "
		       "allocation context `%s' not available."),
		      ptr_map->arglist_name);
	
	/* Create a temporary variable in which to hold the array length. */
	cast_expr len_expr;
	cast_type len_ctype;
	int gotarg = arglist->getargs(ptr_map->arglist_name,
				      "length", &len_expr, &len_ctype);
	assert(gotarg);
	assert(len_expr);
	assert(len_ctype);
	
	/* Break the current chunk.  Since the next datum is optional, its size
	   is variable (zero or non-zero), and therefore we can't possibly
	   continue the current chunk. */
	break_chunk();
	
	/* Finally, fill out the functor that will generate our `if' statement,
	   and give that functor to `mu_union'.  `mu_union' will do glob/chunk
	   management around our `if', and `mu_union_case' will do glob/chunk
	   management for each arm of our `if'. */
	f.len_var_expr              = len_expr;
	f.then_functor.adef         = adef;
	f.then_functor.len_var_expr = len_expr;
	f.then_functor.ptr_expr     = ptr_expr;
	f.then_functor.ptr_ctype    = ptr_ctype;
	f.then_functor.ptr_map      = ptr_map;
	f.else_functor.ptr_expr     = ptr_expr;
	
	mu_union(&f);
	
	/* Restore the array data. */
	array_data = old_array_data;
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1