/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Copyright (c) 1993 Microsoft Corporation Module Name: pickle.cxx Abstract: This module contains methods for generating pickling support. Notes: Author: Ryszard K. Kott (ryszardk) Sep 28, 1993 Revision History: ------------------------------------------------------------------------*/ #include "nulldefs.h" extern "C" { #include #include #include } #include #include #include #include #include #include // STRING_COMPONENT #include #include #define ACT_LEN "_ActualLength" #define REQ_LEN "_RequiredLength" #define PRE_PAD "_PrePad" #define PCHAR_CAST "(char __RPC_FAR *)" #define PPCHAR_CAST "(char __RPC_FAR * __RPC_FAR *)" #define PVOID_CAST "(void __RPC_FAR *)" #define PPVOID_CAST "(void __RPC_FAR * __RPC_FAR *)" #define BUFFER "_prpcmsg->Buffer" #define BUFFER_LEN "_prpcmsg->BufferLength" extern char * STRING_TABLE[ LAST_COMPONENT ]; // needed for old print extern OutputManager * pOutput; // needed for local decl extern CTXTMGR * pGlobalContext; // needed to decorate // type subgraphs PickleManager * pPicControlBlock; // ======================================================================== // // PickleControlBlock. // // ======================================================================== PickleManager::PickleManager( SIDE_T Side, BOOL fEncode, BOOL fDecode ): Sides( Side ), fEncodeAtIf( fEncode ), fDecodeAtIf( fDecode ), fUseProcessing( FALSE ) { PicPrint = new MopPrintManager; } PickleManager::~PickleManager() { delete PicPrint; } // ======================================================================== // // Emitting pickle routines for a type. // // ======================================================================== STATUS_T node_def::PickleCodeGen( void ) /*++ Routine description: This routine is the entry point for the type pickle generation. Arguments: --*/ { SIDE_T Side = pPicControlBlock->GetSides(); char * pTypeName = GetSymName(); if ( Side & HEADER_SIDE ) PickleTypePrototypes( pTypeName ); if ( Side & CLIENT_STUB ) { pPicControlBlock->GetPrintManager()->SetSide( CLIENT_STUB ); //.. Simulate appropriate environment for the walking methods. //.. Set up a param node pointing to a pointer node pointing to //.. the def node. node_param * DummyParam = new node_param; node_pointer * DummyPointer = new node_pointer( ATTR_REF ); DummyParam->SetChild( DummyPointer ); DummyParam->SetEdgeType( EDGE_USE ); DummyParam->SetSymName( PICKLE_OBJECT_NAME ); BOOL fEncodeUsed = FInSummary( ATTR_ENCODE ) || pPicControlBlock->GetEncodeAtIf(); BOOL fDecodeUsed = FInSummary( ATTR_DECODE ) || pPicControlBlock->GetDecodeAtIf(); if ( fEncodeUsed ) ((node_skl *)DummyParam)->SetAttribute( ATTR_IN ); if ( fDecodeUsed ) ((node_skl *)DummyParam)->SetAttribute( ATTR_OUT ); DummyPointer->SetChild( this ); BufferManager TempBuffer( 8, LAST_COMPONENT, STRING_TABLE ); //.. Decoratre stuff as needed pPicControlBlock->SetUseProcessing(); pPicControlBlock->SetDummyParam( DummyParam ); pGlobalContext->PushContext( DummyParam ); pGlobalContext->PushContext( DummyPointer ); UseProcessing(); pGlobalContext->PopContext(); pGlobalContext->PopContext(); pPicControlBlock->ResetUseProcessing(); //.. For some types a macro is generated instead of the size routine. int OptimCode = IsPickleSizeOptimizable(); PickleSize( &TempBuffer, DummyParam, OptimCode, FALSE ); PickleSize( &TempBuffer, DummyParam, OptimCode, TRUE ); if ( fEncodeUsed ) PickleSerialize ( &TempBuffer, DummyParam, OptimCode ); if ( fDecodeUsed ) PickleDeserialize( &TempBuffer, DummyParam, OptimCode ); } return( STATUS_OK ); } BOOL node_def::IsAPredefinedType( void ) { return( strcmp( GetSymName(), "error_status_t" ) == 0 || strcmp( GetSymName(), "wchar_t" ) == 0 ); } void node_def::PickleTypePrototypes( char * pTypeName ) /*++ Routine description: This routine emits prototypes for the type pickling routines. size_t _Size ( __RPC_FAR * _pObject ); size_t _AlignSize( MIDL_ES_HANDLE _Handle, __RPC_FAR * _pObject ); void _Encode ( MIDL_ES_HANDLE _Handle, __RPC_FAR * _pObject ); void _Decode ( MIDL_ES_HANDLE _Handle, __RPC_FAR * _pObject ); Actually, _pObject is defined as PICKLE_OBJECT_NAME. If the type is a base type, _size routine is a macro #define _Size( x ) (unsigned long) Arguments: --*/ { MopPrintManager * pPrint = pPicControlBlock->GetPrintManager(); pPrint->SetSide( HEADER_SIDE ); pPrint->NewLine(); pPrint->EmitString( "/* Pickling routines for type %s */", pTypeName ); pPrint->NewLine(); if ( FInSummary( ATTR_ENCODE ) || pPicControlBlock->GetEncodeAtIf() ) { if ( IsPickleSizeOptimizable() ) { node_skl * pNode = GetBasicType(); unsigned long BaseTypeSize = pNode->GetSize( 0 ); pPrint->EmitString( "#define %s_Size( x ) (unsigned long)sizeof(", pTypeName ); pPrint->EmitString( "%s)", pNode->GetSymName() ); pPrint->NewLine(); } else { pPrint->EmitString( "size_t \n%s_Size( ", pTypeName ); pPrint->EmitString( "%s __RPC_FAR * " PICKLE_OBJECT_NAME " );", pTypeName ); pPrint->NewLine(); } pPrint->EmitString( "size_t \n%s_AlignSize( MIDL_ES_HANDLE _Handle, ", pTypeName ); pPrint->EmitString( "%s __RPC_FAR * " PICKLE_OBJECT_NAME " );", pTypeName ); pPrint->NewLine(); pPrint->EmitString( "void \n%s_Encode( MIDL_ES_HANDLE _Handle, ", pTypeName ); pPrint->EmitString( "%s __RPC_FAR * " PICKLE_OBJECT_NAME " );", pTypeName ); pPrint->NewLine(); } if ( FInSummary( ATTR_DECODE ) || pPicControlBlock->GetDecodeAtIf() ) { pPrint->EmitString( "void \n%s_Decode( MIDL_ES_HANDLE _Handle, ", pTypeName ); pPrint->EmitString( "%s __RPC_FAR * " PICKLE_OBJECT_NAME " );", pTypeName ); pPrint->NewLine(); } } // ======================================================================== // // Helpers for size optimization decisions affecting routines or macros // that are generated. // // ======================================================================== int node_def::IsPickleSizeOptimizable( void ) { //.. Cannot find a routine for nailing down sizeable types ... //.. Should be available somewhere, dammit! NODE_T Type = GetBasicType()->GetNodeType(); static struct _NodeTypeToOptimCode { PicOptimCode Code; NODE_T Type; } PicTypeToOptimCode[] = { PicNoOptimization, NODE_ILLEGAL, PicOptimFloat, NODE_FLOAT, PicOptimDouble, NODE_DOUBLE, PicOptimHyper, NODE_HYPER, PicOptimLong, NODE_LONG, PicNoOptimization, NODE_LONGLONG, PicOptimShort, NODE_SHORT, PicNoOptimization, NODE_INT, PicOptimByte, NODE_SMALL, PicOptimChar, NODE_CHAR, PicOptimShort, NODE_BOOLEAN, PicOptimByte, NODE_BYTE, PicNoOptimization, NODE_VOID, PicNoOptimization, NODE_HANDLE_T, PicOptimLong, NODE_ERROR_STATUS_T, PicOptimShort, NODE_ENUM, PicOptimShort, NODE_WCHAR_T }; int i = 0; while ( i < sizeof(PicTypeToOptimCode)/sizeof(_NodeTypeToOptimCode) ) { if ( Type == PicTypeToOptimCode[ i ].Type ) return( PicTypeToOptimCode[ i ].Code ); i++; } return( PicNoOptimization ); } //.. Currrently these tables encode optimization that doesn't bother with //.. endianness and floating point differences. //.. The only thing of importance recognized here is the size of an entity. //.. The ordering of the strings follows PicOptimCode enum type definition: //.. NoOptimization, Byte, Short, Long, Hyper, Char, Float, Double. static char * PicOptimizationType[ PicOptimEnumSize + 1 ] = { "PicNoOptimization!", "char", "short", "long", "double", "char", "long", "double" }; static char * PicOptimizationTypeSuffix[ PicOptimEnumSize + 1 ] = { "PicNoOptimization!", "Byte", "Short", "Long", "Hyper", "Byte", "Long", "Hyper" }; // ======================================================================== // // Wrappers for local declarations and indentation. // // ======================================================================== void PickleProcedureProlog( int DeclCode ) /*++ Routine description: This routine generates more or less of local variables depending on the level of optimization. Argument: DecCode - what is expected: 4 - no declariations at all optimized rotuines 3 - stub related variables only (*_Size) 2 - stub related + Act (*_AlignSize) 1 - stub related + Act + Req (*_Decode) 0 - stub related + Act, Req + Pad (*_Encode) --*/ { pOutput->InitBlock( CLIENT_STUB ); if ( DeclCode == 4 ) return; if ( DeclCode == 0) pOutput->Print( CLIENT_STUB, MOP_TAB "size_t " PRE_PAD ";\n" ); if ( DeclCode <= 1) pOutput->Print( CLIENT_STUB, MOP_TAB "size_t " REQ_LEN ";\n" ); if ( DeclCode <= 2) pOutput->Print( CLIENT_STUB, MOP_TAB "size_t " ACT_LEN ";\n" ); pOutput->Print( CLIENT_STUB, MOP_TAB "char * _tempbuf;\n" ); pOutput->Print( CLIENT_STUB, MOP_TAB "char * _savebuf;\n" ); pOutput->ProcedureProlog( CLIENT_STUB, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE ); pOutput->Print( CLIENT_STUB, MOP_TAB "((void)(_status));\n" ); pOutput->Print( CLIENT_STUB, MOP_TAB "((void)(_tempbuf));\n" ); pOutput->Print( CLIENT_STUB, MOP_TAB "((void)(_savebuf));\n\n" MOP_TAB ); } void PickleProcedureEpilog( void ) { pOutput->ExitBlock( CLIENT_STUB ); } // ======================================================================== // // Generating the pickle sizing routine. // // ======================================================================== void node_def::PickleSize( BufferManager * pBuffer, node_param * pDummyParam, int OptimCode, BOOL fAlignSize ) /*++ Routine description: This routine generates the pickling sizing routines: size_t _Size( * PICKLE_OBJECT_NAME ) { prpcmsg = &rpcmsg; I_MesMessageInit( prpcmsg );; return( prpcmsg->BufferLength ); } size_t _AlignSize( MIDL_ES_HANDLE _Handle, * PICKLE_OBJECT_NAME ) { prpcmsg = &_Handle->rpcmsg; _ActualLentgh = prpcmsg->BufferLength; prpcmsg->BufferLength += MIDL_ES_HEADER_PAD( prpcmsg->BufferLength ); prpcmsg->BufferLength += MIDL_ES_HEADER_SIZE; return( prpcmsg->BufferLength - _ActualLength ); } However for the types that can be optimized (currently base types and such), instead of the first routine a macro is generated in the header file and the second routine is just a ndr library call. #define _Size sizeof( type ) size_t _AlignSize( MIDL_ES_HANDLE _Handle, * PICKLE_OBJECT_NAME ) { ((void)PICKLE_OBJECT_NAME); return( I_MesAlignSize( _Handle )); } Arguments: pBuffer - output connection pDummyParam - Subtree for the "parameter" with the def node SizingStatus- controls how many decl to emit fAlignSize - controls which sizing routine to emit --*/ { MopPrintManager * pPrint = pPicControlBlock->GetPrintManager(); if ( OptimCode && ! fAlignSize ) { //.. A macro in the header file generated. return; } //.. Header is mostly the same pPrint->EmitLine( "\nsize_t" ); pPrint->Emit( GetSymName() ); pPrint->Emit( fAlignSize ? "_AlignSize( " : "_Size( " ); if ( fAlignSize ) pPrint->Emit( "MIDL_ES_HANDLE _Handle, " ); pPrint->Emit( GetSymName() ); pPrint->EmitLine( " * " PICKLE_OBJECT_NAME " ) " ); PickleProcedureProlog( OptimCode ? 4 : (fAlignSize ? 2 : 3 )); //.. Now the body of the routines. if ( OptimCode ) { pPrint->EmitLineInc( MOP_TAB "((void)" PICKLE_OBJECT_NAME ");" ); pPrint->EmitString( "return( I_MesAlignSize%s( _Handle ));", PicOptimizationTypeSuffix[ OptimCode ] ); pPrint->NewLine(); } else { if ( fAlignSize ) { pPrint->EmitLineInc( "_prpcmsg = &_Handle->rpcmsg;" ); pPrint->EmitLineInc( ACT_LEN " = " BUFFER_LEN ";" ); pPrint->EmitLineInc( BUFFER_LEN " += MIDL_ES_HEADER_PAD(" BUFFER_LEN ");" ); pPrint->EmitLineInc( BUFFER_LEN " += MIDL_ES_HEADER_SIZE;" ); } else pPrint->EmitLineInc( "I_MesMessageInit( _prpcmsg );" ); pPrint->NewLine(); pBuffer->Clear(); pDummyParam->WalkTree( CALC_SIZE, CLIENT_STUB, NODE_PROC, pBuffer ); pPrint->NewLineInc (); if ( fAlignSize ) pPrint->EmitLine( "return( " BUFFER_LEN " - " ACT_LEN " );" ); else pPrint->EmitLine( "return( " BUFFER_LEN " );" ); } PickleProcedureEpilog(); } // ======================================================================== // // Generating the pickle serializing (marshalling) routine. // // ======================================================================== void node_def::PickleSerialize( BufferManager * pBuffer, node_param * pDummyParam, int OptimCode ) /*++ Routine description: This routine generates the pickling serializing routine: void _Encode( MIDL_ES_HANDLE _Handle, * pObject ) { size_t _ActualLen, _RequiredLen _PrePad; _prpcmsg = &_Handle->rpcmsg; _PrePad = MIDL_ES_HEADER_PAD( BUFFER_LEN ); _RequiredLen = _ActualLen = _AlignSize( _Handle, pObject ); (_pHandle->Alloc)( _Handle->UserState, &_Handle->rpcmsg.Buffer, &_ActualLen ); if ( _ActualLen < _RequiredLen ) RpcRaiseException( ERROR_OUTOFMEMORY ); _ActualLen = _RequiredLen - _Size( pObject ); ((char *)_prpcmsg->Buffer) += _PrePad; *((unsigned long *)_prpcmsg->Buffer) = _RequiredLen - _PrePad - MIDL_ES_HEADER_SIZE; ((char *)_prpcmsg->Buffer) += ActualLen - _PrePad; _ActualLen = _RequiredLen; (_Handle->Write)( _Handle->UserState, _prpcmsg->Buffer, &ActualLen ); if ( _ActualLen < _RequiredLen ) RpcRaiseException( ERROR_OUTOFMEMORY ); } However, if the can be optimized, the routine is just a wrapper for a ndr library call. void _Encode( MIDL_ES_HANDLE _Handle, * pObject ) { I_MesEncode( _Handle, PICKLE_OBJECT_NAME ); } Arguments: Note: Size of the object in the buffer can have padding preceding it as well as following it (this one is between the header and the object). So, to get things correctly when deserializing, the size as written to the buffer is equal to the size of the object + padding between the header and the object. --*/ { MopPrintManager * pPrint = pPicControlBlock->GetPrintManager(); char * TypeName = GetSymName(); //.. Header is the same with or without optimization. pPrint->EmitLine( "\nvoid" ); pPrint->EmitStringNLInc( "%s_Encode(", TypeName ); pPrint->EmitLineInc ( "MIDL_ES_HANDLE _Handle, "); pPrint->EmitString ( "%s * " PICKLE_OBJECT_NAME " ) ", TypeName ); pPrint->NewLine(); PickleProcedureProlog( OptimCode ? 4 : 0 ); if ( OptimCode ) { pPrint->EmitString( MOP_TAB "I_MesEncode%s( _Handle, ", PicOptimizationTypeSuffix[ OptimCode ] ); pPrint->EmitString( "(%s __RPC_FAR *)" PICKLE_OBJECT_NAME ");", PicOptimizationType[ OptimCode ] ); pPrint->NewLine(); } else { pPrint->EmitLineInc( "_prpcmsg = & _Handle->rpcmsg;" ); pPrint->EmitLineInc( PRE_PAD " = MIDL_ES_HEADER_PAD(" BUFFER_LEN ");" ); pPrint->EmitStringNLInc( REQ_LEN " = " ACT_LEN " = %s_AlignSize( _Handle, " PICKLE_OBJECT_NAME " );", TypeName ); pPrint->EmitLineInc( "(_Handle->Alloc)( _Handle->UserState," ); pPrint->EmitLineInc( " " PPCHAR_CAST "&" BUFFER "," ); pPrint->EmitLineInc( " &" ACT_LEN " );" ); pPrint->EmitLineInc( "if ( " ACT_LEN " < " REQ_LEN " )" ); pPrint->EmitLineInc( MOP_TAB "RpcRaiseException( ERROR_OUTOFMEMORY );\n" ); pPrint->EmitStringNLInc( ACT_LEN " = " REQ_LEN " - %s_Size( " PICKLE_OBJECT_NAME " );", TypeName ); pPrint->EmitLineInc( PCHAR_CAST BUFFER " += " PRE_PAD ";" ); pPrint->EmitLineInc( "*((unsigned long *)" BUFFER ") = (unsigned long)" REQ_LEN " - " PRE_PAD " - MIDL_ES_HEADER_SIZE;" ); pPrint->EmitLineInc( PCHAR_CAST BUFFER " += " ACT_LEN " - " PRE_PAD ";" ); pBuffer->Clear(); pDummyParam->WalkTree( SEND_NODE, CLIENT_STUB, NODE_PROC, pBuffer ); pPrint->NewLineInc(); pPrint->EmitLineInc( ACT_LEN " = " REQ_LEN ";" ); pPrint->EmitLineInc( "(_Handle->Write)( _Handle->UserState," ); pPrint->EmitLineInc( " " PCHAR_CAST BUFFER "," ); pPrint->EmitLineInc( " " ACT_LEN ");" ); pPrint->EmitLineInc( "if ( " ACT_LEN " < " REQ_LEN " )" ); pPrint->EmitLineInc( MOP_TAB "RpcRaiseException( ERROR_OUTOFMEMORY );\n" ); } PickleProcedureEpilog(); } // ======================================================================== // // Generating the pickle deserializing (unmarshalling) routine. // // ======================================================================== void node_def::PickleDeserialize( BufferManager * pBuffer, node_param * pDummyParam, int OptimCode ) /*++ Routine description: This routine generates the pickling deserializing routine: void _Decode( MIDL_ES_HANDLE _Handle, * PICKLE_OBJECT_NAME ) { size_t _RequiredLen, ActualLen; _prpcmsg = &_Handle->rpcmsg; _ReqiredLen = _ActualLen = MIDL_ES_HEADER_PAD( BUFFER ) + MIDL_ES_HEADER_SIZE; (_Handle->Read)( _Handle->UserState, &_prpcmsg->Buffer, &_ActualLen ); if ( _ActualLen < _RequiredLen ) RpcRaiseException( ERROR_OUTOFMEMORY ); BUFFER += MIDL_ES_HEADER_PAD( BUFFER ) _ReqiredLen = _ActualLen = (size_t)*((unsigned long *)_prpcmsg->Buffer); ((char *)_prpcmsg->Buffer) += MIDL_ES_HEADER_SIZE; (_Handle->Read)( _Handle->UserState, &_prpcmsg->Buffer, &_ActualLen ); if ( _ActualLen < _RequiredLen ) RpcRaiseException( ERROR_OUTOFMEMORY ); } However, if the can be optimized, the routine is just a wrapper for a ndr library call. void _Decode( MIDL_ES_HANDLE _Handle, * pObject ) { I_MesDecode( _Handle, PICKLE_OBJECT_NAME ); } Arguments: --*/ { MopPrintManager * pPrint = pPicControlBlock->GetPrintManager(); char * TypeName = GetSymName(); //.. Header is the same with or without optimization. pPrint->EmitLine( "\nvoid" ); pPrint->EmitStringNLInc( "%s_Decode(", TypeName ); pPrint->EmitLineInc ( "MIDL_ES_HANDLE _Handle, "); pPrint->EmitString ( "%s * " PICKLE_OBJECT_NAME " ) ", TypeName ); pPrint->NewLine(); PickleProcedureProlog( OptimCode ? 4 : 1 ); if ( OptimCode ) { pPrint->EmitString( MOP_TAB "I_MesDecode%s( _Handle, ", PicOptimizationTypeSuffix[ OptimCode ] ); pPrint->EmitString( "(%s __RPC_FAR *)" PICKLE_OBJECT_NAME ");", PicOptimizationType[ OptimCode ] ); pPrint->NewLine(); } else { pPrint->EmitLine ( "_prpcmsg = & _Handle->rpcmsg;\n" ); pPrint->EmitLineInc( REQ_LEN " = " ACT_LEN " = MIDL_ES_HEADER_PAD(" BUFFER ") +" ); pPrint->EmitLineInc( MOP_TAB MOP_TAB " MIDL_ES_HEADER_SIZE;" ); pPrint->EmitLineInc( "(_Handle->Read)( _Handle->UserState," ); pPrint->EmitLineInc( " " PPCHAR_CAST "&" BUFFER "," ); pPrint->EmitLineInc( " &" ACT_LEN " );" ); pPrint->EmitLineInc( "if ( " ACT_LEN " < " REQ_LEN " )" ); pPrint->EmitLineInc( MOP_TAB "RpcRaiseException( ERROR_OUTOFMEMORY );\n" ); pPrint->EmitLineInc( PCHAR_CAST BUFFER " += MIDL_ES_HEADER_PAD(" BUFFER ");" ); pPrint->EmitLineInc( REQ_LEN " = " ACT_LEN " = (size_t)*((unsigned long *)" BUFFER ");" ); pPrint->EmitLineInc( PCHAR_CAST BUFFER " += MIDL_ES_HEADER_SIZE;" ); pPrint->EmitLineInc( "(_Handle->Read)( _Handle->UserState," ); pPrint->EmitLineInc( " " PPCHAR_CAST "&" BUFFER "," ); pPrint->EmitLineInc( " &" ACT_LEN " );" ); pPrint->EmitLineInc( "if ( " ACT_LEN " < " REQ_LEN " )" ); pPrint->EmitLineInc( MOP_TAB "RpcRaiseException( ERROR_OUTOFMEMORY );\n" ); pBuffer->Clear(); pDummyParam->WalkTree( RECV_NODE, CLIENT_STUB, NODE_PROC, pBuffer ); } PickleProcedureEpilog(); } // ======================================================================== // // Interface layer. // // ======================================================================== BOOL node_source::HasAnyPicklingAttr( void ) { STATUS_T Status; type_node_list FileList; node_skl * pNode; BOOL HasAny = FALSE; if ( (Status = GetMembers( &FileList )) != STATUS_OK ) return( FALSE ); FileList.Init(); while ( FileList.GetPeer( &pNode ) == STATUS_OK ) if ( ((node_file *)pNode)->HasAnyPicklingAttr() ) { HasAny = TRUE; break; } return( HasAny ); } BOOL node_file::HasAnyPicklingAttr( void ) { STATUS_T Status; type_node_list IfList; node_skl * pNode; BOOL HasAny = FALSE; if ( (Status = GetMembers( &IfList )) != STATUS_OK ) return( FALSE ); IfList.Init(); while ( IfList.GetPeer( &pNode ) == STATUS_OK ) if ( ((node_interface *)pNode)->HasAnyPicklingAttr() ) { HasAny = TRUE; break; } return( HasAny ); } BOOL node_interface::HasAnyPicklingAttr( void ) { STATUS_T Status; type_node_list NodeList; node_skl * pNode; BOOL HasAny = FALSE; if ( FInSummary( ATTR_ENCODE ) || FInSummary( ATTR_DECODE ) ) return( TRUE ); if ( (Status = GetMembers( &NodeList )) != STATUS_OK ) return( FALSE ); NodeList.Init(); while ( NodeList.GetPeer( &pNode ) == STATUS_OK ) if ( pNode->GetNodeType() == NODE_DEF && ((node_def *)pNode)->HasAnyPicklingAttr() ) { HasAny = TRUE; break; } return( HasAny ); } /* BOOL node_proc::HasAnyPicklingAttr( void ) { return( FALSE ); } */ BOOL node_def::HasAnyPicklingAttr( void ) { return( FInSummary( ATTR_ENCODE ) || FInSummary( ATTR_DECODE ) ); }