├── Cflat.cpp ├── Cflat.h ├── CflatConfig.h ├── CflatGlobal.h ├── CflatGlobalConfig.h ├── CflatHelper.h ├── CflatMacros.h ├── Internal ├── CflatErrorMessages.inl ├── CflatExpressions.inl ├── CflatGlobalFunctions.inl └── CflatStatements.inl ├── README.md ├── UnrealModule ├── Cflat.Build.cs ├── CflatUnrealAid.h ├── Private │ ├── CflatDebugAdapter.cpp │ ├── CflatModule.cpp │ └── CflatModuleAutoRegister.inl └── Public │ ├── CflatDebugAdapter.h │ └── CflatModule.h └── tests └── tests.cpp /Cflat.h: -------------------------------------------------------------------------------- 1 | 2 | /////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Cflat v0.80 5 | // Embeddable lightweight scripting language with C++ syntax 6 | // 7 | // Copyright (c) 2019-2025 Arturo Cepeda Pérez and contributors 8 | // 9 | // --------------------------------------------------------------------------- 10 | // 11 | // This software is provided 'as-is', without any express or implied 12 | // warranty. In no event will the authors be held liable for any damages 13 | // arising from the use of this software. 14 | // 15 | // Permission is granted to anyone to use this software for any purpose, 16 | // including commercial applications, and to alter it and redistribute it 17 | // freely, subject to the following restrictions: 18 | // 19 | // 1. The origin of this software must not be misrepresented; you must not 20 | // claim that you wrote the original software. If you use this software 21 | // in a product, an acknowledgment in the product documentation would be 22 | // appreciated but is not required. 23 | // 24 | // 2. Altered source versions must be plainly marked as such, and must not be 25 | // misrepresented as being the original software. 26 | // 27 | // 3. This notice may not be removed or altered from any source distribution. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #pragma once 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "CflatConfig.h" 43 | #include "CflatMacros.h" 44 | 45 | #define CflatMalloc Cflat::Memory::malloc() 46 | #define CflatFree Cflat::Memory::free() 47 | 48 | #define CflatHasFlag(pBitMask, pFlag) ((pBitMask & (int)pFlag) > 0) 49 | #define CflatSetFlag(pBitMask, pFlag) (pBitMask |= (int)pFlag) 50 | #define CflatResetFlag(pBitMask, pFlag) (pBitMask &= ~((int)pFlag)) 51 | 52 | #define CflatInvokeCtor(pClassName, pPtr) new (pPtr) pClassName 53 | #define CflatInvokeDtor(pClassName, pPtr) (pPtr)->~pClassName() 54 | 55 | #define CflatSTLVector(T) std::vector> 56 | #define CflatSTLDeque(T) std::deque> 57 | #define CflatSTLSet(T) std::set, Cflat::Memory::STLAllocator> 58 | #define CflatSTLMap(T, U) std::map, Cflat::Memory::STLAllocator>> 59 | #define CflatSTLString std::basic_string, Cflat::Memory::STLAllocator> 60 | 61 | #define CflatArgsVector(T) Cflat::Memory::StackVector 62 | 63 | #if !defined (CflatAPI) 64 | # define CflatAPI 65 | #endif 66 | 67 | namespace Cflat 68 | { 69 | typedef uint32_t Hash; 70 | 71 | class CflatAPI Memory 72 | { 73 | public: 74 | typedef void* (*mallocFunction)(size_t pSize); 75 | typedef void (*freeFunction)(void* pPtr); 76 | 77 | private: 78 | static mallocFunction smmalloc; 79 | static freeFunction smfree; 80 | 81 | public: 82 | static void setFunctions(mallocFunction pmalloc, freeFunction pfree); 83 | 84 | static mallocFunction malloc(); 85 | static freeFunction free(); 86 | 87 | template 88 | class STLAllocator 89 | { 90 | public: 91 | typedef T value_type; 92 | typedef T* pointer; 93 | typedef const T* const_pointer; 94 | typedef T& reference; 95 | typedef const T& const_reference; 96 | typedef std::size_t size_type; 97 | typedef std::ptrdiff_t difference_type; 98 | 99 | template 100 | struct rebind { typedef STLAllocator other; }; 101 | 102 | pointer address(reference pRef) const { return &pRef; } 103 | const_pointer address(const_reference pRef) const { return &pRef; } 104 | 105 | STLAllocator() {} 106 | STLAllocator(const STLAllocator&) {} 107 | template 108 | STLAllocator(const STLAllocator&) {} 109 | ~STLAllocator() {} 110 | 111 | size_type max_size() const { return SIZE_MAX / sizeof(T); } 112 | 113 | pointer allocate(size_type pNum, const void* = nullptr) 114 | { 115 | return (pointer)CflatMalloc(pNum * sizeof(T)); 116 | } 117 | void construct(pointer pPtr, const T& pValue) 118 | { 119 | CflatInvokeCtor(T, pPtr)(pValue); 120 | } 121 | void destroy(pointer pPtr) 122 | { 123 | CflatInvokeDtor(T, pPtr); 124 | } 125 | void deallocate(pointer pPtr, size_type) 126 | { 127 | CflatFree(pPtr); 128 | } 129 | }; 130 | 131 | template 132 | class StackVector 133 | { 134 | public: 135 | typedef T* iterator; 136 | typedef const T* const_iterator; 137 | 138 | private: 139 | iterator mFirst; 140 | iterator mEnd; 141 | size_t mSize; 142 | T mData[Capacity]; 143 | 144 | void destroy(T* pElement) 145 | { 146 | CflatInvokeDtor(T, pElement); 147 | memset(pElement, 0, sizeof(T)); 148 | } 149 | 150 | public: 151 | StackVector() 152 | : mFirst(&mData[0]) 153 | , mEnd(mFirst) 154 | , mSize(0u) 155 | { 156 | } 157 | 158 | inline T& at(size_t pIndex) 159 | { 160 | CflatAssert(pIndex < mSize); 161 | return mData[pIndex]; 162 | } 163 | inline const T& at(size_t pIndex) const 164 | { 165 | CflatAssert(pIndex < mSize); 166 | return mData[pIndex]; 167 | } 168 | inline T& operator[](size_t pIndex) 169 | { 170 | CflatAssert(pIndex < mSize); 171 | return mData[pIndex]; 172 | } 173 | inline const T& operator[](size_t pIndex) const 174 | { 175 | CflatAssert(pIndex < mSize); 176 | return mData[pIndex]; 177 | } 178 | T& front() 179 | { 180 | CflatAssert(mSize > 0u); 181 | return mData[0u]; 182 | } 183 | const T& front() const 184 | { 185 | CflatAssert(mSize > 0u); 186 | return mData[0u]; 187 | } 188 | T& back() 189 | { 190 | CflatAssert(mSize > 0u); 191 | return mData[mSize - 1u]; 192 | } 193 | const T& back() const 194 | { 195 | CflatAssert(mSize > 0u); 196 | return mData[mSize - 1u]; 197 | } 198 | T* data() noexcept 199 | { 200 | return mSize > 0u ? &mData[0] : nullptr; 201 | } 202 | const T* data() const noexcept 203 | { 204 | return mSize > 0u ? &mData[0] : nullptr; 205 | } 206 | 207 | inline iterator begin() 208 | { 209 | return mFirst; 210 | } 211 | inline const_iterator begin() const 212 | { 213 | return mFirst; 214 | } 215 | inline iterator end() 216 | { 217 | return mEnd; 218 | } 219 | inline const_iterator end() const 220 | { 221 | return mEnd; 222 | } 223 | 224 | inline bool empty() const 225 | { 226 | return mSize == 0u; 227 | } 228 | inline size_t size() const 229 | { 230 | return mSize; 231 | } 232 | inline size_t capacity() const 233 | { 234 | return Capacity; 235 | } 236 | 237 | void clear() 238 | { 239 | mSize = 0u; 240 | mEnd = mFirst; 241 | } 242 | void push_back(const T& pElement) 243 | { 244 | CflatAssert(mSize < Capacity); 245 | mData[mSize++] = pElement; 246 | mEnd++; 247 | } 248 | void emplace_back() 249 | { 250 | CflatAssert(mSize < Capacity); 251 | mData[mSize++] = T(); 252 | mEnd++; 253 | } 254 | void pop_back() 255 | { 256 | CflatAssert(mSize > 0u); 257 | destroy(&mData[mSize - 1u]); 258 | mSize--; 259 | mEnd--; 260 | } 261 | void resize(size_t pSize) 262 | { 263 | CflatAssert(pSize <= Capacity); 264 | for(size_t i = pSize; i < mSize; i++) 265 | { 266 | destroy(&mData[i]); 267 | } 268 | mSize = pSize; 269 | mEnd = &mData[mSize]; 270 | } 271 | 272 | iterator insert(const_iterator pIterator, const T& pElement) 273 | { 274 | CflatAssert(pIterator >= mFirst && pIterator <= mEnd); 275 | const size_t insertionIndex = pIterator - mFirst; 276 | memmove(mFirst + insertionIndex + 1, mFirst + insertionIndex, (mEnd - pIterator) * sizeof(T)); 277 | mData[insertionIndex] = pElement; 278 | mSize++; 279 | mEnd++; 280 | return mFirst + insertionIndex; 281 | } 282 | iterator erase(const_iterator pIterator) 283 | { 284 | CflatAssert(pIterator >= mFirst && pIterator <= mEnd); 285 | destroy(pIterator); 286 | const size_t deletionIndex = pIterator - mFirst; 287 | memmove(mFirst + deletionIndex, mFirst + deletionIndex + 1, (mEnd - pIterator - 1) * sizeof(T)); 288 | mSize--; 289 | mEnd--; 290 | return mFirst + deletionIndex; 291 | } 292 | 293 | inline bool operator==(const StackVector& pOther) const 294 | { 295 | if(mSize != pOther.mSize) 296 | { 297 | return false; 298 | } 299 | for(size_t i = 0u; i < mSize; i++) 300 | { 301 | if(mData[i] != pOther[i]) 302 | { 303 | return false; 304 | } 305 | } 306 | return true; 307 | } 308 | inline bool operator==(const CflatSTLVector(T)& pSTLVector) const 309 | { 310 | if(mSize != pSTLVector.size()) 311 | { 312 | return false; 313 | } 314 | for(size_t i = 0u; i < mSize; i++) 315 | { 316 | if(mData[i] != pSTLVector[i]) 317 | { 318 | return false; 319 | } 320 | } 321 | return true; 322 | } 323 | }; 324 | 325 | template 326 | struct StackPool 327 | { 328 | char mMemory[Size]; 329 | char* mPointer; 330 | 331 | StackPool() 332 | : mPointer(mMemory) 333 | { 334 | } 335 | 336 | void reset() 337 | { 338 | mPointer = mMemory; 339 | } 340 | 341 | const char* push(size_t pSize) 342 | { 343 | CflatAssert((mPointer - mMemory + pSize) < Size); 344 | 345 | const char* dataPtr = mPointer; 346 | mPointer += pSize; 347 | 348 | return dataPtr; 349 | } 350 | const char* push(const char* pData, size_t pSize) 351 | { 352 | CflatAssert((mPointer - mMemory + pSize) < Size); 353 | memcpy(mPointer, pData, pSize); 354 | 355 | const char* dataPtr = mPointer; 356 | mPointer += pSize; 357 | 358 | return dataPtr; 359 | } 360 | void pop(size_t pSize) 361 | { 362 | mPointer -= pSize; 363 | CflatAssert(mPointer >= mMemory); 364 | } 365 | }; 366 | 367 | template 368 | struct StringsRegistry 369 | { 370 | char mMemory[Size]; 371 | char* mPointer; 372 | 373 | typedef CflatSTLMap(Hash, const char*) Registry; 374 | Registry mRegistry; 375 | 376 | StringsRegistry() 377 | : mPointer(mMemory + 1) 378 | { 379 | mMemory[0] = '\0'; 380 | mRegistry[0u] = mMemory; 381 | } 382 | 383 | const char* registerString(Hash pHash, const char* pString) 384 | { 385 | Registry::const_iterator it = mRegistry.find(pHash); 386 | 387 | if(it != mRegistry.end()) 388 | { 389 | return it->second; 390 | } 391 | 392 | char* ptr = mPointer; 393 | mRegistry[pHash] = ptr; 394 | 395 | const size_t stringLength = strlen(pString); 396 | CflatAssert((mPointer + stringLength) < (mMemory + Size)); 397 | 398 | memcpy(ptr, pString, stringLength); 399 | ptr[stringLength] = '\0'; 400 | 401 | mPointer += stringLength + 1; 402 | 403 | return ptr; 404 | } 405 | const char* retrieveString(Hash pHash) 406 | { 407 | Registry::const_iterator it = mRegistry.find(pHash); 408 | 409 | if(it != mRegistry.end()) 410 | { 411 | return it->second; 412 | } 413 | 414 | return mMemory; 415 | } 416 | }; 417 | 418 | template 419 | struct WideStringsRegistry 420 | { 421 | wchar_t mMemory[Size]; 422 | wchar_t* mPointer; 423 | 424 | typedef CflatSTLMap(Hash, const wchar_t*) Registry; 425 | Registry mRegistry; 426 | 427 | WideStringsRegistry() 428 | : mPointer(mMemory + 1) 429 | { 430 | mMemory[0] = L'\0'; 431 | mRegistry[0u] = mMemory; 432 | } 433 | 434 | const wchar_t* registerString(Hash pHash, const char* pString) 435 | { 436 | Registry::const_iterator it = mRegistry.find(pHash); 437 | 438 | if(it != mRegistry.end()) 439 | { 440 | return it->second; 441 | } 442 | 443 | wchar_t* ptr = mPointer; 444 | mRegistry[pHash] = ptr; 445 | 446 | const size_t wStrSize = mbstowcs(nullptr, pString, 0); 447 | const size_t availableSize = Size - 1 - (mPointer - mMemory); 448 | 449 | CflatAssert(wStrSize < availableSize - 1); 450 | 451 | mbstowcs(ptr, pString, availableSize); 452 | ptr[wStrSize] = L'\0'; 453 | 454 | mPointer += wStrSize + 1; 455 | 456 | return ptr; 457 | } 458 | const wchar_t* retrieveString(Hash pHash) 459 | { 460 | Registry::const_iterator it = mRegistry.find(pHash); 461 | 462 | if(it != mRegistry.end()) 463 | { 464 | return it->second; 465 | } 466 | 467 | return mMemory; 468 | } 469 | }; 470 | }; 471 | 472 | template 473 | bool operator==(const Memory::STLAllocator&, const Memory::STLAllocator&) { return true; } 474 | template 475 | bool operator!=(const Memory::STLAllocator&, const Memory::STLAllocator&) { return false; } 476 | 477 | template 478 | bool operator==(const CflatSTLVector(T)& pSTLVector, const CflatArgsVector(T)& pArgsVector) 479 | { 480 | return pArgsVector == pSTLVector; 481 | } 482 | 483 | 484 | enum class TypeCategory : uint8_t 485 | { 486 | BuiltIn, 487 | Enum, 488 | EnumClass, 489 | StructOrClass 490 | }; 491 | 492 | enum class TypeUsageFlags : uint8_t 493 | { 494 | Const = 1 << 0, 495 | ConstPointer = 1 << 1, 496 | Reference = 1 << 2, 497 | Array = 1 << 3 498 | }; 499 | 500 | 501 | CflatAPI Hash hash(const char* pString); 502 | 503 | 504 | struct Program; 505 | class Namespace; 506 | 507 | 508 | struct CflatAPI Identifier 509 | { 510 | typedef Memory::StringsRegistry NamesRegistry; 511 | static NamesRegistry* smNames; 512 | 513 | static NamesRegistry* getNamesRegistry(); 514 | static void releaseNamesRegistry(); 515 | 516 | const char* mName; 517 | uint32_t mNameLength; 518 | Hash mHash; 519 | 520 | Identifier(); 521 | Identifier(const char* pName); 522 | 523 | const char* findFirstSeparator() const; 524 | const char* findLastSeparator() const; 525 | 526 | bool operator==(const Identifier& pOther) const; 527 | bool operator!=(const Identifier& pOther) const; 528 | }; 529 | 530 | struct CflatAPI Type 531 | { 532 | Namespace* mNamespace; 533 | Type* mParent; 534 | Identifier mIdentifier; 535 | size_t mSize; 536 | TypeCategory mCategory; 537 | 538 | virtual ~Type(); 539 | 540 | protected: 541 | Type(Namespace* pNamespace, const Identifier& pIdentifier); 542 | 543 | public: 544 | virtual Hash getHash() const; 545 | 546 | bool isVoid() const; 547 | bool isDecimal() const; 548 | bool isInteger() const; 549 | 550 | bool compatibleWith(const Type& pOther) const; 551 | }; 552 | 553 | struct CflatAPI TypeUsage 554 | { 555 | static const CflatArgsVector(TypeUsage)& kEmptyList(); 556 | 557 | Type* mType; 558 | uint16_t mArraySize; 559 | uint8_t mPointerLevel; 560 | uint8_t mFlags; 561 | 562 | TypeUsage(); 563 | 564 | size_t getSize() const; 565 | 566 | bool isPointer() const; 567 | bool isConst() const; 568 | bool isConstPointer() const; 569 | bool isReference() const; 570 | bool isArray() const; 571 | 572 | bool compatibleWith(const TypeUsage& pOther) const; 573 | 574 | bool operator==(const TypeUsage& pOther) const; 575 | bool operator!=(const TypeUsage& pOther) const; 576 | }; 577 | 578 | struct CflatAPI TypeAlias 579 | { 580 | Identifier mIdentifier; 581 | TypeUsage mTypeUsage; 582 | uint32_t mScopeLevel; 583 | 584 | TypeAlias(); 585 | TypeAlias(const Identifier& pIdentifier, const TypeUsage& pTypeUsage); 586 | }; 587 | 588 | struct CflatAPI Member 589 | { 590 | Identifier mIdentifier; 591 | TypeUsage mTypeUsage; 592 | uint16_t mOffset; 593 | 594 | Member(const Identifier& pIdentifier); 595 | }; 596 | 597 | 598 | enum class ValueBufferType : uint8_t 599 | { 600 | Uninitialized, // uninitialized 601 | Stack, // owned, allocated on the stack 602 | Heap, // owned, allocated on the heap 603 | External // not owned 604 | }; 605 | 606 | enum class ValueInitializationHint : uint8_t 607 | { 608 | None, // no hint 609 | Stack // to be allocated on the stack 610 | }; 611 | 612 | typedef Memory::StackPool EnvironmentStack; 613 | 614 | struct CflatAPI Value 615 | { 616 | static const CflatArgsVector(Value)& kEmptyList(); 617 | 618 | TypeUsage mTypeUsage; 619 | ValueBufferType mValueBufferType; 620 | ValueInitializationHint mValueInitializationHint; 621 | char* mValueBuffer; 622 | EnvironmentStack* mStack; 623 | 624 | Value(); 625 | Value(const Value& pOther); 626 | ~Value(); 627 | 628 | void reset(); 629 | 630 | void initOnStack(const TypeUsage& pTypeUsage, EnvironmentStack* pStack); 631 | void initOnHeap(const TypeUsage& pTypeUsage); 632 | void initExternal(const TypeUsage& pTypeUsage); 633 | 634 | void set(const void* pDataSource); 635 | void assign(const void* pDataSource); 636 | 637 | Value& operator=(const Value& pOther); 638 | }; 639 | 640 | struct CflatAPI UsingDirective 641 | { 642 | Namespace* mNamespace; 643 | uint32_t mBlockLevel; 644 | 645 | UsingDirective(Namespace* pNamespace); 646 | }; 647 | 648 | enum class FunctionFlags : uint16_t 649 | { 650 | Static = 1 << 0, 651 | Variadic = 1 << 1 652 | }; 653 | 654 | struct CflatAPI Function 655 | { 656 | Namespace* mNamespace; 657 | Identifier mIdentifier; 658 | TypeUsage mReturnTypeUsage; 659 | const Program* mProgram; 660 | uint16_t mLine; 661 | uint16_t mFlags; 662 | CflatSTLVector(TypeUsage) mTemplateTypes; 663 | CflatSTLVector(TypeUsage) mParameters; 664 | CflatSTLVector(Identifier) mParameterIdentifiers; 665 | CflatSTLVector(UsingDirective) mUsingDirectives; 666 | 667 | std::function execute; 668 | 669 | Function(const Identifier& pIdentifier); 670 | ~Function(); 671 | }; 672 | 673 | enum class MethodFlags : uint16_t 674 | { 675 | Const = 1 << 0 676 | }; 677 | 678 | struct CflatAPI Method 679 | { 680 | Identifier mIdentifier; 681 | TypeUsage mReturnTypeUsage; 682 | uint16_t mFlags; 683 | CflatSTLVector(TypeUsage) mTemplateTypes; 684 | CflatSTLVector(TypeUsage) mParameters; 685 | 686 | std::function execute; 687 | 688 | Method(const Identifier& pIdentifier); 689 | ~Method(); 690 | }; 691 | 692 | struct CflatAPI MethodUsage 693 | { 694 | Method* mMethod; 695 | size_t mOffset; 696 | 697 | MethodUsage(); 698 | }; 699 | 700 | enum class InstanceFlags : uint16_t 701 | { 702 | EnumValue = 1 << 0 703 | }; 704 | 705 | struct CflatAPI Instance 706 | { 707 | TypeUsage mTypeUsage; 708 | Identifier mIdentifier; 709 | Value mValue; 710 | uint32_t mScopeLevel; 711 | uint16_t mFlags; 712 | 713 | Instance(); 714 | Instance(const TypeUsage& pTypeUsage, const Identifier& pIdentifier); 715 | }; 716 | 717 | 718 | class CflatAPI TypesHolder 719 | { 720 | private: 721 | typedef CflatSTLMap(Hash, Type*) TypesRegistry; 722 | TypesRegistry mTypes; 723 | 724 | typedef CflatSTLMap(Hash, TypeAlias) TypeAliasesRegistry; 725 | TypeAliasesRegistry mTypeAliases; 726 | 727 | public: 728 | ~TypesHolder(); 729 | 730 | template 731 | T* registerType(const Identifier& pIdentifier, Namespace* pNamespace, Type* pParent) 732 | { 733 | T* type = (T*)CflatMalloc(sizeof(T)); 734 | CflatInvokeCtor(T, type)(pNamespace, pIdentifier); 735 | 736 | const Hash hash = type->getHash(); 737 | CflatAssert(mTypes.find(hash) == mTypes.end()); 738 | 739 | type->mParent = pParent; 740 | mTypes[hash] = type; 741 | 742 | return type; 743 | } 744 | 745 | template 746 | T* registerTemplate(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes, 747 | Namespace* pNamespace, Type* pParent) 748 | { 749 | T* type = (T*)CflatMalloc(sizeof(T)); 750 | CflatInvokeCtor(T, type)(pNamespace, pIdentifier); 751 | 752 | type->mTemplateTypes.resize(pTemplateTypes.size()); 753 | memcpy(&type->mTemplateTypes[0], &pTemplateTypes[0], pTemplateTypes.size() * sizeof(TypeUsage)); 754 | 755 | const Hash hash = type->getHash(); 756 | CflatAssert(mTypes.find(hash) == mTypes.end()); 757 | 758 | type->mParent = pParent; 759 | mTypes[hash] = type; 760 | 761 | return type; 762 | } 763 | 764 | Type* getType(const Identifier& pIdentifier) const; 765 | Type* getType(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes) const; 766 | 767 | void registerTypeAlias(const Identifier& pIdentifier, const TypeUsage& pTypeUsage); 768 | const TypeAlias* getTypeAlias(const Identifier& pIdentifier) const; 769 | 770 | bool deregisterType(Type* pType); 771 | 772 | void getAllTypes(CflatSTLVector(Type*)* pOutTypes) const; 773 | }; 774 | 775 | class CflatAPI FunctionsHolder 776 | { 777 | private: 778 | typedef CflatSTLMap(Hash, CflatSTLVector(Function*)) FunctionsRegistry; 779 | FunctionsRegistry mFunctions; 780 | 781 | public: 782 | ~FunctionsHolder(); 783 | 784 | Function* registerFunction(const Identifier& pIdentifier); 785 | 786 | Function* getFunction(const Identifier& pIdentifier) const; 787 | Function* getFunction(const Identifier& pIdentifier, 788 | const CflatArgsVector(TypeUsage)& pParameterTypes, 789 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 790 | Function* getFunctionPerfectMatch(const Identifier& pIdentifier, 791 | const CflatArgsVector(TypeUsage)& pParameterTypes, 792 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 793 | Function* getFunction(const Identifier& pIdentifier, 794 | const CflatArgsVector(Value)& pArguments, 795 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 796 | 797 | CflatSTLVector(Function*)* getFunctions(const Identifier& pIdentifier) const; 798 | void getAllFunctions(CflatSTLVector(Function*)* pOutFunctions) const; 799 | size_t getFunctionsCount() const; 800 | 801 | bool deregisterFunctions(const Identifier& pIdentifier); 802 | 803 | private: 804 | Function* getFunction(const Identifier& pIdentifier, 805 | const CflatArgsVector(TypeUsage)& pParameterTypes, 806 | const CflatArgsVector(TypeUsage)& pTemplateTypes, 807 | bool pRequirePerfectMatch) const; 808 | }; 809 | 810 | class CflatAPI InstancesHolder 811 | { 812 | private: 813 | CflatSTLDeque(Instance) mInstances; 814 | 815 | public: 816 | InstancesHolder(); 817 | ~InstancesHolder(); 818 | 819 | Instance* setVariable(const TypeUsage& pTypeUsage, const Identifier& pIdentifier, const Value& pValue); 820 | Value* getVariable(const Identifier& pIdentifier) const; 821 | 822 | Instance* registerInstance(const TypeUsage& pTypeUsage, const Identifier& pIdentifier); 823 | Instance* retrieveInstance(const Identifier& pIdentifier) const; 824 | void releaseInstances(uint32_t pScopeLevel, bool pExecuteDestructors); 825 | 826 | void getAllInstances(CflatSTLVector(Instance*)* pOutInstances) const; 827 | }; 828 | 829 | 830 | struct CflatAPI BuiltInType : Type 831 | { 832 | BuiltInType(Namespace* pNamespace, const Identifier& pIdentifier); 833 | }; 834 | 835 | struct CflatAPI Enum : Type 836 | { 837 | InstancesHolder mInstancesHolder; 838 | 839 | Enum(Namespace* pNamespace, const Identifier& pIdentifier); 840 | }; 841 | 842 | struct CflatAPI EnumClass : Type 843 | { 844 | InstancesHolder mInstancesHolder; 845 | 846 | EnumClass(Namespace* pNamespace, const Identifier& pIdentifier); 847 | }; 848 | 849 | struct BaseType 850 | { 851 | Type* mType; 852 | uint16_t mOffset; 853 | }; 854 | 855 | struct CflatAPI Struct : Type 856 | { 857 | static const int8_t kInvalidCachedMethodIndex = -1; 858 | 859 | CflatSTLVector(TypeUsage) mTemplateTypes; 860 | CflatSTLVector(BaseType) mBaseTypes; 861 | CflatSTLVector(Member) mMembers; 862 | CflatSTLVector(Method) mMethods; 863 | 864 | TypesHolder mTypesHolder; 865 | FunctionsHolder mFunctionsHolder; 866 | InstancesHolder mInstancesHolder; 867 | 868 | uint8_t mAlignment; 869 | 870 | int8_t mCachedMethodIndexDefaultConstructor; 871 | int8_t mCachedMethodIndexCopyConstructor; 872 | int8_t mCachedMethodIndexDestructor; 873 | 874 | Struct(Namespace* pNamespace, const Identifier& pIdentifier); 875 | 876 | virtual Hash getHash() const override; 877 | 878 | bool derivedFrom(Type* pBaseType) const; 879 | uint16_t getOffset(Type* pBaseType) const; 880 | 881 | template 882 | T* registerType(const Identifier& pIdentifier) 883 | { 884 | return mTypesHolder.registerType(pIdentifier, mNamespace, this); 885 | } 886 | template 887 | T* registerTemplate(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes) 888 | { 889 | return mTypesHolder.registerTemplate(pIdentifier, pTemplateTypes, mNamespace, this); 890 | } 891 | Type* getType(const Identifier& pIdentifier) const; 892 | Type* getType(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes) const; 893 | 894 | void registerTypeAlias(const Identifier& pIdentifier, const TypeUsage& pTypeUsage); 895 | const TypeAlias* getTypeAlias(const Identifier& pIdentifier) const; 896 | 897 | Function* registerStaticMethod(const Identifier& pIdentifier); 898 | Function* getStaticMethod(const Identifier& pIdentifier) const; 899 | Function* getStaticMethod(const Identifier& pIdentifier, 900 | const CflatArgsVector(TypeUsage)& pParameterTypes, 901 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 902 | Function* getStaticMethod(const Identifier& pIdentifier, 903 | const CflatArgsVector(Value)& pArguments, 904 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 905 | CflatSTLVector(Function*)* getStaticMethods(const Identifier& pIdentifier) const; 906 | 907 | void setStaticMember(const TypeUsage& pTypeUsage, const Identifier& pIdentifier, 908 | const Value& pValue); 909 | Value* getStaticMember(const Identifier& pIdentifier) const; 910 | Instance* getStaticMemberInstance(const Identifier& pIdentifier) const; 911 | 912 | Member* findMember(const Identifier& pIdentifier) const; 913 | 914 | Method* getDefaultConstructor() const; 915 | Method* getCopyConstructor() const; 916 | Method* getDestructor() const; 917 | Method* findConstructor(const CflatArgsVector(TypeUsage)& pParameterTypes) const; 918 | Method* findConstructor(const CflatArgsVector(Value)& pArguments) const; 919 | Method* findMethod(const Identifier& pIdentifier) const; 920 | Method* findMethod(const Identifier& pIdentifier, 921 | const CflatArgsVector(TypeUsage)& pParameterTypes, 922 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 923 | Method* findMethod(const Identifier& pIdentifier, 924 | const CflatArgsVector(Value)& pArguments, 925 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 926 | Function* findStaticMethod(const Identifier& pIdentifier, 927 | const CflatArgsVector(TypeUsage)& pParameterTypes, 928 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 929 | MethodUsage findMethodUsage(const Identifier& pIdentifier, size_t pOffset, 930 | const CflatArgsVector(TypeUsage)& pParameterTypes, 931 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 932 | }; 933 | 934 | struct CflatAPI Class : Struct 935 | { 936 | Class(Namespace* pNamespace, const Identifier& pIdentifier); 937 | }; 938 | 939 | 940 | class CflatAPI TypeHelper 941 | { 942 | public: 943 | enum class Compatibility 944 | { 945 | PerfectMatch, 946 | ImplicitCastableInteger, 947 | ImplicitCastableIntegerFloat, 948 | ImplicitCastableFloat, 949 | ImplicitCastableInheritance, 950 | ImplicitConstructable, 951 | Incompatible 952 | }; 953 | 954 | static void registerCustomPerfectMatch(Type* pTypeA, Type* pTypeB); 955 | static void releaseCustomPerfectMatchesRegistry(); 956 | 957 | static Compatibility getCompatibility(const TypeUsage& pParameter, 958 | const TypeUsage& pArgument, uint32_t pRecursionDepth = 0u); 959 | static size_t calculateAlignment(const TypeUsage& pTypeUsage); 960 | 961 | private: 962 | static bool isCustomPerfectMatch(Type* pTypeA, Type* pTypeB); 963 | 964 | typedef CflatSTLMap(Hash, CflatSTLSet(Hash)) CustomPerfectMatchesRegistry; 965 | static CustomPerfectMatchesRegistry smCustomPerfectMatchesRegistry; 966 | }; 967 | 968 | 969 | enum class TokenType 970 | { 971 | Punctuation, 972 | Number, 973 | Character, 974 | WideCharacter, 975 | String, 976 | WideString, 977 | Keyword, 978 | Identifier, 979 | Operator 980 | }; 981 | 982 | struct Token 983 | { 984 | TokenType mType; 985 | char* mStart; 986 | size_t mLength; 987 | uint16_t mLine; 988 | }; 989 | 990 | 991 | class CflatAPI Tokenizer 992 | { 993 | public: 994 | static void tokenize(const char* pCode, CflatSTLVector(Token)& pTokens); 995 | 996 | static bool isValidIdentifierCharacter(char pCharacter); 997 | static bool isValidIdentifierBeginningCharacter(char pCharacter); 998 | }; 999 | 1000 | 1001 | struct Expression; 1002 | 1003 | struct Statement; 1004 | struct StatementBlock; 1005 | struct StatementUsingDirective; 1006 | struct StatementTypeDefinition; 1007 | struct StatementNamespaceDeclaration; 1008 | struct StatementVariableDeclaration; 1009 | struct StatementFunctionDeclaration; 1010 | struct StatementStructDeclaration; 1011 | struct StatementIf; 1012 | struct StatementSwitch; 1013 | struct StatementWhile; 1014 | struct StatementDoWhile; 1015 | struct StatementFor; 1016 | struct StatementForRangeBased; 1017 | struct StatementBreak; 1018 | struct StatementContinue; 1019 | struct StatementReturn; 1020 | 1021 | class Environment; 1022 | 1023 | struct CflatAPI Program 1024 | { 1025 | Identifier mIdentifier; 1026 | CflatSTLString mCode; 1027 | CflatSTLVector(Statement*) mStatements; 1028 | 1029 | ~Program(); 1030 | }; 1031 | 1032 | 1033 | class CflatAPI Namespace 1034 | { 1035 | private: 1036 | Identifier mIdentifier; 1037 | Identifier mFullIdentifier; 1038 | 1039 | Namespace* mParent; 1040 | Environment* mEnvironment; 1041 | 1042 | typedef CflatSTLMap(Hash, Namespace*) NamespacesRegistry; 1043 | NamespacesRegistry mNamespaces; 1044 | 1045 | TypesHolder mTypesHolder; 1046 | FunctionsHolder mFunctionsHolder; 1047 | InstancesHolder mInstancesHolder; 1048 | 1049 | Namespace* getChild(Hash pNameHash) const; 1050 | 1051 | public: 1052 | Namespace(const Identifier& pName, Namespace* pParent, Environment* pEnvironment); 1053 | ~Namespace(); 1054 | 1055 | const Identifier& getIdentifier() const; 1056 | const Identifier& getFullIdentifier() const; 1057 | Namespace* getParent() const; 1058 | 1059 | Namespace* getNamespace(const Identifier& pName) const; 1060 | Namespace* requestNamespace(const Identifier& pName); 1061 | 1062 | template 1063 | T* registerType(const Identifier& pIdentifier) 1064 | { 1065 | const char* lastSeparator = pIdentifier.findLastSeparator(); 1066 | 1067 | if(lastSeparator) 1068 | { 1069 | char buffer[kDefaultLocalStringBufferSize]; 1070 | const size_t nsIdentifierLength = lastSeparator - pIdentifier.mName; 1071 | strncpy(buffer, pIdentifier.mName, nsIdentifierLength); 1072 | buffer[nsIdentifierLength] = '\0'; 1073 | const Identifier nsIdentifier(buffer); 1074 | const Identifier typeIdentifier(lastSeparator + 2); 1075 | return requestNamespace(nsIdentifier)->registerType(typeIdentifier); 1076 | } 1077 | 1078 | return mTypesHolder.registerType(pIdentifier, this, nullptr); 1079 | } 1080 | template 1081 | T* registerTemplate(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes) 1082 | { 1083 | const char* lastSeparator = pIdentifier.findLastSeparator(); 1084 | 1085 | if(lastSeparator) 1086 | { 1087 | char buffer[kDefaultLocalStringBufferSize]; 1088 | const size_t nsIdentifierLength = lastSeparator - pIdentifier.mName; 1089 | strncpy(buffer, pIdentifier.mName, nsIdentifierLength); 1090 | buffer[nsIdentifierLength] = '\0'; 1091 | const Identifier nsIdentifier(buffer); 1092 | const Identifier typeIdentifier(lastSeparator + 2); 1093 | return requestNamespace(nsIdentifier)->registerTemplate(typeIdentifier, pTemplateTypes); 1094 | } 1095 | 1096 | return mTypesHolder.registerTemplate(pIdentifier, pTemplateTypes, this, nullptr); 1097 | } 1098 | Type* getType(const Identifier& pIdentifier, bool pExtendSearchToParent = false) const; 1099 | Type* getType(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes, 1100 | bool pExtendSearchToParent = false) const; 1101 | 1102 | void registerTypeAlias(const Identifier& pIdentifier, const TypeUsage& pTypeUsage); 1103 | const TypeAlias* getTypeAlias(const Identifier& pIdentifier) const; 1104 | 1105 | bool deregisterType(Type* pType); 1106 | 1107 | TypeUsage getTypeUsage(const char* pTypeName) const; 1108 | 1109 | Function* registerFunction(const Identifier& pIdentifier); 1110 | Function* getFunction(const Identifier& pIdentifier, bool pExtendSearchToParent = false) const; 1111 | Function* getFunction(const Identifier& pIdentifier, 1112 | const CflatArgsVector(TypeUsage)& pParameterTypes, 1113 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList(), 1114 | bool pExtendSearchToParent = false) const; 1115 | Function* getFunctionPerfectMatch(const Identifier& pIdentifier, 1116 | const CflatArgsVector(TypeUsage)& pParameterTypes, 1117 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList(), 1118 | bool pExtendSearchToParent = false) const; 1119 | Function* getFunction(const Identifier& pIdentifier, 1120 | const CflatArgsVector(Value)& pArguments, 1121 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList(), 1122 | bool pExtendSearchToParent = false) const; 1123 | CflatSTLVector(Function*)* getFunctions(const Identifier& pIdentifier, 1124 | bool pExtendSearchToParent = false) const; 1125 | bool deregisterFunctions(const Identifier& pIdentifier); 1126 | 1127 | Instance* setVariable(const TypeUsage& pTypeUsage, const Identifier& pIdentifier, const Value& pValue); 1128 | Value* getVariable(const Identifier& pIdentifier, bool pExtendSearchToParent = false) const; 1129 | 1130 | Instance* registerInstance(const TypeUsage& pTypeUsage, const Identifier& pIdentifier); 1131 | Instance* retrieveInstance(const Identifier& pIdentifier, bool pExtendSearchToParent = false) const; 1132 | void releaseInstances(uint32_t pScopeLevel, bool pExecuteDestructors); 1133 | 1134 | void getAllNamespaces(CflatSTLVector(Namespace*)* pOutNamespaces, bool pRecursively = false) const; 1135 | void getAllTypes(CflatSTLVector(Type*)* pOutTypes, bool pRecursively = false) const; 1136 | void getAllInstances(CflatSTLVector(Instance*)* pOutInstances, bool pRecursively = false) const; 1137 | void getAllFunctions(CflatSTLVector(Function*)* pOutFunctions, bool pRecursively = false) const; 1138 | }; 1139 | 1140 | 1141 | enum class MacroArgumentType : uint8_t 1142 | { 1143 | Default, 1144 | Stringize, 1145 | TokenPaste 1146 | }; 1147 | 1148 | struct Macro 1149 | { 1150 | uint8_t mParametersCount; 1151 | CflatSTLString mName; 1152 | CflatSTLVector(CflatSTLString) mBody; 1153 | }; 1154 | 1155 | enum class ContextType 1156 | { 1157 | Parsing, 1158 | Execution 1159 | }; 1160 | 1161 | struct CflatAPI Context 1162 | { 1163 | public: 1164 | ContextType mType; 1165 | 1166 | Program* mProgram; 1167 | uint32_t mBlockLevel; 1168 | uint32_t mScopeLevel; 1169 | CflatSTLVector(Namespace*) mNamespaceStack; 1170 | CflatSTLVector(UsingDirective) mUsingDirectives; 1171 | CflatSTLVector(TypeAlias) mTypeAliases; 1172 | CflatSTLString mStringBuffer; 1173 | InstancesHolder mLocalInstancesHolder; 1174 | EnvironmentStack mStack; 1175 | 1176 | protected: 1177 | Context(ContextType pType, Namespace* pGlobalNamespace); 1178 | }; 1179 | 1180 | struct CflatAPI ParsingContext : Context 1181 | { 1182 | CflatSTLString mPreprocessedCode; 1183 | CflatSTLVector(Token) mTokens; 1184 | size_t mTokenIndex; 1185 | 1186 | struct RegisteredInstance 1187 | { 1188 | Identifier mIdentifier; 1189 | Namespace* mNamespace; 1190 | uint32_t mScopeLevel; 1191 | }; 1192 | CflatSTLVector(RegisteredInstance) mRegisteredInstances; 1193 | 1194 | Function* mCurrentFunction; 1195 | 1196 | struct LocalNamespace 1197 | { 1198 | Namespace* mNamespace; 1199 | uint32_t mScopeLevel; 1200 | }; 1201 | CflatSTLVector(LocalNamespace) mLocalNamespaceStack; 1202 | uint32_t mLocalNamespaceGlobalIndex; 1203 | 1204 | ParsingContext(Namespace* pGlobalNamespace); 1205 | }; 1206 | 1207 | enum class CastType 1208 | { 1209 | CStyle, 1210 | Static, 1211 | Dynamic, 1212 | Reinterpret 1213 | }; 1214 | 1215 | struct CflatAPI CallStackEntry 1216 | { 1217 | const Program* mProgram; 1218 | const Function* mFunction; 1219 | uint16_t mLine; 1220 | 1221 | CallStackEntry(const Program* pProgram, const Function* pFunction = nullptr); 1222 | }; 1223 | 1224 | typedef CflatSTLVector(CallStackEntry) CallStack; 1225 | 1226 | enum class JumpStatement : uint16_t 1227 | { 1228 | None, 1229 | Break, 1230 | Continue, 1231 | Return 1232 | }; 1233 | 1234 | struct CflatAPI ExecutionContext : Context 1235 | { 1236 | JumpStatement mJumpStatement; 1237 | Memory::StackVector mReturnValues; 1238 | CallStack mCallStack; 1239 | 1240 | ExecutionContext(Namespace* pGlobalNamespace); 1241 | }; 1242 | 1243 | 1244 | class CflatAPI Environment 1245 | { 1246 | public: 1247 | enum class Settings : uint32_t 1248 | { 1249 | DisallowStaticPointers = 1 << 0, 1250 | DisallowDynamicCast = 1 << 1 1251 | }; 1252 | 1253 | private: 1254 | enum class PreprocessorError : uint8_t 1255 | { 1256 | InvalidPreprocessorDirective, 1257 | InvalidMacroArgumentCount, 1258 | 1259 | Count 1260 | }; 1261 | enum class CompileError : uint8_t 1262 | { 1263 | UnexpectedSymbol, 1264 | Expected, 1265 | UndefinedType, 1266 | UndefinedVariable, 1267 | UndefinedFunction, 1268 | VariableRedefinition, 1269 | ParameterRedefinition, 1270 | UninitializedReference, 1271 | ArrayInitializationExpected, 1272 | NonHomogeneousTypeList, 1273 | NoDefaultConstructor, 1274 | NoCopyConstructor, 1275 | InvalidNumericValue, 1276 | InvalidType, 1277 | InvalidAssignment, 1278 | InvalidMemberAccessOperatorPtr, 1279 | InvalidMemberAccessOperatorNonPtr, 1280 | InvalidOperator, 1281 | InvalidConditionalExpression, 1282 | InvalidCast, 1283 | InvalidEscapeSequence, 1284 | MissingMember, 1285 | MissingStaticMember, 1286 | MissingConstructor, 1287 | MissingMethod, 1288 | MissingStaticMethod, 1289 | NonIntegerValue, 1290 | UnknownNamespace, 1291 | CannotModifyConstExpression, 1292 | CannotCallNonConstMethod, 1293 | MissingReturnStatement, 1294 | MissingReturnExpression, 1295 | IncompatibleReturnExpressionType, 1296 | StaticPointersNotAllowed, 1297 | DynamicCastNotAllowed, 1298 | 1299 | Count 1300 | }; 1301 | enum class RuntimeError : uint8_t 1302 | { 1303 | NullPointerAccess, 1304 | InvalidArrayIndex, 1305 | DivisionByZero, 1306 | MissingFunctionImplementation, 1307 | 1308 | Count 1309 | }; 1310 | 1311 | uint32_t mSettings; 1312 | 1313 | CflatSTLVector(Macro) mMacros; 1314 | 1315 | typedef CflatSTLMap(Hash, Program*) ProgramsRegistry; 1316 | ProgramsRegistry mPrograms; 1317 | 1318 | typedef Memory::StringsRegistry LiteralStringsPool; 1319 | typedef Memory::WideStringsRegistry LiteralWideStringsPool; 1320 | LiteralStringsPool mLiteralStringsPool; 1321 | LiteralWideStringsPool mLiteralWideStringsPool; 1322 | 1323 | typedef CflatSTLMap(uint64_t, Value) StaticValuesRegistry; 1324 | StaticValuesRegistry mLocalStaticValues; 1325 | 1326 | ExecutionContext mExecutionContext; 1327 | CflatSTLString mErrorMessage; 1328 | 1329 | Namespace mGlobalNamespace; 1330 | 1331 | Type* mTypeAuto; 1332 | Type* mTypeVoid; 1333 | Type* mTypeInt32; 1334 | Type* mTypeUInt32; 1335 | Type* mTypeFloat; 1336 | Type* mTypeDouble; 1337 | 1338 | TypeUsage mTypeUsageVoid; 1339 | TypeUsage mTypeUsageSizeT; 1340 | TypeUsage mTypeUsageBool; 1341 | TypeUsage mTypeUsageCString; 1342 | TypeUsage mTypeUsageWideString; 1343 | TypeUsage mTypeUsageCharacter; 1344 | TypeUsage mTypeUsageWideCharacter; 1345 | TypeUsage mTypeUsageVoidPtr; 1346 | 1347 | typedef void (*ExecutionHook)(Environment* pEnvironment, const CallStack& pCallStack); 1348 | ExecutionHook mExecutionHook; 1349 | 1350 | void registerBuiltInTypes(); 1351 | 1352 | TypeUsage parseTypeUsage(ParsingContext& pContext, size_t pTokenLastIndex) const; 1353 | 1354 | void throwPreprocessorError(ParsingContext& pContext, PreprocessorError pError, 1355 | size_t pCursor, const char* pArg = ""); 1356 | void throwCompileError(ParsingContext& pContext, CompileError pError, 1357 | const char* pArg1 = "", const char* pArg2 = ""); 1358 | void throwCompileErrorUnexpectedSymbol(ParsingContext& pContext); 1359 | 1360 | void preprocess(ParsingContext& pContext, const char* pCode); 1361 | void tokenize(ParsingContext& pContext) const; 1362 | void parse(ParsingContext& pContext); 1363 | 1364 | Expression* parseExpression(ParsingContext& pContext, size_t pTokenLastIndex, 1365 | bool pNullAllowed = false); 1366 | Expression* parseExpressionSingleToken(ParsingContext& pContext); 1367 | Expression* parseExpressionMultipleTokens(ParsingContext& pContext, 1368 | size_t pTokenFirstIndex, size_t pTokenLastIndex); 1369 | 1370 | Expression* parseExpressionLiteralString(ParsingContext& pContext, TokenType pTokenType); 1371 | Expression* parseExpressionLiteralCharacter(ParsingContext& pContext, TokenType pTokenType); 1372 | Expression* parseExpressionUnaryOperator(ParsingContext& pContext, Expression* pOperand, 1373 | const char* pOperator, bool pPostOperator); 1374 | Expression* parseExpressionCast(ParsingContext& pContext, CastType pCastType, 1375 | size_t pTokenLastIndex); 1376 | Expression* parseExpressionFunctionCall(ParsingContext& pContext, 1377 | const Identifier& pFunctionIdentifier); 1378 | Expression* parseExpressionMethodCall(ParsingContext& pContext, Expression* pMemberAccess); 1379 | Expression* parseExpressionObjectConstruction(ParsingContext& pContext, Type* pType); 1380 | 1381 | size_t findClosureTokenIndex(ParsingContext& pContext, char pOpeningChar, char pClosureChar, 1382 | size_t pTokenIndexLimit = 0u) const; 1383 | size_t findOpeningTokenIndex(ParsingContext& pContext, char pOpeningChar, char pClosureChar, 1384 | size_t pClosureIndex) const; 1385 | size_t findSeparationTokenIndex(ParsingContext& pContext, char pSeparationChar, 1386 | size_t pClosureIndex) const; 1387 | 1388 | uint8_t getBinaryOperatorPrecedence(ParsingContext& pContext, size_t pTokenIndex) const; 1389 | bool isTemplate(ParsingContext& pContext, size_t pOpeningTokenIndex, size_t pClosureTokenIndex) const; 1390 | bool isTemplate(ParsingContext& pContext, size_t pTokenLastIndex) const; 1391 | 1392 | bool isCastAllowed(CastType pCastType, const TypeUsage& pFrom, const TypeUsage& pTo) const; 1393 | bool isMethodCallAllowed(Method* pMethod, const TypeUsage& pOwnerTypeUsage) const; 1394 | 1395 | Statement* parseStatement(ParsingContext& pContext); 1396 | StatementBlock* parseStatementBlock(ParsingContext& pContext, 1397 | bool pAlterScope, bool pAllowInGlobalScope); 1398 | StatementUsingDirective* parseStatementUsingDirective(ParsingContext& pContext); 1399 | StatementTypeDefinition* parseStatementTypeDefinition(ParsingContext& pContext); 1400 | StatementNamespaceDeclaration* parseStatementNamespaceDeclaration(ParsingContext& pContext); 1401 | StatementVariableDeclaration* parseStatementVariableDeclaration(ParsingContext& pContext, 1402 | TypeUsage& pTypeUsage, const Identifier& pIdentifier, bool pStatic); 1403 | StatementFunctionDeclaration* parseStatementFunctionDeclaration(ParsingContext& pContext, 1404 | const TypeUsage& pReturnType, bool pStatic); 1405 | StatementStructDeclaration* parseStatementStructDeclaration(ParsingContext& pContext); 1406 | StatementIf* parseStatementIf(ParsingContext& pContext); 1407 | StatementSwitch* parseStatementSwitch(ParsingContext& pContext); 1408 | StatementWhile* parseStatementWhile(ParsingContext& pContext); 1409 | StatementDoWhile* parseStatementDoWhile(ParsingContext& pContext); 1410 | Statement* parseStatementFor(ParsingContext& pContext); 1411 | StatementFor* parseStatementForRegular(ParsingContext& pContext, 1412 | size_t pInitializationClosureTokenIndex); 1413 | StatementForRangeBased* parseStatementForRangeBased(ParsingContext& pContext, 1414 | size_t pVariableClosureTokenIndex); 1415 | StatementBreak* parseStatementBreak(ParsingContext& pContext); 1416 | StatementContinue* parseStatementContinue(ParsingContext& pContext); 1417 | StatementReturn* parseStatementReturn(ParsingContext& pContext); 1418 | 1419 | bool parseFunctionCallArguments(ParsingContext& pContext, CflatSTLVector(Expression*)* pArguments, 1420 | CflatSTLVector(TypeUsage)* pTemplateTypes = nullptr); 1421 | 1422 | const TypeUsage& getTypeUsage(Expression* pExpression) const; 1423 | 1424 | Type* findType(const Context& pContext, const Identifier& pIdentifier, 1425 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 1426 | Function* findFunction(const Context& pContext, const Identifier& pIdentifier, 1427 | const CflatArgsVector(TypeUsage)& pParameterTypes, 1428 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 1429 | Function* findFunction(const Context& pContext, const Identifier& pIdentifier, 1430 | const CflatArgsVector(Value)& pArguments, 1431 | const CflatArgsVector(TypeUsage)& pTemplateTypes = TypeUsage::kEmptyList()) const; 1432 | 1433 | void registerTypeAlias( 1434 | Context& pContext, const Identifier& pIdentifier, const TypeUsage& pTypeUsage); 1435 | 1436 | Instance* registerInstance(Context& pContext, const TypeUsage& pTypeUsage, 1437 | const Identifier& pIdentifier); 1438 | Instance* retrieveInstance(Context& pContext, const Identifier& pIdentifier) const; 1439 | 1440 | void incrementBlockLevel(Context& pContext); 1441 | void decrementBlockLevel(Context& pContext); 1442 | void incrementScopeLevel(Context& pContext); 1443 | void decrementScopeLevel(Context& pContext); 1444 | 1445 | void throwRuntimeError(ExecutionContext& pContext, RuntimeError pError, const char* pArg = ""); 1446 | 1447 | void evaluateExpression(ExecutionContext& pContext, Expression* pExpression, Value* pOutValue); 1448 | void getInstanceDataValue(ExecutionContext& pContext, Expression* pExpression, Value* pOutValue); 1449 | void getAddressOfValue(ExecutionContext& pContext, const Value& pInstanceDataValue, Value* pOutValue); 1450 | void getArgumentValues(ExecutionContext& pContext, const CflatSTLVector(TypeUsage)& pParameters, 1451 | const CflatSTLVector(Expression*)& pExpressions, CflatArgsVector(Value)& pValues); 1452 | void prepareArgumentsForFunctionCall(ExecutionContext& pContext, 1453 | const CflatSTLVector(TypeUsage)& pParameters, const CflatArgsVector(Value)& pOriginalValues, 1454 | CflatArgsVector(Value)& pPreparedValues); 1455 | void applyUnaryOperator(ExecutionContext& pContext, const Value& pOperand, const char* pOperator, 1456 | Value* pOutValue); 1457 | void applyBinaryOperator(ExecutionContext& pContext, const Value& pLeft, const Value& pRight, 1458 | const char* pOperator, Value* pOutValue); 1459 | void performAssignment(ExecutionContext& pContext, const Value& pValue, 1460 | const char* pOperator, Value* pInstanceDataValue); 1461 | void performStaticCast(ExecutionContext& pContext, const Value& pValueToCast, 1462 | const TypeUsage& pTargetTypeUsage, Value* pOutValue); 1463 | void performIntegerCast(ExecutionContext& pContext, const Value& pValueToCast, 1464 | const TypeUsage& pTargetTypeUsage, Value* pOutValue); 1465 | void performIntegerFloatCast(ExecutionContext& pContext, const Value& pValueToCast, 1466 | const TypeUsage& pTargetTypeUsage, Value* pOutValue); 1467 | void performFloatCast(ExecutionContext& pContext, const Value& pValueToCast, 1468 | const TypeUsage& pTargetTypeUsage, Value* pOutValue); 1469 | void performInheritanceCast(ExecutionContext& pContext, const Value& pValueToCast, 1470 | const TypeUsage& pTargetTypeUsage, Value* pOutValue); 1471 | void performImplicitConstruction(ExecutionContext& pContext, Type* pCtorType, 1472 | const Value& pCtorArg, Value* pObjectValue); 1473 | void assignValue(ExecutionContext& pContext, const Value& pSource, Value* pTarget, 1474 | bool pDeclaration); 1475 | void assignValue(ExecutionContext& pContext, const Value& pSource, Value* pTarget, 1476 | bool pDeclaration, TypeHelper::Compatibility pCompatibility); 1477 | 1478 | static void assertValueInitialization(ExecutionContext& pContext, const TypeUsage& pTypeUsage, 1479 | Value* pOutValue); 1480 | 1481 | static int64_t getValueAsInteger(const Value& pValue); 1482 | static double getValueAsDecimal(const Value& pValue); 1483 | static void setValueAsInteger(int64_t pInteger, Value* pOutValue); 1484 | static void setValueAsDecimal(double pDecimal, Value* pOutValue); 1485 | 1486 | static void getTypeFullName(Type* pType, CflatSTLString* pOutString); 1487 | 1488 | static bool containsReturnStatement(Statement* pStatement); 1489 | 1490 | void initArgumentsForFunctionCall(Function* pFunction, CflatArgsVector(Value)& pArgs); 1491 | bool tryCallDefaultConstructor(ExecutionContext& pContext, Instance* pInstance, Type* pType, size_t pOffset = 0); 1492 | 1493 | void execute(ExecutionContext& pContext, const Program& pProgram); 1494 | void execute(ExecutionContext& pContext, Statement* pStatement); 1495 | 1496 | public: 1497 | static void assignReturnValueFromFunctionCall(const TypeUsage& pReturnTypeUsage, 1498 | const void* pReturnValue, Value* pOutValue); 1499 | 1500 | public: 1501 | Environment(); 1502 | ~Environment(); 1503 | 1504 | void addSetting(Settings pSetting); 1505 | void removeSetting(Settings pSetting); 1506 | 1507 | void defineMacro(const char* pDefinition, const char* pBody); 1508 | 1509 | Namespace* getGlobalNamespace(); 1510 | Namespace* getNamespace(const Identifier& pIdentifier); 1511 | Namespace* requestNamespace(const Identifier& pIdentifier); 1512 | 1513 | template 1514 | T* registerType(const Identifier& pIdentifier) 1515 | { 1516 | return mGlobalNamespace.registerType(pIdentifier); 1517 | } 1518 | template 1519 | T* registerTemplate(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes) 1520 | { 1521 | return mGlobalNamespace.registerTemplate(pIdentifier, pTemplateTypes); 1522 | } 1523 | void registerTypeAlias(const Identifier& pIdentifier, const TypeUsage& pTypeUsage); 1524 | Type* getType(const Identifier& pIdentifier) const; 1525 | Type* getType(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pTemplateTypes) const; 1526 | 1527 | TypeUsage getTypeUsage(const char* pTypeName, Namespace* pNamespace = nullptr) const; 1528 | 1529 | Function* registerFunction(const Identifier& pIdentifier); 1530 | Function* getFunction(const Identifier& pIdentifier) const; 1531 | Function* getFunction(const Identifier& pIdentifier, const CflatArgsVector(TypeUsage)& pParameterTypes) const; 1532 | Function* getFunction(const Identifier& pIdentifier, const CflatArgsVector(Value)& pArguments) const; 1533 | CflatSTLVector(Function*)* getFunctions(const Identifier& pIdentifier) const; 1534 | 1535 | Instance* setVariable(const TypeUsage& pTypeUsage, const Identifier& pIdentifier, const Value& pValue); 1536 | Value* getVariable(const Identifier& pIdentifier) const; 1537 | 1538 | Instance* registerInstance(const TypeUsage& pTypeUsage, const Identifier& pIdentifier); 1539 | Instance* retrieveInstance(const Identifier& pIdentifier) const; 1540 | 1541 | void voidFunctionCall(Function* pFunction); 1542 | template 1543 | void voidFunctionCall(Function* pFunction, Args... pArgs) 1544 | { 1545 | CflatAssert(pFunction); 1546 | 1547 | constexpr size_t argsCount = sizeof...(Args); 1548 | CflatAssert(argsCount == pFunction->mParameters.size()); 1549 | 1550 | mErrorMessage.clear(); 1551 | 1552 | Cflat::Value returnValue; 1553 | 1554 | CflatArgsVector(Value) args; 1555 | initArgumentsForFunctionCall(pFunction, args); 1556 | 1557 | const void* argData[argsCount] = { pArgs... }; 1558 | 1559 | for(size_t i = 0u; i < argsCount; i++) 1560 | { 1561 | args[i].set(argData[i]); 1562 | } 1563 | 1564 | pFunction->execute(args, &returnValue); 1565 | 1566 | while(!args.empty()) 1567 | { 1568 | args.pop_back(); 1569 | } 1570 | } 1571 | template 1572 | ReturnType returnFunctionCall(Function* pFunction) 1573 | { 1574 | CflatAssert(pFunction); 1575 | 1576 | mErrorMessage.clear(); 1577 | 1578 | Cflat::Value returnValue; 1579 | returnValue.initOnStack(pFunction->mReturnTypeUsage, &mExecutionContext.mStack); 1580 | 1581 | CflatArgsVector(Value) args; 1582 | 1583 | pFunction->execute(args, &returnValue); 1584 | 1585 | return *(reinterpret_cast(returnValue.mValueBuffer)); 1586 | } 1587 | template 1588 | ReturnType returnFunctionCall(Function* pFunction, Args... pArgs) 1589 | { 1590 | CflatAssert(pFunction); 1591 | 1592 | constexpr size_t argsCount = sizeof...(Args); 1593 | CflatAssert(argsCount == pFunction->mParameters.size()); 1594 | 1595 | mErrorMessage.clear(); 1596 | 1597 | Cflat::Value returnValue; 1598 | returnValue.initOnStack(pFunction->mReturnTypeUsage, &mExecutionContext.mStack); 1599 | 1600 | CflatArgsVector(Value) args; 1601 | initArgumentsForFunctionCall(pFunction, args); 1602 | 1603 | const void* argData[argsCount] = { pArgs... }; 1604 | 1605 | for(size_t i = 0u; i < argsCount; i++) 1606 | { 1607 | args[i].set(argData[i]); 1608 | } 1609 | 1610 | pFunction->execute(args, &returnValue); 1611 | 1612 | while(!args.empty()) 1613 | { 1614 | args.pop_back(); 1615 | } 1616 | 1617 | return *(reinterpret_cast(returnValue.mValueBuffer)); 1618 | } 1619 | 1620 | bool load(const char* pProgramName, const char* pCode); 1621 | bool load(const char* pFilePath); 1622 | 1623 | const char* getErrorMessage(); 1624 | 1625 | void setExecutionHook(ExecutionHook pExecutionHook); 1626 | bool evaluateExpression(const char* pExpression, Value* pOutValue); 1627 | 1628 | void throwCustomRuntimeError(const char* pErrorMessage); 1629 | 1630 | void resetStatics(); 1631 | }; 1632 | } 1633 | -------------------------------------------------------------------------------- /CflatConfig.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/CflatConfig.h -------------------------------------------------------------------------------- /CflatGlobal.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/CflatGlobal.h -------------------------------------------------------------------------------- /CflatGlobalConfig.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/CflatGlobalConfig.h -------------------------------------------------------------------------------- /CflatHelper.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/CflatHelper.h -------------------------------------------------------------------------------- /Internal/CflatErrorMessages.inl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/Internal/CflatErrorMessages.inl -------------------------------------------------------------------------------- /Internal/CflatExpressions.inl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/Internal/CflatExpressions.inl -------------------------------------------------------------------------------- /Internal/CflatGlobalFunctions.inl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/Internal/CflatGlobalFunctions.inl -------------------------------------------------------------------------------- /Internal/CflatStatements.inl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/Internal/CflatStatements.inl -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cflat 2 | ### Embeddable lightweight scripting language with C++ syntax 3 | 4 | Embeddable scripting languages provide the possibility of implementing and tweaking features during the software development process without the need of recompiling or restarting after each code change. This is particularly useful when working with large codebases which take long to compile. 5 | 6 | Unfortunately, such an advantage usually implies some additional runtime costs, like a slow execution time (at least in comparison with what would be an equivalent compiled version of the same code written in C++) and a big amount of heap allocations, which might be considerable in performance-critical software like videogames. 7 | 8 | Cflat is an embeddable scripting language whose syntax is 100% compatible with C++, what means that all the scripts written for Cflat can be actually compiled in release builds along with the rest of the code. 9 | 10 | **In this case, compile means compile** - the scripts are not compiled into some kind of bytecode, though: they are compiled into machine code for the specific platform you are targeting, just like the rest of the C++ code from the project. 11 | 12 | Both software engineers and other members of the team can benefit from Cflat. Regarding the second group, one might say that C++ is not the best choice for developers who are not software engineers or programmers, and C++ is indeed not as friendly as other scripting languages, but the truth is that high-level C++ code, which is the kind of code you usually write in scripts, does not look that different from other widely used languages like C# or Java(Script). 13 | 14 | Cflat does not intend to be a fully featured C++ interpreter, but rather a lightweight scripting language whose syntax is 100% compatible with C++. This means that: 15 | - Only a (small) subset of features from C++ are available in Cflat 16 | - Everything from Cflat can be compiled with any C++11 compliant compiler 17 | 18 | In case you are looking for a proper C++ interpreter, you might want to take a look at the following alternatives: 19 | - [cling](https://github.com/root-project/cling) (extremely heavy, depends on clang and llvm) 20 | - [Ch](https://www.softintegration.com/) (commercial) 21 | - [CINT](http://www.hanno.jp/gotom/Cint.html) 22 | 23 | Or, if what you need is a way of getting your C++ code hot-reloaded and recompiled, there are also some good options out there: 24 | - [Runtime Compiled C++](https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledCPlusPlus) 25 | - [Live++](https://liveplusplus.tech/) (commercial) 26 | 27 | 28 | ## FAQ 29 | 30 | Is any C++ code compatible with Cflat? 31 | - *No. Cflat supports only a rather small subset of features from the C++11 standard.* 32 | 33 | 34 | Is any Cflat code compatible with C++? 35 | - *Yes. Cflat code can be compiled with any existing C++11 compiler.* 36 | 37 | 38 | Is it in Cflat's roadmap to eventually support all the features from C++? 39 | - *No. The idea is to keep it simple and lightweight. Although it is indeed planned to progressively add features to it and make it more powerful, Cflat will never provide all the features any C++ standard provides.* 40 | 41 | 42 | Is Cflat going to provide any extra features outside from C++ in the future? 43 | - *No. Cflat code shall always be 100% compatible with C++.* 44 | 45 | 46 | Does Cflat require any external dependencies? 47 | - *No. Only standard C++11 features and STL containers are required.* 48 | 49 | 50 | Is Cflat cross-platform? 51 | - *Yes, as cross-platform as any C++ code can be. If there is a C++11 compiler for the platform you are targeting, Cflat can be used on it.* 52 | 53 | 54 | ## Documentation 55 | ### Getting started 56 | 57 | To integrate Cflat in a project, you just have to add the `Cflat.cpp` file to it and make sure that the header files from the Cflat directory are accessible. In order to take advantage of Cflat, you will need a Cflat environment: 58 | 59 | ```cpp 60 | #include "Cflat/Cflat.h" 61 | 62 | // ... 63 | 64 | Cflat::Environment env; 65 | ``` 66 | 67 | You can then register the functions and types you would like to have exposed for scripting. Note that Cflat environments are empty by default, without anything registered apart from the built-in types, so you can decide exactly what you need to expose. The `Cflat.h` header provides you with a bunch of convenience macros to do that: 68 | 69 | ```cpp 70 | { 71 | CflatRegisterFunctionReturnParams1(&env, float, floor, float); 72 | CflatRegisterFunctionReturnParams1(&env, float, sqrtf, float); 73 | CflatRegisterFunctionReturnParams2(&env, float, powf, float, float); 74 | } 75 | ``` 76 | 77 | ```cpp 78 | struct TestStruct 79 | { 80 | int var1; 81 | int var2; 82 | }; 83 | 84 | { 85 | CflatRegisterStruct(&env, TestStruct); 86 | CflatStructAddMember(&env, TestStruct, int, var1); 87 | CflatStructAddMember(&env, TestStruct, int, var2); 88 | } 89 | ``` 90 | 91 | ```cpp 92 | enum TestEnum 93 | { 94 | kFirstValue, 95 | kSecondValue 96 | }; 97 | 98 | { 99 | CflatRegisterEnum(&env, TestEnum); 100 | CflatEnumAddValue(&env, TestEnum, kFirstValue); 101 | CflatEnumAddValue(&env, TestEnum, kSecondValue); 102 | } 103 | ``` 104 | 105 | ```cpp 106 | enum class TestEnum 107 | { 108 | kFirstValue, 109 | kSecondValue 110 | }; 111 | 112 | { 113 | CflatRegisterEnumClass(&env, TestEnum); 114 | CflatEnumClassAddValue(&env, TestEnum, kFirstValue); 115 | CflatEnumClassAddValue(&env, TestEnum, kSecondValue); 116 | } 117 | ``` 118 | 119 | ```cpp 120 | struct Base 121 | { 122 | int baseMember; 123 | }; 124 | struct Derived : Base 125 | { 126 | int derivedMember; 127 | }; 128 | 129 | { 130 | CflatRegisterStruct(&env, Base); 131 | CflatStructAddMember(&env, Base, int, baseMember); 132 | } 133 | { 134 | CflatRegisterStruct(&env, Derived); 135 | CflatStructAddBaseType(&env, Derived, Base); 136 | CflatStructAddMember(&env, Derived, int, derivedMember); 137 | } 138 | ``` 139 | 140 | ```cpp 141 | struct BaseA 142 | { 143 | int baseAMember; 144 | }; 145 | struct BaseB 146 | { 147 | int baseBMember; 148 | }; 149 | struct Derived : BaseA, BaseB 150 | { 151 | int derivedMember; 152 | }; 153 | 154 | { 155 | CflatRegisterStruct(&env, BaseA); 156 | CflatStructAddMember(&env, BaseA, int, baseAMember); 157 | } 158 | { 159 | CflatRegisterStruct(&env, BaseB); 160 | CflatStructAddMember(&env, BaseB, int, baseBMember); 161 | } 162 | { 163 | CflatRegisterStruct(&env, Derived); 164 | CflatStructAddBaseType(&env, Derived, BaseA); 165 | CflatStructAddBaseType(&env, Derived, BaseB); 166 | CflatStructAddMember(&env, Derived, int, derivedMember); 167 | } 168 | ``` 169 | 170 | ```cpp 171 | struct OuterType 172 | { 173 | struct InnerType 174 | { 175 | int value; 176 | }; 177 | enum InnerEnum 178 | { 179 | kInnerEnumValue 180 | }; 181 | }; 182 | 183 | { 184 | CflatRegisterStruct(&env, OuterType); 185 | } 186 | { 187 | CflatRegisterNestedStruct(&env, OuterType, InnerType); 188 | CflatStructAddMember(&env, InnerType, int, value); 189 | } 190 | { 191 | CflatRegisterNestedEnum(&env, OuterType, InnerEnum); 192 | CflatNestedEnumAddValue(&env, OuterType, InnerEnum, kInnerEnumValue); 193 | } 194 | ``` 195 | 196 | ```cpp 197 | struct TestStruct 198 | { 199 | TestStruct(); 200 | ~TestStruct(); 201 | 202 | void method(int pValue); 203 | int constMethod(int pValue) const; 204 | 205 | static void staticMethod(int pValue); 206 | }; 207 | 208 | { 209 | CflatRegisterStruct(&env, TestStruct); 210 | CflatStructAddConstructor(&env, TestStruct); 211 | CflatStructAddDestructor(&env, TestStruct); 212 | CflatStructAddMethodVoidParams1(&env, TestStruct, void, method, int); 213 | CflatStructAddMethodReturnParams1(&env, TestStruct, int, constMethod, int) CflatMethodConst; 214 | CflatStructAddStaticMethodVoidParams1(&env, TestStruct, void, staticMethod, int); 215 | } 216 | ``` 217 | 218 | The first argument for the macros can be both a pointer to the environment, or a pointer to the namespace where the type to register is defined: 219 | 220 | ```cpp 221 | namespace Math 222 | { 223 | struct Vector3 224 | { 225 | float x; 226 | float y; 227 | float z; 228 | }; 229 | } 230 | 231 | { 232 | using namespace Math; 233 | Cflat::Namespace* ns = env.requestNamespace("Math"); 234 | 235 | { 236 | CflatRegisterStruct(ns, Vector3); 237 | CflatStructAddMember(ns, Vector3, float, x); 238 | CflatStructAddMember(ns, Vector3, float, y); 239 | CflatStructAddMember(ns, Vector3, float, z); 240 | } 241 | } 242 | ``` 243 | 244 | Note that bindings to **default constructors** are not generated automatically, but they need to be added explicitly by using the `CflatAddConstructor` macro: 245 | 246 | ```cpp 247 | { 248 | CflatRegisterStruct(ns, Vector3); 249 | CflatAddConstructor(ns, Vector3); 250 | // ... 251 | } 252 | ``` 253 | 254 | If **return by value** is required for registered structs and classes, the copy constructor must be added for them (whether there is a custom implementation or not): 255 | 256 | ```cpp 257 | { 258 | CflatRegisterStruct(ns, Vector3); 259 | CflatAddCopyConstructor(ns, Vector3); 260 | // ... 261 | } 262 | ``` 263 | 264 | **Overloaded methods or functions** must be registered once per overload: 265 | 266 | ```cpp 267 | class TestClass 268 | { 269 | public: 270 | void method(int pValue); 271 | void method(float pValue); 272 | }; 273 | 274 | { 275 | CflatRegisterClass(&env, TestClass); 276 | CflatClassAddMethodVoidParams1(&env, TestClass, void, method, int); 277 | CflatClassAddMethodVoidParams1(&env, TestClass, void, method, float); 278 | } 279 | ``` 280 | 281 | Methods and functions with **default arguments** must be registered once per number of parameters: 282 | 283 | ```cpp 284 | class TestClass 285 | { 286 | public: 287 | void method(int pValue1 = 0, int pValue2 = 0); 288 | }; 289 | 290 | { 291 | CflatRegisterClass(&env, TestClass); 292 | CflatClassAddMethodVoid(&env, TestClass, void, method); 293 | CflatClassAddMethodVoidParams1(&env, TestClass, void, method, int); 294 | CflatClassAddMethodVoidParams2(&env, TestClass, void, method, int, int); 295 | } 296 | ``` 297 | 298 | **Operators** can be registered as regular methods or functions, with the name being `operator` concatenated with the operator itself, without any spaces in between: 299 | 300 | ```cpp 301 | struct TestStruct 302 | { 303 | const TestStruct operator+(int pValue) const; 304 | }; 305 | 306 | { 307 | CflatRegisterStruct(&env, TestStruct); 308 | CflatStructAddMethodReturnParams1(&env, TestStruct, const TestStruct, operator+, int); 309 | } 310 | ``` 311 | 312 | For more complex standard types and global values, you can take advantage of the helpers included in `CflatHelper.h`: 313 | 314 | ```cpp 315 | #include "Cflat/CflatHelper.h" 316 | 317 | // ... 318 | 319 | Cflat::Helper::registerStdString(&env); // std::string 320 | Cflat::Helper::registerStdOut(&env); // std::cout 321 | 322 | CflatRegisterSTLVector(&env, int); // std::vector 323 | CflatRegisterSTLVector(&env, float); // std::vector 324 | 325 | CflatRegisterSTLMap(&env, int, float); // std::map 326 | ``` 327 | 328 | In case you need to register **template structs or classes**, you can take the way the helper registers STL types as a reference. 329 | 330 | 331 | ### Loading scripts into the environment 332 | 333 | It is possible to load scripts into the environment both passing the code as a string and passing the path of the file: 334 | 335 | ```cpp 336 | env.load("test", "const char* str = \"Hello world!\";"); 337 | env.load("./scripts/test.cpp"); 338 | ``` 339 | 340 | 341 | ### Accessing script values and executing script functions 342 | 343 | ```cpp 344 | // 345 | // Cflat script 346 | // 347 | namespace CfTest 348 | { 349 | static const float kTestConst = 42.0f; 350 | 351 | static void voidFunc(int pA, int pB) 352 | { 353 | // ... 354 | } 355 | static int returnFunc(int pA, int pB) 356 | { 357 | // ... 358 | } 359 | } 360 | ``` 361 | 362 | ```cpp 363 | // 364 | // cpp file 365 | // 366 | Cflat::Value* testConstValue = env.getVariable("CfTest::kTestConst"); 367 | const float testConst = CflatValueAs(testConstValue, float); 368 | 369 | const int a = 42; 370 | const int b = 10; 371 | 372 | Cflat::Function* voidFunc = env.getFunction("CfTest::voidFunc"); 373 | env.voidFunctionCall(voidFunc, &a, &b); 374 | 375 | Cflat::Function* returnFunc = env.getFunction("CfTest::returnFunc"); 376 | const int returnValue = env.returnFunctionCall(returnFunc, &a, &b); 377 | ``` 378 | 379 | 380 | ### Switching between interpreter and compiler 381 | 382 | Cflat comes with the `CflatGlobal.h` header file, which provides a convenient way to write code that takes advantage of the Cflat scripting system in development configurations and gets the scripts compiled into machine code in final configurations. The first thing to do is to open the `CflatGlobalConfig.h` header and set both the path where the Cflat includes are, and the path where the scripts are: 383 | 384 | ```cpp 385 | #if defined CFLAT_ENABLED 386 | 387 | // Relative directory where the Cflat headers are located 388 | # define CflatHeadersPath ./Cflat 389 | 390 | #else 391 | 392 | // Relative directory where the scripts are located 393 | # define CflatScriptsPath ./scripts 394 | 395 | #endif 396 | ``` 397 | 398 | Note that the base directory used as the reference for those relative paths must be defined in the list of additional include directories for the compiler. Otherwise, the `#include CflatScript(...)` directives (see below) will not compile, since the macro is resolved using brackets (<...>) instead of quotes ("..."). 399 | 400 | Making use of `CflatGlobal.h` requires that you implement the following functions (it can be in any cpp of the project): 401 | 402 | ```cpp 403 | #if defined CFLAT_ENABLED 404 | namespace CflatGlobal 405 | { 406 | Cflat::Environment gEnv; 407 | std::mutex gMutex; 408 | 409 | Cflat::Environment* getEnvironment() 410 | { 411 | return &gEnv; 412 | } 413 | void lockEnvironment() 414 | { 415 | gMutex.lock(); 416 | } 417 | void unlockEnvironment() 418 | { 419 | gMutex.unlock(); 420 | } 421 | void onError(const char* pErrorMessage) 422 | { 423 | std::cerr << "[Cflat] " << pErrorMessage << std::endl; 424 | } 425 | } 426 | #endif 427 | ``` 428 | 429 | Then you have to make sure that the source files that need to access scripts include `CflatGlobal.h`, whether directly in the source file itself, or through some kind of common header which all source files of the project include: 430 | 431 | ```cpp 432 | #include "Cflat/CflatGlobal.h" 433 | ``` 434 | 435 | In order to define in what configurations the scripts should be interpreted through the Cflat environment, you need to make sure that `CFLAT_ENABLED` is one of the preprocessor definitions in those configurations. In all the configurations where `CFLAT_ENABLED` is not defined, the scripts will be compiled into machine code. 436 | The next step is to include the script or scripts you want to access from the source file: 437 | 438 | ```cpp 439 | #include CflatScript(test.cpp) 440 | ``` 441 | 442 | Once that's done, you can already retrieve values and call functions from the included script. 443 | 444 | ```cpp 445 | // 446 | // Cflat script 447 | // 448 | namespace CfTest 449 | { 450 | static const float kTestConst = 42.0f; 451 | 452 | static int add(int pA, int pB) 453 | { 454 | return pA + pB; 455 | } 456 | } 457 | ``` 458 | 459 | ```cpp 460 | // 461 | // cpp file 462 | // 463 | #include CflatScript(test.cpp) 464 | 465 | // ... 466 | 467 | const float testConst = CflatGet(float, CfTest::kTestConst); 468 | std::cout << “kTestConst: “ << testConst << std::endl; 469 | 470 | const int a = 42; 471 | const int b = 10; 472 | int addTest; 473 | CflatReturnCall(addTest, int, CfTest::add, CflatArg(a), CflatArg(b)); 474 | 475 | std::cout << “addTest: “ << addTest << std::endl; 476 | ``` 477 | 478 | Regarding function calls, note that there are two different macros defined in `CflatGlobal.h`, depending on whether the function to call returns something or not (`CflatReturnCall` and `CflatVoidCall`, respectively), and that you have to use the `CflatArg` macro for each argument. 479 | 480 | 481 | ### Using a custom allocator 482 | 483 | You can define custom functions both for allocating and for releasing dynamic memory as follows: 484 | 485 | ```cpp 486 | Cflat::Memory::malloc = [](size_t pSize) -> void* 487 | { 488 | return myCustomAllocatorMalloc(pSize); 489 | }; 490 | Cflat::Memory::free = [](void* pPtr) 491 | { 492 | myCustomAllocatorFree(pPtr); 493 | }; 494 | ``` 495 | 496 | NOTE - if you use a custom allocator, remember to release the identifier names registry before shutting down the application (this is not required otherwise): 497 | 498 | ```cpp 499 | Cflat::Identifier::releaseNamesRegistry(); 500 | ``` 501 | 502 | 503 | ### Execution hook 504 | 505 | There is the possibility of registering an execution hook, for example to implement script debugging features in your application: 506 | 507 | ```cpp 508 | void executionHook(Cflat::Environment* pEnv, const Cflat::CallStack& pCallStack) 509 | { 510 | // ... 511 | } 512 | 513 | env.setExecutionHook(executionHook); 514 | ``` 515 | 516 | The function is then called right before each statement is executed. The `evaluateExpression` method, provided by the environment, allows you to inspect and modify values. 517 | 518 | 519 | ## Support the project 520 | 521 | I work on this project in my spare time. If you would like to support it, you can [buy me a coffee!](https://ko-fi.com/arturocepeda) 522 | 523 | 524 | ## License 525 | 526 | Cflat is distributed with a *zlib* license, and is free to use for both non-commercial and commercial projects: 527 | 528 | ``` 529 | Copyright (c) 2019-2025 Arturo Cepeda Pérez 530 | 531 | This software is provided 'as-is', without any express or implied 532 | warranty. In no event will the authors be held liable for any damages 533 | arising from the use of this software. 534 | 535 | Permission is granted to anyone to use this software for any purpose, 536 | including commercial applications, and to alter it and redistribute it 537 | freely, subject to the following restrictions: 538 | 539 | 1. The origin of this software must not be misrepresented; you must not 540 | claim that you wrote the original software. If you use this software 541 | in a product, an acknowledgment in the product documentation would be 542 | appreciated but is not required. 543 | 544 | 2. Altered source versions must be plainly marked as such, and must not be 545 | misrepresented as being the original software. 546 | 547 | 3. This notice may not be removed or altered from any source distribution. 548 | ``` 549 | 550 | -------------------------------------------------------------------------------- /UnrealModule/Cflat.Build.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.IO; 3 | using UnrealBuildTool; 4 | 5 | public class Cflat : ModuleRules 6 | { 7 | public Cflat(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | // The criteria to define "CFLAT_ENABLED" can be changed as needed 10 | if(Target.Type == TargetType.Editor) 11 | { 12 | PublicDefinitions.Add("CFLAT_ENABLED"); 13 | } 14 | 15 | if(PublicDefinitions.Contains("CFLAT_ENABLED")) 16 | { 17 | PrivateDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "DirectoryWatcher", "Slate", "SlateCore", "UnrealEd", "Sockets", "Json" }); 18 | 19 | PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); 20 | PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "../..")); 21 | } 22 | else 23 | { 24 | PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "../../../..")); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UnrealModule/Private/CflatDebugAdapter.cpp: -------------------------------------------------------------------------------- 1 | #if defined(CFLAT_ENABLED) 2 | 3 | # include "CflatDebugAdapter.h" 4 | 5 | # include "Sockets.h" 6 | # include "SocketSubsystem.h" 7 | 8 | static const uint8 kMessageEndMarker = 0x04; 9 | 10 | static const double kSleepTime = 0.033; 11 | static const double kSocketWaitTime = 0.5; 12 | static const double kConnectionCheckInterval = 0.5; 13 | 14 | CflatDebugAdapter::CflatDebugAdapter() 15 | : mSocketSubsystem(nullptr), 16 | mListener(nullptr), 17 | mListening(false), 18 | mDebugLog(false), 19 | mListeningAdress("127.0.0.1") 20 | { 21 | } 22 | 23 | CflatDebugAdapter::~CflatDebugAdapter() 24 | { 25 | if (mSocketSubsystem && mSocket) 26 | { 27 | mSocketSubsystem->DestroySocket(mSocket); 28 | mSocket = nullptr; 29 | } 30 | } 31 | 32 | bool CflatDebugAdapter::Start(int32 pPort) 33 | { 34 | if (!FSlateApplication::IsInitialized()) 35 | { 36 | return false; 37 | } 38 | mPort = pPort; 39 | mConnectionLastCheck = FPlatformTime::Seconds(); 40 | mSocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); 41 | mListener = mSocketSubsystem->CreateSocket( 42 | NAME_Stream, TEXT("Cflat Debug Adapater"), false); 43 | mThread = FRunnableThread::Create( 44 | this, TEXT("CflatDebuggerConnection"), 0, TPri_BelowNormal); 45 | 46 | Listen(); 47 | 48 | return true; 49 | } 50 | 51 | void CflatDebugAdapter::AddRequestListener( 52 | FName pRequestName, EventCallback pCallback) 53 | { 54 | mRequestCallbacks.Add(pRequestName, pCallback); 55 | } 56 | 57 | TSharedPtr CflatDebugAdapter::CreateResponse( 58 | const TSharedPtr pRequest, bool pSuccess) 59 | { 60 | TSharedPtr jsonObject = MakeShareable(new FJsonObject); 61 | jsonObject->SetStringField(TEXT("type"), TEXT("response")); 62 | jsonObject->SetBoolField(TEXT("success"), pSuccess); 63 | jsonObject->SetNumberField(TEXT("seq"), 0); 64 | 65 | { 66 | TSharedPtr val = pRequest->TryGetField(TEXT("seq")); 67 | jsonObject->SetField(TEXT("request_seq"), val); 68 | } 69 | { 70 | TSharedPtr val = pRequest->TryGetField(TEXT("command")); 71 | jsonObject->SetField(TEXT("command"), val); 72 | } 73 | 74 | return jsonObject; 75 | } 76 | 77 | TSharedPtr CflatDebugAdapter::CreateEvent(const FString& pName) 78 | { 79 | TSharedPtr jsonObject = MakeShareable(new FJsonObject); 80 | jsonObject->SetStringField(TEXT("type"), TEXT("event")); 81 | jsonObject->SetStringField(TEXT("event"), pName); 82 | 83 | return jsonObject; 84 | } 85 | 86 | void CflatDebugAdapter::SendEvent(const TSharedPtr pEvent) 87 | { 88 | if (!IsConnected()) 89 | { 90 | return; 91 | } 92 | 93 | FString jsonString; 94 | auto writer = 95 | TJsonWriterFactory>::Create( 96 | &jsonString); 97 | FJsonSerializer::Serialize(pEvent.ToSharedRef(), writer); 98 | 99 | SendString(jsonString); 100 | } 101 | 102 | void CflatDebugAdapter::SendResponse(const TSharedPtr pResponse) 103 | { 104 | if (!IsConnected()) 105 | { 106 | return; 107 | } 108 | 109 | FString jsonString; 110 | auto writer = 111 | TJsonWriterFactory>::Create( 112 | &jsonString); 113 | FJsonSerializer::Serialize(pResponse.ToSharedRef(), writer); 114 | 115 | SendString(jsonString); 116 | } 117 | 118 | bool CflatDebugAdapter::Listen() 119 | { 120 | if (!mListening) 121 | { 122 | bool addrValid = true; 123 | bool canBindAll = false; 124 | 125 | TSharedPtr addr = mSocketSubsystem->CreateInternetAddr(); 126 | addr->SetIp(*mListeningAdress, addrValid); 127 | addr->SetPort(mPort); 128 | 129 | if (!addrValid) 130 | { 131 | UE_LOG( 132 | LogTemp, 133 | Error, 134 | TEXT("[CflatDebugAdapter] Socket address not valid: %s"), 135 | *addr->ToString(true)); 136 | return false; 137 | } 138 | 139 | if (!mListener->Bind(*addr)) 140 | { 141 | UE_LOG( 142 | LogTemp, 143 | Error, 144 | TEXT("[CflatDebugAdapter] Could not bind Socket to address: %s"), 145 | *addr->ToString(true)); 146 | return false; 147 | } 148 | 149 | mListening = mListener->Listen(0); 150 | if (mListening) 151 | { 152 | UE_LOG( 153 | LogTemp, 154 | Log, 155 | TEXT("[CflatDebugAdapter] Socket listening to: %s"), 156 | *addr->ToString(true)); 157 | } 158 | else 159 | { 160 | UE_LOG( 161 | LogTemp, 162 | Error, 163 | TEXT("[CflatDebugAdapter] Socket failed to listen to: %s"), 164 | *addr->ToString(true)); 165 | } 166 | } 167 | return mListening; 168 | } 169 | 170 | bool CflatDebugAdapter::Disconnect() 171 | { 172 | if (mSocket && mSocketSubsystem) 173 | { 174 | mSocketSubsystem->DestroySocket(mSocket); 175 | mSocket = nullptr; 176 | return true; 177 | } 178 | return false; 179 | } 180 | 181 | bool CflatDebugAdapter::IsConnected() 182 | { 183 | return mSocket != nullptr && mSocket->GetConnectionState() == SCS_Connected; 184 | } 185 | 186 | void CflatDebugAdapter::CheckListener() 187 | { 188 | if (mConnectionLastCheck + kConnectionCheckInterval > 189 | FPlatformTime::Seconds()) 190 | { 191 | return; 192 | } 193 | 194 | mConnectionLastCheck = FPlatformTime::Seconds(); 195 | 196 | if (mListening) 197 | { 198 | bool hasPendingConnection = false; 199 | if (mListener->HasPendingConnection(hasPendingConnection)) 200 | { 201 | if (hasPendingConnection) 202 | { 203 | 204 | UE_LOG( 205 | LogTemp, Log, TEXT("[CflatDebugAdapter] Has Pending Connection!")); 206 | 207 | if (mSocket) 208 | { 209 | UE_LOG( 210 | LogTemp, Log, TEXT("[CflatDebugAdapter] Destroying old Socket")); 211 | mSocketSubsystem->DestroySocket(mSocket); 212 | } 213 | 214 | FSocket* incomming = mListener->Accept(TEXT("Request")); 215 | if (incomming) 216 | { 217 | mSocket = incomming; 218 | UE_LOG(LogTemp, Log, TEXT("[CflatDebugAdapter] Connected!")); 219 | } 220 | else 221 | { 222 | mSocket = nullptr; 223 | 224 | ESocketErrors errorCode = mSocketSubsystem->GetLastErrorCode(); 225 | FString errorStr = mSocketSubsystem->GetSocketError(); 226 | 227 | UE_LOG( 228 | LogTemp, 229 | Error, 230 | TEXT("Error accepting expected connection [%d] %s"), 231 | (int32)errorCode, 232 | *errorStr); 233 | } 234 | } 235 | } 236 | } 237 | } 238 | 239 | void CflatDebugAdapter::ParseMessageData(const uint8* pData, int32 pSize) 240 | { 241 | const char* charData = (const char*)pData; 242 | FString jsonString(charData, pSize); 243 | 244 | auto jsonReader = TJsonReaderFactory<>::Create(jsonString); 245 | 246 | TSharedPtr jsonObject; 247 | if (!FJsonSerializer::Deserialize(jsonReader, jsonObject)) 248 | { 249 | UE_LOG( 250 | LogTemp, 251 | Error, 252 | TEXT("[CflatDebugAdapter] Invalid json received: %s"), 253 | *jsonString); 254 | return; 255 | } 256 | 257 | if (mDebugLog) 258 | { 259 | UE_LOG( 260 | LogTemp, 261 | Log, 262 | TEXT("[CflatDebugAdapter] -->> Received json(%d): %s"), 263 | pSize, 264 | *jsonString); 265 | } 266 | 267 | FString messageType; 268 | if (!jsonObject->TryGetStringField(TEXT("type"), messageType)) 269 | { 270 | UE_LOG( 271 | LogTemp, 272 | Error, 273 | TEXT("[CflatDebugAdapter] Invalid message received: %s"), 274 | *jsonString); 275 | return; 276 | } 277 | 278 | if (messageType == (TEXT("request"))) 279 | { 280 | FString command; 281 | if (!jsonObject->TryGetStringField(TEXT("command"), command)) 282 | { 283 | UE_LOG( 284 | LogTemp, 285 | Error, 286 | TEXT("[CflatDebugAdapter] Request is missing command"), 287 | *messageType); 288 | return; 289 | } 290 | 291 | FName commandName(command); 292 | auto callback = mRequestCallbacks.Find(commandName); 293 | if (callback) 294 | { 295 | if (mDebugLog) 296 | { 297 | UE_LOG( 298 | LogTemp, 299 | Warning, 300 | TEXT("[CflatDebugAdapter] Calling callback: %s"), 301 | *commandName.ToString()); 302 | } 303 | (*callback)(jsonObject); 304 | } 305 | else 306 | { 307 | if (mDebugLog) 308 | { 309 | UE_LOG( 310 | LogTemp, 311 | Log, 312 | TEXT("[CflatDebugAdapter] No Callback registered for command: %s"), 313 | *command); 314 | } 315 | SendResponse(CreateResponse(jsonObject, false)); 316 | return; 317 | } 318 | } 319 | } 320 | 321 | void CflatDebugAdapter::ReadData() 322 | { 323 | int32 bytesRead = 0; 324 | bool success = mSocket->Recv(mIncomingBuffer, kIncomingBufferSize, bytesRead); 325 | if (success && bytesRead > 0) 326 | { 327 | int32 dataBegin = 0; 328 | for (int32 bi = 0; bi < bytesRead; ++bi) 329 | { 330 | if (mIncomingBuffer[bi] == kMessageEndMarker) 331 | { 332 | mIncomingBuffer[bi] = 0; 333 | ParseMessageData(&mIncomingBuffer[dataBegin], bi + 1); 334 | dataBegin = bi + 1; 335 | } 336 | } 337 | } 338 | else 339 | { 340 | UE_LOG(LogTemp, Error, TEXT("[CflatDebugAdapter] Error Receiving data")); 341 | } 342 | } 343 | 344 | void CflatDebugAdapter::SendString(const FString& pString) 345 | { 346 | if (!IsConnected()) 347 | { 348 | UE_LOG( 349 | LogTemp, 350 | Error, 351 | TEXT("[CflatDebugAdapter] Cannot send data. No connection.")); 352 | return; 353 | } 354 | 355 | if (mDebugLog) 356 | { 357 | UE_LOG( 358 | LogTemp, 359 | Log, 360 | TEXT("[CflatDebugAdapter] <<-- Sending Data: %s"), 361 | *pString); 362 | } 363 | 364 | if (mSocket->Wait( 365 | ESocketWaitConditions::WaitForWrite, 366 | FTimespan::FromSeconds(kSocketWaitTime))) 367 | { 368 | const auto ansiString = StringCast(*pString); 369 | int32 bytesSent = 0; 370 | mSocket->Send((const uint8*)ansiString.Get(), pString.Len() + 1, bytesSent); 371 | mSocket->Send(&kMessageEndMarker, 1, bytesSent); 372 | } 373 | } 374 | 375 | uint32 CflatDebugAdapter::Run() 376 | { 377 | while (true) 378 | { 379 | FPlatformProcess::Sleep(kSleepTime); 380 | 381 | CheckListener(); 382 | 383 | if (mSocket) 384 | { 385 | uint32 pendingDataSize = 0u; 386 | if (mSocket->HasPendingData(pendingDataSize)) 387 | { 388 | ReadData(); 389 | } 390 | } 391 | } 392 | 393 | return 0; 394 | } 395 | 396 | #endif // CFLAT_ENABLED 397 | -------------------------------------------------------------------------------- /UnrealModule/Private/CflatModule.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arturocepeda/Cflat/9559dfaf0273e78aa8033f986af4f047f24b656f/UnrealModule/Private/CflatModule.cpp -------------------------------------------------------------------------------- /UnrealModule/Private/CflatModuleAutoRegister.inl: -------------------------------------------------------------------------------- 1 | 2 | // UE includes - Source Code Navigation for getting module paths 3 | #include "SourceCodeNavigation.h" 4 | 5 | namespace AutoRegister 6 | { 7 | // Constants 8 | static const FName kFunctionScriptName("ScriptName"); 9 | static const FName kMetaComment("Comment"); 10 | static const FName kBlueprintType("BlueprintType"); 11 | static const FName kNotBlueprintType("NotBlueprintType"); 12 | static const size_t kCharConversionBufferSize = 128u; 13 | static const FString kStringEmpty = ""; 14 | 15 | // For Aid Header generation 16 | static const FString kSpacing = " "; 17 | static const FString kNewLineWithIndent1 = "\n "; 18 | static const FString kNewLineWithIndent2 = "\n "; 19 | static const FString kHeaderSeparator = "//----------------------------------------------------------------------------//"; 20 | static const Cflat::Identifier kEmptyId; 21 | 22 | 23 | 24 | void UObjFuncExecute(UFunction* pFunction, UObject* pObject, const CflatArgsVector(Cflat::Value)& pArgs, 25 | Cflat::Value* pOutReturnValue, const Cflat::TypeUsage& pReturnType) 26 | { 27 | const size_t kParamBuffMax = 1024; 28 | uint8 stack[kParamBuffMax] = {0}; 29 | 30 | // Add parameteres to Stack 31 | uint32_t paramIndex = 0u; 32 | for (FProperty* property = (FProperty*)(pFunction->ChildProperties); property && (property->PropertyFlags&(CPF_Parm)) == CPF_Parm; property = (FProperty*)property->Next) 33 | { 34 | if (property->HasAnyPropertyFlags(CPF_ReturnParm)) 35 | { 36 | continue; 37 | } 38 | 39 | size_t offset = property->GetOffset_ForUFunction(); 40 | size_t size = property->GetSize(); 41 | 42 | check(offset + size < kParamBuffMax); 43 | 44 | if(paramIndex < pArgs.size()) 45 | { 46 | memcpy(&stack[offset], pArgs[paramIndex].mValueBuffer, size); 47 | } 48 | else 49 | { 50 | FName metadataKey(FString::Printf(TEXT("CPP_Default_%s"), *property->GetName())); 51 | if (pFunction->HasMetaData(metadataKey)) 52 | { 53 | FString defaultValue = pFunction->GetMetaData(metadataKey); 54 | property->ImportText_Direct(*defaultValue, &stack[offset], nullptr, PPF_None); 55 | } 56 | else 57 | { 58 | UE_LOG(LogTemp, Error, TEXT("[Cflat] Too many arguments for function:: %s"), *pFunction->GetName()); 59 | return; 60 | } 61 | } 62 | 63 | paramIndex++; 64 | } 65 | 66 | uint8* returnAddress = nullptr; 67 | if (pFunction->ReturnValueOffset != MAX_uint16 && pOutReturnValue) 68 | { 69 | check(pFunction->ReturnValueOffset < kParamBuffMax); 70 | returnAddress = &stack[pFunction->ReturnValueOffset]; 71 | } 72 | 73 | // Call function 74 | pObject->ProcessEvent(pFunction, stack); 75 | 76 | // Retrieve return/out values 77 | paramIndex = 0u; 78 | for (FProperty* property = (FProperty*)(pFunction->ChildProperties); property && (property->PropertyFlags&(CPF_Parm)) == CPF_Parm; property = (FProperty*)property->Next) 79 | { 80 | 81 | if (property->HasAnyPropertyFlags(CPF_ReturnParm)) 82 | { 83 | check(returnAddress); 84 | Cflat::Environment::assignReturnValueFromFunctionCall(pReturnType, returnAddress, pOutReturnValue); 85 | } 86 | else if (property->HasAnyPropertyFlags(CPF_OutParm)) 87 | { 88 | size_t offset = property->GetOffset_ForUFunction(); 89 | check(paramIndex < pArgs.size()); 90 | 91 | void* target = pArgs[paramIndex].mValueBuffer; 92 | size_t size = pArgs[paramIndex].mTypeUsage.getSize(); 93 | 94 | check(offset + size < kParamBuffMax); 95 | 96 | memcpy(target, &stack[offset], size); 97 | } 98 | 99 | paramIndex++; 100 | } 101 | } 102 | 103 | struct RegisteredFunctionInfo 104 | { 105 | UFunction* mFunction; 106 | Cflat::Identifier mIdentifier; 107 | Cflat::TypeUsage mReturnType; 108 | FString mName; 109 | int mFirstDefaultParamIndex; 110 | int mRegisteredIndex; 111 | CflatSTLVector(Cflat::TypeUsage) mParameters; 112 | }; 113 | 114 | struct RegisteredInfo 115 | { 116 | Cflat::Struct* mStruct; 117 | Cflat::Identifier mIdentifier; 118 | TSet mDependencies; 119 | TArray mFunctions; 120 | TArray mProperties; 121 | TSet mStaticFunctions; 122 | int mMembersCount; 123 | int mMethodCount; 124 | int mFunctionCount; 125 | FName mHeader; 126 | bool mIsClass; 127 | }; 128 | 129 | struct RegisteredEnumInfo 130 | { 131 | Cflat::Type* mEnum; 132 | FName mHeader; 133 | }; 134 | 135 | 136 | struct PerHeaderTypes 137 | { 138 | TSet mEnums; 139 | TSet mStructs; 140 | TSet mClasses; 141 | TSet mIncluded; 142 | FString mHeaderContent; 143 | UPackage* mPackage; 144 | }; 145 | 146 | 147 | class TypesRegister 148 | { 149 | public: 150 | TSet mAllowedModules; 151 | TMap mIgnorePackageCache; 152 | TMap mPackagePaths; 153 | TMap mRegisteredEnums; 154 | TMap mRegisteredStructs; 155 | TMap mRegisteredClasses; 156 | TMap mRegisteredInterfaces; 157 | TMap mCflatTypeToStruct; 158 | TMap mCflatTypeToEnum; 159 | TMap mCflatTypeToHeader; 160 | TMap mTypesPerHeader; 161 | TSet mHeaderEnumsToIgnore; 162 | TSet mHeaderStructsToIgnore; 163 | TSet mHeaderClassesToIgnore; 164 | TSet mHeaderAlreadyIncluded; 165 | TSet mIgnoredTypes; 166 | TSet mForwardDeclartionTypes; 167 | double mTimeStarted; // For Debugging 168 | 169 | Cflat::Environment* mEnv = nullptr; 170 | Cflat::TypeUsage mUObjectTypeUsage; 171 | 172 | TypesRegister(Cflat::Environment* pEnv) : mEnv(pEnv) 173 | { 174 | mTimeStarted = FPlatformTime::Seconds(); 175 | // Pre cache source files 176 | FSourceCodeNavigation::GetSourceFileDatabase(); 177 | } 178 | 179 | bool IsCflatIdentifierRegistered(const char* pTypeName) 180 | { 181 | Cflat::Hash typeNameHash = Cflat::hash(pTypeName); 182 | const Cflat::Identifier::NamesRegistry* registry = Cflat::Identifier::getNamesRegistry(); 183 | 184 | return registry->mRegistry.find(typeNameHash) != registry->mRegistry.end(); 185 | } 186 | 187 | bool IsCflatIdentifierRegistered(const FString& pTypeName) 188 | { 189 | char nameBuff[kCharConversionBufferSize]; 190 | if (pTypeName.EndsWith(TEXT("*"))) 191 | { 192 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, 193 | *(pTypeName.Mid(0, pTypeName.Len() - 1))); 194 | } 195 | else 196 | { 197 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *pTypeName); 198 | } 199 | 200 | return IsCflatIdentifierRegistered(nameBuff); 201 | } 202 | 203 | bool IsCflatIdentifierRegistered(const FString& pTypeName, const FString& pExtendedType) 204 | { 205 | int32 templateIndexBegin = 0; 206 | int32 templateIndexEnd = 0; 207 | 208 | bool typeIsRegistered = false; 209 | 210 | if (pTypeName.FindChar(TCHAR('<'), templateIndexBegin) && 211 | pTypeName.FindLastChar(TCHAR('>'), templateIndexEnd) && templateIndexBegin < templateIndexEnd) 212 | { 213 | if (templateIndexBegin == 0) 214 | { 215 | FString typeBase = pTypeName.Mid(templateIndexBegin + 1, templateIndexEnd - 1); 216 | typeIsRegistered = IsCflatIdentifierRegistered(typeBase, kStringEmpty); 217 | } 218 | else 219 | { 220 | FString typeBase = pTypeName.Mid(0, templateIndexBegin); 221 | FString typeTemplate = pTypeName.Mid(templateIndexBegin, templateIndexEnd); 222 | typeIsRegistered = IsCflatIdentifierRegistered(typeBase, typeTemplate); 223 | } 224 | } 225 | else 226 | { 227 | typeIsRegistered = IsCflatIdentifierRegistered(pTypeName); 228 | } 229 | 230 | if (!typeIsRegistered) 231 | { 232 | return false; 233 | } 234 | 235 | if (pExtendedType.IsEmpty()) 236 | { 237 | return typeIsRegistered; 238 | } 239 | 240 | return IsCflatIdentifierRegistered(pExtendedType, kStringEmpty); 241 | } 242 | 243 | Cflat::Struct* GetCflatStructFromUStruct(UStruct* pStruct) 244 | { 245 | const TCHAR* prefix = pStruct->GetPrefixCPP(); 246 | FString className = FString::Printf(TEXT("%s%s"), prefix, *pStruct->GetName()); 247 | 248 | char nameBuff[kCharConversionBufferSize]; 249 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *className); 250 | 251 | if (!IsCflatIdentifierRegistered(nameBuff)) 252 | { 253 | return nullptr; 254 | } 255 | 256 | Cflat::Type* type = mEnv->getType(nameBuff); 257 | if (type) 258 | { 259 | return static_cast(type); 260 | } 261 | return nullptr; 262 | } 263 | 264 | RegisteredInfo* FindRegisteredInfoFromUStruct(UStruct* pStruct) 265 | { 266 | RegisteredInfo* depRegInfo = mRegisteredStructs.Find(pStruct); 267 | if (depRegInfo) 268 | { 269 | return depRegInfo; 270 | } 271 | depRegInfo = mRegisteredClasses.Find(pStruct); 272 | if (depRegInfo) 273 | { 274 | return depRegInfo; 275 | } 276 | return nullptr; 277 | } 278 | 279 | bool CheckShouldIgnoreModule(UPackage* pPackage) 280 | { 281 | if (pPackage == nullptr) 282 | { 283 | return true; 284 | } 285 | 286 | bool ignoreModule = false; 287 | bool *cachedIgnore= mIgnorePackageCache.Find(pPackage); 288 | 289 | if (cachedIgnore) 290 | { 291 | ignoreModule = *cachedIgnore; 292 | } 293 | else 294 | { 295 | FString modulePath; 296 | FName moduleName = FPackageName::GetShortFName(pPackage->GetFName()); 297 | if (!mAllowedModules.Contains(moduleName)) 298 | { 299 | ignoreModule = true; 300 | } 301 | else if(FSourceCodeNavigation::FindModulePath(pPackage, modulePath)) 302 | { 303 | // Ignore Editor and Develper modules 304 | ignoreModule = moduleName.ToString().EndsWith(TEXT("Editor")) || modulePath.Contains(TEXT("/Editor/")) || 305 | modulePath.Contains(TEXT("/Developer/")); 306 | } 307 | else 308 | { 309 | ignoreModule = true; 310 | } 311 | mIgnorePackageCache.Add(pPackage, ignoreModule); 312 | if (!ignoreModule) 313 | { 314 | mPackagePaths.Add(pPackage, modulePath); 315 | } 316 | } 317 | 318 | return ignoreModule; 319 | } 320 | 321 | bool CheckShouldRegisterType(UStruct* pStruct) 322 | { 323 | if (mIgnoredTypes.Find(pStruct->GetFName())) 324 | { 325 | return false; 326 | } 327 | 328 | // Already registered 329 | if (mRegisteredStructs.Contains(pStruct)) 330 | { 331 | return false; 332 | } 333 | if (mRegisteredClasses.Contains(pStruct)) 334 | { 335 | return false; 336 | } 337 | 338 | if (CheckShouldIgnoreModule(pStruct->GetPackage())) 339 | { 340 | return false; 341 | } 342 | 343 | if (pStruct->GetBoolMetaData(kBlueprintType)) 344 | { 345 | return true; 346 | } 347 | 348 | if (pStruct->GetBoolMetaData(kNotBlueprintType)) 349 | { 350 | return false; 351 | } 352 | 353 | int propertyCount = 0; 354 | for (TFieldIterator propIt(pStruct, EFieldIterationFlags::None); propIt; ++propIt) 355 | { 356 | propertyCount++; 357 | if (propIt->HasAnyPropertyFlags(CPF_NativeAccessSpecifierProtected | 358 | CPF_NativeAccessSpecifierPrivate | 359 | CPF_EditorOnly)) 360 | { 361 | continue; 362 | } 363 | 364 | if (propIt->HasAnyPropertyFlags(CPF_BlueprintVisible | CPF_BlueprintAssignable | CPF_Edit)) 365 | { 366 | return true; 367 | } 368 | } 369 | 370 | int functionCount = 0; 371 | for (TFieldIterator funcIt(pStruct, EFieldIterationFlags::None); funcIt; ++funcIt) 372 | { 373 | functionCount++; 374 | UFunction* function = *funcIt; 375 | 376 | if (!function->HasAnyFunctionFlags(FUNC_EditorOnly)) 377 | { 378 | return true; 379 | } 380 | } 381 | 382 | // Nothing in the type to be registered, but it can still be used as a handle 383 | if (propertyCount == 0 && functionCount == 0) 384 | { 385 | return true; 386 | } 387 | 388 | return false; 389 | } 390 | 391 | bool CheckShouldRegisterInterface(UClass* pInterface) 392 | { 393 | if (mIgnoredTypes.Find(pInterface->GetFName())) 394 | { 395 | return false; 396 | } 397 | 398 | // Already registered 399 | if (mRegisteredInterfaces.Contains(pInterface)) 400 | { 401 | return false; 402 | } 403 | 404 | if (CheckShouldIgnoreModule(pInterface->GetPackage())) 405 | { 406 | return false; 407 | } 408 | 409 | if (pInterface->HasAnyClassFlags(CLASS_CompiledFromBlueprint)) 410 | { 411 | return false; 412 | } 413 | 414 | return true; 415 | } 416 | 417 | bool GetFunctionParameters(UFunction* pFunction, Cflat::TypeUsage& pReturn, CflatSTLVector(Cflat::TypeUsage)& pParams, int& pOutFirstDefaultParamIndex) 418 | { 419 | pOutFirstDefaultParamIndex = -1; 420 | 421 | char nameBuff[kCharConversionBufferSize]; 422 | 423 | for (TFieldIterator propIt(pFunction); propIt && propIt->HasAnyPropertyFlags(CPF_Parm); ++propIt) 424 | { 425 | FString extendedType; 426 | FString cppType; 427 | 428 | if (propIt->IsA()) 429 | { 430 | FByteProperty* byteProp = static_cast(*propIt); 431 | if (byteProp->Enum) 432 | { 433 | UEnum* uEnum = byteProp->Enum; 434 | if (mRegisteredEnums.Find(uEnum) == nullptr) 435 | { 436 | return false; 437 | } 438 | cppType = uEnum->CppType; 439 | } 440 | } 441 | 442 | if (cppType.IsEmpty()) 443 | { 444 | cppType = propIt->GetCPPType(&extendedType); 445 | if (!IsCflatIdentifierRegistered(cppType, extendedType)) 446 | { 447 | return false; 448 | } 449 | } 450 | 451 | if (!extendedType.IsEmpty()) 452 | { 453 | cppType += extendedType; 454 | } 455 | if (propIt->HasAnyPropertyFlags(CPF_ConstParm)) 456 | { 457 | cppType = "const " + cppType; 458 | } 459 | 460 | if (propIt->HasAnyPropertyFlags(CPF_ReferenceParm) || propIt->HasAnyPropertyFlags(CPF_OutParm)) 461 | { 462 | // Treat return refs as copies 463 | if (!propIt->HasAnyPropertyFlags(CPF_ReturnParm)) 464 | { 465 | cppType += TEXT("&"); 466 | } 467 | } 468 | 469 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *cppType); 470 | const TypeUsage type = mEnv->getTypeUsage(nameBuff); 471 | 472 | if (type.mType == nullptr) 473 | { 474 | return false; 475 | } 476 | 477 | if (propIt->HasAnyPropertyFlags(CPF_ReturnParm)) 478 | { 479 | pReturn = type; 480 | continue; 481 | } 482 | 483 | if (pOutFirstDefaultParamIndex == -1) 484 | { 485 | FString metaDataName = FString::Printf(TEXT("CPP_Default_%s"), *propIt->GetName()); 486 | 487 | if (pFunction->HasMetaData(*metaDataName)) 488 | { 489 | pOutFirstDefaultParamIndex = pParams.size(); 490 | } 491 | } 492 | 493 | pParams.push_back(type); 494 | } 495 | 496 | return true; 497 | } 498 | 499 | void RegisterCflatFunction(Cflat::Struct* pCfStruct, UFunction* pFunction, Cflat::Identifier pIdentifier, 500 | const CflatSTLVector(Cflat::TypeUsage)& pParameters, Cflat::TypeUsage pReturnType) 501 | { 502 | if (pFunction->HasAnyFunctionFlags(FUNC_Static)) 503 | { 504 | Cflat::Function* staticFunc = pCfStruct->registerStaticMethod(pIdentifier); 505 | staticFunc->mReturnTypeUsage = pReturnType; 506 | staticFunc->mParameters = pParameters; 507 | 508 | staticFunc->execute = [pFunction, pReturnType](const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 509 | { 510 | UObject* context = pFunction->GetOuterUClassUnchecked()->ClassDefaultObject; 511 | UObjFuncExecute(pFunction, context, pArguments, pOutReturnValue, pReturnType); 512 | }; 513 | } 514 | else 515 | { 516 | pCfStruct->mMethods.push_back(Cflat::Method(pIdentifier)); 517 | Cflat::Method* method = &pCfStruct->mMethods.back(); 518 | method->mReturnTypeUsage = pReturnType; 519 | method->mParameters = pParameters; 520 | if (pFunction->HasAnyFunctionFlags(FUNC_Const)) 521 | { 522 | CflatSetFlag(method->mFlags, Cflat::MethodFlags::Const); 523 | } 524 | 525 | method->execute = [pFunction, pReturnType] (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 526 | { 527 | UObject* thisObj = CflatValueAs(&pThis, UObject*); 528 | UObjFuncExecute(pFunction, thisObj, pArguments, pOutReturnValue, pReturnType); 529 | }; 530 | } 531 | } 532 | 533 | void RegisterInterfaceFunction(Cflat::Struct* pCfStruct, UFunction* pFunction, Cflat::Identifier pIdentifier, 534 | const CflatSTLVector(Cflat::TypeUsage)& pParameters, Cflat::TypeUsage pReturnType) 535 | { 536 | char nameBuff[kCharConversionBufferSize]; 537 | snprintf(nameBuff, kCharConversionBufferSize - 1, "Execute_%s", pIdentifier.mName); 538 | CflatSTLVector(Cflat::TypeUsage) parameters; 539 | parameters.reserve(pParameters.size() + 1); 540 | parameters.push_back(mUObjectTypeUsage); 541 | parameters.insert(parameters.end(), pParameters.begin(), pParameters.end()); 542 | Cflat::Function* staticFunc = pCfStruct->registerStaticMethod(nameBuff); 543 | staticFunc->mReturnTypeUsage = pReturnType; 544 | staticFunc->mParameters = parameters; 545 | 546 | staticFunc->execute = [pFunction, pReturnType](const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) { 547 | check(pArguments.size() >= 1); 548 | UObject* Obj = CflatValueAs(&pArguments[0], UObject*); 549 | UObjFuncExecute(pFunction, Obj, pArguments, pOutReturnValue, pReturnType); 550 | }; 551 | } 552 | 553 | void AddDependencyIfNeeded(RegisteredInfo* pRegInfo, Cflat::TypeUsage* pType) 554 | { 555 | if (pRegInfo->mStruct == pType->mType) 556 | { 557 | return; 558 | } 559 | 560 | FName* header = mCflatTypeToHeader.Find(pType->mType); 561 | if (!header) 562 | { 563 | return; 564 | } 565 | 566 | if (pType->isPointer() || pType->isReference()) 567 | { 568 | mForwardDeclartionTypes.Add(pType->mType); 569 | } 570 | else 571 | { 572 | pRegInfo->mDependencies.Add(pType->mType); 573 | } 574 | } 575 | 576 | void GatherFunctionInfos(UStruct* pStruct, TArray& pOutFunctions) 577 | { 578 | char funcName[kCharConversionBufferSize]; 579 | 580 | int count = 0; 581 | for (TFieldIterator funcIt(pStruct); funcIt; ++funcIt) 582 | { 583 | UFunction* function = *funcIt; 584 | 585 | UStruct* funcOwner = static_cast(function->GetOuter()); 586 | if (funcOwner != pStruct) 587 | { 588 | continue; 589 | } 590 | 591 | // Ignore Editor 592 | if (function->HasAnyFunctionFlags(FUNC_EditorOnly)) 593 | { 594 | continue; 595 | } 596 | 597 | // Ignore non public 598 | if (function->HasAnyFunctionFlags(FUNC_Private | FUNC_Protected)) 599 | { 600 | continue; 601 | } 602 | 603 | pOutFunctions.Add({}); 604 | RegisteredFunctionInfo& funcInfo = pOutFunctions.Last(); 605 | 606 | if (!GetFunctionParameters(function, funcInfo.mReturnType, funcInfo.mParameters, funcInfo.mFirstDefaultParamIndex)) 607 | { 608 | pOutFunctions.Pop(false); 609 | continue; 610 | } 611 | 612 | funcInfo.mFunction = function; 613 | funcInfo.mName = function->GetName(); 614 | funcInfo.mRegisteredIndex = count++; 615 | 616 | FPlatformString::Convert(funcName, kCharConversionBufferSize, *funcInfo.mName); 617 | funcInfo.mIdentifier = Cflat::Identifier(funcName); 618 | } 619 | } 620 | 621 | void RegisterUStructFunctions(UStruct* pStruct, RegisteredInfo* pRegInfo) 622 | { 623 | Cflat::Struct* cfStruct = pRegInfo->mStruct; 624 | 625 | GatherFunctionInfos(pStruct, pRegInfo->mFunctions); 626 | 627 | for (RegisteredFunctionInfo& info : pRegInfo->mFunctions) 628 | { 629 | AddDependencyIfNeeded(pRegInfo, &info.mReturnType); 630 | for (int i = 0; i < info.mParameters.size(); ++i) 631 | { 632 | AddDependencyIfNeeded(pRegInfo, &info.mParameters[i]); 633 | } 634 | 635 | RegisterCflatFunction(cfStruct, info.mFunction, info.mIdentifier, info.mParameters, info.mReturnType); 636 | 637 | if (info.mFirstDefaultParamIndex == -1) 638 | { 639 | continue; 640 | } 641 | 642 | // Functions with default parameter 643 | CflatSTLVector(Cflat::TypeUsage) parametersForDefault; 644 | parametersForDefault.reserve(info.mParameters.size()); 645 | for (int i = 0; i < info.mParameters.size() - 1; ++i) 646 | { 647 | parametersForDefault.push_back(info.mParameters[i]); 648 | if (i >= info.mFirstDefaultParamIndex - 1) 649 | { 650 | RegisterCflatFunction(cfStruct, info.mFunction, info.mIdentifier, parametersForDefault, info.mReturnType); 651 | } 652 | } 653 | } 654 | 655 | pRegInfo->mMembersCount = cfStruct->mMembers.size(); 656 | pRegInfo->mMethodCount = cfStruct->mMethods.size(); 657 | 658 | { 659 | CflatSTLVector(Function*) staticFunctions; 660 | cfStruct->mFunctionsHolder.getAllFunctions(&staticFunctions); 661 | pRegInfo->mFunctionCount = staticFunctions.size(); 662 | 663 | for (int i = 0; i < staticFunctions.size(); ++i) 664 | { 665 | pRegInfo->mStaticFunctions.Add(staticFunctions[i]); 666 | } 667 | } 668 | } 669 | 670 | void RegisterInterfaceFunctions(UStruct* pInterface, RegisteredInfo* pRegInfo) 671 | { 672 | Cflat::Struct* cfStruct = pRegInfo->mStruct; 673 | 674 | GatherFunctionInfos(pInterface, pRegInfo->mFunctions); 675 | 676 | for (RegisteredFunctionInfo& info : pRegInfo->mFunctions) 677 | { 678 | AddDependencyIfNeeded(pRegInfo, &info.mReturnType); 679 | for (int i = 0; i < info.mParameters.size(); ++i) 680 | { 681 | AddDependencyIfNeeded(pRegInfo, &info.mParameters[i]); 682 | } 683 | 684 | RegisterCflatFunction(cfStruct, info.mFunction, info.mIdentifier, info.mParameters, info.mReturnType); 685 | if (info.mFunction->HasAllFunctionFlags(FUNC_BlueprintEvent | FUNC_Event)) 686 | { 687 | RegisterInterfaceFunction(cfStruct, info.mFunction, info.mIdentifier, info.mParameters, info.mReturnType); 688 | } 689 | 690 | if (info.mFirstDefaultParamIndex == -1) 691 | { 692 | continue; 693 | } 694 | 695 | // Functions with default parameter 696 | CflatSTLVector(Cflat::TypeUsage) parametersForDefault; 697 | parametersForDefault.reserve(info.mParameters.size()); 698 | for (int i = 0; i < info.mParameters.size() - 1; ++i) 699 | { 700 | parametersForDefault.push_back(info.mParameters[i]); 701 | if (i >= info.mFirstDefaultParamIndex - 1) 702 | { 703 | RegisterCflatFunction(cfStruct, info.mFunction, info.mIdentifier, parametersForDefault, info.mReturnType); 704 | } 705 | } 706 | } 707 | 708 | pRegInfo->mMembersCount = cfStruct->mMembers.size(); 709 | pRegInfo->mMethodCount = cfStruct->mMethods.size(); 710 | 711 | { 712 | CflatSTLVector(Function*) staticFunctions; 713 | cfStruct->mFunctionsHolder.getAllFunctions(&staticFunctions); 714 | pRegInfo->mFunctionCount = staticFunctions.size(); 715 | 716 | for (int i = 0; i < staticFunctions.size(); ++i) 717 | { 718 | pRegInfo->mStaticFunctions.Add(staticFunctions[i]); 719 | } 720 | } 721 | } 722 | 723 | void RegisterUScriptStructConstructors(UScriptStruct* pStruct, RegisteredInfo* pRegInfo) 724 | { 725 | Cflat::Struct* cfStruct = pRegInfo->mStruct; 726 | 727 | const Cflat::Identifier emptyId; 728 | UScriptStruct::ICppStructOps* structOps = pStruct->GetCppStructOps(); 729 | 730 | if(structOps == nullptr) 731 | { 732 | return; 733 | } 734 | 735 | if (structOps->HasNoopConstructor()) 736 | { 737 | cfStruct->mCachedMethodIndexDefaultConstructor = cfStruct->mMethods.size(); 738 | cfStruct->mMethods.push_back(Cflat::Method(emptyId)); 739 | Cflat::Method* method = &cfStruct->mMethods.back(); 740 | method->execute = [] (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 741 | { 742 | }; 743 | } 744 | else if (structOps->HasZeroConstructor()) 745 | { 746 | cfStruct->mCachedMethodIndexDefaultConstructor = cfStruct->mMethods.size(); 747 | cfStruct->mMethods.push_back(Cflat::Method(emptyId)); 748 | Cflat::Method* method = &cfStruct->mMethods.back(); 749 | method->execute = [structOps] (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 750 | { 751 | memset(pThis.mValueBuffer, 0, structOps->GetSize()); 752 | }; 753 | } 754 | // Default Constructor 755 | else 756 | { 757 | cfStruct->mCachedMethodIndexDefaultConstructor = cfStruct->mMethods.size(); 758 | cfStruct->mMethods.push_back(Cflat::Method(emptyId)); 759 | Cflat::Method* method = &cfStruct->mMethods.back(); 760 | method->execute = [structOps] (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 761 | { 762 | void* thiz = CflatValueAs(&pThis, void*); 763 | structOps->Construct(thiz); 764 | }; 765 | } 766 | } 767 | 768 | void RegisterUStructProperties(UStruct* pStruct, RegisteredInfo* pRegInfo) 769 | { 770 | Cflat::Struct* cfStruct = pRegInfo->mStruct; 771 | for (TFieldIterator propIt(pStruct); propIt; ++propIt) 772 | { 773 | FProperty* prop = *propIt; 774 | if (prop->HasAnyPropertyFlags(CPF_NativeAccessSpecifierProtected | 775 | CPF_NativeAccessSpecifierPrivate | 776 | CPF_EditorOnly)) 777 | { 778 | continue; 779 | } 780 | 781 | UStruct* owner = prop->GetOwnerStruct(); 782 | if (owner != pStruct) 783 | { 784 | continue; 785 | } 786 | 787 | FString extendedType; 788 | FString cppType = prop->GetCPPType(&extendedType); 789 | 790 | if (!IsCflatIdentifierRegistered(cppType, extendedType)) 791 | { 792 | continue; 793 | } 794 | 795 | if (!extendedType.IsEmpty()) 796 | { 797 | cppType += extendedType; 798 | } 799 | 800 | char nameBuff[kCharConversionBufferSize]; 801 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *prop->GetName()); 802 | const Cflat::Identifier memberIdentifier(nameBuff); 803 | Cflat::Member member(memberIdentifier); 804 | 805 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *cppType); 806 | 807 | member.mTypeUsage = mEnv->getTypeUsage(nameBuff); 808 | 809 | // Type not recognized 810 | if (member.mTypeUsage.mType == nullptr) 811 | { 812 | continue; 813 | } 814 | 815 | member.mOffset = (uint16_t)prop->GetOffset_ForInternal(); 816 | cfStruct->mMembers.push_back(member); 817 | 818 | pRegInfo->mProperties.Push(prop); 819 | 820 | AddDependencyIfNeeded(pRegInfo, &member.mTypeUsage); 821 | } 822 | } 823 | 824 | Cflat::Struct* RegisterInterface(UStruct* pInterface) 825 | { 826 | { 827 | RegisteredInfo* regInfo = mRegisteredInterfaces.Find(pInterface); 828 | if (regInfo) 829 | { 830 | return regInfo->mStruct; 831 | } 832 | } 833 | 834 | FString interfaceName = FString::Printf(TEXT("I%s"), *pInterface->GetName()); 835 | char classTypeName[kCharConversionBufferSize]; 836 | FPlatformString::Convert(classTypeName, kCharConversionBufferSize, *interfaceName); 837 | const Cflat::Identifier interfaceIdentifier(classTypeName); 838 | 839 | Cflat::Type* type = mEnv->getType(interfaceIdentifier); 840 | if (type) 841 | { 842 | return static_cast(type); 843 | } 844 | Cflat::Struct* interfaceStruct = mEnv->registerType(interfaceIdentifier); 845 | interfaceStruct->mSize = sizeof(void*); 846 | interfaceStruct->mAlignment = alignof(void*); 847 | 848 | RegisteredInfo& regInfo = mRegisteredInterfaces.Add(pInterface, {}); 849 | regInfo.mStruct = interfaceStruct; 850 | regInfo.mIdentifier = interfaceStruct->mIdentifier; 851 | 852 | 853 | UPackage* package = pInterface->GetPackage(); 854 | const FString& modulePath = package->GetMetaData()->GetValue(pInterface, TEXT("ModuleRelativePath")); 855 | regInfo.mHeader = FName(*modulePath); 856 | 857 | mCflatTypeToStruct.Add(interfaceStruct, pInterface); 858 | mCflatTypeToHeader.Add(interfaceStruct, regInfo.mHeader); 859 | 860 | return interfaceStruct; 861 | } 862 | 863 | Cflat::Struct* RegisterUStruct(TMap& pRegisterMap, UStruct* pStruct) 864 | { 865 | // Early out if already registered 866 | { 867 | RegisteredInfo* regInfo = pRegisterMap.Find(pStruct); 868 | if (regInfo) 869 | { 870 | return regInfo->mStruct; 871 | } 872 | } 873 | 874 | Cflat::Struct* cfStruct = nullptr; 875 | { 876 | FString structName = FString::Printf(TEXT("%s%s"), pStruct->GetPrefixCPP(), *pStruct->GetName()); 877 | char classTypeName[kCharConversionBufferSize]; 878 | FPlatformString::Convert(classTypeName, kCharConversionBufferSize, *structName); 879 | const Cflat::Identifier classTypeIdentifier(classTypeName); 880 | Cflat::Type* type = mEnv->getType(classTypeIdentifier); 881 | if (type) 882 | { 883 | return static_cast(type); 884 | } 885 | cfStruct = mEnv->registerType(classTypeIdentifier); 886 | } 887 | cfStruct->mSize = pStruct->GetStructureSize(); 888 | cfStruct->mAlignment = pStruct->GetMinAlignment(); 889 | 890 | bool isClass = pStruct->IsA(); 891 | 892 | // Register Super Class/Struct 893 | { 894 | Cflat::Type* baseCflatType = nullptr; 895 | UStruct* superStruct = pStruct->GetSuperStruct(); 896 | 897 | if (superStruct) 898 | { 899 | // Register base class/structure 900 | baseCflatType = RegisterUStruct(pRegisterMap, superStruct); 901 | if (superStruct->IsA()) 902 | { 903 | UClass* baseClass = static_cast(superStruct); 904 | if (baseClass->HasAnyClassFlags(CLASS_Interface)) 905 | { 906 | RegisterInterface(baseClass); 907 | } 908 | } 909 | } 910 | 911 | if (baseCflatType) 912 | { 913 | cfStruct->mBaseTypes.emplace_back(); 914 | Cflat::BaseType& baseType = cfStruct->mBaseTypes.back(); 915 | baseType.mType = baseCflatType; 916 | baseType.mOffset = 0u; 917 | } 918 | 919 | if (isClass) 920 | { 921 | UClass* uClass = static_cast(pStruct); 922 | for (int32 i = 0; i < uClass->Interfaces.Num(); ++i) 923 | { 924 | FImplementedInterface& iface = uClass->Interfaces[i]; 925 | { 926 | RegisterUStruct(mRegisteredClasses, iface.Class); 927 | Cflat::Struct* cfIface = RegisterInterface(iface.Class); 928 | 929 | cfStruct->mBaseTypes.emplace_back(); 930 | Cflat::BaseType& baseType = cfStruct->mBaseTypes.back(); 931 | baseType.mType = cfIface; 932 | baseType.mOffset = iface.PointerOffset; 933 | } 934 | } 935 | } 936 | } 937 | 938 | RegisteredInfo& regInfo = pRegisterMap.Add(pStruct, {}); 939 | regInfo.mIsClass = isClass; 940 | regInfo.mStruct = cfStruct; 941 | regInfo.mIdentifier = cfStruct->mIdentifier; 942 | if (!cfStruct->mBaseTypes.empty()) 943 | { 944 | for (int i = 0; i < cfStruct->mBaseTypes.size(); ++i) 945 | { 946 | Cflat::Type* baseCflatType = cfStruct->mBaseTypes[i].mType; 947 | regInfo.mDependencies.Add(baseCflatType); 948 | } 949 | } 950 | { 951 | UPackage* package = pStruct->GetPackage(); 952 | const FString& modulePath = package->GetMetaData()->GetValue(pStruct, TEXT("ModuleRelativePath")); 953 | if (modulePath.IsEmpty()) 954 | { 955 | /* Package path not found, so we use its name as reference for sorting headers. */ 956 | regInfo.mHeader = package->GetFName(); 957 | } 958 | else 959 | { 960 | regInfo.mHeader = FName(*modulePath); 961 | } 962 | } 963 | mCflatTypeToStruct.Add(cfStruct, pStruct); 964 | mCflatTypeToHeader.Add(cfStruct, regInfo.mHeader); 965 | 966 | return cfStruct; 967 | } 968 | 969 | void RegisterRegularEnum(UEnum* pUEnum, const Cflat::Identifier& pEnumIdentifier, Cflat::Namespace* pNamespace) 970 | { 971 | char nameBuff[kCharConversionBufferSize]; 972 | 973 | if (pNamespace->getType(pEnumIdentifier)) 974 | { 975 | return; 976 | } 977 | 978 | Cflat::Enum* cfEnum = pNamespace->registerType(pEnumIdentifier); 979 | if (pUEnum->GetBoolMetaData(kBlueprintType)) 980 | { 981 | cfEnum->mSize = sizeof(uint8); 982 | } 983 | else 984 | { 985 | cfEnum->mSize = sizeof(int64); 986 | } 987 | 988 | Cflat::TypeUsage enumTypeUsage; 989 | enumTypeUsage.mType = cfEnum; 990 | CflatSetFlag(enumTypeUsage.mFlags, Cflat::TypeUsageFlags::Const); 991 | 992 | for (int32 i = 0; i < pUEnum->NumEnums() - 1; ++i) 993 | { 994 | int64 value = pUEnum->GetValueByIndex(i); 995 | FString enumValueName = pUEnum->GetNameStringByIndex(i); 996 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *enumValueName); 997 | Cflat::Identifier idEnumValueName(nameBuff); 998 | 999 | Cflat::Instance* enumInstance = cfEnum->mInstancesHolder.registerInstance(enumTypeUsage, idEnumValueName); 1000 | enumInstance->mValue.initOnHeap(enumTypeUsage); 1001 | enumInstance->mValue.set(&value); 1002 | CflatSetFlag(enumInstance->mFlags, Cflat::InstanceFlags::EnumValue); 1003 | Cflat::Instance* nsInstance = pNamespace->registerInstance(enumTypeUsage, idEnumValueName); 1004 | nsInstance->mValue = enumInstance->mValue; 1005 | CflatSetFlag(nsInstance->mFlags, Cflat::InstanceFlags::EnumValue); 1006 | } 1007 | 1008 | RegisteredEnumInfo& regInfo = mRegisteredEnums.Add(pUEnum, {}); 1009 | regInfo.mEnum = cfEnum; 1010 | { 1011 | UPackage* package = pUEnum->GetPackage(); 1012 | const FString& modulePath = package->GetMetaData()->GetValue(pUEnum, TEXT("ModuleRelativePath")); 1013 | regInfo.mHeader = FName(*modulePath); 1014 | } 1015 | mCflatTypeToEnum.Add(cfEnum, pUEnum); 1016 | mCflatTypeToHeader.Add(cfEnum, regInfo.mHeader); 1017 | } 1018 | 1019 | void RegisterRegularEnum(UEnum* pUEnum) 1020 | { 1021 | char nameBuff[kCharConversionBufferSize]; 1022 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *pUEnum->GetName()); 1023 | 1024 | if (IsCflatIdentifierRegistered(nameBuff)) 1025 | { 1026 | return; 1027 | } 1028 | Cflat::Identifier enumIdentifier(nameBuff); 1029 | RegisterRegularEnum(pUEnum, enumIdentifier, mEnv->getGlobalNamespace()); 1030 | } 1031 | 1032 | void RegisterEnumNamespaced(UEnum* pUEnum) 1033 | { 1034 | char nameBuff[kCharConversionBufferSize]; 1035 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *pUEnum->GetName()); 1036 | 1037 | Cflat::Identifier idEnumName(nameBuff); 1038 | 1039 | Cflat::Namespace* globalNamespace = mEnv->getGlobalNamespace(); 1040 | Cflat::Namespace* enumNamespace = globalNamespace->getNamespace(idEnumName); 1041 | if (enumNamespace) 1042 | { 1043 | return; 1044 | } 1045 | 1046 | enumNamespace = globalNamespace->requestNamespace(idEnumName); 1047 | Cflat::Identifier idEnumType("Type"); 1048 | 1049 | RegisterRegularEnum(pUEnum, idEnumType, enumNamespace); 1050 | } 1051 | 1052 | void RegisterEnumClass(UEnum* pUEnum) 1053 | { 1054 | char nameBuff[kCharConversionBufferSize]; 1055 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *pUEnum->GetName()); 1056 | 1057 | if (IsCflatIdentifierRegistered(nameBuff)) 1058 | { 1059 | return; 1060 | } 1061 | 1062 | Cflat::Identifier idEnumName(nameBuff); 1063 | if (mEnv->getType(idEnumName)) 1064 | { 1065 | return; 1066 | } 1067 | Cflat::EnumClass* cfEnum = mEnv->registerType(idEnumName); 1068 | if (pUEnum->GetBoolMetaData(kBlueprintType)) 1069 | { 1070 | cfEnum->mSize = sizeof(uint8); 1071 | } 1072 | else 1073 | { 1074 | cfEnum->mSize = sizeof(int64); 1075 | } 1076 | 1077 | Cflat::TypeUsage enumTypeUsage; 1078 | enumTypeUsage.mType = cfEnum; 1079 | CflatSetFlag(enumTypeUsage.mFlags, Cflat::TypeUsageFlags::Const); 1080 | 1081 | for (int32 i = 0; i < pUEnum->NumEnums() - 1; ++i) 1082 | { 1083 | int64 value = pUEnum->GetValueByIndex(i); 1084 | FString enumValueName = pUEnum->GetNameStringByIndex(i); 1085 | FPlatformString::Convert(nameBuff, kCharConversionBufferSize, *enumValueName); 1086 | Cflat::Identifier idEnumValueName(nameBuff); 1087 | 1088 | Cflat::Instance* enumInstance = cfEnum->mInstancesHolder.registerInstance(enumTypeUsage, idEnumValueName); 1089 | enumInstance->mValue.initOnHeap(enumTypeUsage); 1090 | enumInstance->mValue.set(&value); 1091 | CflatSetFlag(enumInstance->mFlags, Cflat::InstanceFlags::EnumValue); 1092 | } 1093 | 1094 | RegisteredEnumInfo& regInfo = mRegisteredEnums.Add(pUEnum, {}); 1095 | regInfo.mEnum = cfEnum; 1096 | { 1097 | UPackage* package = pUEnum->GetPackage(); 1098 | const FString& modulePath = package->GetMetaData()->GetValue(pUEnum, TEXT("ModuleRelativePath")); 1099 | regInfo.mHeader = FName(*modulePath); 1100 | } 1101 | mCflatTypeToEnum.Add(cfEnum, pUEnum); 1102 | mCflatTypeToHeader.Add(cfEnum, regInfo.mHeader); 1103 | } 1104 | 1105 | void RegisterEnums() 1106 | { 1107 | for (TObjectIterator enumIt; enumIt; ++enumIt) 1108 | { 1109 | UEnum* uEnum = *enumIt; 1110 | 1111 | if (mIgnoredTypes.Find(uEnum->GetFName())) 1112 | { 1113 | continue; 1114 | } 1115 | 1116 | { 1117 | UObject* outer = uEnum->GetOuter(); 1118 | if (outer && CheckShouldIgnoreModule(outer->GetPackage())) 1119 | { 1120 | continue; 1121 | } 1122 | } 1123 | 1124 | UEnum::ECppForm enumForm = uEnum->GetCppForm(); 1125 | 1126 | switch(enumForm) 1127 | { 1128 | case UEnum::ECppForm::Regular: 1129 | RegisterRegularEnum(uEnum); 1130 | break; 1131 | case UEnum::ECppForm::Namespaced: 1132 | RegisterEnumNamespaced(uEnum); 1133 | break; 1134 | case UEnum::ECppForm::EnumClass: 1135 | RegisterEnumClass(uEnum); 1136 | break; 1137 | } 1138 | } 1139 | } 1140 | 1141 | void RegisterStructs() 1142 | { 1143 | for (TObjectIterator structIt; structIt; ++structIt) 1144 | { 1145 | UScriptStruct* scriptStruct = *structIt; 1146 | 1147 | // Register Native Structs only 1148 | if ((scriptStruct->StructFlags & STRUCT_Native) == 0u) 1149 | { 1150 | continue; 1151 | } 1152 | UStruct* uStruct = static_cast(scriptStruct); 1153 | if (!CheckShouldRegisterType(uStruct)) 1154 | { 1155 | continue; 1156 | } 1157 | 1158 | RegisterUStruct(mRegisteredStructs, uStruct); 1159 | } 1160 | } 1161 | 1162 | void RegisterClasses() 1163 | { 1164 | RegisterUStruct(mRegisteredClasses, UObject::StaticClass()); 1165 | RegisterUStruct(mRegisteredClasses, UInterface::StaticClass()); 1166 | RegisterUStruct(mRegisteredClasses, UField::StaticClass()); 1167 | RegisterUStruct(mRegisteredClasses, UStruct::StaticClass()); 1168 | RegisterUStruct(mRegisteredClasses, UClass::StaticClass()); 1169 | RegisterUStruct(mRegisteredClasses, UScriptStruct::StaticClass()); 1170 | RegisterUStruct(mRegisteredClasses, ULineBatchComponent::StaticClass()); 1171 | 1172 | for (TObjectIterator classIt; classIt; ++classIt) 1173 | { 1174 | UClass* uClass = *classIt; 1175 | UStruct* uStruct = static_cast(uClass); 1176 | if (!CheckShouldRegisterType(uStruct)) 1177 | { 1178 | continue; 1179 | } 1180 | RegisterUStruct(mRegisteredClasses, uStruct); 1181 | 1182 | if (uClass->HasAnyClassFlags(CLASS_Interface)) 1183 | { 1184 | if (CheckShouldRegisterInterface(uClass)) 1185 | { 1186 | RegisterInterface(uClass); 1187 | } 1188 | } 1189 | } 1190 | } 1191 | 1192 | void RegisterProperties() 1193 | { 1194 | for (auto& pair : mRegisteredStructs) 1195 | { 1196 | RegisterUStructProperties(pair.Key, &pair.Value); 1197 | } 1198 | for (auto& pair : mRegisteredClasses) 1199 | { 1200 | RegisterUStructProperties(pair.Key, &pair.Value); 1201 | } 1202 | } 1203 | 1204 | void RegisterCastFromObject(UClass* pClass, Cflat::Struct* pCfStruct, const Cflat::TypeUsage& pParamTypeUsage) 1205 | { 1206 | Cflat::TypeUsage typeUsage; 1207 | typeUsage.mType = pCfStruct; 1208 | 1209 | Cflat::TypeUsage returnTypeUsage; 1210 | returnTypeUsage.mType = pCfStruct; 1211 | returnTypeUsage.mPointerLevel = 1u; 1212 | 1213 | Cflat::Function* castFromObjectFunction = mEnv->registerFunction("Cast"); 1214 | castFromObjectFunction->mTemplateTypes.push_back(typeUsage); 1215 | castFromObjectFunction->mParameters.push_back(pParamTypeUsage); 1216 | castFromObjectFunction->mReturnTypeUsage = returnTypeUsage; 1217 | castFromObjectFunction->execute = [pClass](const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 1218 | { 1219 | CflatAssert(pArguments.size() == 1); 1220 | CflatAssert(pArguments[0].mTypeUsage.mType->mCategory == TypeCategory::StructOrClass); 1221 | 1222 | char* ptr = nullptr; 1223 | 1224 | UObject* uObj = CflatValueAs(&pArguments[0], UObject*); 1225 | if (uObj) 1226 | { 1227 | UClass* sourceClass = uObj->GetClass(); 1228 | 1229 | if (sourceClass == pClass) 1230 | { 1231 | ptr = CflatValueAs(&pArguments[0], char*); 1232 | } 1233 | else if (sourceClass->IsChildOf(pClass)) 1234 | { 1235 | ptr = CflatValueAs(&pArguments[0], char*); 1236 | } 1237 | } 1238 | 1239 | pOutReturnValue->set(&ptr); 1240 | }; 1241 | } 1242 | 1243 | void RegisterFunctions() 1244 | { 1245 | mUObjectTypeUsage = mEnv->getTypeUsage("UObject*"); 1246 | const Cflat::TypeUsage uClassTypeUsage = mEnv->getTypeUsage("UClass*"); 1247 | const Cflat::TypeUsage uScriptStructTypUsage = mEnv->getTypeUsage("UScriptStruct*"); 1248 | const Cflat::Identifier staticStructIdentifier = "StaticStruct"; 1249 | const Cflat::Identifier staticClassIdentifier = "StaticClass"; 1250 | 1251 | for (auto& pair : mRegisteredStructs) 1252 | { 1253 | // Register StaticStruct method 1254 | UStruct* uStruct = pair.Key; 1255 | UScriptStruct* uScriptStruct = static_cast(uStruct); 1256 | Cflat::Struct* cfStruct = pair.Value.mStruct; 1257 | { 1258 | Cflat::Function* function = cfStruct->registerStaticMethod(staticStructIdentifier); 1259 | function->mReturnTypeUsage = uScriptStructTypUsage; 1260 | function->execute = [uStruct](const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 1261 | { 1262 | CflatAssert(pOutReturnValue); 1263 | pOutReturnValue->set(&uStruct); 1264 | }; 1265 | } 1266 | RegisterUScriptStructConstructors(uScriptStruct, &pair.Value); 1267 | RegisterUStructFunctions(pair.Key, &pair.Value); 1268 | } 1269 | for (auto& pair : mRegisteredClasses) 1270 | { 1271 | UStruct* uStruct = pair.Key; 1272 | UClass* uClass = static_cast(uStruct); 1273 | Cflat::Struct* cfStruct = pair.Value.mStruct; 1274 | // Register StaticClass method 1275 | { 1276 | Cflat::Function* function = cfStruct->registerStaticMethod(staticClassIdentifier); 1277 | function->mReturnTypeUsage = uClassTypeUsage; 1278 | function->execute = [uClass](const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 1279 | { 1280 | CflatAssert(pOutReturnValue); 1281 | pOutReturnValue->set(&uClass); 1282 | }; 1283 | } 1284 | if (uClass->HasAnyClassFlags(CLASS_Interface)) 1285 | { 1286 | pair.Value.mFunctionCount = cfStruct->mFunctionsHolder.getFunctionsCount(); 1287 | } 1288 | else 1289 | { 1290 | RegisterUStructFunctions(uStruct, &pair.Value); 1291 | } 1292 | RegisterCastFromObject(uClass, cfStruct, mUObjectTypeUsage); 1293 | } 1294 | for (auto& pair : mRegisteredInterfaces) 1295 | { 1296 | RegisterInterfaceFunctions(pair.Key, &pair.Value); 1297 | } 1298 | } 1299 | 1300 | void RegisterGameInstanceSubsystem(Cflat::Class* pGameInsance, UClass* pClass, Cflat::Struct* pCfStruct) 1301 | { 1302 | Cflat::TypeUsage typeUsage; 1303 | typeUsage.mType = pCfStruct; 1304 | 1305 | Cflat::TypeUsage returnTypeUsage; 1306 | returnTypeUsage.mType = pCfStruct; 1307 | returnTypeUsage.mPointerLevel = 1u; 1308 | 1309 | const size_t methodIndex = pGameInsance->mMethods.size() - 1u; 1310 | Cflat::Method getSubsystemMethod("GetSubsystem"); 1311 | getSubsystemMethod.mTemplateTypes.push_back(typeUsage); 1312 | getSubsystemMethod.mReturnTypeUsage = returnTypeUsage; 1313 | getSubsystemMethod.execute = [pGameInsance, methodIndex, pClass](const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) 1314 | { 1315 | Cflat::Method* method = &pGameInsance->mMethods[methodIndex]; 1316 | CflatAssert(method->mParameters.size() == pArguments.size()); 1317 | CflatAssert(pOutReturnValue); 1318 | UGameInstanceSubsystem* result = CflatValueAs(&pThis, UGameInstance*)->GetSubsystemBase(pClass); 1319 | pOutReturnValue->set(&result); 1320 | }; 1321 | pGameInsance->mMethods.push_back(getSubsystemMethod); 1322 | } 1323 | 1324 | void RegisterSubsystems() 1325 | { 1326 | const Cflat::TypeUsage uObjectTypeUsage = mEnv->getTypeUsage("UObject*"); 1327 | Cflat::Class* gameInstanceClass = static_cast(mEnv->getGlobalNamespace()->getType("UGameInstance")); 1328 | for (auto& pair : mRegisteredClasses) 1329 | { 1330 | UStruct* uStruct = pair.Key; 1331 | if (uStruct != UGameInstanceSubsystem::StaticClass() && 1332 | uStruct->IsChildOf(UGameInstanceSubsystem::StaticClass())) 1333 | { 1334 | UClass* uClass = static_cast(uStruct); 1335 | Cflat::Struct* cfStruct = pair.Value.mStruct; 1336 | RegisterGameInstanceSubsystem(gameInstanceClass, uClass, cfStruct); 1337 | } 1338 | } 1339 | } 1340 | 1341 | PerHeaderTypes* GetOrCreateHeaderType(UStruct* pStruct, TMap& pHeaders) 1342 | { 1343 | RegisteredInfo* regInfo = FindRegisteredInfoFromUStruct(pStruct); 1344 | 1345 | check(regInfo); 1346 | 1347 | PerHeaderTypes* types = pHeaders.Find(regInfo->mHeader); 1348 | if (types == nullptr) 1349 | { 1350 | types = &(pHeaders.Add(regInfo->mHeader, {})); 1351 | types->mPackage = pStruct->GetPackage(); 1352 | } 1353 | 1354 | return types; 1355 | } 1356 | 1357 | PerHeaderTypes* GetOrCreateHeaderType(UEnum* pEnum, TMap& pHeaders) 1358 | { 1359 | RegisteredEnumInfo* regInfo = mRegisteredEnums.Find(pEnum); 1360 | 1361 | check(regInfo); 1362 | 1363 | PerHeaderTypes* types = pHeaders.Find(regInfo->mHeader); 1364 | if (types == nullptr) 1365 | { 1366 | types = &(pHeaders.Add(regInfo->mHeader, {})); 1367 | types->mPackage = pEnum->GetPackage(); 1368 | } 1369 | 1370 | return types; 1371 | } 1372 | 1373 | 1374 | PerHeaderTypes* GetOrCreateHeaderType(FName pHeader, TMap& pHeaders) 1375 | { 1376 | PerHeaderTypes* types = pHeaders.Find(pHeader); 1377 | if (types == nullptr) 1378 | { 1379 | types = &(pHeaders.Add(pHeader, {})); 1380 | } 1381 | 1382 | return types; 1383 | } 1384 | 1385 | void MapTypesPerHeaders() 1386 | { 1387 | for (const auto& pair : mRegisteredEnums) 1388 | { 1389 | PerHeaderTypes* types = GetOrCreateHeaderType(pair.Key, mTypesPerHeader); 1390 | types->mEnums.Add(pair.Key); 1391 | } 1392 | 1393 | for (const auto& pair : mRegisteredStructs) 1394 | { 1395 | PerHeaderTypes* types = GetOrCreateHeaderType(pair.Key, mTypesPerHeader); 1396 | types->mStructs.Add(pair.Key); 1397 | } 1398 | 1399 | for (const auto& pair : mRegisteredClasses) 1400 | { 1401 | PerHeaderTypes* types = GetOrCreateHeaderType(pair.Key, mTypesPerHeader); 1402 | types->mClasses.Add(pair.Key); 1403 | } 1404 | 1405 | for (const auto& pair : mRegisteredInterfaces) 1406 | { 1407 | PerHeaderTypes* types = GetOrCreateHeaderType(pair.Key, mTypesPerHeader); 1408 | types->mClasses.Add(pair.Key); 1409 | } 1410 | } 1411 | 1412 | void AidHeaderAppendEnum(const UEnum* pUEnum, FString& pOutContent) 1413 | { 1414 | FString strEnum = "\n\n"; 1415 | UEnum::ECppForm enumForm = pUEnum->GetCppForm(); 1416 | 1417 | if (pUEnum->HasMetaData(TEXT("Comment"))) 1418 | { 1419 | strEnum.Append(pUEnum->GetMetaData(TEXT("Comment"))); 1420 | strEnum.Append("\n"); 1421 | } 1422 | 1423 | FString declarationBegin = {}; 1424 | FString declarationEnd = {}; 1425 | FString newLineSpace = kNewLineWithIndent1; 1426 | 1427 | const bool isBitFlags = pUEnum->HasMetaData(TEXT("Bitflags")); 1428 | const bool isBlueprintType = pUEnum->GetBoolMetaData(kBlueprintType); 1429 | const TCHAR* enumIntType = TEXT(""); 1430 | if (isBitFlags) 1431 | { 1432 | if (isBlueprintType) 1433 | { 1434 | enumIntType = TEXT(" : uint8"); 1435 | } 1436 | else 1437 | { 1438 | enumIntType = TEXT(" : uint32"); 1439 | } 1440 | } 1441 | 1442 | switch (enumForm) 1443 | { 1444 | case UEnum::ECppForm::Regular: 1445 | declarationBegin = FString::Printf(TEXT("enum %s%s\n{"), *pUEnum->GetName(), enumIntType); 1446 | declarationEnd = "\n};"; 1447 | break; 1448 | case UEnum::ECppForm::Namespaced: 1449 | declarationBegin = FString::Printf( 1450 | TEXT("namespace %s\n{%senum Type%s{"), 1451 | *pUEnum->GetName(), 1452 | *kNewLineWithIndent1, 1453 | *kNewLineWithIndent1); 1454 | declarationEnd = kNewLineWithIndent1 + "};\n}"; 1455 | newLineSpace = kNewLineWithIndent2; 1456 | break; 1457 | case UEnum::ECppForm::EnumClass: 1458 | declarationBegin = FString::Printf(TEXT("enum class %s%s\n{"), *pUEnum->GetName(), enumIntType); 1459 | declarationEnd = "\n};"; 1460 | break; 1461 | } 1462 | 1463 | strEnum.Append(declarationBegin); 1464 | 1465 | int32 enumCount = pUEnum->NumEnums() - 1; 1466 | for (int32 i = 0; i < enumCount; ++i) 1467 | { 1468 | FString enumComment = pUEnum->GetMetaData(TEXT("Comment"), i); 1469 | int64 value = pUEnum->GetValueByIndex(i); 1470 | FString enumValueName = pUEnum->GetNameStringByIndex(i); 1471 | strEnum.Append(newLineSpace); 1472 | if (!enumComment.IsEmpty()) 1473 | { 1474 | enumComment.RemoveFromEnd(TEXT("\n")); 1475 | strEnum.Append(enumComment); 1476 | strEnum.Append(newLineSpace); 1477 | } 1478 | if (isBitFlags) 1479 | { 1480 | if (isBlueprintType) 1481 | { 1482 | strEnum.Append(FString::Printf(TEXT("%s = 0x%02x"), *enumValueName, value)); 1483 | } 1484 | else 1485 | { 1486 | strEnum.Append(FString::Printf(TEXT("%s = 0x%08x"), *enumValueName, value)); 1487 | } 1488 | } 1489 | else 1490 | { 1491 | strEnum.Append(FString::Printf(TEXT("%s = %d"), *enumValueName, value)); 1492 | } 1493 | if (i < enumCount - 1) 1494 | { 1495 | strEnum.Append(","); 1496 | } 1497 | } 1498 | strEnum.Append(declarationEnd); 1499 | pOutContent.Append(strEnum); 1500 | } 1501 | 1502 | FString FunctionInfoToString(const RegisteredFunctionInfo& pInfo, int pDefaultParameterIndex = -1) 1503 | { 1504 | FString funcStr = ""; 1505 | UFunction* func = pInfo.mFunction; 1506 | bool hasDefaultParameter = pInfo.mFirstDefaultParamIndex != -1 && pDefaultParameterIndex != -1; 1507 | 1508 | if (!hasDefaultParameter) 1509 | { 1510 | FString comment = func->GetMetaData(kMetaComment); 1511 | if (!comment.IsEmpty()) 1512 | { 1513 | comment.RemoveFromEnd(TEXT("\n")); 1514 | funcStr.Append(comment); 1515 | funcStr.Append(kNewLineWithIndent1); 1516 | } 1517 | } 1518 | 1519 | if (func->HasAnyFunctionFlags(FUNC_Static)) 1520 | { 1521 | funcStr.Append("static "); 1522 | } 1523 | 1524 | FProperty* returnProperty = func->GetReturnProperty(); 1525 | if (returnProperty) 1526 | { 1527 | if (returnProperty->HasAnyPropertyFlags(CPF_ConstParm)) 1528 | { 1529 | funcStr.Append("const "); 1530 | } 1531 | FString extendedType; 1532 | FString cppType; 1533 | if (returnProperty->IsA()) 1534 | { 1535 | FByteProperty* byteProp = static_cast(returnProperty); 1536 | if (byteProp->Enum) 1537 | { 1538 | UEnum* uEnum = byteProp->Enum; 1539 | cppType = uEnum->CppType.IsEmpty() ? uEnum->GetName() : uEnum->CppType; 1540 | } 1541 | } 1542 | if (cppType.IsEmpty()) 1543 | { 1544 | cppType = returnProperty->GetCPPType(&extendedType); 1545 | } 1546 | funcStr.Append(cppType); 1547 | if (!extendedType.IsEmpty()) 1548 | { 1549 | funcStr.Append(extendedType); 1550 | } 1551 | if (returnProperty->HasAnyPropertyFlags(CPF_ReferenceParm)) 1552 | { 1553 | funcStr.Append("& "); 1554 | } 1555 | } 1556 | else 1557 | { 1558 | funcStr.Append("void"); 1559 | } 1560 | funcStr.Append(" "); 1561 | funcStr.Append(pInfo.mIdentifier.mName); 1562 | funcStr.Append("("); 1563 | 1564 | 1565 | bool defaultParamsBegan = false; 1566 | int32 propCount = 0; 1567 | for (TFieldIterator propIt(func); 1568 | propIt && propIt->HasAnyPropertyFlags(CPF_Parm) && 1569 | !propIt->HasAnyPropertyFlags(CPF_ReturnParm); 1570 | ++propIt, ++propCount) 1571 | { 1572 | if (hasDefaultParameter && propCount >= pDefaultParameterIndex) 1573 | { 1574 | if (!defaultParamsBegan) 1575 | { 1576 | defaultParamsBegan = true; 1577 | funcStr.Append("/*"); 1578 | } 1579 | } 1580 | if (propCount > 0) 1581 | { 1582 | funcStr.Append(", "); 1583 | } 1584 | 1585 | FString extendedType; 1586 | FString cppType; 1587 | if (propIt->IsA()) 1588 | { 1589 | FByteProperty* byteProp = static_cast(*propIt); 1590 | if (byteProp->Enum) 1591 | { 1592 | UEnum* uEnum = byteProp->Enum; 1593 | cppType = uEnum->CppType.IsEmpty() ? uEnum->GetName() : uEnum->CppType; 1594 | } 1595 | } 1596 | if (cppType.IsEmpty()) 1597 | { 1598 | cppType = propIt->GetCPPType(&extendedType); 1599 | } 1600 | funcStr.Append(cppType); 1601 | 1602 | if (!extendedType.IsEmpty()) 1603 | { 1604 | funcStr.Append(extendedType); 1605 | } 1606 | if (propIt->HasAnyPropertyFlags(CPF_OutParm)) 1607 | { 1608 | funcStr.Append("&"); 1609 | } 1610 | funcStr.Append(" "); 1611 | funcStr.Append(propIt->GetName()); 1612 | 1613 | if (defaultParamsBegan) 1614 | { 1615 | FString metaDataName = FString::Printf(TEXT("CPP_Default_%s"), *propIt->GetName()); 1616 | const FString* metaDataValue = func->FindMetaData(*metaDataName); 1617 | if (metaDataValue && !metaDataValue->IsEmpty()) 1618 | { 1619 | funcStr.Append(" = "); 1620 | funcStr.Append(*metaDataValue); 1621 | } 1622 | } 1623 | } 1624 | if (defaultParamsBegan) 1625 | { 1626 | funcStr.Append(" */"); 1627 | } 1628 | funcStr.Append(")"); 1629 | if (func->HasAnyFunctionFlags(FUNC_Const)) 1630 | { 1631 | funcStr.Append(" const"); 1632 | } 1633 | funcStr.Append(";"); 1634 | return funcStr; 1635 | } 1636 | 1637 | FString FunctionInfoInterfaceWrapperToString(const RegisteredFunctionInfo& pInfo, int pDefaultParameterIndex = -1) 1638 | { 1639 | FString funcStr = ""; 1640 | UFunction* func = pInfo.mFunction; 1641 | bool hasDefaultParameter = pInfo.mFirstDefaultParamIndex != -1 && pDefaultParameterIndex != -1; 1642 | 1643 | if (!hasDefaultParameter) 1644 | { 1645 | FString comment = func->GetMetaData(kMetaComment); 1646 | if (!comment.IsEmpty()) 1647 | { 1648 | comment.RemoveFromEnd(TEXT("\n")); 1649 | funcStr.Append(comment); 1650 | funcStr.Append(kNewLineWithIndent1); 1651 | } 1652 | } 1653 | 1654 | funcStr.Append("static "); 1655 | 1656 | FProperty* returnProperty = func->GetReturnProperty(); 1657 | if (returnProperty) 1658 | { 1659 | if (returnProperty->HasAnyPropertyFlags(CPF_ConstParm)) 1660 | { 1661 | funcStr.Append("const "); 1662 | } 1663 | FString extendedType; 1664 | FString cppType; 1665 | if (returnProperty->IsA()) 1666 | { 1667 | FByteProperty* byteProp = static_cast(returnProperty); 1668 | if (byteProp->Enum) 1669 | { 1670 | UEnum* uEnum = byteProp->Enum; 1671 | cppType = uEnum->CppType.IsEmpty() ? uEnum->GetName() : uEnum->CppType; 1672 | } 1673 | } 1674 | if (cppType.IsEmpty()) 1675 | { 1676 | cppType = returnProperty->GetCPPType(&extendedType); 1677 | } 1678 | funcStr.Append(cppType); 1679 | if (!extendedType.IsEmpty()) 1680 | { 1681 | funcStr.Append(extendedType); 1682 | } 1683 | if (returnProperty->HasAnyPropertyFlags(CPF_ReferenceParm)) 1684 | { 1685 | funcStr.Append("& "); 1686 | } 1687 | } 1688 | else 1689 | { 1690 | funcStr.Append("void"); 1691 | } 1692 | funcStr.Append(" Execute_"); 1693 | funcStr.Append(pInfo.mIdentifier.mName); 1694 | funcStr.Append("("); 1695 | 1696 | 1697 | bool defaultParamsBegan = false; 1698 | int32 propCount = 1; 1699 | funcStr.Append("UObject* Obj"); 1700 | for (TFieldIterator propIt(func); 1701 | propIt && propIt->HasAnyPropertyFlags(CPF_Parm) && 1702 | !propIt->HasAnyPropertyFlags(CPF_ReturnParm); 1703 | ++propIt, ++propCount) 1704 | { 1705 | if (hasDefaultParameter && propCount >= pDefaultParameterIndex) 1706 | { 1707 | if (!defaultParamsBegan) 1708 | { 1709 | defaultParamsBegan = true; 1710 | funcStr.Append("/*"); 1711 | } 1712 | } 1713 | if (propCount > 0) 1714 | { 1715 | funcStr.Append(", "); 1716 | } 1717 | 1718 | FString extendedType; 1719 | FString cppType; 1720 | if (propIt->IsA()) 1721 | { 1722 | FByteProperty* byteProp = static_cast(*propIt); 1723 | if (byteProp->Enum) 1724 | { 1725 | UEnum* uEnum = byteProp->Enum; 1726 | cppType = uEnum->CppType.IsEmpty() ? uEnum->GetName() : uEnum->CppType; 1727 | } 1728 | } 1729 | if (cppType.IsEmpty()) 1730 | { 1731 | cppType = propIt->GetCPPType(&extendedType); 1732 | } 1733 | funcStr.Append(cppType); 1734 | 1735 | if (!extendedType.IsEmpty()) 1736 | { 1737 | funcStr.Append(extendedType); 1738 | } 1739 | if (propIt->HasAnyPropertyFlags(CPF_OutParm)) 1740 | { 1741 | funcStr.Append("&"); 1742 | } 1743 | funcStr.Append(" "); 1744 | funcStr.Append(propIt->GetName()); 1745 | 1746 | if (defaultParamsBegan) 1747 | { 1748 | FString metaDataName = FString::Printf(TEXT("CPP_Default_%s"), *propIt->GetName()); 1749 | const FString* metaDataValue = func->FindMetaData(*metaDataName); 1750 | if (metaDataValue && !metaDataValue->IsEmpty()) 1751 | { 1752 | funcStr.Append(" = "); 1753 | funcStr.Append(*metaDataValue); 1754 | } 1755 | } 1756 | } 1757 | if (defaultParamsBegan) 1758 | { 1759 | funcStr.Append(" */"); 1760 | } 1761 | funcStr.Append(");"); 1762 | return funcStr; 1763 | } 1764 | 1765 | void AidHeaderAppendStruct(UStruct* pUStruct, FString& pOutContent) 1766 | { 1767 | const RegisteredInfo* regInfo = mRegisteredStructs.Find(pUStruct); 1768 | if (regInfo == nullptr) 1769 | { 1770 | return; 1771 | } 1772 | Cflat::Struct* cfStruct = regInfo->mStruct; 1773 | // Check if the struct was overwritten 1774 | { 1775 | Cflat::Type* type = mEnv->getType(regInfo->mIdentifier); 1776 | if (!type) 1777 | { 1778 | return; 1779 | } 1780 | Cflat::Struct* regStruct = static_cast(type); 1781 | // Was overwriten, ignore it 1782 | if (regStruct != cfStruct) 1783 | { 1784 | return; 1785 | } 1786 | } 1787 | 1788 | FString strStruct = "\n"; 1789 | 1790 | // Struct declaration 1791 | { 1792 | if (pUStruct->HasMetaData(kMetaComment)) 1793 | { 1794 | strStruct.Append(pUStruct->GetMetaData(kMetaComment)); 1795 | } 1796 | strStruct.Append("\nstruct "); 1797 | strStruct.Append(cfStruct->mIdentifier.mName); 1798 | 1799 | // Base types 1800 | if (cfStruct->mBaseTypes.size() > 0) 1801 | { 1802 | strStruct.Append(" :"); 1803 | for (size_t i = 0; i < cfStruct->mBaseTypes.size(); ++i) 1804 | { 1805 | strStruct.Append(" public "); 1806 | strStruct.Append(cfStruct->mBaseTypes[i].mType->mIdentifier.mName); 1807 | if (i < cfStruct->mBaseTypes.size() - 1) 1808 | { 1809 | strStruct.Append(","); 1810 | } 1811 | } 1812 | } 1813 | } 1814 | 1815 | // Body 1816 | strStruct.Append("\n{"); 1817 | FString publicPropStr = {}; 1818 | 1819 | // constructor 1820 | for (size_t i = 0u; i < cfStruct->mMethods.size(); i++) 1821 | { 1822 | if (cfStruct->mMethods[i].mIdentifier == kEmptyId) 1823 | { 1824 | Cflat::Method* method = &cfStruct->mMethods[i]; 1825 | FString funcStr = kNewLineWithIndent1; 1826 | funcStr.Append(cfStruct->mIdentifier.mName); 1827 | funcStr.Append("("); 1828 | 1829 | for (size_t j = 0u; j < method->mParameters.size(); j++) 1830 | { 1831 | if (j > 0) 1832 | { 1833 | funcStr.Append(", "); 1834 | } 1835 | Cflat::TypeUsage* paramUsage = &method->mParameters[j]; 1836 | funcStr.Append(paramUsage->mType->mIdentifier.mName); 1837 | } 1838 | funcStr.Append(");"); 1839 | strStruct.Append(funcStr); 1840 | } 1841 | } 1842 | strStruct.Append(kNewLineWithIndent1 + "static UScriptStruct* StaticStruct();"); 1843 | 1844 | // properties 1845 | for (const FProperty* prop : regInfo->mProperties) 1846 | { 1847 | // Inherited properties should be in their base classes 1848 | UStruct* owner = prop->GetOwnerStruct(); 1849 | if (owner != pUStruct) 1850 | { 1851 | continue; 1852 | } 1853 | 1854 | // Ignore Protected/Private properties 1855 | if (prop->HasAnyPropertyFlags( 1856 | CPF_NativeAccessSpecifierProtected | 1857 | CPF_NativeAccessSpecifierPrivate)) 1858 | { 1859 | continue; 1860 | } 1861 | FString propStr = kNewLineWithIndent1; 1862 | 1863 | if (prop->HasMetaData(kMetaComment)) 1864 | { 1865 | FString comment = prop->GetMetaData(kMetaComment); 1866 | comment.RemoveFromEnd(TEXT("\n")); 1867 | propStr.Append(comment); 1868 | propStr.Append(kNewLineWithIndent1); 1869 | } 1870 | { 1871 | FString extendedType; 1872 | propStr.Append(prop->GetCPPType(&extendedType)); 1873 | if (!extendedType.IsEmpty()) 1874 | { 1875 | propStr.Append(extendedType); 1876 | } 1877 | } 1878 | propStr.Append(" "); 1879 | propStr.Append(prop->GetName() + ";"); 1880 | 1881 | publicPropStr.Append(propStr); 1882 | } 1883 | 1884 | // Members that where manually extended 1885 | if (cfStruct->mMembers.size() > regInfo->mMembersCount) 1886 | { 1887 | publicPropStr.Append("\n"); 1888 | publicPropStr.Append(kNewLineWithIndent1); 1889 | publicPropStr.Append("// Begin manually extended members: "); 1890 | for (int i = regInfo->mMembersCount; i < cfStruct->mMembers.size(); ++i) 1891 | { 1892 | FString propStr = UnrealModule::GetMemberAsString(&cfStruct->mMembers[i]); 1893 | publicPropStr.Append(kNewLineWithIndent1); 1894 | publicPropStr.Append(propStr + ";"); 1895 | } 1896 | publicPropStr.Append(kNewLineWithIndent1); 1897 | publicPropStr.Append("// End manually extended members"); 1898 | } 1899 | 1900 | // functions 1901 | FString publicFuncStr = {}; 1902 | 1903 | for (const RegisteredFunctionInfo& info : regInfo->mFunctions) 1904 | { 1905 | { 1906 | FString funcStr = FunctionInfoToString(info); 1907 | publicFuncStr.Append(kNewLineWithIndent1); 1908 | publicFuncStr.Append(funcStr); 1909 | } 1910 | 1911 | if (info.mFirstDefaultParamIndex != -1) 1912 | { 1913 | for (int i = info.mParameters.size() - 1; i >= 0; --i) 1914 | { 1915 | if (i >= info.mFirstDefaultParamIndex) 1916 | { 1917 | FString funcStr = FunctionInfoToString(info, i); 1918 | publicFuncStr.Append(kNewLineWithIndent1); 1919 | publicFuncStr.Append(funcStr); 1920 | } 1921 | } 1922 | } 1923 | } 1924 | 1925 | 1926 | // Manually extended methods/functions 1927 | if (cfStruct->mMethods.size() > regInfo->mMethodCount) 1928 | { 1929 | publicFuncStr.Append("\n"); 1930 | publicFuncStr.Append(kNewLineWithIndent1); 1931 | publicFuncStr.Append("// Begin Methods manually extended: "); 1932 | for (int i = regInfo->mMethodCount; i < cfStruct->mMethods.size(); ++i) 1933 | { 1934 | FString methodStr = UnrealModule::GetMethodAsString(&cfStruct->mMethods[i]); 1935 | publicFuncStr.Append(kNewLineWithIndent1); 1936 | publicFuncStr.Append(methodStr + ";"); 1937 | } 1938 | publicFuncStr.Append(kNewLineWithIndent1); 1939 | publicFuncStr.Append("// End Methods manually extended"); 1940 | } 1941 | 1942 | size_t functionCount = cfStruct->mFunctionsHolder.getFunctionsCount(); 1943 | if (functionCount > regInfo->mFunctionCount) 1944 | { 1945 | CflatSTLVector(Function*) functions; 1946 | cfStruct->mFunctionsHolder.getAllFunctions(&functions); 1947 | publicFuncStr.Append("\n"); 1948 | publicFuncStr.Append(kNewLineWithIndent1); 1949 | publicFuncStr.Append("// Begin Functions manually extended: "); 1950 | for (int i = 0; i < functions.size(); ++i) 1951 | { 1952 | if (regInfo->mStaticFunctions.Find(functions[i])) 1953 | { 1954 | continue; 1955 | } 1956 | FString funcStr = UnrealModule::GetFunctionAsString(functions[i]); 1957 | publicFuncStr.Append(kNewLineWithIndent1 + "static "); 1958 | publicFuncStr.Append(funcStr + ";"); 1959 | } 1960 | publicFuncStr.Append(kNewLineWithIndent1); 1961 | publicFuncStr.Append("// End Functions manually extended"); 1962 | } 1963 | 1964 | if (!publicPropStr.IsEmpty()) 1965 | { 1966 | strStruct.Append("\n"); 1967 | strStruct.Append(publicPropStr); 1968 | } 1969 | strStruct.Append(publicFuncStr); 1970 | strStruct.Append("\n};"); 1971 | 1972 | pOutContent.Append(strStruct); 1973 | } 1974 | 1975 | void AidHeaderAppendInterface(UClass* pUClass, FString& pOutContent) 1976 | { 1977 | const RegisteredInfo* regInfo = mRegisteredInterfaces.Find(pUClass); 1978 | if (regInfo == nullptr) 1979 | { 1980 | return; 1981 | } 1982 | 1983 | Cflat::Struct* cfStruct = regInfo->mStruct; 1984 | 1985 | // Check if the struct was overwritten 1986 | { 1987 | Cflat::Type* type = mEnv->getType(regInfo->mIdentifier); 1988 | if (!type) 1989 | { 1990 | return; 1991 | } 1992 | Cflat::Struct* regStruct = static_cast(type); 1993 | // Was overwriten, ignore it 1994 | if (regStruct != cfStruct) 1995 | { 1996 | return; 1997 | } 1998 | } 1999 | 2000 | FString strClass = "\n"; 2001 | 2002 | // Interface declaration 2003 | strClass.Append("\nclass "); 2004 | strClass.Append(cfStruct->mIdentifier.mName); 2005 | // Body 2006 | strClass.Append("\n{"); 2007 | 2008 | // functions 2009 | FString publicFuncStr = ""; 2010 | 2011 | for (const RegisteredFunctionInfo& info : regInfo->mFunctions) 2012 | { 2013 | { 2014 | FString funcStr = FunctionInfoToString(info); 2015 | publicFuncStr.Append(kNewLineWithIndent1); 2016 | publicFuncStr.Append(funcStr); 2017 | } 2018 | 2019 | if (info.mFirstDefaultParamIndex != -1) 2020 | { 2021 | for (int i = info.mParameters.size() - 1; i >= 0; --i) 2022 | { 2023 | if (i >= info.mFirstDefaultParamIndex) 2024 | { 2025 | FString funcStr = FunctionInfoToString(info, i); 2026 | publicFuncStr.Append(kNewLineWithIndent1); 2027 | publicFuncStr.Append(funcStr); 2028 | } 2029 | } 2030 | } 2031 | if (info.mFunction->HasAllFunctionFlags(FUNC_BlueprintEvent | FUNC_Event)) 2032 | { 2033 | FString funcStr = FunctionInfoInterfaceWrapperToString(info); 2034 | publicFuncStr.Append(kNewLineWithIndent1); 2035 | publicFuncStr.Append(funcStr); 2036 | } 2037 | } 2038 | 2039 | // Manually extended methods/functions 2040 | if (cfStruct->mMethods.size() > regInfo->mMethodCount) 2041 | { 2042 | publicFuncStr.Append("\n"); 2043 | publicFuncStr.Append(kNewLineWithIndent1); 2044 | publicFuncStr.Append("// Begin Methods manually extended: "); 2045 | TMap> registeredTemplated; 2046 | 2047 | for (int i = regInfo->mMethodCount; i < cfStruct->mMethods.size(); ++i) 2048 | { 2049 | const Cflat::Method* method = &cfStruct->mMethods[i]; 2050 | 2051 | FString methodStr = UnrealModule::GetMethodAsString(method); 2052 | bool hasTemplates = method->mTemplateTypes.size() > 0; 2053 | if (hasTemplates) 2054 | { 2055 | TArray* methods = registeredTemplated.Find(methodStr); 2056 | if (methods == nullptr) 2057 | { 2058 | methods = ®isteredTemplated.Add(methodStr, {}); 2059 | } 2060 | methods->Add(method); 2061 | } 2062 | else 2063 | { 2064 | 2065 | publicFuncStr.Append(kNewLineWithIndent1); 2066 | publicFuncStr.Append(methodStr + ";"); 2067 | } 2068 | } 2069 | 2070 | for (const auto& it : registeredTemplated) 2071 | { 2072 | FString typesComment = "/** T available as: "; 2073 | for (const Cflat::Method* method : it.Value) 2074 | { 2075 | for (const Cflat::TypeUsage& templateType : method->mTemplateTypes) 2076 | { 2077 | typesComment.Append(kNewLineWithIndent1); 2078 | typesComment.Append("* "); 2079 | typesComment.Append(UnrealModule::GetTypeUsageAsString(templateType)); 2080 | } 2081 | } 2082 | typesComment.Append(kNewLineWithIndent1); 2083 | typesComment.Append("*/"); 2084 | 2085 | publicFuncStr.Append(kNewLineWithIndent1); 2086 | publicFuncStr.Append(typesComment); 2087 | publicFuncStr.Append(kNewLineWithIndent1); 2088 | publicFuncStr.Append(it.Key + ";"); 2089 | } 2090 | 2091 | publicFuncStr.Append(kNewLineWithIndent1); 2092 | publicFuncStr.Append("// End Methods manually extended"); 2093 | } 2094 | 2095 | size_t functionCount = cfStruct->mFunctionsHolder.getFunctionsCount(); 2096 | if (functionCount > regInfo->mFunctionCount) 2097 | { 2098 | CflatSTLVector(Function*) functions; 2099 | cfStruct->mFunctionsHolder.getAllFunctions(&functions); 2100 | publicFuncStr.Append("\n"); 2101 | publicFuncStr.Append(kNewLineWithIndent1); 2102 | publicFuncStr.Append("// Begin Functions manually extended: "); 2103 | for (int i = 0; i < functions.size(); ++i) 2104 | { 2105 | if (regInfo->mStaticFunctions.Find(functions[i])) 2106 | { 2107 | continue; 2108 | } 2109 | FString funcStr = UnrealModule::GetFunctionAsString(functions[i]); 2110 | publicFuncStr.Append(kNewLineWithIndent1 + "static "); 2111 | publicFuncStr.Append(funcStr + ";"); 2112 | } 2113 | publicFuncStr.Append(kNewLineWithIndent1); 2114 | publicFuncStr.Append("// End Functions manually extended"); 2115 | } 2116 | 2117 | strClass.Append("\npublic:"); 2118 | strClass.Append(publicFuncStr); 2119 | strClass.Append("\n};"); 2120 | 2121 | pOutContent.Append(strClass); 2122 | } 2123 | 2124 | void AidHeaderAppendClass(UStruct* pUStruct, FString& pOutContent) 2125 | { 2126 | const RegisteredInfo* regInfo = mRegisteredClasses.Find(pUStruct); 2127 | if (regInfo == nullptr) 2128 | { 2129 | return; 2130 | } 2131 | 2132 | UClass* uClass = static_cast(pUStruct); 2133 | if (uClass->HasAnyClassFlags(CLASS_Interface)) 2134 | { 2135 | AidHeaderAppendInterface(uClass, pOutContent); 2136 | } 2137 | 2138 | Cflat::Struct* cfStruct = regInfo->mStruct; 2139 | 2140 | // Check if the struct was overwritten 2141 | { 2142 | Cflat::Type* type = mEnv->getType(regInfo->mIdentifier); 2143 | if (!type) 2144 | { 2145 | return; 2146 | } 2147 | Cflat::Struct* regStruct = static_cast(type); 2148 | // Was overwriten, ignore it 2149 | if (regStruct != cfStruct) 2150 | { 2151 | return; 2152 | } 2153 | } 2154 | 2155 | FString strClass = "\n"; 2156 | 2157 | // Class declaration 2158 | { 2159 | if (uClass->HasMetaData(kMetaComment)) 2160 | { 2161 | strClass.Append(uClass->GetMetaData(kMetaComment)); 2162 | } 2163 | strClass.Append("\nclass "); 2164 | strClass.Append(cfStruct->mIdentifier.mName); 2165 | 2166 | // Base types 2167 | if (cfStruct->mBaseTypes.size() > 0) 2168 | { 2169 | strClass.Append(" :"); 2170 | for (size_t i = 0; i < cfStruct->mBaseTypes.size(); ++i) 2171 | { 2172 | strClass.Append(" public "); 2173 | strClass.Append(cfStruct->mBaseTypes[i].mType->mIdentifier.mName); 2174 | if (i < cfStruct->mBaseTypes.size() - 1) 2175 | { 2176 | strClass.Append(","); 2177 | } 2178 | } 2179 | } 2180 | } 2181 | 2182 | // Body 2183 | strClass.Append("\n{"); 2184 | FString publicPropStr = ""; 2185 | 2186 | // properties 2187 | for (const FProperty* prop : regInfo->mProperties) 2188 | { 2189 | // Inherited properties should be in their base classes 2190 | if (prop->GetOwnerClass() != uClass) 2191 | { 2192 | continue; 2193 | } 2194 | 2195 | // Ignore non public properties 2196 | if (!prop->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPublic)) 2197 | { 2198 | continue; 2199 | } 2200 | FString propStr = kNewLineWithIndent1; 2201 | 2202 | if (prop->HasMetaData(kMetaComment)) 2203 | { 2204 | FString comment = prop->GetMetaData(kMetaComment); 2205 | comment.RemoveFromEnd(TEXT("\n")); 2206 | propStr.Append(comment); 2207 | propStr.Append(kNewLineWithIndent1); 2208 | } 2209 | { 2210 | FString extendedType; 2211 | propStr.Append(prop->GetCPPType(&extendedType)); 2212 | if (!extendedType.IsEmpty()) 2213 | { 2214 | propStr.Append(extendedType); 2215 | } 2216 | } 2217 | propStr.Append(" "); 2218 | propStr.Append(prop->GetName() + ";"); 2219 | 2220 | publicPropStr.Append(propStr); 2221 | } 2222 | 2223 | // Members that where manually extended 2224 | if (cfStruct->mMembers.size() > regInfo->mMembersCount) 2225 | { 2226 | publicPropStr.Append("\n"); 2227 | publicPropStr.Append(kNewLineWithIndent1); 2228 | publicPropStr.Append("// Begin manually extended members: "); 2229 | for (int i = regInfo->mMembersCount; i < cfStruct->mMembers.size(); ++i) 2230 | { 2231 | FString propStr = UnrealModule::GetMemberAsString(&cfStruct->mMembers[i]); 2232 | publicPropStr.Append(kNewLineWithIndent1); 2233 | publicPropStr.Append(propStr + ";"); 2234 | } 2235 | publicPropStr.Append(kNewLineWithIndent1); 2236 | publicPropStr.Append("// End manually extended members"); 2237 | } 2238 | 2239 | // functions 2240 | FString publicFuncStr = kNewLineWithIndent1 + "static UClass* StaticClass();"; 2241 | 2242 | for (const RegisteredFunctionInfo& info : regInfo->mFunctions) 2243 | { 2244 | { 2245 | FString funcStr = FunctionInfoToString(info); 2246 | publicFuncStr.Append(kNewLineWithIndent1); 2247 | publicFuncStr.Append(funcStr); 2248 | } 2249 | 2250 | if (info.mFirstDefaultParamIndex != -1) 2251 | { 2252 | for (int i = info.mParameters.size() - 1; i >= 0; --i) 2253 | { 2254 | if (i >= info.mFirstDefaultParamIndex) 2255 | { 2256 | FString funcStr = FunctionInfoToString(info, i); 2257 | publicFuncStr.Append(kNewLineWithIndent1); 2258 | publicFuncStr.Append(funcStr); 2259 | } 2260 | } 2261 | } 2262 | } 2263 | 2264 | // Manually extended methods/functions 2265 | if (cfStruct->mMethods.size() > regInfo->mMethodCount) 2266 | { 2267 | publicFuncStr.Append("\n"); 2268 | publicFuncStr.Append(kNewLineWithIndent1); 2269 | publicFuncStr.Append("// Begin Methods manually extended: "); 2270 | TMap> registeredTemplated; 2271 | 2272 | for (int i = regInfo->mMethodCount; i < cfStruct->mMethods.size(); ++i) 2273 | { 2274 | const Cflat::Method* method = &cfStruct->mMethods[i]; 2275 | 2276 | FString methodStr = UnrealModule::GetMethodAsString(method); 2277 | bool hasTemplates = method->mTemplateTypes.size() > 0; 2278 | if (hasTemplates) 2279 | { 2280 | TArray* methods = registeredTemplated.Find(methodStr); 2281 | if (methods == nullptr) 2282 | { 2283 | methods = ®isteredTemplated.Add(methodStr, {}); 2284 | } 2285 | methods->Add(method); 2286 | } 2287 | else 2288 | { 2289 | 2290 | publicFuncStr.Append(kNewLineWithIndent1); 2291 | publicFuncStr.Append(methodStr + ";"); 2292 | } 2293 | } 2294 | 2295 | for (const auto& it : registeredTemplated) 2296 | { 2297 | FString typesComment = "/** T available as: "; 2298 | for (const Cflat::Method* method : it.Value) 2299 | { 2300 | for (const Cflat::TypeUsage& templateType : method->mTemplateTypes) 2301 | { 2302 | typesComment.Append(kNewLineWithIndent1); 2303 | typesComment.Append("* "); 2304 | typesComment.Append(UnrealModule::GetTypeUsageAsString(templateType)); 2305 | } 2306 | } 2307 | typesComment.Append(kNewLineWithIndent1); 2308 | typesComment.Append("*/"); 2309 | 2310 | publicFuncStr.Append(kNewLineWithIndent1); 2311 | publicFuncStr.Append(typesComment); 2312 | publicFuncStr.Append(kNewLineWithIndent1); 2313 | publicFuncStr.Append(it.Key + ";"); 2314 | } 2315 | 2316 | publicFuncStr.Append(kNewLineWithIndent1); 2317 | publicFuncStr.Append("// End Methods manually extended"); 2318 | } 2319 | 2320 | size_t functionCount = cfStruct->mFunctionsHolder.getFunctionsCount(); 2321 | if (functionCount > regInfo->mFunctionCount) 2322 | { 2323 | CflatSTLVector(Function*) functions; 2324 | cfStruct->mFunctionsHolder.getAllFunctions(&functions); 2325 | publicFuncStr.Append("\n"); 2326 | publicFuncStr.Append(kNewLineWithIndent1); 2327 | publicFuncStr.Append("// Begin Functions manually extended: "); 2328 | for (int i = 0; i < functions.size(); ++i) 2329 | { 2330 | if (regInfo->mStaticFunctions.Find(functions[i])) 2331 | { 2332 | continue; 2333 | } 2334 | FString funcStr = UnrealModule::GetFunctionAsString(functions[i]); 2335 | publicFuncStr.Append(kNewLineWithIndent1 + "static "); 2336 | publicFuncStr.Append(funcStr + ";"); 2337 | } 2338 | publicFuncStr.Append(kNewLineWithIndent1); 2339 | publicFuncStr.Append("// End Functions manually extended"); 2340 | } 2341 | 2342 | strClass.Append("\npublic:"); 2343 | if (!publicPropStr.IsEmpty()) 2344 | { 2345 | strClass.Append(publicPropStr); 2346 | strClass.Append("\n"); 2347 | } 2348 | strClass.Append(publicFuncStr); 2349 | strClass.Append("\n};"); 2350 | 2351 | pOutContent.Append(strClass); 2352 | } 2353 | 2354 | void AppendStructWithDependenciesRecursively(FName pHeader, PerHeaderTypes& pTypes, UStruct* pStruct) 2355 | { 2356 | if (pTypes.mIncluded.Find(pStruct)) 2357 | { 2358 | return; 2359 | } 2360 | 2361 | if (mHeaderClassesToIgnore.Find(pStruct->GetFName()) || mHeaderStructsToIgnore.Find(pStruct->GetFName())) 2362 | { 2363 | return; 2364 | } 2365 | 2366 | RegisteredInfo* regInfo = FindRegisteredInfoFromUStruct(pStruct); 2367 | if (!regInfo) 2368 | { 2369 | return; 2370 | } 2371 | 2372 | pTypes.mIncluded.Add(pStruct); 2373 | 2374 | for (Cflat::Type* cfType : regInfo->mDependencies) 2375 | { 2376 | UStruct** depUStruct = mCflatTypeToStruct.Find(cfType); 2377 | if (!depUStruct) 2378 | { 2379 | continue; 2380 | } 2381 | 2382 | RegisteredInfo* depRegInfo = FindRegisteredInfoFromUStruct(*depUStruct); 2383 | 2384 | if (!depRegInfo) 2385 | { 2386 | continue; 2387 | } 2388 | 2389 | if (depRegInfo->mHeader != pHeader) 2390 | { 2391 | continue; 2392 | } 2393 | 2394 | // Circular dependency. Forward declare it. 2395 | if (depRegInfo->mDependencies.Find(regInfo->mStruct)) 2396 | { 2397 | mForwardDeclartionTypes.Add(cfType); 2398 | mForwardDeclartionTypes.Add(regInfo->mStruct); 2399 | } 2400 | 2401 | AppendStructWithDependenciesRecursively(pHeader, pTypes, *depUStruct); 2402 | } 2403 | 2404 | if (regInfo->mIsClass) 2405 | { 2406 | AidHeaderAppendClass(pStruct, pTypes.mHeaderContent); 2407 | } 2408 | else 2409 | { 2410 | AidHeaderAppendStruct(pStruct, pTypes.mHeaderContent); 2411 | } 2412 | } 2413 | 2414 | void CreateHeaderContent(FName pHeader, TArray& pHeaderIncludeOrder) 2415 | { 2416 | if (mHeaderAlreadyIncluded.Find(pHeader)) 2417 | { 2418 | return; 2419 | } 2420 | 2421 | PerHeaderTypes* types = mTypesPerHeader.Find(pHeader); 2422 | if (!types) 2423 | { 2424 | return; 2425 | } 2426 | 2427 | mHeaderAlreadyIncluded.Add(pHeader); 2428 | 2429 | // First we check for header dependency 2430 | for (const UStruct* uStruct : types->mStructs) 2431 | { 2432 | if (mHeaderStructsToIgnore.Find(uStruct->GetFName())) 2433 | { 2434 | continue; 2435 | } 2436 | 2437 | RegisteredInfo* regInfo = mRegisteredStructs.Find(uStruct); 2438 | if (!regInfo) 2439 | { 2440 | continue; 2441 | } 2442 | 2443 | for (Cflat::Type* cfType : regInfo->mDependencies) 2444 | { 2445 | FName* header = mCflatTypeToHeader.Find(cfType); 2446 | 2447 | if (!header || *header == pHeader) 2448 | { 2449 | continue; 2450 | } 2451 | 2452 | if (!mHeaderAlreadyIncluded.Find(*header)) 2453 | { 2454 | CreateHeaderContent(*header, pHeaderIncludeOrder); 2455 | } 2456 | } 2457 | } 2458 | 2459 | for (const UStruct* uStruct : types->mClasses) 2460 | { 2461 | if (mHeaderClassesToIgnore.Find(uStruct->GetFName())) 2462 | { 2463 | continue; 2464 | } 2465 | 2466 | RegisteredInfo* regInfo = mRegisteredClasses.Find(uStruct); 2467 | if (!regInfo) 2468 | { 2469 | regInfo = mRegisteredInterfaces.Find(uStruct); 2470 | } 2471 | if (!regInfo) 2472 | { 2473 | continue; 2474 | } 2475 | 2476 | for (Cflat::Type* cfType : regInfo->mDependencies) 2477 | { 2478 | FName* header = mCflatTypeToHeader.Find(cfType); 2479 | 2480 | if (!header || *header == pHeader) 2481 | { 2482 | continue; 2483 | } 2484 | 2485 | if (!mHeaderAlreadyIncluded.Find(*header)) 2486 | { 2487 | CreateHeaderContent(*header, pHeaderIncludeOrder); 2488 | } 2489 | } 2490 | } 2491 | 2492 | pHeaderIncludeOrder.Add(pHeader); 2493 | 2494 | // Generate the header strings 2495 | { 2496 | const FString kHeaderName = pHeader == NAME_None ? TEXT("Unknown Header") : pHeader.ToString(); 2497 | types->mHeaderContent = FString::Printf(TEXT("\n\n%s\n// %s\n%s"), *kHeaderSeparator, *kHeaderName, *kHeaderSeparator); 2498 | } 2499 | 2500 | // Enums 2501 | for (const UEnum* uEnum : types->mEnums) 2502 | { 2503 | if (mHeaderEnumsToIgnore.Find(uEnum->GetFName())) 2504 | { 2505 | continue; 2506 | } 2507 | AidHeaderAppendEnum(uEnum, types->mHeaderContent); 2508 | } 2509 | 2510 | for (UStruct* uStruct : types->mStructs) 2511 | { 2512 | if (mHeaderStructsToIgnore.Find(uStruct->GetFName())) 2513 | { 2514 | continue; 2515 | } 2516 | 2517 | AppendStructWithDependenciesRecursively(pHeader, *types, uStruct); 2518 | } 2519 | 2520 | for (UStruct* uStruct : types->mClasses) 2521 | { 2522 | if (mHeaderClassesToIgnore.Find(uStruct->GetFName())) 2523 | { 2524 | continue; 2525 | } 2526 | 2527 | AppendStructWithDependenciesRecursively(pHeader, *types, uStruct); 2528 | } 2529 | 2530 | } 2531 | 2532 | void GenerateAidHeader(const FString& pFilePath) 2533 | { 2534 | FString content = "// Auto Generated From Auto Registered UClasses"; 2535 | content.Append("\n#pragma once"); 2536 | content.Append("\n#if defined (CFLAT_ENABLED)"); 2537 | 2538 | FString includeContent = "// Auto Generated From Auto Registered UClasses"; 2539 | includeContent.Append("\n#pragma once"); 2540 | 2541 | MapTypesPerHeaders(); 2542 | 2543 | TArray headerIncludeOrder; 2544 | headerIncludeOrder.Reserve(mTypesPerHeader.Num()); 2545 | 2546 | for (auto& typesPair : mTypesPerHeader) 2547 | { 2548 | CreateHeaderContent(typesPair.Key, headerIncludeOrder); 2549 | } 2550 | 2551 | // Forward declartions 2552 | { 2553 | FString fwdStructs = "\n\n// Forward Structs Declaration"; 2554 | FString fwdClasses = "\n\n// Forward Classes Declaration"; 2555 | 2556 | for (Cflat::Type* fwdType : mForwardDeclartionTypes) 2557 | { 2558 | UStruct** uStruct = mCflatTypeToStruct.Find(fwdType); 2559 | if (!uStruct) 2560 | { 2561 | continue; 2562 | } 2563 | 2564 | RegisteredInfo* regInfo = mRegisteredStructs.Find(*uStruct); 2565 | if (mRegisteredStructs.Find(*uStruct)) 2566 | { 2567 | fwdStructs.Append("\nstruct "); 2568 | fwdStructs.Append(fwdType->mIdentifier.mName); 2569 | fwdStructs.Append(";"); 2570 | } 2571 | else if (mRegisteredClasses.Find(*uStruct)) 2572 | { 2573 | fwdClasses.Append("\nclass "); 2574 | fwdClasses.Append(fwdType->mIdentifier.mName); 2575 | fwdClasses.Append(";"); 2576 | } 2577 | } 2578 | 2579 | content.Append(fwdStructs); 2580 | content.Append(fwdClasses); 2581 | content.Append("\n"); 2582 | } 2583 | 2584 | for (const FName& headerName : headerIncludeOrder) 2585 | { 2586 | PerHeaderTypes& types = mTypesPerHeader[headerName]; 2587 | content.Append(types.mHeaderContent); 2588 | 2589 | const FString* modulePath = mPackagePaths.Find(types.mPackage); 2590 | if (modulePath == nullptr) 2591 | { 2592 | continue; 2593 | } 2594 | 2595 | FString headerPath = headerName.ToString(); 2596 | if (headerPath.IsEmpty() || headerPath.StartsWith(TEXT("Private/"))) 2597 | { 2598 | continue; 2599 | } 2600 | 2601 | if (!headerPath.StartsWith(TEXT("Public/"))) 2602 | { 2603 | if (modulePath->Contains("Source/Runtime/Engine")) 2604 | { 2605 | if (!headerPath.StartsWith(TEXT("Classes/"))) 2606 | { 2607 | continue; 2608 | } 2609 | } 2610 | } 2611 | 2612 | FString fullPath = (*modulePath) / headerPath; 2613 | if (FPaths::FileExists(fullPath)) 2614 | { 2615 | includeContent.Append(FString::Printf(TEXT("\n#include \"%s\""), *fullPath)); 2616 | } 2617 | } 2618 | 2619 | content.Append("\n\n#endif // CFLAT_ENABLED"); 2620 | 2621 | FString aidFilePath = pFilePath + "/_aid.gen.h"; 2622 | if(!FFileHelper::SaveStringToFile(content, *aidFilePath, FFileHelper::EEncodingOptions::ForceUTF8)) 2623 | { 2624 | UE_LOG(LogTemp, Error, TEXT("[Cflat] Could not write Aid Header File: %s"), *aidFilePath); 2625 | } 2626 | FString includeFilePath = pFilePath + "/_includes.gen.h"; 2627 | if(!FFileHelper::SaveStringToFile(includeContent, *includeFilePath, FFileHelper::EEncodingOptions::ForceUTF8)) 2628 | { 2629 | UE_LOG(LogTemp, Error, TEXT("[Cflat] Could not write Include Header File: %s"), *includeFilePath); 2630 | } 2631 | } 2632 | 2633 | void CallRegisteredTypeCallbacks(UStruct* pUStruct, const RegisteredInfo& pInfo, const UnrealModule::RegisteringCallbacks& pRegisteringCallbacks) 2634 | { 2635 | Cflat::Struct* cfStruct = pInfo.mStruct; 2636 | 2637 | FName typeName(*UnrealModule::GetTypeNameAsString(cfStruct)); 2638 | TArray baseTypes; 2639 | for (size_t i = 0; i < cfStruct->mBaseTypes.size(); ++i) 2640 | { 2641 | const Cflat::Type* baseType = cfStruct->mBaseTypes[i].mType; 2642 | baseTypes.Add(FName(*UnrealModule::GetTypeNameAsString(baseType))); 2643 | } 2644 | 2645 | if (pRegisteringCallbacks.RegisteredType) 2646 | { 2647 | pRegisteringCallbacks.RegisteredType(typeName, baseTypes); 2648 | } 2649 | 2650 | if (pRegisteringCallbacks.RegisteredStruct) 2651 | { 2652 | pRegisteringCallbacks.RegisteredStruct(cfStruct, pUStruct); 2653 | } 2654 | 2655 | TArray parameterNames; 2656 | TArray parameterTypes; 2657 | TArray parameterDefaultValues; 2658 | 2659 | const FString kEmptyString(""); 2660 | 2661 | for (const RegisteredFunctionInfo& funcInfo : pInfo.mFunctions) 2662 | { 2663 | parameterNames.Empty(false); 2664 | parameterTypes.Empty(false); 2665 | parameterDefaultValues.Empty(false); 2666 | 2667 | FName funcName(funcInfo.mIdentifier.mName); 2668 | bool hasDefaultParameter = funcInfo.mFirstDefaultParamIndex != -1; 2669 | UFunction* func = funcInfo.mFunction; 2670 | 2671 | int32 propCount = 0; 2672 | for (TFieldIterator propIt(func); 2673 | propIt && propIt->HasAnyPropertyFlags(CPF_Parm) && !propIt->HasAnyPropertyFlags(CPF_ReturnParm); 2674 | ++propIt, ++propCount) 2675 | { 2676 | FString parameterType = UnrealModule::GetTypeUsageAsString(funcInfo.mParameters[propCount]); 2677 | parameterTypes.Add(FName(*parameterType)); 2678 | parameterNames.Add(propIt->GetFName()); 2679 | if (hasDefaultParameter) 2680 | { 2681 | if (propCount >= funcInfo.mFirstDefaultParamIndex) 2682 | { 2683 | FString metaDataName = FString::Printf(TEXT("CPP_Default_%s"), *propIt->GetName()); 2684 | const FString* metaDataValue = func->FindMetaData(*metaDataName); 2685 | if (metaDataValue) 2686 | { 2687 | parameterDefaultValues.Add(*metaDataValue); 2688 | } 2689 | else 2690 | { 2691 | parameterDefaultValues.Add(kEmptyString); 2692 | } 2693 | } 2694 | else 2695 | { 2696 | parameterDefaultValues.Add(kEmptyString); 2697 | } 2698 | } 2699 | } 2700 | 2701 | if (funcInfo.mFunction->HasAnyFunctionFlags(FUNC_Static)) 2702 | { 2703 | if (pRegisteringCallbacks.RegisteredFunction) 2704 | { 2705 | pRegisteringCallbacks.RegisteredFunction(func, typeName, funcName, parameterTypes, parameterNames, parameterDefaultValues); 2706 | } 2707 | } 2708 | else 2709 | { 2710 | if (pRegisteringCallbacks.RegisteredMethod) 2711 | { 2712 | pRegisteringCallbacks.RegisteredMethod(func, typeName, funcName, parameterTypes, parameterNames, parameterDefaultValues); 2713 | } 2714 | } 2715 | } 2716 | } 2717 | 2718 | void CallRegisteringCallbacks(const UnrealModule::RegisteringCallbacks& pRegisteringCallbacks) 2719 | { 2720 | for (const auto& pair : mRegisteredStructs) 2721 | { 2722 | CallRegisteredTypeCallbacks(pair.Key, pair.Value, pRegisteringCallbacks); 2723 | } 2724 | for (const auto& pair : mRegisteredClasses) 2725 | { 2726 | CallRegisteredTypeCallbacks(pair.Key, pair.Value, pRegisteringCallbacks); 2727 | } 2728 | for (const auto& pair : mRegisteredInterfaces) 2729 | { 2730 | CallRegisteredTypeCallbacks(pair.Key, pair.Value, pRegisteringCallbacks); 2731 | } 2732 | 2733 | if (pRegisteringCallbacks.RegisteredEnum) 2734 | { 2735 | for (const auto& pair : mRegisteredEnums) 2736 | { 2737 | pRegisteringCallbacks.RegisteredEnum(pair.Value.mEnum, pair.Key); 2738 | } 2739 | } 2740 | 2741 | if (pRegisteringCallbacks.RegisteredType) 2742 | { 2743 | // Global Namespace 2744 | pRegisteringCallbacks.RegisteredType(NAME_None, {}); 2745 | } 2746 | 2747 | if (pRegisteringCallbacks.RegisteredFunction) 2748 | { 2749 | // Cast 2750 | pRegisteringCallbacks.RegisteredFunction(nullptr, NAME_None, FName("Cast"), {FName("UObject*")}, {FName("Src")}, {}); 2751 | } 2752 | } 2753 | 2754 | 2755 | void AppendClassAndFunctionsForDebugging(UStruct* pStruct, const RegisteredInfo* pRegInfo, FString& pOutString) 2756 | { 2757 | Cflat::Struct* cfStruct = pRegInfo->mStruct; 2758 | if (cfStruct == nullptr) 2759 | { 2760 | pOutString.Append(FString::Printf(TEXT("\n\nNOT FOUND: %s\n\n"), *pStruct->GetFullName())); 2761 | return; 2762 | } 2763 | 2764 | FString strMembers; 2765 | for (size_t i = 0; i < cfStruct->mMembers.size(); ++i) 2766 | { 2767 | const Cflat::Member* member = &cfStruct->mMembers[i]; 2768 | strMembers.Append("\n\t"); 2769 | strMembers.Append(UnrealModule::GetMemberAsString(member)); 2770 | strMembers.Append(";"); 2771 | } 2772 | 2773 | FString strFunctions; 2774 | { 2775 | CflatSTLVector(Function*) functions; 2776 | cfStruct->mFunctionsHolder.getAllFunctions(&functions); 2777 | for (size_t i = 0; i < functions.size(); ++i) 2778 | { 2779 | const Cflat::Function* function = functions[i]; 2780 | strFunctions.Append("\n\t"); 2781 | strFunctions.Append(UnrealModule::GetFunctionAsString(function)); 2782 | strFunctions.Append(";"); 2783 | } 2784 | } 2785 | 2786 | FString strMethods; 2787 | { 2788 | for (size_t i = 0; i < cfStruct->mMethods.size(); ++i) 2789 | { 2790 | const Cflat::Method* method = &cfStruct->mMethods[i]; 2791 | strMethods.Append("\n\t"); 2792 | strMethods.Append(UnrealModule::GetMethodAsString(method)); 2793 | strMethods.Append(";"); 2794 | } 2795 | } 2796 | 2797 | FString strBaseClasses; 2798 | for (int i = 0; i < cfStruct->mBaseTypes.size(); ++i) 2799 | { 2800 | if (i == 0) 2801 | { 2802 | strBaseClasses.Append(" : "); 2803 | } 2804 | else 2805 | { 2806 | strBaseClasses.Append(", "); 2807 | } 2808 | Cflat::Type* baseType = cfStruct->mBaseTypes[i].mType; 2809 | uint16_t offset = cfStruct->mBaseTypes[i].mOffset; 2810 | strBaseClasses.Append(baseType->mIdentifier.mName); 2811 | strBaseClasses.Appendf(TEXT(" (offset: %d)"), (int)offset); 2812 | } 2813 | 2814 | pOutString.Append("\n\n"); 2815 | pOutString.Append(cfStruct->mIdentifier.mName); 2816 | pOutString.Append(strBaseClasses); 2817 | pOutString.Append("\n"); 2818 | pOutString.Append(pStruct->GetFullName()); 2819 | pOutString.Append("\n"); 2820 | pOutString.Append("Header: "); 2821 | pOutString.Append(pRegInfo->mHeader.ToString()); 2822 | pOutString.Append("\n"); 2823 | pOutString.Append("Properties:"); 2824 | pOutString.Append(strMembers); 2825 | pOutString.Append("\n"); 2826 | pOutString.Append("Methods:"); 2827 | pOutString.Append(strMethods); 2828 | pOutString.Append("\n"); 2829 | pOutString.Append("Functions:"); 2830 | pOutString.Append(strFunctions); 2831 | } 2832 | 2833 | void PrintDebugStats() 2834 | { 2835 | UE_LOG(LogTemp, Log, TEXT("[Cflat] AutoRegisterCflatTypes: total: %d time: %f"), 2836 | mRegisteredStructs.Num() + mRegisteredClasses.Num(), 2837 | FPlatformTime::Seconds() - mTimeStarted); 2838 | { 2839 | const Cflat::Identifier::NamesRegistry* registry = 2840 | Cflat::Identifier::getNamesRegistry(); 2841 | const char* buffBegin = (const char*)registry->mPointer; 2842 | const char* buffEnd = (const char*)(®istry->mMemory); 2843 | int sizeDiff = buffBegin - buffEnd; 2844 | int count = registry->mRegistry.size(); 2845 | UE_LOG(LogTemp, Log, TEXT("\n\n[Cflat] StringRegistry count: %d usage: %d of %d\n\n"), count, sizeDiff, Cflat::kIdentifierStringsPoolSize); 2846 | } 2847 | 2848 | { 2849 | FString addedStructs = {}; 2850 | for (const auto& pair : mRegisteredStructs) 2851 | { 2852 | AppendClassAndFunctionsForDebugging(pair.Key, &pair.Value, addedStructs); 2853 | } 2854 | UE_LOG(LogTemp, Log, TEXT("\n\n[Cflat][Registered UStructs]\n\n%s\n\n\n"), *addedStructs); 2855 | 2856 | FString addedClasses = {}; 2857 | for (const auto& pair : mRegisteredClasses) 2858 | { 2859 | AppendClassAndFunctionsForDebugging(pair.Key, &pair.Value, addedClasses); 2860 | } 2861 | UE_LOG(LogTemp, Log, TEXT("\n\n[Cflat][Registered Classes]\n\n%s\n\n\n"), *addedClasses); 2862 | 2863 | FString addedInterfaces = {}; 2864 | for (const auto& pair : mRegisteredInterfaces) 2865 | { 2866 | AppendClassAndFunctionsForDebugging(pair.Key, &pair.Value, addedInterfaces); 2867 | } 2868 | UE_LOG(LogTemp, Log, TEXT("\n\n[Cflat][Registered Interfaces]\n\n%s\n\n\n"), *addedInterfaces); 2869 | } 2870 | 2871 | { 2872 | TMap moduleCount; 2873 | 2874 | for (const auto& pair : mRegisteredStructs) 2875 | { 2876 | UPackage* classPackage = pair.Key->GetPackage(); 2877 | FName moduleName = FPackageName::GetShortFName(classPackage->GetFName()); 2878 | int32* count = moduleCount.Find(moduleName); 2879 | if (count) 2880 | { 2881 | (*count)++; 2882 | } 2883 | else 2884 | { 2885 | moduleCount.Add(moduleName, 1); 2886 | } 2887 | } 2888 | for (const auto& pair : mRegisteredClasses) 2889 | { 2890 | UPackage* classPackage = pair.Key->GetPackage(); 2891 | FName moduleName = FPackageName::GetShortFName(classPackage->GetFName()); 2892 | int32* count = moduleCount.Find(moduleName); 2893 | if (count) 2894 | { 2895 | (*count)++; 2896 | } 2897 | else 2898 | { 2899 | moduleCount.Add(moduleName, 1); 2900 | } 2901 | } 2902 | 2903 | int32 total = 0; 2904 | struct ModuleCount 2905 | { 2906 | FName name; 2907 | int32 count; 2908 | }; 2909 | 2910 | TArray sortedModuleCount; 2911 | for (auto& it : moduleCount) 2912 | { 2913 | sortedModuleCount.Add({it.Key, it.Value}); 2914 | total += it.Value; 2915 | } 2916 | 2917 | sortedModuleCount.Sort([](const ModuleCount& A, const ModuleCount& B) { return A.count > B.count; }); 2918 | 2919 | FString modulesCountStr = TEXT("\n\nRegistered Types Per Module:\n\n"); 2920 | for (auto& it : sortedModuleCount) 2921 | { 2922 | modulesCountStr.Append(FString::Printf(TEXT("%s,%d\n"), *it.name.ToString(), it.count)); 2923 | } 2924 | UE_LOG(LogTemp, Log, TEXT("%s\n\nTotal: %d"), *modulesCountStr, total); 2925 | } 2926 | } 2927 | 2928 | }; 2929 | 2930 | } // namespace AutoRegister 2931 | -------------------------------------------------------------------------------- /UnrealModule/Public/CflatDebugAdapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(CFLAT_ENABLED) 4 | # include "CoreMinimal.h" 5 | 6 | class FSocket; 7 | 8 | class CFLAT_API CflatDebugAdapter : public FRunnable 9 | { 10 | public: 11 | CflatDebugAdapter(); 12 | ~CflatDebugAdapter(); 13 | 14 | typedef void (*EventCallback)(const TSharedPtr); 15 | 16 | bool Start(int32 pPort = 6663); 17 | void AddRequestListener(FName pRequestName, EventCallback pCallback); 18 | TSharedPtr 19 | CreateResponse(const TSharedPtr pRequest, bool pSuccess); 20 | TSharedPtr CreateEvent(const FString& pName); 21 | 22 | void SendEvent(const TSharedPtr pEvent); 23 | void SendResponse(const TSharedPtr pResponse); 24 | 25 | bool IsConnected(); 26 | bool Disconnect(); 27 | 28 | protected: 29 | static const int32 kIncomingBufferSize = 1024; 30 | 31 | ISocketSubsystem* mSocketSubsystem; 32 | FSocket* mListener; 33 | FSocket* mSocket; 34 | double mConnectionLastCheck; 35 | bool mListening; 36 | bool mDebugLog; 37 | FString mListeningAdress; 38 | int32 mPort; 39 | uint8 mIncomingBuffer[kIncomingBufferSize]; 40 | 41 | FRunnableThread* mThread; 42 | TMap mRequestCallbacks; 43 | 44 | bool Listen(); 45 | 46 | void CheckListener(); 47 | 48 | void ParseMessageData(const uint8* pData, int32 pSize); 49 | void ReadData(); 50 | void SendString(const FString& pString); 51 | 52 | uint32 Run() override; 53 | }; 54 | 55 | #endif // CFLAT_ENABLED 56 | -------------------------------------------------------------------------------- /UnrealModule/Public/CflatModule.h: -------------------------------------------------------------------------------- 1 | 2 | /////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Cflat v0.80 5 | // Embeddable lightweight scripting language with C++ syntax 6 | // 7 | // Copyright (c) 2019-2025 Arturo Cepeda Pérez and contributors 8 | // 9 | // --------------------------------------------------------------------------- 10 | // 11 | // This software is provided 'as-is', without any express or implied 12 | // warranty. In no event will the authors be held liable for any damages 13 | // arising from the use of this software. 14 | // 15 | // Permission is granted to anyone to use this software for any purpose, 16 | // including commercial applications, and to alter it and redistribute it 17 | // freely, subject to the following restrictions: 18 | // 19 | // 1. The origin of this software must not be misrepresented; you must not 20 | // claim that you wrote the original software. If you use this software 21 | // in a product, an acknowledgment in the product documentation would be 22 | // appreciated but is not required. 23 | // 24 | // 2. Altered source versions must be plainly marked as such, and must not be 25 | // misrepresented as being the original software. 26 | // 27 | // 3. This notice may not be removed or altered from any source distribution. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | 32 | #pragma once 33 | 34 | 35 | #if defined (CFLAT_ENABLED) 36 | 37 | // MSVC specifics 38 | #if defined (_MSC_VER) 39 | # define CflatAPI CFLAT_API // __declspec(dllexport) / __declspec(dllimport) 40 | # pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS 41 | #endif 42 | 43 | // Use Unreal's check as assert 44 | #define CflatAssert check 45 | 46 | // Cflat includes 47 | #include "../../CflatGlobal.h" 48 | #include "../../CflatHelper.h" 49 | 50 | 51 | // 52 | // UnrealModule static class 53 | // 54 | namespace Cflat 55 | { 56 | class CFLAT_API UnrealModule 57 | { 58 | public: 59 | typedef TFunction ScriptFilterDelegate; 60 | 61 | typedef TFunction&)> OnScriptReloadedCallback; 62 | typedef TFunction&, const TArray&)> OnScriptReloadFailedCallback; 63 | 64 | typedef void (*OnFunctionCallErrorCallback)(Cflat::Environment* pEnv, Cflat::Function* pFunction, void* pData); 65 | 66 | struct RegisteringCallbacks 67 | { 68 | void (*RegisteredType)(FName pTypeName, const TArray& pBaseTypes); 69 | void (*RegisteredStruct)(Cflat::Struct* pCflatStruct, UStruct* pUStruct); 70 | void (*RegisteredEnum)(Cflat::Type* pCflatEnum, UEnum* pUEnum); 71 | void (*RegisteredMethod)( 72 | UFunction* pFunction, 73 | FName pOwnerName, 74 | FName pFunctionName, 75 | const TArray& pParameterTypes, 76 | const TArray& pParameterNames, 77 | const TArray& pParameterDefaultValues); 78 | void (*RegisteredFunction)( 79 | UFunction* pFunction, 80 | FName pOwnerName, 81 | FName pFunctionName, 82 | const TArray& pParameterTypes, 83 | const TArray& pParameterNames, 84 | const TArray& pParameterDefaultValues); 85 | void (*ManuallyRegisteredMethod)( 86 | FName pOwnerName, const char* pDeclaration); 87 | void (*ManuallyRegisteredFunction)( 88 | FName pOwnerName, const char* pDeclaration); 89 | }; 90 | 91 | static void Init(); 92 | static void LoadScripts(const FString& pFileExtension, ScriptFilterDelegate pFilterDelegate = nullptr); 93 | static void RegisterTypes(); 94 | static void RegisterFileWatcher(const FString& pFileExtension); 95 | 96 | // pRegisterComplementaryTypesCallback is called between registering types and properties/functions 97 | static void AutoRegisterCflatTypes(const TSet& pModules, const TSet& pIgnoredTypes, void (*pRegisterComplementaryTypesCallback)(void) = nullptr); 98 | static void SetRegisteringCallbacks(const RegisteringCallbacks& pRegisteringCallbacks); 99 | static const RegisteringCallbacks& GetRegisteringCallbacks(); 100 | static void GenerateAidHeaderFile(); 101 | 102 | static void RegisterOnScriptReloadedCallback(UObject* pOwner, OnScriptReloadedCallback pCallback); 103 | static void DeregisterOnScriptReloadedCallbacks(UObject* pOwner); 104 | static void RegisterOnScriptReloadFailedCallback(UObject* pOwner, OnScriptReloadFailedCallback pCallback); 105 | static void DeregisterOnScriptReloadFailedCallback(UObject* pOwner); 106 | 107 | static void CallFunction(Cflat::Function* pFunction, 108 | const CflatArgsVector(Cflat::Value)& pArgs, Cflat::Value* pOutReturnValue, 109 | OnFunctionCallErrorCallback pOnErrorCallback, void* pOnErrorCallbackData = nullptr); 110 | 111 | static FString GetTypeNameAsString(const Cflat::Type* pType); 112 | static FString GetTypeUsageAsString(const Cflat::TypeUsage& pTypeUsage); 113 | static FString GetValueAsString(const Cflat::Value* pValue); 114 | static FString GetMemberAsString(const Cflat::Member* pMember); 115 | static FString GetMethodAsString(const Cflat::Method* pMethod); 116 | static FString GetFunctionAsString(const Cflat::Function* pFunction); 117 | 118 | private: 119 | struct OnScriptReloadedCallbackEntry 120 | { 121 | UObject* mOwner; 122 | OnScriptReloadedCallback mCallback; 123 | }; 124 | static TArray smOnScriptReloadedCallbacks; 125 | 126 | struct OnScriptReloadFailedEntry 127 | { 128 | UObject* mOwner; 129 | OnScriptReloadFailedCallback mCallback; 130 | }; 131 | static TArray smOnScriptReloadFailedCallbacks; 132 | 133 | static bool LoadScript(const FString& pFilePath); 134 | }; 135 | } 136 | 137 | 138 | 139 | // 140 | // Macros for registering Unreal shared pointers 141 | // 142 | #define CflatRegisterTObjectPtr(pEnvironmentPtr, T) \ 143 | { \ 144 | CflatRegisterTemplateClassTypes1(pEnvironmentPtr, TObjectPtr, T); \ 145 | CflatClassAddConstructorParams1(pEnvironmentPtr, TObjectPtr, T*); \ 146 | CflatClassAddMethodReturn(pEnvironmentPtr, TObjectPtr, T*, Get); \ 147 | } 148 | 149 | // 150 | // Macros for registering Unreal containers 151 | // 152 | #define CflatRegisterTArray(pEnvironmentPtr, T) \ 153 | { \ 154 | CflatRequestInitializerListType(pEnvironmentPtr, T); \ 155 | } \ 156 | { \ 157 | CflatRegisterTemplateClassTypes1(pEnvironmentPtr, TArray, T); \ 158 | CflatClassAddConstructor(pEnvironmentPtr, TArray); \ 159 | CflatClassAddConstructorParams1(pEnvironmentPtr, TArray, std::initializer_list); \ 160 | CflatClassAddMethodReturn(pEnvironmentPtr, TArray, bool, IsEmpty) CflatMethodConst; \ 161 | CflatClassAddMethodReturn(pEnvironmentPtr, TArray, int32, Num) CflatMethodConst; \ 162 | CflatClassAddMethodVoidParams1(pEnvironmentPtr, TArray, void, Reserve, int32); \ 163 | CflatClassAddMethodVoidParams1(pEnvironmentPtr, TArray, void, SetNum, int32); \ 164 | CflatClassAddMethodVoidParams1(pEnvironmentPtr, TArray, void, SetNumZeroed, int32); \ 165 | CflatClassAddMethodVoidParams1(pEnvironmentPtr, TArray, void, SetNumUninitialized, int32); \ 166 | CflatClassAddMethodVoid(pEnvironmentPtr, TArray, void, Empty); \ 167 | CflatClassAddMethodVoidParams1(pEnvironmentPtr, TArray, void, Empty, int32); \ 168 | CflatClassAddMethodVoidParams1(pEnvironmentPtr, TArray, void, RemoveAt, int32); \ 169 | { \ 170 | const size_t methodIndex = type->mMethods.size(); \ 171 | Cflat::Method method("Add"); \ 172 | Cflat::TypeUsage paramTypeUsage = (pEnvironmentPtr)->getTypeUsage(#T); CflatValidateTypeUsage(paramTypeUsage); \ 173 | CflatMakeTypeUsageConst(paramTypeUsage); \ 174 | method.execute = [type, methodIndex] \ 175 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 176 | { \ 177 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 178 | CflatAssert(method->mParameters.size() == pArguments.size()); \ 179 | CflatValueAs(&pThis, TArray*)->Add(CflatValueAs(&pArguments[0], T)); \ 180 | }; \ 181 | method.mParameters.push_back(paramTypeUsage); \ 182 | type->mMethods.push_back(method); \ 183 | } \ 184 | { \ 185 | const size_t methodIndex = type->mMethods.size(); \ 186 | Cflat::Method method("operator[]"); \ 187 | method.mReturnTypeUsage = (pEnvironmentPtr)->getTypeUsage(#T "&"); \ 188 | method.mParameters.push_back((pEnvironmentPtr)->getTypeUsage("int")); \ 189 | Cflat::Environment* environment = pEnvironmentPtr; \ 190 | method.execute = [environment, type, methodIndex] \ 191 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 192 | { \ 193 | TArray* thisArray = CflatValueAs(&pThis, TArray*); \ 194 | const int elementIndex = CflatValueAs(&pArguments[0], int); \ 195 | if(elementIndex >= thisArray->Num() || elementIndex < 0) \ 196 | { \ 197 | char errorMessage[256]; \ 198 | snprintf \ 199 | ( \ 200 | errorMessage, \ 201 | sizeof(errorMessage), \ 202 | "invalid TArray index (size %d, index %d)", \ 203 | thisArray->Num(), \ 204 | elementIndex \ 205 | ); \ 206 | environment->throwCustomRuntimeError(errorMessage); \ 207 | return; \ 208 | } \ 209 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 210 | CflatAssert(pOutReturnValue); \ 211 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 212 | CflatAssert(method->mParameters.size() == pArguments.size()); \ 213 | T& result = thisArray->operator[](elementIndex); \ 214 | Cflat::Environment::assignReturnValueFromFunctionCall(method->mReturnTypeUsage, &result, pOutReturnValue); \ 215 | }; \ 216 | type->mMethods.push_back(method); \ 217 | } \ 218 | { \ 219 | const size_t methodIndex = type->mMethods.size(); \ 220 | Cflat::Method method("begin"); \ 221 | method.mReturnTypeUsage = templateTypes.back(); \ 222 | CflatMakeTypeUsagePointer(method.mReturnTypeUsage); \ 223 | method.execute = [type, methodIndex] \ 224 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 225 | { \ 226 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 227 | CflatAssert(pOutReturnValue); \ 228 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 229 | T* result = CflatValueAs(&pThis, TArray*)->GetData(); \ 230 | pOutReturnValue->set(&result); \ 231 | }; \ 232 | type->mMethods.push_back(method); \ 233 | } \ 234 | { \ 235 | const size_t methodIndex = type->mMethods.size(); \ 236 | Cflat::Method method("end"); \ 237 | method.mReturnTypeUsage = templateTypes.back(); \ 238 | CflatMakeTypeUsagePointer(method.mReturnTypeUsage); \ 239 | method.execute = [type, methodIndex] \ 240 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 241 | { \ 242 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 243 | CflatAssert(pOutReturnValue); \ 244 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 245 | T* result = CflatValueAs(&pThis, TArray*)->GetData() + CflatValueAs(&pThis, TArray*)->Num(); \ 246 | pOutReturnValue->set(&result); \ 247 | }; \ 248 | type->mMethods.push_back(method); \ 249 | } \ 250 | } 251 | 252 | #define CflatRegisterTSet(pEnvironmentPtr, T) \ 253 | { \ 254 | CflatRegisterTemplateClassTypes1(pEnvironmentPtr, TSet, T); \ 255 | using TRangedForIterator = TSet::TRangedForIterator; \ 256 | Cflat::Class* setType = type; \ 257 | Cflat::Class* rangedForIteratorType = nullptr; \ 258 | { \ 259 | rangedForIteratorType = type->registerType("TRangedForIterator"); \ 260 | rangedForIteratorType->mSize = sizeof(TRangedForIterator); \ 261 | type = rangedForIteratorType; \ 262 | TypeUsage elementRefTypeUsage = setType->mTemplateTypes[0]; \ 263 | elementRefTypeUsage.mFlags |= (uint8_t)Cflat::TypeUsageFlags::Reference; \ 264 | TypeUsage rangedForIteratorRefTypeUsage; \ 265 | rangedForIteratorRefTypeUsage.mType = rangedForIteratorType; \ 266 | rangedForIteratorRefTypeUsage.mFlags |= (uint8_t)Cflat::TypeUsageFlags::Reference; \ 267 | TypeUsage rangedForIteratorConstRefTypeUsage = rangedForIteratorRefTypeUsage; \ 268 | rangedForIteratorConstRefTypeUsage.mFlags |= (uint8_t)Cflat::TypeUsageFlags::Const; \ 269 | CflatClassAddCopyConstructor(pEnvironmentPtr, TRangedForIterator); \ 270 | { \ 271 | type->mMethods.push_back(Cflat::Method("operator++")); \ 272 | const size_t methodIndex = type->mMethods.size() - 1u; \ 273 | Cflat::Method* method = &type->mMethods.back(); \ 274 | method->mReturnTypeUsage = rangedForIteratorRefTypeUsage; \ 275 | method->execute = [type, methodIndex] \ 276 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 277 | { \ 278 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 279 | CflatAssert(pOutReturnValue); \ 280 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 281 | TRangedForIterator& result = CflatValueAs(&pThis, TRangedForIterator*)->operator++(); \ 282 | pOutReturnValue->set(&result); \ 283 | }; \ 284 | } \ 285 | { \ 286 | type->mMethods.push_back(Cflat::Method("operator*")); \ 287 | const size_t methodIndex = type->mMethods.size() - 1u; \ 288 | Cflat::Method* method = &type->mMethods.back(); \ 289 | method->mReturnTypeUsage = elementRefTypeUsage; \ 290 | method->execute = [type, methodIndex] \ 291 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 292 | { \ 293 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 294 | CflatAssert(pOutReturnValue); \ 295 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 296 | T& result = CflatValueAs(&pThis, TRangedForIterator*)->operator*(); \ 297 | pOutReturnValue->set(&result); \ 298 | }; \ 299 | } \ 300 | { \ 301 | type->mMethods.push_back(Cflat::Method("operator!=")); \ 302 | const size_t methodIndex = type->mMethods.size() - 1u; \ 303 | Cflat::Method* method = &type->mMethods.back(); \ 304 | method->mReturnTypeUsage = (pEnvironmentPtr)->getTypeUsage("bool"); \ 305 | method->mParameters.push_back(rangedForIteratorConstRefTypeUsage); \ 306 | method->execute = [type, methodIndex] \ 307 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 308 | { \ 309 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 310 | CflatAssert(pOutReturnValue); \ 311 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 312 | bool result = CflatValueAs(&pThis, TRangedForIterator*)->operator!= \ 313 | ( \ 314 | CflatValueAs(&pArguments[0], const TRangedForIterator&) \ 315 | ); \ 316 | pOutReturnValue->set(&result); \ 317 | }; \ 318 | } \ 319 | type = setType; \ 320 | } \ 321 | CflatClassAddConstructor(pEnvironmentPtr, TSet); \ 322 | CflatClassAddCopyConstructor(pEnvironmentPtr, TSet); \ 323 | CflatClassAddMethodReturn(pEnvironmentPtr, TSet, bool, IsEmpty) CflatMethodConst; \ 324 | CflatClassAddMethodReturn(pEnvironmentPtr, TSet, int32, Num) CflatMethodConst; \ 325 | CflatClassAddMethodVoid(pEnvironmentPtr, TSet, void, Empty); \ 326 | CflatClassAddMethodReturnParams1(pEnvironmentPtr, TSet, bool, Contains, const T&) CflatMethodConst; \ 327 | CflatClassAddMethodReturnParams1(pEnvironmentPtr, TSet, T*, Find, const T&); \ 328 | { \ 329 | const size_t methodIndex = type->mMethods.size(); \ 330 | Cflat::Method method("Add"); \ 331 | Cflat::TypeUsage paramTypeUsage = (pEnvironmentPtr)->getTypeUsage(#T); CflatValidateTypeUsage(paramTypeUsage); \ 332 | CflatMakeTypeUsageConst(paramTypeUsage); \ 333 | method.execute = [type, methodIndex] \ 334 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 335 | { \ 336 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 337 | CflatAssert(method->mParameters.size() == pArguments.size()); \ 338 | CflatValueAs(&pThis, TSet*)->Add(CflatValueAs(&pArguments[0], T)); \ 339 | }; \ 340 | method.mParameters.push_back(paramTypeUsage); \ 341 | type->mMethods.push_back(method); \ 342 | } \ 343 | { \ 344 | const size_t methodIndex = type->mMethods.size(); \ 345 | Cflat::Method method("begin"); \ 346 | method.mReturnTypeUsage.mType = rangedForIteratorType; \ 347 | method.execute = [type, methodIndex] \ 348 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 349 | { \ 350 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 351 | CflatAssert(pOutReturnValue); \ 352 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 353 | TRangedForIterator result = CflatValueAs(&pThis, TSet*)->begin(); \ 354 | Cflat::Environment::assignReturnValueFromFunctionCall(method->mReturnTypeUsage, &result, pOutReturnValue); \ 355 | }; \ 356 | type->mMethods.push_back(method); \ 357 | } \ 358 | { \ 359 | const size_t methodIndex = type->mMethods.size(); \ 360 | Cflat::Method method("end"); \ 361 | method.mReturnTypeUsage.mType = rangedForIteratorType; \ 362 | method.execute = [type, methodIndex] \ 363 | (const Cflat::Value& pThis, const CflatArgsVector(Cflat::Value)& pArguments, Cflat::Value* pOutReturnValue) \ 364 | { \ 365 | Cflat::Method* method = &type->mMethods[methodIndex]; \ 366 | CflatAssert(pOutReturnValue); \ 367 | CflatAssert(pOutReturnValue->mTypeUsage.compatibleWith(method->mReturnTypeUsage)); \ 368 | TRangedForIterator result = CflatValueAs(&pThis, TSet*)->end(); \ 369 | Cflat::Environment::assignReturnValueFromFunctionCall(method->mReturnTypeUsage, &result, pOutReturnValue); \ 370 | }; \ 371 | type->mMethods.push_back(method); \ 372 | } \ 373 | } 374 | 375 | #define CflatRegisterTSubclassOf(pEnvironmentPtr, T) \ 376 | { \ 377 | CflatRegisterTemplateClassTypes1(pEnvironmentPtr, TSubclassOf, T); \ 378 | CflatClassAddCopyConstructor(pEnvironmentPtr, TSubclassOf); \ 379 | CflatClassAddConstructorParams1(pEnvironmentPtr, TSubclassOf, UClass*); \ 380 | CflatClassAddMethodReturn(pEnvironmentPtr, TSubclassOf, UClass*, Get); \ 381 | CflatClassAddMethodReturnParams1(pEnvironmentPtr, TSubclassOf, TSubclassOf&, operator=, UClass*); \ 382 | CflatClassAddMethodReturn(pEnvironmentPtr, TSubclassOf, UClass*, operator*); CflatMethodConst; \ 383 | } 384 | 385 | #define CflatRegisterCastFromUObject(pEnvironmentPtr, T) \ 386 | { \ 387 | CflatRegisterTemplateFunctionReturnParams1(pEnvironmentPtr, T, T*, Cast, UObject*); \ 388 | } 389 | 390 | 391 | #endif 392 | --------------------------------------------------------------------------------