diff options
author | schmidt <douglascraigschmidt@users.noreply.github.com> | 1997-07-02 05:14:19 +0000 |
---|---|---|
committer | schmidt <douglascraigschmidt@users.noreply.github.com> | 1997-07-02 05:14:19 +0000 |
commit | 05834cf795347886b4daf40d2497a4efae91aec3 (patch) | |
tree | 342202567424d86b13f973916f58c0511331046f /TAO/tao/interp.cpp | |
parent | 1a9acde7970222a4434ccf485d0895090960da7b (diff) | |
download | ATCD-05834cf795347886b4daf40d2497a4efae91aec3.tar.gz |
*** empty log message ***
Diffstat (limited to 'TAO/tao/interp.cpp')
-rw-r--r-- | TAO/tao/interp.cpp | 1440 |
1 files changed, 1440 insertions, 0 deletions
diff --git a/TAO/tao/interp.cpp b/TAO/tao/interp.cpp new file mode 100644 index 00000000000..e3ddc3d6161 --- /dev/null +++ b/TAO/tao/interp.cpp @@ -0,0 +1,1440 @@ +// @(#)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. + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include "orb.h" +#include "cdr.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 [TC_KIND_COUNT] = +{ + { 0, 1, 0 }, // tk_null + { 0, 1, 0 }, // tk_void + + { 0, 1, 0, 0 }, // tk_short + { 0, 1, 0, 0 }, // tk_long + { 0, 1, 0, 0 }, // tk_ushort + { 0, 1, 0, 0 }, // tk_ulong + + { 0, 1, 0, 0 }, // tk_float + { 0, 1, 0, 0 }, // tk_double + + { 0, 1, 0, 0 }, // tk_boolean + { 0, 1, 0, 0 }, // tk_char + { 0, 1, 0, 0 }, // tk_octet + { 0, 1, 0, 0 }, // tk_any + + { 0, 1, 0, 0 }, // tk_TypeCode + { 0, 1, 0, 0 }, // tk_Principal + { 0, 1, 0, skip_encapsulation }, // tk_objref + + { 0, 1, calc_struct_attributes, 0 }, // tk_struct + { 0, 1, calc_union_attributes, 0 }, // tk_union + + { 0, 1, 0, skip_encapsulation }, // tk_enum + { 0, 1, 0, skip_long }, // tk_string + { 0, 1, 0, skip_encapsulation }, // tk_sequence + { 0, 1, calc_array_attributes, 0 }, // tk_array + + // + // Two TCKind values added in 94-11-7 + // + { 0, 1, calc_alias_attributes, 0 }, // tk_alias + { 0, 1, calc_exception_attributes, 0 }, // 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 }, // tk_longlong + { 0, 1, 0, 0 }, // tk_ulonglong + { 0, 1, 0, 0 }, // tk_longdouble + { 0, 1, 0, 0 }, // tk_wchar + { 0, 1, 0, skip_long } // 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). + +#if defined (unix) || defined (VXWORKS) +#define setup_entry(x,t) \ + { \ + struct align_struct_ ## t { \ + x one; \ + char dummy [33 - sizeof(x)]; \ + x two; \ + }; \ + \ + align_struct_ ## t align; \ + table [t].size = sizeof (x); \ + table [t].alignment = \ + (char*) &align.two - (char*) &align.one - 32; \ + } + +#else // PC "fixed" alignment +#define setup_entry(x,t) \ + { \ + table [t].size = sizeof (x); \ + table [t].alignment = 1; \ + } + +#endif + +// 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, tk_enum); + table [tk_enum].size = sizeof (generic_enum); + table [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 (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 (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 (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 <= tk_void + || kind == tk_except) + { + env.exception (new CORBA_BAD_TYPECODE (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 (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 (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: tk_string, tk_wstring, + // tk_objref, tk_enum, and tk_sequence. + + if (tc) + { + CORBA_ULong len; + + tc->_kind = kind; + switch (kind) + { + default: + assert (table [kind].skipper == 0); + break; + + case tk_string: + case tk_wstring: + if (stream->get_ulong (len) == CORBA_B_FALSE) + { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + tc->_length = len; + break; + + case tk_enum: + case tk_objref: + case tk_sequence: + if (stream->get_ulong (len) == CORBA_B_FALSE) + { + env.exception (new CORBA_BAD_TYPECODE (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 (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 "tk_struct" (or "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 [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 (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 (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 (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 (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 (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 tk_short: + case tk_ushort: + case tk_wchar: + { + CORBA_Short s; + + if (!stream->get_short (s)) + { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + break; + + case tk_long: + case tk_ulong: + case tk_enum: + { + CORBA_Long l; + + if (!stream->get_long (l)) + { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + break; + + case tk_boolean: + case tk_char: + { + char c; + + if (!stream->get_byte (c)) + { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + break; + + default: + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // We also don't care about any member name. + + if (!stream->skip_string ()) + { + env.exception (new CORBA_BAD_TYPECODE (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 (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 (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 (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 (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 (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 tk_short: + case 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 (COMPLETED_NO)); + } + break; + + case tk_long: + case 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 (COMPLETED_NO)); + } + break; + + case 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 (COMPLETED_NO)); + } + break; + + case 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 (COMPLETED_NO)); + } + break; + + case 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 (COMPLETED_NO)); + } + break; + + case 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 (COMPLETED_NO)); + } + break; + + default: + env.exception (new CORBA_BAD_TYPECODE (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 (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 (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 (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + if (!stream->get_ulong (member_count)) + { // member count + env.exception (new CORBA_BAD_TYPECODE (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; + + 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 (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 (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 (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 <= tk_objref + || (tk_longlong <= _kind && _kind <= 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 tk_string: + case tk_enum: + return visit (this, value1, value2, context, env); + + // Typedefs just add a delay, while we skip the typedef ID + // and name ... + + case 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 tk_except: + value1 = sizeof (CORBA_Exception) + (char *) value1; + value2 = sizeof (CORBA_Exception) + (char *) value2; + // FALLTHROUGH + + case 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 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 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 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 (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::prv_size (CORBA_Environment &env) +{ + if (_kind >= TC_KIND_COUNT) + { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + env.clear (); + + if (table [_kind].calc == 0) + { + _prv_state->tc_size_known_ = CORBA_B_TRUE; + _prv_state->tc_size_ = table [_kind].size; + return _prv_state->tc_size_; + } + + size_t alignment; + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t) _length); + + _prv_state->tc_size_known_ = CORBA_B_TRUE; + _prv_state->tc_size_ = table [_kind].calc (&stream, alignment, env); + return _prv_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::prv_alignment (CORBA_Environment &env) +{ + if (_kind >= TC_KIND_COUNT) + { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + env.clear (); + + if (table [_kind].calc == 0) + { + _prv_state->tc_alignment_known_ = CORBA_B_TRUE; + _prv_state->tc_alignment_ = table [_kind].alignment; + return _prv_state->tc_alignment_; + } + + size_t alignment; + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t) _length); + + (void) table [_kind].calc (&stream, alignment, env); + _prv_state->tc_alignment_known_ = CORBA_B_TRUE; + _prv_state->tc_alignment_ = alignment; + return alignment; +} |