/*
 * Copyright (c) 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/c/libcast.h>
#include <mom/c/libpres_c.h>

#include "fluke.h"

/*
 *
 */

void w_client_stub(pres_c_1 *pres, int stub_idx)
{
	assert(pres);
	
	pres_c_stub        *stub   = &pres->stubs.stubs_val[stub_idx];
	pres_c_client_stub *cstub  = &stub->pres_c_stub_u.cstub;
	cast_def           *cfunc  = &pres->stubs_cast.
				     cast_scope_val[cstub->c_func];
	cast_func_type     *cfunct = &cfunc->u.cast_def_u_u.func_type;
	
	mint_ref            operation_itype;
	pres_c_inline       operation_inline;
	
	int                 assumptions = RPCM_TRUSTED; /* XXX */
	const char	   *emergency_return_string;
	
	cast_stmt           rpc_cast_stmt;
	
	struct mu_abort_block *mab;
	cast_stmt reaper_label;
	char *escape_label;
	
	/*****/
	
	if (cfunct->return_type->kind != CAST_TYPE_VOID)
		emergency_return_string = "return _return";
	else
		emergency_return_string = "return";
	emergency_return_value = cast_new_expr_name(emergency_return_string);
	
	reaper_label = cast_new_label(
		"_flick_reaper",
		cast_new_stmt_expr(emergency_return_value));
	mab = new mu_abort_block();
	mab->set_kind(MABK_THREAD);
	mab->begin();
	
	/* Start the abort code */
	mab->add_stmt(
		cast_new_stmt_expr(
			cast_new_expr_call_3(
				cast_new_expr_name(
					flick_asprintf(
						"flick_%s_client_error",
						pres->pres_context)),
				cast_new_expr_name("fluke"),
				cast_new_expr_name("fluke"),
				cast_new_expr_name(mab->
						   use_current_label()))));
	/* The order of the musts below is crucial to
	   the abort code working properly */
	
	/*
	 * Figure out which of the stub function parameters is the object
	 * reference and create a CAST expression that names it.
	 *
	 * We do this by using a `fluke_target_mu_state' to ``marshal'' the
	 * target object reference.  This process doesn't create any marshaling
	 * code, however.  The `fluke_taregt_mu_state' knows that when it would
	 * ordinarily marshal an object reference (to the target object), it
	 * should instead simply store the CAST name of the object in its
	 * `target_cast_expr' slot.  Clever, eh?
	 */
	fluke_target_mu_state must_target(the_state,
					  (MUST_ENCODE),
					  assumptions, "client");
	
	/* Set the stub state for error handling
	   and anything else that needs to know
	   where the stub is currently at in execution. */
	must_target.add_stmt(must_target.
			     change_stub_state(FLICK_STATE_PROLOGUE));
	must_target.add_stmt(must_target.
			     change_stub_state(FLICK_STATE_MARSHAL));
	must_target.abort_block = mab;
	
	/* Set up the argument list to catch parameter names we may need. */
	must_target.arglist = new mu_state_arglist("params");
	must_target.arglist->add("params", "object");
	
	must_target.mu_func_params(cstub->c_func, cstub->target_itype,
				   cstub->target_i);
	must_target.mu_end();
	
	/* Build the client object marshaling code. */
	/* XXX --- Not yet supported. */
	if (cstub->client_i) {
		warn("This back end doesn't support client references.");
	}
	
	/*
	 * Build the request marshaling/deallocation code.  We strip away the
	 * ``collapsed union'' goo that encodes the IDL and interface IDs.  We
	 * don't need this information for Fluke IPC because it is manifest in
	 * the object references.
	 */
	fluke_mu_state must_in(the_state,
			       (MUST_ENCODE | MUST_DEALLOCATE | MUST_REQUEST),
			       assumptions, "client");
	
	must_in.abort_block = mab;
	
	remove_idl_and_interface_ids(pres,
				     cstub->request_itype, cstub->request_i,
				     &operation_itype, &operation_inline);
	must_in.mu_func_params(cstub->c_func,
			       operation_itype, operation_inline);
	must_in.mu_end();
	
	/* Get an abort label for the printf's down below to use */
	escape_label = mab->use_current_label();
	
	struct mu_abort_block *out_abort_block;

	/* If its a oneway stub we don't want to have output
	   musts making abort code that won't be used.  So
	   we make a new context and pass it to the musts. */
	if (cstub->op_flags & PRES_C_STUB_OP_FLAG_ONEWAY) {
		out_abort_block = new mu_abort_block();
		out_abort_block->set_kind(MABK_THREAD);
		out_abort_block->begin();
	}
	else
		out_abort_block = mab;
	/*
	 * Build the reply unmarshaling/allocation code.  Again, we strip away
	 * all of the ``collapsed union'' IDL and interface goo.  This time we
	 * strip away the operation reply identifier, too.  (Note that this is
	 * just a fixed number indicating what operation is being replied to.
	 * Any success/failure code is a separate message component!)
	 */
	fluke_mu_state must_out(the_state,
				(MUST_DECODE | MUST_ALLOCATE | MUST_REPLY),
				assumptions, "client");
	
	/* Set up the argument list to catch parameter names we may need. */
	must_out.arglist = new mu_state_arglist("params");
	must_out.arglist->add("params", "environment");
	
	must_out.change_stub_state(FLICK_STATE_UNMARSHAL);
	must_out.abort_block = out_abort_block;
	/* Make a span node to check for message size requirements */
	must_out.current_span = new mu_msg_span;
	must_out.current_span->set_kind(MSK_SEQUENTIAL);
	must_out.current_span->begin();
	
	remove_idl_and_interface_ids(pres,
				     cstub->reply_itype, cstub->reply_i,
				     &operation_itype, &operation_inline);
	remove_operation_id(pres,
			    operation_itype, operation_inline,
			    &operation_itype, &operation_inline);
	must_out.mu_func_params(cstub->c_func,
				operation_itype, operation_inline);
	must_out.mu_end();
	/* Collapse the span tree and commit the values */
	must_out.current_span->end();
	must_out.current_span->collapse();
	must_out.current_span->commit();
	
	mab->end();
	mab->set_reaper_label(reaper_label);
	/* Remove any unneeded abort code */
	mab->rollback();
	
	/* Output the client stub. */
	
	cast_w_func_type(cfunc->name, cfunct, 0);
	w_printf("\n{\n");
	w_i_printf(1, "flick_marshal_stream_struct _stream_struct;\n");
	w_i_printf(1, "const flick_marshal_stream_t _stream ");
	w_printf("= &_stream_struct;\n\n");
	w_i_printf(1, "fluke_error_t _rc;\n");
	
	if (cfunct->return_type->kind != CAST_TYPE_VOID) {
		/*
		 * Initialize `_return' to be zero.  This eliminates compiler
		 * warnings about returning uninitialized data.
		 */
		cast_expr init_expr
			= cast_new_expr_assign_to_zero(
				cast_new_expr_name("_return"),
				cfunct->return_type,
				&(pres->cast)
				);
		
		w_indent(1);
		cast_w_type(cast_new_scoped_name("_return", NULL),
			    cfunct->return_type, 1);
		w_printf(";\n");
		
		w_printf("\n");
		cast_w_stmt(cast_new_stmt_expr(init_expr), 1);
	} else
		w_printf("\n");
	
	w_i_printf(1,
		   "if( !flick_%s_client_start_encode() ) {\n",
		   must_in.get_be_name());
	/* Output the request marshaling code. */
	cast_w_stmt(must_in.c_block, 2);
	w_i_printf(2,
		   "flick_%s_client_end_encode();\n",
		   must_in.get_be_name());
	
	/*
	 * Make the RPC.  If the client stub parametr list includes SIDs, use
	 * the secure version of the RPC function.
	 */
	/*
	 * Until December 1997, secure Flask stubs used server SID parameters.
	 * But now, server SIDs are not presented by the MOM/Fluke PG and are
	 * not supported by the MOM-on-Fluke runtime.  Warn if the server SIDs
	 * parameters are present; the old assertions are `#if 0'ed out in the
	 * code below.
	 */
	if (must_in.server_sid_cexpr || must_out.server_sid_cexpr) {
		warn("the Fluke transport does not support server SID "
		     "arguments to stubs.");
		warn("ignoring server SID parameters to client stub `%s'.",
		     cfunc->name);
	}
	
	if (!must_in.client_sid_cexpr) {
		/*
		 * The client stub does not include the client SID in its
		 * parameter list.  Make sure that it doesn't include any other
		 * SIDs either, and make the normal RPC call.
		 */
#if 0
		/* See above; server SID params are no longer used in Flask. */
		assert(!must_in.server_sid_cexpr
		       && !must_out.server_sid_cexpr);
#endif
		
		rpc_cast_stmt =
			cast_new_stmt_expr(
				cast_new_expr_assign(
					cast_new_expr_name("_rc"),
					cast_new_expr_call_2(
						cast_new_expr_name(
							"flick_client_send_"
							"request_get_reply"
							),
						/* arg 0 */
						must_target.target_cast_expr,
						/* arg 1 */
						cast_new_expr_name("_stream")
						)));
	} else {
		/*
		 * The client stub does include the client SID in its
		 * parameter list.  Make the secure RPC call.
		 */
#if 0
		/* See above; server SID params are no longer used in Flask. */
		assert(must_in.server_sid_cexpr
		       && must_out.server_sid_cexpr);
#endif
		
		rpc_cast_stmt =
			cast_new_stmt_expr(
				cast_new_expr_assign(
					cast_new_expr_name("_rc"),
					cast_new_expr_call_3(
						cast_new_expr_name(
							"flick_client_send_"
							"request_get_reply_"
							"secure"
							),
						/* arg 0 */
						must_target.target_cast_expr,
						/* arg 1 */
						cast_new_expr_name("_stream"),
						/* arg 2 */
						must_in.client_sid_cexpr
#if 0
						/* Not used; see above. */
						/* arg 3 */
						must_in.server_sid_cexpr,
						/* arg 4 */
						must_out.server_sid_cexpr
#endif
						)));
	}
	cast_w_stmt(must_in.change_stub_state(FLICK_STATE_SEND_RECEIVE), 2);
	cast_w_stmt(rpc_cast_stmt, 2);
	
	/* Check for errors from the IPC operation. */
	w_i_printf(2,
		   "if ((_rc != FLUKE_SUCCESS) "
		   "&& (_rc != FLUKE_IPC_RECV_DISCONNECTED))\n");
	w_i_printf(3,
		   "flick_stub_error(FLICK_ERROR_COMMUNICATION, %s);\n",
		   escape_label);
	
	/* Output the reply unmarshaling code. */
	if (cstub->op_flags & PRES_C_STUB_OP_FLAG_ONEWAY) {
		w_i_printf(1,
			   "}\n");
		w_i_printf(1,
			   "else\n");
		w_i_printf(2,
			   "flick_stub_error(FLICK_ERROR_NO_MEMORY, %s);\n",
			   escape_label);
		w_i_printf(1, "return;\n");
		/* Write the abort code out */
		cast_w_stmt(mab->get_block_label(), 1);
		w_printf("}\n\n");
		return;
	}
	
	w_i_printf(2,
		   "flick_%s_client_start_decode();\n",
		   must_in.get_be_name());
	cast_w_stmt(must_out.c_block, 2);
	w_i_printf(2,
		   "flick_%s_client_end_decode();\n",
		   must_in.get_be_name());
	
	w_i_printf(1,
		   "}\n");
	w_i_printf(1,
		   "else\n");
	w_i_printf(2,
		   "flick_stub_error(FLICK_ERROR_NO_MEMORY, %s);\n",
		   escape_label);
	if (cfunct->return_type->kind != CAST_TYPE_VOID)
		w_i_printf(1, "return _return;\n");
	else
		w_i_printf(1, "return;\n");
	/* Write the abort code out */
	cast_w_stmt(mab->get_block_label(), 1);
	cast_w_stmt(reaper_label, 1);
	w_printf("}\n\n");
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1