// @(#)interp.cpp 1.4 95/11/04 // Copyright 1994-1995 by Sun Microsystems Inc. // All Rights Reserved // // TYPECODE: interpreter, traverses data structures // // This uses the standard C/C++ representation for data, and knows how // to do things like align and pad according to standard rules. It is // driven by CDR marshaled representations of TypeCodes. // // It does two key things: (a) calculate size and alignment // restrictions for the data type described by any given typecode; and // (b) "visits" each element of a data type in the order those // elements are defined in the type's IDL definition. // // A typical use is that some application-specific "visit" function // will be called with a typecode and data value. Then that "visit" // function may choose to use the interpreter's knowledge of the // environment's size, padding, and alignment rules to help it examine // each of the constituents of complex data values. It does so by // making a call to TypeCode::traverse(), and passing itself for // future recursive calls. // // NOTE that this module has system dependent parts, and so should be // examined when porting to new CPU architectures, compilers, and so // forth to make sure it correctly implements the appropriate binary // interfaces. // // Issues of concern are primarily that sizes and representations of // CORBA primitive data types are correct (key issues are verified // when the ORB initializes) and that the alignment rules are // recognized. // // Also, exceptions have vtables in them, which may cause trouble if // they aren't located at the very beginning by the compiler in // question. // // So for example, moving to another CPU architecture which still uses // standard sized two's complement integers and IEEE floating point, // and expects "natural" alignment, won't be hard. Even using PC // style tightly packed data is simple; the alignment rules are just // simpler. Most volume microprocessors used in 1995 are correctly // supported. // // Using data representations that are far from the standard C/C++ // style data layout is probably not practical with this // implementation. LISP systems, as one example, probably won't use // "in-memory" representations much like C/C++, even though its "wire // form" could directly match CDR. // // ALSO, the treatment of exceptions may need to be examined in // language environments which actually rely on C++ exceptions. The // RTTI data that identifies exceptions can easily be ignored by this // interpreter (if it's taught about that compiler's RTTI) but it may // not be practical for any code not generated by that specific C++ // compiler to store such data in the right place to look like a C++ // exception, or to throw exceptions when that's needed. (RTTI == // "Run Time Typing Information", needed to make C++ exceptions work // correctly and partially exposed to users by the ANSI standards // comittee. It provides type-safe "downcasting" and other features // previously unavailable in C++.) // // THREADING NOTE: Data structures being traversed should only be // modified by the thread doing the traversal. The interpretive code // itself is reentrant (recursive!) so presents no threading issues; // only the data being fed to the interpreter must be protected // against concurrency. #if 0 #include "tao/orb.h" #include "tao/cdr.h" #endif #include "tao/corba.h" // Utility routines are used to manipulate CDR-encapsulated TypeCode // parameter lists, calculating the size and alignment of the data // type being described. The TCKind value has always been removed // from the CDR stream when these calculator routines get called. typedef size_t attribute_calculator (CDR *stream, size_t &alignment, CORBA::Environment &env); static attribute_calculator calc_struct_attributes; static attribute_calculator calc_exception_attributes; static attribute_calculator calc_union_attributes; static attribute_calculator calc_alias_attributes; static attribute_calculator calc_array_attributes; // Other utility routines are used to skip the parameter lists when // they're not needed. typedef CORBA::Boolean param_skip_rtn (CDR *); static CORBA::Boolean skip_encapsulation (CDR *stream) { return stream->skip_string (); } static CORBA::Boolean skip_long (CDR *stream) { CORBA::ULong scratch; return stream->get_ulong (scratch); } // Table supporting calculation of size and alignment requirements for // any one instance of a given data types. // // This is indexed via CDR's TCKind values, which are "frozen" as part // of the CDR standard. Entries hold either the size and alignment // values for that data type, or a pointer to a function that is used // to calculate those values. Function pointers are normally needed // only for constructed types. // // A "skipper" routine is provided for some data types whose size is // known statically (e.g. objrefs, structures, strings) but whose // typecodes have parameters that sometimes need to be ignored when // found in a CDR stream. Any attribute calculator routine always // skips parameters in the CDR input stream, so no type with such a // routine also needs a "skipper". // // Rather than growing a set of processor-specific #ifdefs, we // calculate most of this table (except functions) at ORB // initialization time. struct table_element { size_t size; size_t alignment; attribute_calculator *calc; param_skip_rtn *skipper; }; static table_element table [CORBA::TC_KIND_COUNT] = { { 0, 1, 0 }, // CORBA::tk_null { 0, 1, 0 }, // CORBA::tk_void { 0, 1, 0, 0 }, // CORBA::tk_short { 0, 1, 0, 0 }, // CORBA::tk_long { 0, 1, 0, 0 }, // CORBA::tk_ushort { 0, 1, 0, 0 }, // CORBA::tk_ulong { 0, 1, 0, 0 }, // CORBA::tk_float { 0, 1, 0, 0 }, // CORBA::tk_double { 0, 1, 0, 0 }, // CORBA::tk_boolean { 0, 1, 0, 0 }, // CORBA::tk_char { 0, 1, 0, 0 }, // CORBA::tk_octet { 0, 1, 0, 0 }, // CORBA::tk_any { 0, 1, 0, 0 }, // CORBA::tk_TypeCode { 0, 1, 0, 0 }, // CORBA::tk_Principal { 0, 1, 0, skip_encapsulation }, // CORBA::tk_objref { 0, 1, calc_struct_attributes, 0 }, // CORBA::tk_struct { 0, 1, calc_union_attributes, 0 }, // CORBA::tk_union { 0, 1, 0, skip_encapsulation }, // CORBA::tk_enum { 0, 1, 0, skip_long }, // CORBA::tk_string { 0, 1, 0, skip_encapsulation }, // CORBA::tk_sequence { 0, 1, calc_array_attributes, 0 }, // CORBA::tk_array // // Two TCKind values added in 94-11-7 // { 0, 1, calc_alias_attributes, 0 }, // CORBA::tk_alias { 0, 1, calc_exception_attributes, 0 }, // CORBA::tk_except // // Five extended IDL data types, defined in Appendix A of 94-9-32 // but here with different numeric TCKind codes. These types // represent extensions to CORBA (specifically, to IDL) which are // not yet standardized. // { 0, 1, 0, 0 }, // CORBA::tk_longlong { 0, 1, 0, 0 }, // CORBA::tk_ulonglong { 0, 1, 0, 0 }, // CORBA::tk_longdouble { 0, 1, 0, 0 }, // CORBA::tk_wchar { 0, 1, 0, skip_long } // CORBA::tk_wstring }; // Runtime initialization of the table above; note that this compiles // down to a set of assignment statements, with the real work done by // the C++ compiler when this file gets compiled. // // "Natural alignment" is a policy that the processor controls the // alignment of data based on its type. There's variation; some CPUs // have a maximum alignment requirement of two or four bytes, others // have some type-specific exceptions to the normal "alignment == // size" rule. // // "Fixed" alignment ignores data type when establishing alignment; // not all processors support such policies, and those which do often // pay a cost to do so (viz. RISC/CISC discussions). The primary // example of an OS family that chose "fixed" alignment is Microsoft's // x86 systems, which normally align on one byte boundaries to promote // data space efficiency. // // NOTE: typical PC compiler options let you specify other alignments, // but none are "natural". Also, they don't apply consistently to all // data types. Change the "one byte" assumption with extreme caution! // And make sure all header files (e.g. generated by an IDL compiler) // make sure that alignment of IDL-defined data types is consistent // (one byte). enum TCKIND { tk_null = 0, tk_void = 1, tk_short = 2, tk_long = 3, tk_ushort = 4, tk_ulong = 5, tk_float = 6, tk_double = 7, tk_boolean = 8, tk_char = 9, tk_octet = 10, tk_any = 11, tk_TypeCode = 12, tk_Principal = 13, tk_objref = 14, tk_struct = 15, tk_union = 16, tk_enum = 17, tk_string = 18, tk_sequence = 19, tk_array = 20, tk_alias = 21, // 94-11-7 tk_except = 22, // 94-11-7 // these five are OMG-IDL data type extensions tk_longlong = 23, // 94-9-32 Appendix A (+ 2) tk_ulonglong = 24, // 94-9-32 Appendix A (+ 2) tk_longdouble = 25, // 94-9-32 Appendix A (+ 2) tk_wchar = 26, // 94-9-32 Appendix A (+ 2) tk_wstring = 27, // 94-9-32 Appendix A (+ 2) // This symbol is not defined by CORBA 2.0. It's used to speed up // dispatch based on TCKind values, and lets many important ones // just be table lookups. It must always be the last enum value!! TC_KIND_COUNT }; #if defined (unix) || defined (VXWORKS) // @@ Chris, can you please put this magic number macro in a more // prominent place (e.g., in a header file somewhere? #define TAO_ALIGNMENT_MAGIC_NUMBER 64 #define setup_entry(x,t) \ { \ struct align_struct_ ## t { \ x one; \ char dummy [TAO_ALIGNMENT_MAGIC_NUMBER + 1 - sizeof(x)]; \ x two; \ }; \ \ align_struct_ ## t align; \ table [t].size = sizeof (x); \ table [t].alignment = \ (char *) &align.two - (char *) &align.one - TAO_ALIGNMENT_MAGIC_NUMBER; \ } #else // PC "fixed" alignment #define setup_entry(x,t) \ { \ table [t].size = sizeof (x); \ table [t].alignment = 1; \ } #endif /* defined (unix) || defined (VXWORKS) */ // Fills in fixed size and alignment values. void __TC_init_table (void) { setup_entry (CORBA::Short, tk_short); setup_entry (CORBA::Long, tk_long); setup_entry (CORBA::UShort, tk_ushort); setup_entry (CORBA::ULong, tk_ulong); setup_entry (CORBA::Float, tk_float); setup_entry (CORBA::Double, tk_double); setup_entry (CORBA::Boolean, tk_boolean); setup_entry (CORBA::Char, tk_char); setup_entry (CORBA::Octet, tk_octet); setup_entry (CORBA::Any, tk_any); setup_entry (CORBA::TypeCode_ptr, tk_TypeCode); setup_entry (CORBA::Principal_ptr, tk_Principal); setup_entry (CORBA::Object_ptr, tk_objref); enum generic_enum {a, b, c, d}; // XXX workaround for G++ 2.6.3 bug // setup_entry (generic_enum, CORBA::tk_enum); table [CORBA::tk_enum].size = sizeof (generic_enum); table [CORBA::tk_enum].alignment = sizeof (generic_enum); setup_entry (CORBA::String, tk_string); setup_entry (CORBA::OctetSeq, tk_sequence); setup_entry (CORBA::LongLong, tk_longlong); setup_entry (CORBA::ULongLong, tk_ulonglong); setup_entry (CORBA::LongDouble, tk_longdouble); setup_entry (CORBA::WChar, tk_wchar); setup_entry (CORBA::WString, tk_wstring); } #undef setup // For a given typecode, figure out its size and alignment needs. // This version is used mostly when traversing other typecodes, and // follows these rules: // // - Some typecodes are illegal (can't be nested inside others); // - Indirections are allowed; // - The whole typecode (including TCKind enum) is in the stream // // When the routine returns, the stream has skipped this TypeCode. // // "size" is returned, "alignment" is an 'out' parameter. If it is // non-null, "tc" is initialized to hold the contents of the TypeCode; // it depends on the contents of the original stream to be valid. // // XXX explore splitting apart returning the size/alignment data and // the TypeCode initialization; union traversal would benefit a bit, // but it would need more than that to make it as speedy as struct // traversal. static size_t calc_nested_size_and_alignment (CORBA::TypeCode_ptr tc, CDR *original_stream, size_t &alignment, CORBA::Environment &env) { // Get the "kind" ... if this is an indirection, this is a guess // which will soon be updated. CORBA::ULong temp; CORBA::TCKind kind; if (original_stream->get_ulong (temp) == CORBA::B_FALSE) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } env.clear (); kind = (CORBA::TCKind) temp; // Check for indirection, setting up the right CDR stream to use // when getting the rest of the parameters. (We rely on the fact // that indirections may not point to indirections.) CDR indirected_stream; CDR *stream; if (kind == ~0) { CORBA::Long offset; // Get indirection, sanity check it, set up new stream pointing // there. // // XXX access to "real" size limit for this typecode and use it // to check for errors before indirect and to limit the new // stream's length. ULONG_MAX is too much! if (!original_stream->get_long (offset) || offset >= -8 || ((-offset) & 0x03) != 0) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // offset -= 4; // correct for get_long update indirected_stream.next = original_stream->next + (ptr_arith_t) offset; indirected_stream.remaining = (size_t) ULONG_MAX; stream = &indirected_stream; // Fetch indirected-to TCKind, deducing byte order. if (*indirected_stream.next == 0) // big-endian? indirected_stream.do_byteswap = (MY_BYTE_SEX != 0); else indirected_stream.do_byteswap = (MY_BYTE_SEX == 0); if (!indirected_stream.get_ulong (temp)) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } kind = (CORBA::TCKind) temp; } else stream = original_stream; // Check for illegal TCKind enum values ... out of range, or which // represent data values that can't be nested. (Some can't even // exist freestanding!) if (kind >= TC_KIND_COUNT || kind <= CORBA::tk_void || kind == CORBA::tk_except) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Use attribute calculator routine if it exists; these are needed // only for variable-sized data types, with encapsulated parameter // lists that affect the size and alignment of "top level" memory // needed to hold an instance of this type. if (table [kind].calc != 0) { assert (table [kind].size == 0); // Pull encapsulation length out of the stream. if (stream->get_ulong (temp) == CORBA::B_FALSE) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Initialize the TypeCode if requested if (tc) { tc->_kind = kind; tc->_buffer = stream->next; tc->_length = temp; } // Set up a separate stream for the parameters; it may easily // have a different byte order, and this is as simple a way as // any to ensure correctness. Then use the calculator routine // to calculate size and alignment. CDR sub_encapsulation; size_t size; assert (temp <= UINT_MAX); sub_encapsulation.setup_encapsulation (stream->next, (size_t) temp); size = table [kind].calc (&sub_encapsulation, alignment, env); // Check for garbage at end of parameter lists, or other cases // where parameters and the size allocated to them don't jive. stream->skip_bytes ((unsigned) temp); if (stream->next != sub_encapsulation.next) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } return size; } assert (table [kind].size != 0); // fixed size data type // Reinitialize the TypeCode if requested; this consumes any // TypeCode parameters in the stream. They only exist for TCKind // values that have parameters, but which represent fixed-size data // types in the binary representation: CORBA::tk_string, CORBA::tk_wstring, // CORBA::tk_objref, CORBA::tk_enum, and CORBA::tk_sequence. if (tc) { CORBA::ULong len; tc->_kind = kind; switch (kind) { default: assert (table [kind].skipper == 0); break; case CORBA::tk_string: case CORBA::tk_wstring: if (stream->get_ulong (len) == CORBA::B_FALSE) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } tc->_length = len; break; case CORBA::tk_enum: case CORBA::tk_objref: case CORBA::tk_sequence: if (stream->get_ulong (len) == CORBA::B_FALSE) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } tc->_length = len; assert (len < UINT_MAX); tc->_buffer = stream->next; stream->skip_bytes ((unsigned) len); break; } // Otherwise, consume any parameters without stuffing them into // a temporary TypeCode. } else if (table [kind].skipper != 0 && table [kind].skipper (stream) == CORBA::B_FALSE) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Return statically known values. alignment = table [kind].alignment; return table [kind].size; } // Given typecode bytes for a structure (or exception), figure out its // alignment and size; return size, alignment is an 'out' parameter. // Only "CORBA::tk_struct" (or "CORBA::tk_except") has been taken out of the stream // parameter holding the bytes. // // We use a one-pass algorithm, calculating size and inter-element // padding while recording the strongest alignment restriction. Then // we correct the size to account for tail-padding. // // This routine recognizes that exceptions are just structs with some // additional information. Different environments may differ in what // that additional information is, so this routine may need to be // taught about compiler-specific representation of that additional // "RTTI" data. static size_t calc_struct_and_except_attributes (CDR *stream, size_t &alignment, CORBA::Boolean is_exception, CORBA::Environment &env) { CORBA::ULong members; size_t size; // Exceptions are like structs, with key additions (all of which // might need to be be applied to structures!): vtable, typecode, // and refcount. The size must include these "hidden" members. // // NOTE: in environments with "true" C++ exceptions, there may need // to be a slot for additional "RTTI" information; maybe it is part // of the vtable, or maybe not. Or, that information (needed to // determine which 'catch' clauses apply) may only be provided by // the compiler to the runtime support for the "throw" statement. if (is_exception) { size = sizeof (CORBA::Exception); alignment = table [CORBA::tk_TypeCode].alignment; } else { alignment = 1; size = 0; } // skip rest of header (type ID and name) and collect the number of // struct members if (!stream->skip_string () || !stream->skip_string () || !stream->get_ulong (members)) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // iterate over all the members, skipping their names and looking // only at type data. for ( ; members != 0; members--) { size_t member_size; size_t member_alignment; // Skip name of the member. if (!stream->skip_string ()) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Get size and alignment of the member, accounting for // indirection and the various kinds of parameter encoding. member_size = calc_nested_size_and_alignment (0, stream, member_alignment, env); if (env.exception () != 0) return 0; // Round up the struct size to handle member alignment (by adding // internal padding), then update the current size to handle the // member's size. size = (size_t) align_binary (size, member_alignment); size += member_size; // Finally update the overall structure alignment requirement, if // this element must be more strongly aligned. if (member_alignment > alignment) alignment = member_alignment; }; // Round up the structure size to match its overall alignment. This // adds tail padding, if needed. return (size_t) align_binary (size, alignment); } // Calculate size and alignment for a structure. static size_t calc_struct_attributes (CDR *stream, size_t &alignment, CORBA::Environment &env) { return calc_struct_and_except_attributes (stream, alignment, CORBA::B_FALSE, env); } // Calculate size and alignment for an exception. static size_t calc_exception_attributes (CDR *stream, size_t &alignment, CORBA::Environment &env) { return calc_struct_and_except_attributes (stream, alignment, CORBA::B_TRUE, env); } // Calculate and return sizes for both parts of a union, as needed by // other code. Return value is the overall size. The padded size of // the discriminant is needed to traverse the two values separately. // Unfortunately that is not quite practical to do with a single pass // over the typecode: the inter-element padding changes depending on // the strictest alignment required by _any_ arm of the union. size_t calc_key_union_attributes (CDR *stream, size_t &overall_alignment, size_t &discrim_size_with_pad, CORBA::Environment &env) { CORBA::ULong members; CORBA::ULong temp; size_t discrim_size; size_t value_alignment; size_t value_size; overall_alignment = value_alignment = 1; value_size = discrim_size_with_pad = 0; // Skip initial optional members (type ID and name). if (!stream->skip_string () // type ID || !stream->skip_string ()) { // typedef name env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Calculate discriminant size and alignment: it's the first member // of the "struct" representing the union. We detect illegal // discriminant kinds a bit later. CORBA::TypeCode discrim_tc (CORBA::tk_void); discrim_size = calc_nested_size_and_alignment (&discrim_tc, stream, overall_alignment, env); if (env.exception () != 0) return 0; // skip "default used" indicator, and save "member count" if (!stream->get_ulong (temp) // default used || !stream->get_ulong (members)) { // member count env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // iterate over the tuples for all the members; all we care about is // their types, which can affect either alignment or padding // requirement for the union part of the construct. for ( ; members != 0; members--) { size_t member_size, member_alignment; // Skip member label; its size varies with discriminant type, but // here we don't care about its content. This is where illegal // discriminant kinds are detected. // // NOTE: This modifies 94-9-32 Appendix A to stipulate that // "long long" values are not legal as discriminants. switch (discrim_tc._kind) { case CORBA::tk_short: case CORBA::tk_ushort: case CORBA::tk_wchar: { CORBA::Short s; if (!stream->get_short (s)) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } } break; case CORBA::tk_long: case CORBA::tk_ulong: case CORBA::tk_enum: { CORBA::Long l; if (!stream->get_long (l)) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } } break; case CORBA::tk_boolean: case CORBA::tk_char: { char c; if (!stream->get_byte (c)) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } } break; default: env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // We also don't care about any member name. if (!stream->skip_string ()) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Get the member size and alignment. member_size = calc_nested_size_and_alignment (0, stream, member_alignment, env); if (env.exception () != 0) return 0; // Save the largest member and alignment. They don't need to be // changed in sync -- e.g. "long double" size is larger than its // alignment restriction on SPARC, x86, and some m68k platforms. if (member_size > value_size) value_size = member_size; if (member_alignment > value_alignment) value_alignment = member_alignment; } // Round up the discriminator's size to include padding it needs in // order to be followed by the value. discrim_size_with_pad = (size_t) align_binary (discrim_size, value_alignment); // Now calculate the overall size of the structure, which is the // discriminator, inter-element padding, value, and tail padding. // We know all of those except tail padding, which is a function of // the overall alignment. (Ensures that arrays of these can be // safely allocated and accessed!) if (value_alignment > overall_alignment) overall_alignment = value_alignment; return (size_t) align_binary (discrim_size_with_pad + value_size, overall_alignment); } // Calculate size and alignment for a CORBA discriminated union. // // Note that this is really a two-element structure. The first // element is the discriminator; the second is the value. All normal // structure padding/alignment rules apply. In particular, all arms // of the union have the same initial address (adequately aligned for // any of the members). static size_t calc_union_attributes (CDR *stream, size_t &alignment, CORBA::Environment &env) { size_t scratch; return calc_key_union_attributes (stream, alignment, scratch, env); } // Calculate size and alignment for a typedeffed type. static size_t calc_alias_attributes (CDR *stream, size_t &alignment, CORBA::Environment &env) { // Skip type ID and name in the parameter stream if (!stream->skip_string () // type ID || !stream->skip_string ()) // typedef name { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // The typedef is identical to the type for which it stands. return calc_nested_size_and_alignment (0, stream, alignment, env); } // Calculate size and alignment of an array. (All such arrays are // described as single dimensional, even though the IDL definition may // specify a multidimensional array ... such arrays are treated as // nested single dimensional arrays.) static size_t calc_array_attributes (CDR *stream, size_t &alignment, CORBA::Environment &env) { size_t member_size; CORBA::ULong member_count; // get size and alignment of the array member member_size = calc_nested_size_and_alignment (0, stream, alignment, env); if (env.exception () != 0) return 0; // Get and check count of members. if (stream->get_ulong (member_count) == CORBA::B_FALSE || member_count > UINT_MAX) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } // Array size is a function only of member number and count return member_size * (size_t) member_count; } // Visit each of the elements of a structure. static CORBA::TypeCode::traverse_status struct_traverse (CDR *stream, const void *value1, const void *value2, CORBA::TypeCode::traverse_status (_FAR *visit) (CORBA::TypeCode_ptr tc, const void *value1, const void *value2, void *context, CORBA::Environment &env), void *context, CORBA::Environment &env) { // Skip over the type ID and type name in the parameters, then get // the number of members. CORBA::ULong members; if (!stream->skip_string () // type ID || !stream->skip_string () // type name || !stream->get_ulong (members)) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return CORBA::TypeCode::TRAVERSE_STOP; } // Visit each member of the structure/exception. The initial // pointer(s) point at the first values to visit. For structs we // could avoid the inter-member padding ... not for the case of // exceptions. No big deal. // // NOTE: For last element, could turn visit() call into something // subject to compiler's tail call optimization and thus save a // stack frame. CORBA::TypeCode::traverse_status retval; for (retval = CORBA::TypeCode::TRAVERSE_CONTINUE; members != 0 && retval == CORBA::TypeCode::TRAVERSE_CONTINUE; members--) { CORBA::TypeCode member_tc (CORBA::tk_null); size_t size; size_t alignment; // Skip the member's name in the parameter list. if (!stream->skip_string ()) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return CORBA::TypeCode::TRAVERSE_STOP; } // Get the member's size, alignment, and a temporary TypeCode, // skipping that TypeCode in the stream as we do so. // // This accounts for all variations: different or nonexistent // parameter lists, errors such as out-of-range TCKind values or // nested exceptions, and indirected typecodes. size = calc_nested_size_and_alignment (&member_tc, stream, alignment, env); if (env.exception () != 0) return CORBA::TypeCode::TRAVERSE_STOP; // Pad the value pointers to account for the alignment // requirements of this member, then visit. value1 = ptr_align_binary ((const u_char *) value1, alignment); value2 = ptr_align_binary ((const u_char *) value2, alignment); retval = visit (&member_tc, value1, value2, context, env); // Update 'value' pointers to account for the size of the values // just visited. value1 = size + (char *)value1; value2 = size + (char *)value2; if (env.exception () != 0) retval = CORBA::TypeCode::TRAVERSE_STOP; } return retval; } // cast the discriminant values to the right type and compare them. static CORBA::Boolean match_value (CORBA::TCKind kind, CDR *tc_stream, const void *value, CORBA::Environment &env) { CORBA::Boolean retval = CORBA::B_FALSE; switch (kind) { case CORBA::tk_short: case CORBA::tk_ushort: { CORBA::UShort discrim; if (tc_stream->get_ushort (discrim) != CORBA::B_FALSE) retval = (discrim == *(CORBA::UShort *)value); else env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } break; case CORBA::tk_long: case CORBA::tk_ulong: { CORBA::ULong discrim; if (tc_stream->get_ulong (discrim) != CORBA::B_FALSE) retval = (discrim == *(CORBA::ULong *)value); else env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } break; case CORBA::tk_enum: { CORBA::ULong discrim; if (tc_stream->get_ulong (discrim) != CORBA::B_FALSE) retval = (discrim == *(unsigned *)value); else env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } break; case CORBA::tk_boolean: { CORBA::Boolean discrim; if (tc_stream->get_boolean (discrim) != CORBA::B_FALSE) retval = (discrim == *(CORBA::Boolean *)value); else env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } break; case CORBA::tk_char: { CORBA::Char discrim; if (tc_stream->get_char (discrim) != CORBA::B_FALSE) retval = (discrim == *(CORBA::Char *)value); else env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } break; case CORBA::tk_wchar: { CORBA::WChar discrim; if (tc_stream->get_wchar (discrim) != CORBA::B_FALSE) retval = (discrim == *(CORBA::WChar *)value); else env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } break; default: env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); } return retval; } // Visit the two elements of the union: the discrminant, and then any // specific value as indicated by the discriminant of value1. static CORBA::TypeCode::traverse_status union_traverse (CDR *stream, const void *value1, const void *value2, CORBA::TypeCode::traverse_status (_FAR *visit) (CORBA::TypeCode_ptr tc, const void *value1, const void *value2, void *context, CORBA::Environment &env), void *context, CORBA::Environment &env) { size_t discrim_size_with_pad; // Figure out size of discriminant plus padding, used to adjust // value pointers later. This can't be calculated without looking // at all branches of the union ... forcing union traversal to be a // two-pass algorithm, unless/until some data gets squirreled away. { CDR temp_cdr; size_t scratch; temp_cdr.next = stream->next; temp_cdr.remaining = stream->remaining; temp_cdr.do_byteswap = stream->do_byteswap; temp_cdr.do_free = 0; (void) calc_key_union_attributes (&temp_cdr, scratch, discrim_size_with_pad, env); } if (env.exception() != 0) return CORBA::TypeCode::TRAVERSE_STOP; // Skip the optional type ID and type name. if (!stream->skip_string () // type ID, hidden || !stream->skip_string ()) { // typedef name env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return CORBA::TypeCode::TRAVERSE_STOP; } // Get and skip the discriminant's TypeCode. This allow for // indirection (e.g. a complex enum discriminant). We use that // TypeCode to visit the discriminant. // // We know the kind is legal and the TypeCode is valid because this // repeats work we did earlier -- so checks are omitted. CORBA::TypeCode discrim_tc (CORBA::tk_null); { size_t scratch; (void) calc_nested_size_and_alignment (&discrim_tc, stream, scratch, env); } if (visit (&discrim_tc, value1, value2, context, env) == CORBA::TypeCode::TRAVERSE_STOP) return CORBA::TypeCode::TRAVERSE_STOP; // Adjust the pointers to point to the other member of the union; // this ensures alignment for any of the values. Save the pointer // to the discriminant though; we need it to find out which member // to visit! const void *discrim_ptr = value1; value1 = discrim_size_with_pad + (char *) value1; value2 = discrim_size_with_pad + (char *) value2; // Get the flag that tells if there's a "default" arm in this union, // then the number of members in the union. CORBA::Long default_used = 0; CORBA::ULong member_count; if (!stream->get_long (default_used)) { // default used env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return CORBA::TypeCode::TRAVERSE_STOP; } if (!stream->get_ulong (member_count)) { // member count env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return CORBA::TypeCode::TRAVERSE_STOP; } // Scan to find the tuple whose value matches the discriminator. // // While we're scanning, record any default arm's information. If // we can't find a match for the discriminant value, that arm will // be used later. u_char *default_tc_ptr = 0; size_t default_tc_len = 0; while (member_count-- != 0) { // Test to see if the discriminant value matches the one in the // TypeCode; this skips the the discriminant value in this CDR // stream. CORBA::Boolean discrim_matched; discrim_matched = match_value (discrim_tc._kind, stream, discrim_ptr, env); if (env.exception () != 0) return CORBA::TypeCode::TRAVERSE_STOP; // Skip the name of the member; we never care about it. if (!stream->skip_string ()) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return CORBA::TypeCode::TRAVERSE_STOP; } // If this is the default member, remember where its typecode // data is stored; we'll use it later. if (default_used >= 0 && default_used-- == 0) { default_tc_ptr = stream->next; default_tc_len = stream->remaining; } // Get the TypeCode for this member. // // XXX we really don't care about size and alignment this time, // only that we initialize the TypeCode. CORBA::TypeCode tc (CORBA::tk_null); size_t scratch; (void) calc_nested_size_and_alignment (&tc, stream, scratch, env); if (env.exception () != 0) return CORBA::TypeCode::TRAVERSE_STOP; // If we matched, visit the member and return. if (discrim_matched) return visit (&tc, value1, value2, context, env); } // If we get here, it means any default arm should be used. We know // at least the basic sanity checks passed; we don't repeat. if (default_tc_ptr) { CDR temp_str; size_t scratch; CORBA::TypeCode tc (CORBA::tk_null); temp_str.next = default_tc_ptr; temp_str.remaining = default_tc_len; temp_str.do_byteswap = stream->do_byteswap; // Get and use the TypeCode. // // XXX we really don't care about size and alignment this time, // only that we initialize the TypeCode. (void) calc_nested_size_and_alignment (&tc, &temp_str, scratch, env); return visit (&tc, value1, value2, context, env); } return CORBA::TypeCode::TRAVERSE_CONTINUE; } // For each node in "data", visit it. For singleton nodes that's all // but a NOP; for structs, unions, etc it's more interesting. The // visit routine can descend, if it chooses. // // NOTE: this does no memory allocation or deallocation except through // use of the stack. Or at least, it should do none -- if you find // that just traversing a data value allocates any memory, that's a // bug to fix! CORBA::TypeCode::traverse_status CORBA::TypeCode::traverse (const void *value1, const void *value2, CORBA::TypeCode::traverse_status (_FAR *visit) (CORBA::TypeCode_ptr tc, const void *value1, const void *value2, void *context, CORBA::Environment &env), void *context, CORBA::Environment &env) { env.clear (); // Quickly accomodate the bulk of cases, which are just (tail) calls // to the visit() routine. We take advantage of the fact that these // are largely in a convenient numeric range to work around poor // optimization of "switch" code in some compilers. This // improvement has in some cases been more than 5% faster // (significant). // // NOTE: if for some reason the constants in the protocol spec // (including Appendix A) change, this logic may need to be verified // again. Luckily, changing protocol constants is quite rare; they // normally just get added to (at the end). // if (_kind <= CORBA::tk_objref || (CORBA::tk_longlong <= _kind && _kind <= CORBA::tk_wstring)) return visit (this, value1, value2, context, env); // Handle the cases that aren't in convenient numeric ranges. traverse_status retval; switch (_kind) { case CORBA::tk_string: case CORBA::tk_enum: return visit (this, value1, value2, context, env); // Typedefs just add a delay, while we skip the typedef ID // and name ... case CORBA::tk_alias: { CORBA::TypeCode_ptr tcp; CORBA::Environment env2; // XXX rework for efficiency, this doesn't need to allocate // memory during the traversal! tcp = typecode_param (2, env); if (env.exception () != 0) return TRAVERSE_STOP; retval = tcp->traverse (value1, value2, visit, context, env); tcp->Release (); } return retval; // Exceptions in-memory are structures, except that there are data // members "hidden" in front: vtable, typecode, refcount. We skip // them, and allow the traversal code to account for the internal // padding before the other elements of the exception. // // NOTE: see header comment re treatment of these values as "real" // C++ exceptions. C++ RTTI data might need to be skipped. Also, // see the comments in unmarshaling code: hard to throw these // values. // // Not enough of the exception runtime is public for binary // standards to exist for C++ exceptions yet. Compiler-specific // code will need to handle examining, unmarshaling, and throwing // of CORBA exceptions (in C++ environments) for some time. case CORBA::tk_except: value1 = sizeof (CORBA::Exception) + (char *) value1; value2 = sizeof (CORBA::Exception) + (char *) value2; // FALLTHROUGH case CORBA::tk_struct: // XXX for OLE Automation, we'll probably need BOTH exceptions // and structs to inherit IUnknown, hence we'll need to be // skipping the vtable pointer ... { // Create the sub-encapsulation stream that holds the // parameters for the typecode. CDR stream; stream.setup_encapsulation (_buffer, (size_t) _length); return struct_traverse (&stream, value1, value2, visit, context, env); } case CORBA::tk_union: { // visit the discriminant, then search the typecode for the // relevant union member and then visit that member. CDR stream; stream.setup_encapsulation (_buffer, (size_t) _length); return union_traverse (&stream, value1, value2, visit, context, env); } // Sequences are just arrays with bound determined at runtime, // rather than compile time. Multidimensional arrays are nested // C-style: the leftmost dimension in the IDL definition is // "outermost", etc. { CORBA::TypeCode_ptr tc2; size_t size; CORBA::ULong bounds; CORBA::OctetSeq *seq; case CORBA::tk_sequence: // Find out how many elements there are, and adjust the data // pointers to point to those elements rather than to the // sequence itself. seq = (CORBA::OctetSeq *)value1; bounds = seq->length; value1 = seq->buffer; if (value2) { seq = (CORBA::OctetSeq *)value2; value2 = seq->buffer; } goto shared_seq_array_code; case CORBA::tk_array: // Array bounds are in the typecode itself. bounds = ulong_param (1, env); if (env.exception () != 0) return TRAVERSE_STOP; shared_seq_array_code: // Find element's type, and size ... tc2 = typecode_param (0, env); if (env.exception () != 0) return TRAVERSE_STOP; size = tc2->size (env); if (env.exception () != 0) return TRAVERSE_STOP; // ... then visit the elements in order. // // NOTE: for last element, could turn visit() call into // something subject to compiler's tail call optimization and // thus save a stack frame while (bounds--) { if (visit (tc2, value1, value2, context, env) == TRAVERSE_STOP) return TRAVERSE_STOP; value1 = size + (char *) value1; value2 = size + (char *) value2; } CORBA::release (tc2); env.clear (); } return TRAVERSE_CONTINUE; // case ~0: // indirection, illegal at top level default: // invalid/illegal break; } // end switch on typecode "kind" env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return TRAVERSE_STOP; } // Tell user the size of an instance of the data type described by // this typecode ... typically used to allocate memory. size_t CORBA::TypeCode::private_size (CORBA::Environment &env) { if (_kind >= TC_KIND_COUNT) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } env.clear (); if (table [_kind].calc == 0) { _private_state->tc_size_known_ = CORBA::B_TRUE; _private_state->tc_size_ = table [_kind].size; return _private_state->tc_size_; } size_t alignment; CDR stream; stream.setup_encapsulation (_buffer, (size_t) _length); _private_state->tc_size_known_ = CORBA::B_TRUE; _private_state->tc_size_ = table [_kind].calc (&stream, alignment, env); return _private_state->tc_size_; } // Tell user the alignment restriction for the data type described by // an instance of this data type. Rarely used; provided for // completeness. size_t CORBA::TypeCode::private_alignment (CORBA::Environment &env) { if (_kind >= TC_KIND_COUNT) { env.exception (new CORBA::BAD_TYPECODE (CORBA::COMPLETED_NO)); return 0; } env.clear (); if (table [_kind].calc == 0) { _private_state->tc_alignment_known_ = CORBA::B_TRUE; _private_state->tc_alignment_ = table [_kind].alignment; return _private_state->tc_alignment_; } size_t alignment; CDR stream; stream.setup_encapsulation (_buffer, (size_t) _length); (void) table [_kind].calc (&stream, alignment, env); _private_state->tc_alignment_known_ = CORBA::B_TRUE; _private_state->tc_alignment_ = alignment; return alignment; }