/* * 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 #include #include #include #include /*****************************************************************************/ /* 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 () { ... } 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. */