engineXMLExport.cpp
Engine/source/console/engineXMLExport.cpp
A generator that will dump all export structures contained in an engine DLL to an XML file which may then be used by wrapper generators to create a language-specific binding for the engine API.
Public Defines
define
PRIMTYPE(tp) ( < tp >() == type ) \ { \ tp val = < tp >( defaultArgs, offset ); \ value = ( val ); \ }
Public Variables
const char *
Public Functions
DefineEngineFunction(exportEngineAPIToXML , SimXMLDocument * , () , "Create a XML document containing a dump of the entire exported engine <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">API.\n\n</a>" "@return A <a href="/coding/class/classsimxmldocument/">SimXMLDocument</a> containing a dump of the engine's export information or <a href="/coding/file/typesx86unix_8h/#typesx86unix_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a> <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the operation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed.\n\n</a>" "@ingroup Console" )
exportFunction(const EngineFunctionInfo * function, SimXMLDocument * xml)
exportScope(const EngineExportScope * scope, SimXMLDocument * xml, bool addNode)
exportType(const EngineTypeInfo * type, SimXMLDocument * xml)
getArgValue(const EngineFunctionDefaultArguments * defaultArgs, U32 offset)
getDefaultArgumentValue(const EngineFunctionInfo * function, const EngineTypeInfo * type, U32 offset)
const char *
getDocString(const EngineExport * exportInfo)
getTypeName(const EngineTypeInfo * type)
bool
isExportFiltered(const EngineExport * exportInfo)
parseFunctionArgumentNames(const EngineFunctionInfo * function)
Helper to parse argument names out of a prototype string.
Detailed Description
A generator that will dump all export structures contained in an engine DLL to an XML file which may then be used by wrapper generators to create a language-specific binding for the engine API.
Using XML as an intermediary format allows the generators to use all of the export structures without actually having to access them directly in the DLL as native entities.
Public Defines
PRIMTYPE(tp) ( < tp >() == type ) \ { \ tp val = < tp >( defaultArgs, offset ); \ value = ( val ); \ }
Public Variables
const char * sExportFilterList []
Public Functions
DefineEngineFunction(exportEngineAPIToXML , SimXMLDocument * , () , "Create a XML document containing a dump of the entire exported engine <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">API.\n\n</a>" "@return A <a href="/coding/class/classsimxmldocument/">SimXMLDocument</a> containing a dump of the engine's export information or <a href="/coding/file/typesx86unix_8h/#typesx86unix_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a> <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the operation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed.\n\n</a>" "@ingroup Console" )
exportFunction(const EngineFunctionInfo * function, SimXMLDocument * xml)
exportScope(const EngineExportScope * scope, SimXMLDocument * xml, bool addNode)
exportType(const EngineTypeInfo * type, SimXMLDocument * xml)
getArgValue(const EngineFunctionDefaultArguments * defaultArgs, U32 offset)
getDefaultArgumentValue(const EngineFunctionInfo * function, const EngineTypeInfo * type, U32 offset)
getDocString(const EngineExport * exportInfo)
getTypeName(const EngineTypeInfo * type)
isExportFiltered(const EngineExport * exportInfo)
parseFunctionArgumentNames(const EngineFunctionInfo * function)
Helper to parse argument names out of a prototype string.
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "console/engineExports.h" 25#include "console/engineAPI.h" 26#include "console/engineTypes.h" 27#include "console/engineFunctions.h" 28#include "console/SimXMLDocument.h" 29 30 31/// @file 32/// A generator that will dump all export structures contained in an engine 33/// DLL to an XML file which may then be used by wrapper generators to create a 34/// language-specific binding for the engine API. Using XML as an intermediary 35/// format allows the generators to use all of the export structures without 36/// actually having to access them directly in the DLL as native entities. 37 38 39static void exportScope( const EngineExportScope* scope, SimXMLDocument* xml, bool addNode = false ); 40 41 42static String getTypeName( const EngineTypeInfo* type ) 43{ 44 if( !type ) 45 { 46 static String sVoid( "void" ); 47 return sVoid; 48 } 49 50 return type->getFullyQualifiedExportName(); 51} 52 53static const char* getDocString( const EngineExport* exportInfo ) 54{ 55 if( !exportInfo->getDocString() ) 56 return ""; 57 58 return exportInfo->getDocString(); 59} 60 61template< typename T > 62inline T getArgValue( const EngineFunctionDefaultArguments* defaultArgs, U32 offset ) 63{ 64 return *reinterpret_cast< const T* >( defaultArgs->getArgs() + offset ); 65} 66 67 68// List of exports that we want filtered out. This will only be needed as long 69// as the console system is still around. 70static const char* sExportFilterList[] = 71{ 72 "Console", // Console namespace 73}; 74 75static bool isExportFiltered( const EngineExport* exportInfo ) 76{ 77 String qualifiedName = exportInfo->getFullyQualifiedExportName(); 78 79 for( U32 i = 0; i < ( sizeof( sExportFilterList ) / sizeof( sExportFilterList[ 0 ] ) ); ++ i ) 80 if( qualifiedName.compare( sExportFilterList[ i ] ) == 0 ) 81 return true; 82 83 return false; 84} 85 86//============================================================================= 87// Functions. 88//============================================================================= 89// MARK: ---- Functions ---- 90 91//----------------------------------------------------------------------------- 92 93/// Helper to parse argument names out of a prototype string. 94static Vector< String> parseFunctionArgumentNames( const EngineFunctionInfo* function ) 95{ 96 Vector< String> argNames; 97 98 const char* prototype = function->getPrototypeString(); 99 if( !prototype ) 100 return argNames; 101 102 const U32 prototypeLength = dStrlen( prototype ); 103 const char* prototypeEnd = &prototype[ prototypeLength ]; 104 const char* ptr = prototypeEnd - 1; 105 106 // Search for right parenthesis. 107 while( ptr >= prototype && *ptr != ')' ) 108 ptr --; 109 110 if( ptr < prototype ) 111 return argNames; 112 ptr --; 113 114 while( ptr >= prototype && *ptr != '(' ) 115 { 116 // Skip back over spaces. 117 118 while( ptr >= prototype && dIsspace( *ptr ) ) 119 ptr --; 120 if( ptr < prototype ) 121 return argNames; 122 123 // Parse out name. 124 125 const char* end = ptr + 1; 126 while( ptr > prototype && dIsalnum( *ptr ) ) 127 ptr --; 128 const char* start = ptr + 1; 129 130 // Skip back over spaces. 131 132 while( ptr >= prototype && dIsspace( *ptr ) ) 133 ptr --; 134 135 // If we're sure we don't have just a type name without an 136 // argument name, copy out the argument name name. 137 138 if( ptr >= prototype && *ptr != ',' && *ptr != '(' && end > start ) 139 argNames.push_front( String( start, end - start ) ); 140 else 141 argNames.push_front( "" ); 142 143 // Skip back to comma or opening parenthesis. 144 145 U32 parenNestingCount = 0; 146 while( ptr >= prototype ) 147 { 148 if( *ptr == ')' ) 149 parenNestingCount ++; 150 else if( *ptr == '(' ) 151 parenNestingCount --; 152 else if( *ptr == ',' && parenNestingCount == 0 ) 153 { 154 ptr --; 155 break; 156 } 157 else if( *ptr == '(' && parenNestingCount == 0 ) 158 break; 159 160 ptr --; 161 } 162 } 163 164 // Add 'this' parameter if this is a method. 165 166 if( dStrncmp( prototype, "virtual ", sizeof( "virtual " ) - 1 ) == 0 ) 167 argNames.push_front( "this" ); 168 169 return argNames; 170} 171 172//----------------------------------------------------------------------------- 173 174static String getDefaultArgumentValue( const EngineFunctionInfo* function, const EngineTypeInfo* type, U32 offset ) 175{ 176 String value; 177 const EngineFunctionDefaultArguments* defaultArgs = function->getDefaultArguments(); 178 179 switch( type->getTypeKind() ) 180 { 181 case EngineTypeKindPrimitive: 182 { 183 #define PRIMTYPE( tp ) \ 184 if( TYPE< tp >() == type ) \ 185 { \ 186 tp val = getArgValue< tp >( defaultArgs, offset ); \ 187 value = String::ToString( val ); \ 188 } 189 190 PRIMTYPE( bool ); 191 PRIMTYPE( S8 ); 192 PRIMTYPE( U8 ); 193 PRIMTYPE( S32 ); 194 PRIMTYPE( U32 ); 195 PRIMTYPE( F32 ); 196 PRIMTYPE( F64 ); 197 198 //TODO: for now we store string literals in ASCII; needs to be sorted out 199 if( TYPE< const char*>() == type ) 200 { 201 const char* val = getArgValue< const char* >( defaultArgs, offset ); 202 value = val; 203 } 204 205 #undef PRIMTYPE 206 break; 207 } 208 209 case EngineTypeKindEnum: 210 { 211 S32 val = getArgValue< S32 >( defaultArgs, offset ); 212 AssertFatal( type->getEnumTable(), "engineXMLExport - Enum type without table!" ); 213 214 const EngineEnumTable& table = *( type->getEnumTable() ); 215 const U32 numValues = table.getNumValues(); 216 217 for( U32 i = 0; i < numValues; ++ i ) 218 if( table[ i ].getInt() == val ) 219 { 220 value = table[ i ].getName(); 221 break; 222 } 223 224 break; 225 } 226 227 case EngineTypeKindBitfield: 228 { 229 S32 val = getArgValue< S32 >( defaultArgs, offset ); 230 AssertFatal( type->getEnumTable(), "engineXMLExport - Bitfield type without table!" ); 231 232 const EngineEnumTable& table = *( type->getEnumTable() ); 233 const U32 numValues = table.getNumValues(); 234 235 bool isFirst = true; 236 for( U32 i = 0; i < numValues; ++ i ) 237 if( table[ i ].getInt() & val ) 238 { 239 if( !isFirst ) 240 value += '|'; 241 242 value = table[ i ].getName(); 243 isFirst = false; 244 } 245 246 break; 247 } 248 249 case EngineTypeKindStruct: 250 { 251 //TODO: struct type default argument values 252 break; 253 } 254 255 case EngineTypeKindClass: 256 case EngineTypeKindFunction: 257 { 258 // For these two kinds, we support "null" as the only valid 259 // default value. 260 261 const void* ptr = getArgValue< const void* >( defaultArgs, offset ); 262 if( !ptr ) 263 value = "null"; 264 break; 265 } 266 267 default: 268 break; 269 } 270 271 return value; 272} 273 274//----------------------------------------------------------------------------- 275 276static void exportFunction( const EngineFunctionInfo* function, SimXMLDocument* xml ) 277{ 278 if( isExportFiltered( function ) ) 279 return; 280 281 xml->pushNewElement( "EngineFunction" ); 282 283 xml->setAttribute( "name", function->getExportName() ); 284 xml->setAttribute( "returnType", getTypeName( function->getReturnType() ) ); 285 xml->setAttribute( "symbol", function->getBindingName() ); 286 xml->setAttribute( "isCallback", function->isCallout() ? "1" : "0" ); 287 xml->setAttribute( "isVariadic", function->getFunctionType()->isVariadic() ? "1" : "0" ); 288 xml->setAttribute( "docs", getDocString( function ) ); 289 290 xml->pushNewElement( "arguments" ); 291 292 const U32 numArguments = function->getNumArguments(); 293 const U32 numDefaultArguments = ( function->getDefaultArguments() ? function->getDefaultArguments()->mNumDefaultArgs : 0 ); 294 const U32 firstDefaultArg = numArguments - numDefaultArguments; 295 296 Vector< String> argumentNames = parseFunctionArgumentNames( function ); 297 const U32 numArgumentNames = argumentNames.size(); 298 299 // Accumulated offset in function argument frame vector. 300 U32 argFrameOffset = 0; 301 302 for( U32 i = 0; i < numArguments; ++ i ) 303 { 304 xml->pushNewElement( "EngineFunctionArgument" ); 305 const EngineTypeInfo* type = function->getArgumentType( i ); 306 AssertFatal( type != NULL, "exportFunction - Argument cannot have type void!" ); 307 308 String argName; 309 if( i < numArgumentNames ) 310 argName = argumentNames[ i ]; 311 312 xml->setAttribute( "name", argName ); 313 xml->setAttribute( "type", getTypeName( type ) ); 314 315 if( i >= firstDefaultArg ) 316 { 317 String defaultValue = getDefaultArgumentValue( function, type, argFrameOffset ); 318 xml->setAttribute( "defaultValue", defaultValue ); 319 } 320 321 xml->popElement(); 322 323 if( type->getTypeKind() == EngineTypeKindStruct ) 324 argFrameOffset += type->getInstanceSize(); 325 else 326 argFrameOffset += type->getValueSize(); 327 328 #ifdef _PACK_BUG_WORKAROUNDS 329 if( argFrameOffset % 4 > 0 ) 330 argFrameOffset += 4 - ( argFrameOffset % 4 ); 331 #endif 332 } 333 334 xml->popElement(); 335 336 xml->popElement(); 337} 338 339 340//============================================================================= 341// Types. 342//============================================================================= 343// MARK: ---- Types ---- 344 345//----------------------------------------------------------------------------- 346 347static void exportType( const EngineTypeInfo* type, SimXMLDocument* xml ) 348{ 349 // Don't export anonymous types. 350 if( !type->getTypeName()[ 0 ] ) 351 return; 352 353 if( isExportFiltered( type ) ) 354 return; 355 356 const char* nodeName = NULL; 357 switch( type->getTypeKind() ) 358 { 359 case EngineTypeKindPrimitive: 360 nodeName = "EnginePrimitiveType"; 361 break; 362 363 case EngineTypeKindEnum: 364 nodeName = "EngineEnumType"; 365 break; 366 367 case EngineTypeKindBitfield: 368 nodeName = "EngineBitfieldType"; 369 break; 370 371 case EngineTypeKindStruct: 372 nodeName = "EngineStructType"; 373 break; 374 375 case EngineTypeKindClass: 376 nodeName = "EngineClassType"; 377 break; 378 379 default: 380 return; 381 } 382 383 xml->pushNewElement( nodeName ); 384 385 xml->setAttribute( "name", type->getTypeName() ); 386 xml->setAttribute( "size", String::ToString( type->getInstanceSize() ) ); 387 xml->setAttribute( "isAbstract", type->isAbstract() ? "1" : "0" ); 388 xml->setAttribute( "isInstantiable", type->isInstantiable() ? "1" : "0" ); 389 xml->setAttribute( "isDisposable", type->isDisposable() ? "1" : "0" ); 390 xml->setAttribute( "isSingleton", type->isSingleton() ? "1" : "0" ); 391 xml->setAttribute( "docs", getDocString( type ) ); 392 393 if( type->getSuperType() ) 394 xml->setAttribute( "superType", getTypeName( type->getSuperType() ) ); 395 396 if( type->getEnumTable() ) 397 { 398 xml->pushNewElement( "enums" ); 399 400 const EngineEnumTable& table = *( type->getEnumTable() ); 401 const U32 numValues = table.getNumValues(); 402 403 for( U32 i = 0; i < numValues; ++ i ) 404 { 405 xml->pushNewElement( "EngineEnum" ); 406 407 xml->setAttribute( "name", table[ i ].getName() ); 408 xml->setAttribute( "value", String::ToString( table[ i ].getInt() ) ); 409 xml->setAttribute( "docs", table[ i ].getDocString() ? table[ i ].getDocString() : "" ); 410 411 xml->popElement(); 412 } 413 414 xml->popElement(); 415 } 416 else if( type->getFieldTable() ) 417 { 418 xml->pushNewElement( "fields" ); 419 420 const EngineFieldTable& table = *( type->getFieldTable() ); 421 const U32 numFields = table.getNumFields(); 422 423 for( U32 i = 0; i < numFields; ++ i ) 424 { 425 const EngineFieldTable::Field& field = table[ i ]; 426 427 xml->pushNewElement( "EngineField" ); 428 429 xml->setAttribute( "name", field.getName() ); 430 xml->setAttribute( "type", getTypeName( field.getType() ) ); 431 xml->setAttribute( "offset", String::ToString( field.getOffset() ) ); 432 xml->setAttribute( "indexedSize", String::ToString( field.getNumElements() ) ); 433 xml->setAttribute( "docs", field.getDocString() ? field.getDocString() : "" ); 434 435 xml->popElement(); 436 } 437 438 xml->popElement(); 439 } 440 else if( type->getPropertyTable() ) 441 { 442 xml->pushNewElement( "properties" ); 443 444 const EnginePropertyTable& table = *( type->getPropertyTable() ); 445 const U32 numProperties = table.getNumProperties(); 446 U32 groupNestingDepth = 0; 447 448 for( U32 i = 0; i < numProperties; ++ i ) 449 { 450 const EnginePropertyTable::Property& property = table[ i ]; 451 452 if( property.isGroupBegin() ) 453 { 454 groupNestingDepth ++; 455 xml->pushNewElement( "EnginePropertyGroup" ); 456 457 xml->setAttribute( "name", property.getName() ); 458 xml->setAttribute( "indexedSize", String::ToString( property.getNumElements() ) ); 459 xml->setAttribute( "docs", property.getDocString() ? property.getDocString() : "" ); 460 461 xml->pushNewElement( "properties" ); 462 } 463 else if( property.isGroupEnd() ) 464 { 465 groupNestingDepth --; 466 xml->popElement(); 467 xml->popElement(); 468 } 469 else 470 { 471 xml->pushNewElement( "EngineProperty" ); 472 473 xml->setAttribute( "name", property.getName() ); 474 xml->setAttribute( "indexedSize", String::ToString( property.getNumElements() ) ); 475 xml->setAttribute( "isConstant", property.isConstant() ? "1" : "0" ); 476 xml->setAttribute( "isTransient", property.isTransient() ? "1" : "0" ); 477 xml->setAttribute( "isVisible", property.hideInInspectors() ? "0" : "1" ); 478 xml->setAttribute( "docs", property.getDocString() ? property.getDocString() : "" ); 479 480 xml->popElement(); 481 } 482 } 483 484 AssertFatal( !groupNestingDepth, "exportType - Property group nesting mismatch!" ); 485 xml->popElement(); 486 } 487 exportScope( type, xml ); 488 489 xml->popElement(); 490} 491 492 493//============================================================================= 494// Scopes. 495//============================================================================= 496// MARK: ---- Scopes ---- 497 498//----------------------------------------------------------------------------- 499 500static void exportScope( const EngineExportScope* scope, SimXMLDocument* xml, bool addNode ) 501{ 502 if( addNode ) 503 { 504 if( isExportFiltered( scope ) ) 505 return; 506 507 xml->pushNewElement( "EngineExportScope" ); 508 509 xml->setAttribute( "name", scope->getExportName() ); 510 xml->setAttribute( "docs", getDocString( scope ) ); 511 } 512 513 // Dump all contained exports. 514 515 xml->pushNewElement( "exports" ); 516 517 for( const EngineExport* exportInfo = scope->getExports(); exportInfo != NULL; exportInfo = exportInfo->getNextExport() ) 518 { 519 switch( exportInfo->getExportKind() ) 520 { 521 case EngineExportKindScope: 522 exportScope( static_cast< const EngineExportScope* >( exportInfo ), xml, true ); 523 break; 524 525 case EngineExportKindFunction: 526 exportFunction( static_cast< const EngineFunctionInfo* >( exportInfo ), xml ); 527 break; 528 529 case EngineExportKindType: 530 exportType( static_cast< const EngineTypeInfo* >( exportInfo ), xml ); 531 break; 532 533 default: 534 break; 535 } 536 } 537 538 xml->popElement(); 539 540 if( addNode ) 541 xml->popElement(); 542} 543 544//----------------------------------------------------------------------------- 545 546DefineEngineFunction( exportEngineAPIToXML, SimXMLDocument*, (),, 547 "Create a XML document containing a dump of the entire exported engine API.\n\n" 548 "@return A SimXMLDocument containing a dump of the engine's export information or NULL if the operation failed.\n\n" 549 "@ingroup Console" ) 550{ 551 SimXMLDocument* xml = new SimXMLDocument; 552 xml->registerObject(); 553 Sim::getRootGroup()->addObject( xml ); 554 xml->addHeader(); 555 556 exportScope( EngineExportScope::getGlobalScope(), xml, true ); 557 558 return xml; 559} 560
