diff --git a/CMakeLists.txt b/CMakeLists.txt index 15aad16..fba3192 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,9 +218,7 @@ install(EXPORT AthenaTargets DESTINATION ${INSTALL_CMAKE_DIR} COMPONENT Athena) # atdna import # ################ -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/atdna/CMakeLists.txt") - add_subdirectory(atdna) -endif() +add_subdirectory(atdna) ######### # CPack # diff --git a/PKGBUILD b/PKGBUILD index 3d7d29c..7cf3166 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,11 +1,10 @@ # PKGBUILD for libAthena _pkgname=libathena pkgname=$_pkgname-git -pkgver=2.2.0.8.g2490fef -pkgrel=1 +pkgver=2.3.0 pkgdesc="Basic cross platform IO library" arch=('i686' 'x86_64') -source=("${pkgname%-*}::git+https://github.com/libAthena/Athena.git#branch=yaml") +source=("${pkgname%-*}::git+https://github.com/libAthena/Athena.git") options=(staticlibs) license="MIT" makedepends=('git cmake sed') diff --git a/atdna/CMakeLists.txt b/atdna/CMakeLists.txt new file mode 100644 index 0000000..fb9e5b2 --- /dev/null +++ b/atdna/CMakeLists.txt @@ -0,0 +1,122 @@ +############### +# ATDNA Build # +############### + +# Find dependencies +include(FindLLVM.cmake) +if(NOT LLVM_FOUND) + message("-- Unable to locate LLVM installation; skipping atdna") +else() +list(APPEND LLVM_LIBS + clangFrontend + clangTooling + clangDriver + clangSerialization + clangParse + clangSema + clangAnalysis + clangEdit + clangAST + clangLex + clangBasic + LLVMOption + LLVMMCParser + LLVMBitReader + LLVMMC + LLVMSupport) +set(CLANG_INCLUDE_DIR ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_STRING}/include) + +if(UNIX) +list(APPEND PLAT_LIBS z pthread curses dl) +endif() + +# Offer the user the choice of overriding the installation directories +set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") +set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +if(WIN32 AND NOT CYGWIN) + set(INSTALL_CMAKE_DIR cmake) +else() + set(INSTALL_CMAKE_DIR lib/cmake/atdna) +endif() + +# Make relative paths absolute (needed later on) +foreach(p BIN INCLUDE CMAKE) + set(var INSTALL_${p}_DIR) + if(NOT IS_ABSOLUTE "${${var}}") + set(ABS_${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + else() + set(ABS_${var} "${${var}}") + endif() +endforeach() + +# Icon +set(ATHENA_ICO ${CMAKE_CURRENT_SOURCE_DIR}/Athena.ico) + +# Windows resource +if(WIN32) + configure_file(main.rc.in main.rc @ONLY) + set(PLAT_SRCS ${CMAKE_CURRENT_BINARY_DIR}/main.rc) +endif() + +# ATDNA target +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +include_directories(${LLVM_INCLUDE_DIRS}) +link_directories(${LLVM_LIBRARY_DIRS}) +add_executable(atdna main.cpp test.hpp ${PLAT_SRCS}) +target_link_libraries(atdna ${LLVM_LIBS} ${PLAT_LIBS}) +set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS + "INSTALL_PREFIX=${ABS_INSTALL_BIN_DIR};__STDC_LIMIT_MACROS=1;__STDC_CONSTANT_MACROS=1") +if(MSVC) +set_target_properties(atdna PROPERTIES + COMPILE_FLAGS "/GR-") +else() +set_target_properties(atdna PROPERTIES + COMPILE_FLAGS "-std=c++11 -fno-rtti -Wno-unused-parameter" + LINK_FLAGS -std=c++11) +endif() + +# Define installs +install(TARGETS atdna DESTINATION ${INSTALL_BIN_DIR} EXPORT atdnaTargets COMPONENT atdna) +install(DIRECTORY ${CLANG_INCLUDE_DIR}/ DESTINATION ${INSTALL_INCLUDE_DIR}/Athena/clang COMPONENT atdna) + +################## +# Package Export # +################## + +# Add all targets to the build-tree export set +export(TARGETS atdna FILE "${PROJECT_BINARY_DIR}/atdnaTargets.cmake") + +# Export the package for use from the build-tree +# (this registers the build-tree with a global CMake-registry) +export(PACKAGE atdna) + +# Create the atdnaConfig.cmake +# ... for the build tree +set(CONF_CLANG_INCLUDE_DIR "${CLANG_INCLUDE_DIR}") +configure_file(atdnaConfig.cmake.in "${PROJECT_BINARY_DIR}/atdnaConfig.cmake" @ONLY) +# ... for the install tree +set(CONF_CLANG_INCLUDE_DIR "\${ATHENA_INCLUDE_DIR}/clang") +configure_file(atdnaConfig.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/atdnaConfig.cmake" @ONLY) +# ... for both +configure_file(atdnaConfigVersion.cmake.in "${PROJECT_BINARY_DIR}/atdnaConfigVersion.cmake" @ONLY) + +# Install atdnaConfig.cmake +install(FILES + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/atdnaConfig.cmake" + "${PROJECT_BINARY_DIR}/atdnaConfigVersion.cmake" + DESTINATION ${INSTALL_CMAKE_DIR} COMPONENT atdna) + +# Install the export set for use with the install-tree +install(EXPORT atdnaTargets DESTINATION ${INSTALL_CMAKE_DIR} COMPONENT atdna) + +######### +# CTest # +######### + +enable_testing() +add_test(NAME test-dna COMMAND $ -o test.cpp + "-I${ATHENA_INCLUDE_DIR}" -isystem "${CLANG_INCLUDE_DIR}" + ${CMAKE_SOURCE_DIR}/test.hpp) + +endif() + diff --git a/atdna/FindLLVM.cmake b/atdna/FindLLVM.cmake new file mode 100644 index 0000000..f0cb7ce --- /dev/null +++ b/atdna/FindLLVM.cmake @@ -0,0 +1,205 @@ +# - Find LLVM headers and libraries. +# This module locates LLVM and adapts the llvm-config output for use with +# CMake. +# +# A given list of COMPONENTS is passed to llvm-config. +# +# The following variables are defined: +# LLVM_FOUND - true if LLVM was found +# LLVM_CXXFLAGS - C++ compiler flags for files that include LLVM headers. +# LLVM_HOST_TARGET - Target triple used to configure LLVM. +# LLVM_INCLUDE_DIRS - Directory containing LLVM include files. +# LLVM_LDFLAGS - Linker flags to add when linking against LLVM +# (includes -LLLVM_LIBRARY_DIRS). +# LLVM_LIBRARIES - Full paths to the library files to link against. +# LLVM_LIBRARY_DIRS - Directory containing LLVM libraries. +# LLVM_ROOT_DIR - The root directory of the LLVM installation. +# llvm-config is searched for in ${LLVM_ROOT_DIR}/bin. +# LLVM_VERSION_MAJOR - Major version of LLVM. +# LLVM_VERSION_MINOR - Minor version of LLVM. +# LLVM_VERSION_STRING - Full LLVM version string (e.g. 2.9). +# +# Note: The variable names were chosen in conformance with the offical CMake +# guidelines, see ${CMAKE_ROOT}/Modules/readme.txt. + +# Try suffixed versions to pick up the newest LLVM install available on Debian +# derivatives. +# We also want an user-specified LLVM_ROOT_DIR to take precedence over the +# system default locations such as /usr/local/bin. Executing find_program() +# multiples times is the approach recommended in the docs. +set(LLVM_FIND_COMPONENTS "") +if(WIN32) +get_filename_component(LLVM_ROOT_DIR [HKEY_LOCAL_MACHINE\\Software\\LLVM\\LLVM] ABSOLUTE) +endif() + +set(llvm_config_names llvm-config-3.7 llvm-config37 + llvm-config-3.6 llvm-config36 + llvm-config-3.5 llvm-config35 + llvm-config-3.4 llvm-config34 + llvm-config-3.3 llvm-config33 + llvm-config-3.2 llvm-config32 + llvm-config-3.1 llvm-config31 llvm-config) +find_program(LLVM_CONFIG + NAMES ${llvm_config_names} + PATHS ${LLVM_ROOT_DIR}/bin NO_DEFAULT_PATH + DOC "Path to llvm-config tool.") +find_program(LLVM_CONFIG NAMES ${llvm_config_names}) + +if ((WIN32 AND NOT(MINGW OR CYGWIN)) OR NOT LLVM_CONFIG) + if (WIN32) + # A bit of a sanity check: + if( NOT EXISTS ${LLVM_ROOT_DIR}/include/llvm ) + message(FATAL_ERROR "LLVM_ROOT_DIR (${LLVM_ROOT_DIR}) is not a valid LLVM install") + endif() + # We incorporate the CMake features provided by LLVM: + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LLVM_ROOT_DIR}/share/llvm/cmake") + include(LLVMConfig) + # Set properties + set(LLVM_HOST_TARGET ${TARGET_TRIPLE}) + set(LLVM_VERSION_STRING ${LLVM_PACKAGE_VERSION}) + set(LLVM_CXXFLAGS ${LLVM_DEFINITIONS}) + set(LLVM_LDFLAGS "") + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "all-targets" index) + list(APPEND LLVM_FIND_COMPONENTS ${LLVM_TARGETS_TO_BUILD}) + # Work around LLVM bug 21016 + list(FIND LLVM_TARGETS_TO_BUILD "X86" TARGET_X86) + if(TARGET_X86 GREATER -1) + list(APPEND LLVM_FIND_COMPONENTS x86utils) + endif() + # Similar to the work around above, but for AArch64 + list(FIND LLVM_TARGETS_TO_BUILD "AArch64" TARGET_AArch64) + if(TARGET_AArch64 GREATER -1) + list(APPEND LLVM_FIND_COMPONENTS AArch64Utils) + endif() + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "backend" index) + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-2][\\.0-9A-Za-z]*") + # Versions below 3.3 do not support components objcarcopts, option + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "objcarcopts" index) + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "option" index) + endif() + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-4][\\.0-9A-Za-z]*") + # Versions below 3.5 do not support components lto, profiledata + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "lto" index) + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "profiledata" index) + endif() + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-6][\\.0-9A-Za-z]*") + # Versions below 3.7 do not support components debuginfodwarf + # Only debuginfo is available + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "debuginfodwarf" index) + list(APPEND LLVM_FIND_COMPONENTS "debuginfo") + endif() + + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-4][\\.0-9A-Za-z]*") + llvm_map_components_to_libraries(tmplibs ${LLVM_FIND_COMPONENTS}) + else() + llvm_map_components_to_libnames(tmplibs ${LLVM_FIND_COMPONENTS}) + endif() + if(MSVC) + foreach(lib ${tmplibs}) + list(APPEND LLVM_LIBRARIES "${LLVM_LIBRARY_DIRS}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}") + endforeach() + else() + # Rely on the library search path being set correctly via -L on + # MinGW and others, as the library list returned by + # llvm_map_components_to_libraries also includes imagehlp and psapi. + set(LLVM_LDFLAGS "-L${LLVM_LIBRARY_DIRS}") + set(LLVM_LIBRARIES ${tmplibs}) + endif() + + # When using the CMake LLVM module, LLVM_DEFINITIONS is a list + # instead of a string. Later, the list seperators would entirely + # disappear, replace them by spaces instead. A better fix would be + # to switch to add_definitions() instead of throwing strings around. + string(REPLACE ";" " " LLVM_CXXFLAGS "${LLVM_CXXFLAGS}") + else() + if (NOT FIND_LLVM_QUIETLY) + message(WARNING "Could not find llvm-config. Try manually setting LLVM_ROOT_DIR to the prebuilt LLVM prefix to use.") + endif() + endif() +else() + macro(llvm_set var flag) + if(LLVM_FIND_QUIETLY) + set(_quiet_arg ERROR_QUIET) + endif() + execute_process( + COMMAND ${LLVM_CONFIG} --${flag} + OUTPUT_VARIABLE LLVM_${var} + OUTPUT_STRIP_TRAILING_WHITESPACE + ${_quiet_arg} + ) + if(${ARGV2}) + file(TO_CMAKE_PATH "${LLVM_${var}}" LLVM_${var}) + endif() + endmacro() + macro(llvm_set_libs var flag prefix) + if(LLVM_FIND_QUIETLY) + set(_quiet_arg ERROR_QUIET) + endif() + execute_process( + COMMAND ${LLVM_CONFIG} --${flag} ${LLVM_FIND_COMPONENTS} + OUTPUT_VARIABLE tmplibs + OUTPUT_STRIP_TRAILING_WHITESPACE + ${_quiet_arg} + ) + file(TO_CMAKE_PATH "${tmplibs}" tmplibs) + string(REGEX REPLACE "([$^.[|*+?()]|])" "\\\\\\1" pattern "${prefix}/") + string(REGEX MATCHALL "${pattern}[^ ]+" LLVM_${var} ${tmplibs}) + endmacro() + + llvm_set(VERSION_STRING version) + llvm_set(CXXFLAGS cxxflags) + llvm_set(HOST_TARGET host-target) + llvm_set(INCLUDE_DIRS includedir true) + llvm_set(ROOT_DIR prefix true) + + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-2][\\.0-9A-Za-z]*") + # Versions below 3.3 do not support components objcarcopts, option + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "objcarcopts" index) + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "option" index) + endif() + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-4][\\.0-9A-Za-z]*") + # Versions below 3.5 do not support components lto, profiledata + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "lto" index) + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "profiledata" index) + endif() + if(${LLVM_VERSION_STRING} MATCHES "^3\\.[0-6][\\.0-9A-Za-z]*") + # Versions below 3.7 do not support components debuginfodwarf + # Only debuginfo is available + list(REMOVE_ITEM LLVM_FIND_COMPONENTS "debuginfodwarf" index) + list(APPEND LLVM_FIND_COMPONENTS "debuginfo") + endif() + + llvm_set(LDFLAGS ldflags) + if(NOT ${LLVM_VERSION_STRING} MATCHES "^3\\.[0-4][\\.0-9A-Za-z]*") + # In LLVM 3.5+, the system library dependencies (e.g. "-lz") are accessed + # using the separate "--system-libs" flag. + llvm_set(SYSTEM_LIBS system-libs) + string(REPLACE "\n" " " LLVM_LDFLAGS "${LLVM_LDFLAGS} ${LLVM_SYSTEM_LIBS}") + endif() + llvm_set(LIBRARY_DIRS libdir true) + llvm_set_libs(LIBRARIES libfiles "${LLVM_LIBRARY_DIRS}") +endif() + +# On CMake builds of LLVM, the output of llvm-config --cxxflags does not +# include -fno-rtti, leading to linker errors. Be sure to add it. +if(CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")) + if(NOT ${LLVM_CXXFLAGS} MATCHES "-fno-rtti") + set(LLVM_CXXFLAGS "${LLVM_CXXFLAGS} -fno-rtti") + endif() +endif() + +string(REGEX REPLACE "([0-9]+).*" "\\1" LLVM_VERSION_MAJOR "${LLVM_VERSION_STRING}" ) +string(REGEX REPLACE "[0-9]+\\.([0-9]+).*[A-Za-z]*" "\\1" LLVM_VERSION_MINOR "${LLVM_VERSION_STRING}" ) + +# Use the default CMake facilities for handling QUIET/REQUIRED. +include(FindPackageHandleStandardArgs) + +if(${CMAKE_VERSION} VERSION_LESS "2.8.4") + # The VERSION_VAR argument is not supported on pre-2.8.4, work around this. + set(VERSION_VAR dummy) +endif() + +find_package_handle_standard_args(LLVM + REQUIRED_VARS LLVM_ROOT_DIR LLVM_HOST_TARGET + VERSION_VAR LLVM_VERSION_STRING) + diff --git a/atdna/README.md b/atdna/README.md new file mode 100644 index 0000000..7e08d40 --- /dev/null +++ b/atdna/README.md @@ -0,0 +1,3 @@ +# atdna +[User Overview](http://libAthena.github.io/doc/atdna.html) +[DNA Record Reference](http://libAthena.github.io/doc/atdna-ref.html) diff --git a/atdna/atdnaConfig.cmake.in b/atdna/atdnaConfig.cmake.in new file mode 100644 index 0000000..a8f3219 --- /dev/null +++ b/atdna/atdnaConfig.cmake.in @@ -0,0 +1,52 @@ +# - Config file for the atdna package + +# Compute paths +get_filename_component(ATHENA_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +# Our library dependencies (contains definitions for IMPORTED targets) +if(NOT TARGET atdna AND NOT atdna_BINARY_DIR) + include("${ATHENA_CMAKE_DIR}/atdnaTargets.cmake") +endif() + +# Find Athena +find_package(Athena REQUIRED) + +# Super handy macro for adding atdna target +macro(atdna out) + # Make input files source-relative + set(ins "") + foreach(arg ${ARGN}) + list(APPEND ins ${CMAKE_CURRENT_SOURCE_DIR}/${arg}) + endforeach() + + # Get local include directories for atdna + get_property(incdirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + set(inccli "") + foreach(dir ${incdirs}) + list(APPEND inccli "-I${dir}") + endforeach() + + # Get local defines for atdna + get_property(cdefs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS) + set(cdefcli "") + foreach(def ${cdefs}) + list(APPEND cdefcli "-D${def}") + endforeach() + + # MS extra + unset(extraargs) + if(MSVC) + list(APPEND extraargs -fms-compatibility -fexceptions) + if(MSVC_VERSION EQUAL 1800) + list(APPEND extraargs -fms-compatibility-version=18.00) + elseif(MSVC_VERSION EQUAL 1900) + list(APPEND extraargs -fms-compatibility-version=19.00) + endif() + endif() + + # Make target + add_custom_command(OUTPUT ${out} COMMAND $ + ARGS ${extraargs} -o ${out} ${cdefcli} ${inccli} "-I${ATHENA_INCLUDE_DIR}" -isystem "@CONF_CLANG_INCLUDE_DIR@" ${ins} + DEPENDS ${ins} COMMENT "Generating DNA ${out}") +endmacro() + diff --git a/atdna/atdnaConfigVersion.cmake.in b/atdna/atdnaConfigVersion.cmake.in new file mode 100644 index 0000000..84060cc --- /dev/null +++ b/atdna/atdnaConfigVersion.cmake.in @@ -0,0 +1,12 @@ +set(PACKAGE_VERSION "@ATDNA_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() + diff --git a/atdna/main.cpp b/atdna/main.cpp new file mode 100644 index 0000000..52fe843 --- /dev/null +++ b/atdna/main.cpp @@ -0,0 +1,1654 @@ +#include +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Tooling.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Sema.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/Version.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/CommandLine.h" + +static unsigned AthenaError = 0; +#define ATHENA_DNA_BASETYPE "struct Athena::io::DNA" +#define ATHENA_DNA_YAMLTYPE "struct Athena::io::DNAYaml" +#define ATHENA_DNA_READER "__dna_reader" +#define ATHENA_DNA_WRITER "__dna_writer" +#define ATHENA_YAML_READER "__dna_docin" +#define ATHENA_YAML_WRITER "__dna_docout" + +#ifndef INSTALL_PREFIX +#define INSTALL_PREFIX /usr/local +#endif +#define XSTR(s) STR(s) +#define STR(s) #s + +static llvm::cl::opt Help("h", llvm::cl::desc("Alias for -help"), llvm::cl::Hidden); + +static llvm::cl::OptionCategory ATDNAFormatCategory("atdna options"); + +static llvm::cl::opt OutputFilename("o", + llvm::cl::desc("Specify output filename"), + llvm::cl::value_desc("filename"), + llvm::cl::Prefix); + +static llvm::cl::opt FExceptions("fexceptions", + llvm::cl::desc("Enable C++ Exceptions")); +static llvm::cl::opt FMSCompat("fms-compatibility", + llvm::cl::desc("Enable MS header compatibility")); +static llvm::cl::opt FMSCompatVersion("fms-compatibility-version", + llvm::cl::desc("Specify MS compatibility version (18.00 for VS2013, 19.00 for VS2015)")); + +static llvm::cl::list InputFilenames(llvm::cl::Positional, + llvm::cl::desc(""), + llvm::cl::OneOrMore); + +static llvm::cl::list IncludeSearchPaths("I", + llvm::cl::desc("Header search path"), + llvm::cl::Prefix); + +static llvm::cl::list SystemIncludeSearchPaths("isystem", + llvm::cl::desc("System Header search path")); + +static llvm::cl::list PreprocessorDefines("D", + llvm::cl::desc("Preprocessor define"), + llvm::cl::Prefix); + +static llvm::cl::opt EmitIncludes("emit-includes", + llvm::cl::desc("Emit DNA for included files (not just main file)")); + +/* LLVM 3.7 changed the stream type */ +#if LLVM_VERSION_MAJOR > 3 || (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) +using StreamOut = llvm::raw_pwrite_stream; +#else +using StreamOut = llvm::raw_fd_ostream; +#endif + +class ATDNAEmitVisitor : public clang::RecursiveASTVisitor +{ + clang::ASTContext& context; + StreamOut& fileOut; + + bool isDNARecord(const clang::CXXRecordDecl* record, std::string& baseDNA, bool& isYAML) + { + for (const clang::CXXBaseSpecifier& base : record->bases()) + { + const clang::QualType qtp = base.getType().getCanonicalType(); + if (!qtp.getAsString().compare(0, sizeof(ATHENA_DNA_YAMLTYPE)-1, ATHENA_DNA_YAMLTYPE)) + { + isYAML = true; + return true; + } + } + for (const clang::CXXBaseSpecifier& base : record->bases()) + { + const clang::QualType qtp = base.getType().getCanonicalType(); + if (!qtp.getAsString().compare(0, sizeof(ATHENA_DNA_BASETYPE)-1, ATHENA_DNA_BASETYPE)) + return true; + } + for (const clang::CXXBaseSpecifier& base : record->bases()) + { + clang::QualType qtp = base.getType().getCanonicalType(); + const clang::Type* tp = qtp.getTypePtrOrNull(); + if (tp) + { + const clang::CXXRecordDecl* rDecl = tp->getAsCXXRecordDecl(); + if (rDecl) + { + if (isDNARecord(rDecl, baseDNA, isYAML)) + { + bool hasRead = false; + bool hasWrite = false; + for (const clang::CXXMethodDecl* method : rDecl->methods()) + { + std::string compName = method->getDeclName().getAsString(); + if (!compName.compare("read")) + hasRead = true; + else if (!compName.compare("write")) + hasWrite = true; + } + if (hasRead && hasWrite) + baseDNA = rDecl->getName(); + return true; + } + } + } + } + return false; + } + + std::string GetOpString(const clang::Type* theType, unsigned width, + const std::string& fieldName, bool writerPass, + bool& isDNATypeOut) + { + isDNATypeOut = false; + if (writerPass) + { + if (theType->isBuiltinType()) + { + const clang::BuiltinType* bType = (clang::BuiltinType*)theType; + if (bType->isBooleanType()) + { + return ATHENA_DNA_WRITER ".writeBool(" + fieldName + ");"; + } + else if (bType->isUnsignedInteger()) + { + if (width == 8) + return ATHENA_DNA_WRITER ".writeUByte(" + fieldName + ");"; + else if (width == 16) + return ATHENA_DNA_WRITER ".writeUint16(" + fieldName + ");"; + else if (width == 32) + return ATHENA_DNA_WRITER ".writeUint32(" + fieldName + ");"; + else if (width == 64) + return ATHENA_DNA_WRITER ".writeUint64(" + fieldName + ");"; + } + else if (bType->isSignedInteger()) + { + if (width == 8) + return ATHENA_DNA_WRITER ".writeByte(" + fieldName + ");"; + else if (width == 16) + return ATHENA_DNA_WRITER ".writeInt16(" + fieldName + ");"; + else if (width == 32) + return ATHENA_DNA_WRITER ".writeInt32(" + fieldName + ");"; + else if (width == 64) + return ATHENA_DNA_WRITER ".writeInt64(" + fieldName + ");"; + } + else if (bType->isFloatingPoint()) + { + if (width == 32) + return ATHENA_DNA_WRITER ".writeFloat(" + fieldName + ");"; + else if (width == 64) + return ATHENA_DNA_WRITER ".writeDouble(" + fieldName + ");"; + } + } + else if (theType->isRecordType()) + { + const clang::CXXRecordDecl* rDecl = theType->getAsCXXRecordDecl(); + for (const clang::FieldDecl* field : rDecl->fields()) + { + if (!field->getName().compare("clangVec")) + { + const clang::VectorType* vType = (clang::VectorType*)field->getType().getTypePtr(); + if (vType->isVectorType()) + { + const clang::BuiltinType* eType = (clang::BuiltinType*)vType->getElementType().getTypePtr(); + if (!eType->isBuiltinType() || !eType->isFloatingPoint() || + context.getTypeInfo(eType).Width != 32) + continue; + if (vType->getNumElements() == 2) + return ATHENA_DNA_WRITER ".writeVec2f(" + fieldName + ");"; + else if (vType->getNumElements() == 3) + return ATHENA_DNA_WRITER ".writeVec3f(" + fieldName + ");"; + else if (vType->getNumElements() == 4) + return ATHENA_DNA_WRITER ".writeVec4f(" + fieldName + ");"; + } + } + } + std::string baseDNA; + bool isYAML = false; + if (isDNARecord(rDecl, baseDNA, isYAML)) + { + isDNATypeOut = true; + return "write(" ATHENA_DNA_WRITER ");"; + } + } + } + else + { + if (theType->isBuiltinType()) + { + const clang::BuiltinType* bType = (clang::BuiltinType*)theType; + if (bType->isBooleanType()) + { + return ATHENA_DNA_READER ".readBool()"; + } + else if (bType->isUnsignedInteger()) + { + if (width == 8) + return ATHENA_DNA_READER ".readUByte()"; + else if (width == 16) + return ATHENA_DNA_READER ".readUint16()"; + else if (width == 32) + return ATHENA_DNA_READER ".readUint32()"; + else if (width == 64) + return ATHENA_DNA_READER ".readUint64()"; + } + else if (bType->isSignedInteger()) + { + if (width == 8) + return ATHENA_DNA_READER ".readByte()"; + else if (width == 16) + return ATHENA_DNA_READER ".readInt16()"; + else if (width == 32) + return ATHENA_DNA_READER ".readInt32()"; + else if (width == 64) + return ATHENA_DNA_READER ".readInt64()"; + } + else if (bType->isFloatingPoint()) + { + if (width == 32) + return ATHENA_DNA_READER ".readFloat()"; + else if (width == 64) + return ATHENA_DNA_READER ".readDouble()"; + } + } + else if (theType->isRecordType()) + { + const clang::CXXRecordDecl* rDecl = theType->getAsCXXRecordDecl(); + for (const clang::FieldDecl* field : rDecl->fields()) + { + if (!field->getName().compare("clangVec")) + { + const clang::VectorType* vType = (clang::VectorType*)field->getType().getTypePtr(); + if (vType->isVectorType()) + { + const clang::BuiltinType* eType = (clang::BuiltinType*)vType->getElementType().getTypePtr(); + if (!eType->isBuiltinType() || !eType->isFloatingPoint() || + context.getTypeInfo(eType).Width != 32) + continue; + if (vType->getNumElements() == 2) + return ATHENA_DNA_READER ".readVec2f()"; + else if (vType->getNumElements() == 3) + return ATHENA_DNA_READER ".readVec3f()"; + else if (vType->getNumElements() == 4) + return ATHENA_DNA_READER ".readVec4f()"; + } + } + } + std::string baseDNA; + bool isYAML = false; + if (isDNARecord(rDecl, baseDNA, isYAML)) + { + isDNATypeOut = true; + return "read(" ATHENA_DNA_READER ");"; + } + } + } + return std::string(); + } + + std::string GetYAMLString(const clang::Type* theType, unsigned width, + const std::string& fieldName, const std::string& bareFieldName, + bool writerPass, bool& isDNATypeOut) + { + isDNATypeOut = false; + if (writerPass) + { + if (theType->isBuiltinType()) + { + const clang::BuiltinType* bType = (clang::BuiltinType*)theType; + if (bType->isBooleanType()) + { + return ATHENA_YAML_WRITER ".writeBool(\"" + bareFieldName + "\", " + fieldName + ");"; + } + else if (bType->isUnsignedInteger()) + { + if (width == 8) + return ATHENA_YAML_WRITER ".writeUByte(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 16) + return ATHENA_YAML_WRITER ".writeUint16(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 32) + return ATHENA_YAML_WRITER ".writeUint32(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 64) + return ATHENA_YAML_WRITER ".writeUint64(\"" + bareFieldName + "\", " + fieldName + ");"; + } + else if (bType->isSignedInteger()) + { + if (width == 8) + return ATHENA_YAML_WRITER ".writeByte(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 16) + return ATHENA_YAML_WRITER ".writeInt16(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 32) + return ATHENA_YAML_WRITER ".writeInt32(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 64) + return ATHENA_YAML_WRITER ".writeInt64(\"" + bareFieldName + "\", " + fieldName + ");"; + } + else if (bType->isFloatingPoint()) + { + if (width == 32) + return ATHENA_YAML_WRITER ".writeFloat(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (width == 64) + return ATHENA_YAML_WRITER ".writeDouble(\"" + bareFieldName + "\", " + fieldName + ");"; + } + } + else if (theType->isRecordType()) + { + const clang::CXXRecordDecl* rDecl = theType->getAsCXXRecordDecl(); + for (const clang::FieldDecl* field : rDecl->fields()) + { + if (!field->getName().compare("clangVec")) + { + const clang::VectorType* vType = (clang::VectorType*)field->getType().getTypePtr(); + if (vType->isVectorType()) + { + const clang::BuiltinType* eType = (clang::BuiltinType*)vType->getElementType().getTypePtr(); + if (!eType->isBuiltinType() || !eType->isFloatingPoint() || + context.getTypeInfo(eType).Width != 32) + continue; + if (vType->getNumElements() == 2) + return ATHENA_YAML_WRITER ".writeVec2f(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (vType->getNumElements() == 3) + return ATHENA_YAML_WRITER ".writeVec3f(\"" + bareFieldName + "\", " + fieldName + ");"; + else if (vType->getNumElements() == 4) + return ATHENA_YAML_WRITER ".writeVec4f(\"" + bareFieldName + "\", " + fieldName + ");"; + } + } + } + std::string baseDNA; + bool isYAML = false; + if (isDNARecord(rDecl, baseDNA, isYAML)) + { + isDNATypeOut = true; + return "toYAML(" ATHENA_YAML_WRITER ");"; + } + } + } + else + { + if (theType->isBuiltinType()) + { + const clang::BuiltinType* bType = (clang::BuiltinType*)theType; + if (bType->isBooleanType()) + { + return ATHENA_YAML_READER ".readBool(\"" + bareFieldName + "\")"; + } + else if (bType->isUnsignedInteger()) + { + if (width == 8) + return ATHENA_YAML_READER ".readUByte(\"" + bareFieldName + "\")"; + else if (width == 16) + return ATHENA_YAML_READER ".readUint16(\"" + bareFieldName + "\")"; + else if (width == 32) + return ATHENA_YAML_READER ".readUint32(\"" + bareFieldName + "\")"; + else if (width == 64) + return ATHENA_YAML_READER ".readUint64(\"" + bareFieldName + "\")"; + } + else if (bType->isSignedInteger()) + { + if (width == 8) + return ATHENA_YAML_READER ".readByte(\"" + bareFieldName + "\")"; + else if (width == 16) + return ATHENA_YAML_READER ".readInt16(\"" + bareFieldName + "\")"; + else if (width == 32) + return ATHENA_YAML_READER ".readInt32(\"" + bareFieldName + "\")"; + else if (width == 64) + return ATHENA_YAML_READER ".readInt64(\"" + bareFieldName + "\")"; + } + else if (bType->isFloatingPoint()) + { + if (width == 32) + return ATHENA_YAML_READER ".readFloat(\"" + bareFieldName + "\")"; + else if (width == 64) + return ATHENA_YAML_READER ".readDouble(\"" + bareFieldName + "\")"; + } + } + else if (theType->isRecordType()) + { + const clang::CXXRecordDecl* rDecl = theType->getAsCXXRecordDecl(); + for (const clang::FieldDecl* field : rDecl->fields()) + { + if (!field->getName().compare("clangVec")) + { + const clang::VectorType* vType = (clang::VectorType*)field->getType().getTypePtr(); + if (vType->isVectorType()) + { + const clang::BuiltinType* eType = (clang::BuiltinType*)vType->getElementType().getTypePtr(); + if (!eType->isBuiltinType() || !eType->isFloatingPoint() || + context.getTypeInfo(eType).Width != 32) + continue; + if (vType->getNumElements() == 2) + return ATHENA_YAML_READER ".readVec2f(\"" + bareFieldName + "\")"; + else if (vType->getNumElements() == 3) + return ATHENA_YAML_READER ".readVec3f(\"" + bareFieldName + "\")"; + else if (vType->getNumElements() == 4) + return ATHENA_YAML_READER ".readVec4f(\"" + bareFieldName + "\")"; + } + } + } + std::string baseDNA; + bool isYAML = false; + if (isDNARecord(rDecl, baseDNA, isYAML)) + { + isDNATypeOut = true; + return "fromYAML(" ATHENA_YAML_READER ");"; + } + } + } + return std::string(); + } + + void emitIOFuncs(clang::CXXRecordDecl* decl, const std::string& baseDNA) + { + /* Two passes - read then write */ + for (int p=0 ; p<2 ; ++p) + { + if (p) + fileOut << "void " << decl->getQualifiedNameAsString() << "::write(Athena::io::IStreamWriter& " ATHENA_DNA_WRITER ") const\n{\n"; + else + fileOut << "void " << decl->getQualifiedNameAsString() << "::read(Athena::io::IStreamReader& " ATHENA_DNA_READER ")\n{\n"; + int currentEndian = -1; + + if (baseDNA.size()) + { + if (p) + fileOut << " " << baseDNA << "::write(" ATHENA_DNA_WRITER ");\n"; + else + fileOut << " " << baseDNA << "::read(" ATHENA_DNA_READER ");\n"; + } + + for (const clang::FieldDecl* field : decl->fields()) + { + clang::QualType qualType = field->getType(); + clang::TypeInfo regTypeInfo = context.getTypeInfo(qualType); + const clang::Type* regType = qualType.getTypePtrOrNull(); + if (regType->getTypeClass() == clang::Type::Elaborated) + regType = regType->getUnqualifiedDesugaredType(); + + /* Resolve constant array */ + size_t arraySize = 1; + bool isArray = false; + if (regType->getTypeClass() == clang::Type::ConstantArray) + { + isArray = true; + const clang::ConstantArrayType* caType = (clang::ConstantArrayType*)regType; + arraySize = caType->getSize().getZExtValue(); + qualType = caType->getElementType(); + regTypeInfo = context.getTypeInfo(qualType); + regType = qualType.getTypePtrOrNull(); + if (regType->getTypeClass() == clang::Type::Elaborated) + regType = regType->getUnqualifiedDesugaredType(); + } + + for (int e=0 ; egetName().str() + subscript; + } + else + fieldName = field->getName(); + + if (regType->getTypeClass() == clang::Type::TemplateSpecialization) + { + const clang::TemplateSpecializationType* tsType = (const clang::TemplateSpecializationType*)regType; + const clang::TemplateDecl* tsDecl = tsType->getTemplateName().getAsTemplateDecl(); + const clang::TemplateParameterList* classParms = tsDecl->getTemplateParameters(); + + if (!tsDecl->getName().compare("Value")) + { + llvm::APSInt endian(64, -1); + const clang::Expr* endianExpr = nullptr; + if (classParms->size() >= 2) + { + const clang::NamedDecl* endianParm = classParms->getParam(1); + if (endianParm->getKind() == clang::Decl::NonTypeTemplateParm) + { + const clang::NonTypeTemplateParmDecl* nttParm = (clang::NonTypeTemplateParmDecl*)endianParm; + const clang::Expr* defArg = nttParm->getDefaultArgument(); + endianExpr = defArg; + if (!defArg->isIntegerConstantExpr(endian, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(defArg->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(defArg->getSourceRange(), true)); + } + continue; + } + } + } + + clang::QualType templateType; + std::string ioOp; + bool isDNAType = false; + const clang::TemplateArgument* typeArg = nullptr; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Type) + { + typeArg = &arg; + templateType = arg.getAsType().getCanonicalType(); + const clang::Type* type = arg.getAsType().getCanonicalType().getTypePtr(); + ioOp = GetOpString(type, regTypeInfo.Width, fieldName, p, isDNAType); + } + else if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr(); + endianExpr = expr; + if (expr->isIntegerConstantExpr(endian, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(expr->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(expr->getSourceRange(), true)); + } + continue; + } + } + } + + int endianVal = endian.getSExtValue(); + if (endianVal != 0 && endianVal != 1) + { + if (!p) + { + if (endianExpr) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(endianExpr->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(endianExpr->getSourceRange(), true)); + } + else + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + } + continue; + } + + if (ioOp.empty()) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Unable to use type '" + tsDecl->getName().str() + "' with Athena"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + continue; + } + + if (currentEndian != endianVal) + { + if (endianVal == 0) + fileOut << (p ? " " ATHENA_DNA_WRITER ".setEndian(Athena::LittleEndian);\n" : " " ATHENA_DNA_READER ".setEndian(Athena::LittleEndian);\n"); + else if (endianVal == 1) + fileOut << (p ? " " ATHENA_DNA_WRITER ".setEndian(Athena::BigEndian);\n" : " " ATHENA_DNA_READER ".setEndian(Athena::BigEndian);\n"); + currentEndian = endianVal; + } + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " << ioOp << ";\n"; + else + fileOut << " " << ioOp << "\n"; + } + else if (!tsDecl->getName().compare("Vector")) + { + llvm::APSInt endian(64, -1); + const clang::Expr* endianExpr = nullptr; + if (classParms->size() >= 3) + { + const clang::NamedDecl* endianParm = classParms->getParam(2); + if (endianParm->getKind() == clang::Decl::NonTypeTemplateParm) + { + const clang::NonTypeTemplateParmDecl* nttParm = (clang::NonTypeTemplateParmDecl*)endianParm; + const clang::Expr* defArg = nttParm->getDefaultArgument(); + endianExpr = defArg; + if (!defArg->isIntegerConstantExpr(endian, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(defArg->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(defArg->getSourceRange(), true)); + } + continue; + } + } + } + + clang::QualType templateType; + std::string ioOp; + bool isDNAType = false; + std::string sizeExpr; + const clang::TemplateArgument* typeArg = nullptr; + const clang::TemplateArgument* sizeArg = nullptr; + size_t idx = 0; + bool bad = false; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Type) + { + typeArg = &arg; + templateType = arg.getAsType().getCanonicalType(); + clang::TypeInfo typeInfo = context.getTypeInfo(templateType); + static const std::string elemStr = "elem"; + ioOp = GetOpString(templateType.getTypePtr(), typeInfo.Width, elemStr, p, isDNAType); + } + else if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + if (idx == 1) + { + sizeArg = &arg; + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)expr; + if (uExpr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + llvm::raw_string_ostream strStream(sizeExpr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + } + else if (idx == 2) + { + endianExpr = expr; + if (!expr->isIntegerConstantExpr(endian, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(expr->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(expr->getSourceRange(), true)); + } + bad = true; + break; + } + } + } + ++idx; + } + if (bad) + continue; + + int endianVal = endian.getSExtValue(); + if (endianVal != 0 && endianVal != 1) + { + if (!p) + { + if (endianExpr) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(endianExpr->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(endianExpr->getSourceRange(), true)); + } + else + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + } + continue; + } + + if (ioOp.empty()) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Unable to use type '" + templateType.getAsString() + "' with Athena"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + continue; + } + + if (sizeExpr.empty()) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Unable to use count variable with Athena"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + continue; + } + + if (currentEndian != endianVal) + { + if (endianVal == 0) + fileOut << (p ? " " ATHENA_DNA_WRITER ".setEndian(Athena::LittleEndian);\n" : " " ATHENA_DNA_READER ".setEndian(Athena::LittleEndian);\n"); + else if (endianVal == 1) + fileOut << (p ? " " ATHENA_DNA_WRITER ".setEndian(Athena::BigEndian);\n" : " " ATHENA_DNA_READER ".setEndian(Athena::BigEndian);\n"); + currentEndian = endianVal; + } + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + { + fileOut << " " << fieldName << ".clear();\n" + " " << fieldName << ".reserve(" << sizeExpr << ");\n"; + if (isDNAType) + fileOut << " for (size_t i=0 ; i<(" << sizeExpr << ") ; ++i)\n" + " {\n" + " " << fieldName << ".emplace_back();\n" + " " << fieldName << ".back()." << ioOp << "\n" + " }\n"; + else + fileOut << " for (size_t i=0 ; i<(" << sizeExpr << ") ; ++i)\n" + " " << fieldName << ".push_back(" << ioOp << ");\n"; + } + else + { + fileOut << " for (const auto& elem : " << fieldName << ")\n"; + if (isDNAType) + fileOut << " elem." << ioOp << "\n"; + else + fileOut << " " << ioOp << "\n"; + } + + } + else if (!tsDecl->getName().compare("Buffer")) + { + const clang::Expr* sizeExpr = nullptr; + std::string sizeExprStr; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)arg.getAsExpr()->IgnoreImpCasts(); + if (uExpr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + sizeExpr = argExpr; + llvm::raw_string_ostream strStream(sizeExprStr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + } + } + if (sizeExprStr.empty()) + { + if (!p) + { + if (sizeExpr) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(sizeExpr->getLocStart(), AthenaError); + diag.AddString("Unable to use size variable with Athena"); + diag.AddSourceRange(clang::CharSourceRange(sizeExpr->getSourceRange(), true)); + } + else + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Unable to use size variable with Athena"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + } + continue; + } + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + { + fileOut << " " << fieldName << ".reset(new atUint8[" << sizeExprStr << "]);\n" + " " ATHENA_DNA_READER ".readUBytesToBuf(" << fieldName << ".get(), " << sizeExprStr << ");\n"; + } + else + { + fileOut << " " ATHENA_DNA_WRITER ".writeUBytes(" << fieldName << ".get(), " << sizeExprStr << ");\n"; + } + } + else if (!tsDecl->getName().compare("String")) + { + const clang::Expr* sizeExpr = nullptr; + std::string sizeExprStr; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)expr; + llvm::APSInt sizeLiteral; + if (expr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + sizeExpr = argExpr; + llvm::raw_string_ostream strStream(sizeExprStr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + else if (expr->isIntegerConstantExpr(sizeLiteral, context)) + { + sizeExprStr = sizeLiteral.toString(10); + } + } + } + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " ATHENA_DNA_READER ".readString(" << sizeExprStr << ");\n"; + else + { + fileOut << " " ATHENA_DNA_WRITER ".writeString(" << fieldName; + if (sizeExprStr.size()) + fileOut << ", " << sizeExprStr; + fileOut << ");\n"; + } + } + else if (!tsDecl->getName().compare("WString")) + { + llvm::APSInt endian(64, -1); + const clang::Expr* endianExpr = nullptr; + if (classParms->size() >= 2) + { + const clang::NamedDecl* endianParm = classParms->getParam(1); + if (endianParm->getKind() == clang::Decl::NonTypeTemplateParm) + { + const clang::NonTypeTemplateParmDecl* nttParm = (clang::NonTypeTemplateParmDecl*)endianParm; + const clang::Expr* defArg = nttParm->getDefaultArgument(); + endianExpr = defArg; + if (!defArg->isIntegerConstantExpr(endian, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(defArg->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(defArg->getSourceRange(), true)); + } + continue; + } + } + } + + const clang::Expr* sizeExpr = nullptr; + std::string sizeExprStr; + size_t idx = 0; + bool bad = false; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + if (idx == 0) + { + llvm::APSInt sizeLiteral; + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)expr; + if (expr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + sizeExpr = argExpr; + llvm::raw_string_ostream strStream(sizeExprStr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + else if (expr->isIntegerConstantExpr(sizeLiteral, context)) + { + sizeExprStr = sizeLiteral.toString(10); + } + } + else if (idx == 1) + { + endianExpr = expr; + if (!expr->isIntegerConstantExpr(endian, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(expr->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(expr->getSourceRange(), true)); + } + bad = true; + break; + } + } + } + ++idx; + } + if (bad) + continue; + + int endianVal = endian.getSExtValue(); + if (endianVal != 0 && endianVal != 1) + { + if (!p) + { + if (endianExpr) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(endianExpr->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(endianExpr->getSourceRange(), true)); + } + else + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Endian value must be 'BigEndian' or 'LittleEndian'"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + } + continue; + } + + if (currentEndian != endianVal) + { + if (endianVal == 0) + fileOut << (p ? " " ATHENA_DNA_WRITER ".setEndian(Athena::LittleEndian);\n" : " " ATHENA_DNA_READER ".setEndian(Athena::LittleEndian);\n"); + else if (endianVal == 1) + fileOut << (p ? " " ATHENA_DNA_WRITER ".setEndian(Athena::BigEndian);\n" : " " ATHENA_DNA_READER ".setEndian(Athena::BigEndian);\n"); + currentEndian = endianVal; + } + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " ATHENA_DNA_READER ".readWString(" << sizeExprStr << ");\n"; + else + { + fileOut << " " ATHENA_DNA_WRITER ".writeWString(" << fieldName; + if (sizeExprStr.size()) + fileOut << ", " << sizeExprStr; + fileOut << ");\n"; + } + } + else if (!tsDecl->getName().compare("WStringAsString")) + { + const clang::Expr* sizeExpr = nullptr; + std::string sizeExprStr; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)expr; + llvm::APSInt sizeLiteral; + if (expr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + sizeExpr = argExpr; + llvm::raw_string_ostream strStream(sizeExprStr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + else if (expr->isIntegerConstantExpr(sizeLiteral, context)) + { + sizeExprStr = sizeLiteral.toString(10); + } + } + } + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " ATHENA_DNA_READER ".readWStringAsString(" << sizeExprStr << ");\n"; + else + { + fileOut << " " ATHENA_DNA_WRITER ".writeStringAsWString(" << fieldName; + if (sizeExprStr.size()) + fileOut << ", " << sizeExprStr; + fileOut << ");\n"; + } + } + else if (!tsDecl->getName().compare("Seek")) + { + size_t idx = 0; + const clang::Expr* offsetExpr = nullptr; + std::string offsetExprStr; + llvm::APSInt direction(64, 0); + const clang::Expr* directionExpr = nullptr; + bool bad = false; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + if (!idx) + { + offsetExpr = expr; + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)expr; + llvm::APSInt offsetLiteral; + if (expr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + offsetExpr = argExpr; + llvm::raw_string_ostream strStream(offsetExprStr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + else if (expr->isIntegerConstantExpr(offsetLiteral, context)) + { + offsetExprStr = offsetLiteral.toString(10); + } + } + else + { + directionExpr = expr; + if (!expr->isIntegerConstantExpr(direction, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(expr->getLocStart(), AthenaError); + diag.AddString("Unable to use non-constant direction expression in Athena"); + diag.AddSourceRange(clang::CharSourceRange(expr->getSourceRange(), true)); + } + bad = true; + break; + } + } + } + ++idx; + } + if (bad) + continue; + + int64_t directionVal = direction.getSExtValue(); + if (directionVal < 0 || directionVal > 2) + { + if (!p) + { + if (directionExpr) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(directionExpr->getLocStart(), AthenaError); + diag.AddString("Direction parameter must be 'Begin', 'Current', or 'End'"); + diag.AddSourceRange(clang::CharSourceRange(directionExpr->getSourceRange(), true)); + } + else + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Direction parameter must be 'Begin', 'Current', or 'End'"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + } + } + continue; + } + + fileOut << " /* " << fieldName << " */\n"; + if (directionVal == 0) + { + if (!p) + fileOut << " " ATHENA_DNA_READER ".seek(" << offsetExprStr << ", Athena::Begin);\n"; + else + fileOut << " " ATHENA_DNA_WRITER ".seek(" << offsetExprStr << ", Athena::Begin);\n"; + } + else if (directionVal == 1) + { + if (!p) + fileOut << " " ATHENA_DNA_READER ".seek(" << offsetExprStr << ", Athena::Current);\n"; + else + fileOut << " " ATHENA_DNA_WRITER ".seek(" << offsetExprStr << ", Athena::Current);\n"; + } + else if (directionVal == 2) + { + if (!p) + fileOut << " " ATHENA_DNA_READER ".seek(" << offsetExprStr << ", Athena::End);\n"; + else + fileOut << " " ATHENA_DNA_WRITER ".seek(" << offsetExprStr << ", Athena::End);\n"; + } + + } + else if (!tsDecl->getName().compare("Align")) + { + llvm::APSInt align(64, 0); + bool bad = false; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr(); + if (!expr->isIntegerConstantExpr(align, context)) + { + if (!p) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(expr->getLocStart(), AthenaError); + diag.AddString("Unable to use non-constant align expression in Athena"); + diag.AddSourceRange(clang::CharSourceRange(expr->getSourceRange(), true)); + } + bad = true; + break; + } + } + } + if (bad) + continue; + + int64_t alignVal = align.getSExtValue(); + if (alignVal) + { + fileOut << " /* " << fieldName << " */\n"; + if (alignVal == 32) + { + if (!p) + fileOut << " " ATHENA_DNA_READER ".seekAlign32();\n"; + else + fileOut << " " ATHENA_DNA_WRITER ".seekAlign32();\n"; + } + else if (align.isPowerOf2()) + { + if (!p) + fileOut << " " ATHENA_DNA_READER ".seek((" ATHENA_DNA_READER ".position() + " << alignVal-1 << ") & ~" << alignVal-1 << ", Athena::Begin);\n"; + else + fileOut << " " ATHENA_DNA_WRITER ".seek((" ATHENA_DNA_WRITER ".position() + " << alignVal-1 << ") & ~" << alignVal-1 << ", Athena::Begin);\n"; + } + else + { + if (!p) + fileOut << " " ATHENA_DNA_READER ".seek((" ATHENA_DNA_READER ".position() + " << alignVal-1 << ") / " << alignVal << " * " << alignVal << ", Athena::Begin);\n"; + else + fileOut << " " ATHENA_DNA_WRITER ".seek((" ATHENA_DNA_WRITER ".position() + " << alignVal-1 << ") / " << alignVal << " * " << alignVal << ", Athena::Begin);\n"; + } + } + } + + } + + else if (regType->getTypeClass() == clang::Type::Record) + { + const clang::CXXRecordDecl* cxxRDecl = regType->getAsCXXRecordDecl(); + std::string baseDNA; + bool isYAML = false; + if (cxxRDecl && isDNARecord(cxxRDecl, baseDNA, isYAML)) + { + fileOut << " /* " << fieldName << " */\n" + " " << fieldName << (p ? ".write(" ATHENA_DNA_WRITER ");\n" : ".read(" ATHENA_DNA_READER ");\n"); + currentEndian = -1; + break; + } + } + + } + + } + + fileOut << "}\n\n"; + } + } + + void emitYAMLFuncs(clang::CXXRecordDecl* decl, const std::string& baseDNA) + { + /* Two passes - read then write */ + for (int p=0 ; p<2 ; ++p) + { + if (p) + fileOut << "void " << decl->getQualifiedNameAsString() << "::toYAML(Athena::io::YAMLDocWriter& " ATHENA_YAML_WRITER ") const\n{\n"; + else + fileOut << "void " << decl->getQualifiedNameAsString() << "::fromYAML(Athena::io::YAMLDocReader& " ATHENA_YAML_READER ")\n{\n"; + + if (baseDNA.size()) + { + if (p) + fileOut << " " << baseDNA << "::toYAML(" ATHENA_YAML_WRITER ");\n"; + else + fileOut << " " << baseDNA << "::fromYAML(" ATHENA_YAML_READER ");\n"; + } + + for (const clang::FieldDecl* field : decl->fields()) + { + clang::QualType qualType = field->getType(); + clang::TypeInfo regTypeInfo = context.getTypeInfo(qualType); + const clang::Type* regType = qualType.getTypePtrOrNull(); + if (regType->getTypeClass() == clang::Type::Elaborated) + regType = regType->getUnqualifiedDesugaredType(); + + /* Resolve constant array */ + size_t arraySize = 1; + bool isArray = false; + if (regType->getTypeClass() == clang::Type::ConstantArray) + { + isArray = true; + const clang::ConstantArrayType* caType = (clang::ConstantArrayType*)regType; + arraySize = caType->getSize().getZExtValue(); + qualType = caType->getElementType(); + regTypeInfo = context.getTypeInfo(qualType); + regType = qualType.getTypePtrOrNull(); + if (regType->getTypeClass() == clang::Type::Elaborated) + regType = regType->getUnqualifiedDesugaredType(); + + if (!p) + fileOut << " " ATHENA_YAML_READER ".enterSubVector(\"" << field->getName() << "\");\n"; + else + fileOut << " " ATHENA_YAML_WRITER ".enterSubVector(\"" << field->getName() << "\");\n"; + } + + for (int e=0 ; egetName(); + std::string fieldName; + if (isArray) + { + char subscript[16]; + snprintf(subscript, 16, "[%d]", e); + fieldName = fieldNameBare + subscript; + } + else + fieldName = fieldNameBare; + + if (regType->getTypeClass() == clang::Type::TemplateSpecialization) + { + const clang::TemplateSpecializationType* tsType = (const clang::TemplateSpecializationType*)regType; + const clang::TemplateDecl* tsDecl = tsType->getTemplateName().getAsTemplateDecl(); + const clang::TemplateParameterList* classParms = tsDecl->getTemplateParameters(); + + if (!tsDecl->getName().compare("Value")) + { + llvm::APSInt endian(64, -1); + const clang::Expr* endianExpr = nullptr; + if (classParms->size() >= 2) + { + const clang::NamedDecl* endianParm = classParms->getParam(1); + if (endianParm->getKind() == clang::Decl::NonTypeTemplateParm) + { + const clang::NonTypeTemplateParmDecl* nttParm = (clang::NonTypeTemplateParmDecl*)endianParm; + const clang::Expr* defArg = nttParm->getDefaultArgument(); + endianExpr = defArg; + if (!defArg->isIntegerConstantExpr(endian, context)) + continue; + } + } + + clang::QualType templateType; + std::string ioOp; + bool isDNAType = false; + const clang::TemplateArgument* typeArg = nullptr; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Type) + { + typeArg = &arg; + templateType = arg.getAsType().getCanonicalType(); + const clang::Type* type = arg.getAsType().getCanonicalType().getTypePtr(); + ioOp = GetYAMLString(type, regTypeInfo.Width, fieldName, fieldNameBare, p, isDNAType); + } + else if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr(); + endianExpr = expr; + if (expr->isIntegerConstantExpr(endian, context)) + continue; + } + } + + int endianVal = endian.getSExtValue(); + if (endianVal != 0 && endianVal != 1) + continue; + + if (ioOp.empty()) + continue; + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " << ioOp << ";\n"; + else + fileOut << " " << ioOp << "\n"; + } + else if (!tsDecl->getName().compare("Vector")) + { + llvm::APSInt endian(64, -1); + const clang::Expr* endianExpr = nullptr; + if (classParms->size() >= 3) + { + const clang::NamedDecl* endianParm = classParms->getParam(2); + if (endianParm->getKind() == clang::Decl::NonTypeTemplateParm) + { + const clang::NonTypeTemplateParmDecl* nttParm = (clang::NonTypeTemplateParmDecl*)endianParm; + const clang::Expr* defArg = nttParm->getDefaultArgument(); + endianExpr = defArg; + if (!defArg->isIntegerConstantExpr(endian, context)) + continue; + } + } + + clang::QualType templateType; + std::string ioOp; + bool isDNAType = false; + std::string sizeExpr; + const clang::TemplateArgument* typeArg = nullptr; + const clang::TemplateArgument* sizeArg = nullptr; + size_t idx = 0; + bool bad = false; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Type) + { + typeArg = &arg; + templateType = arg.getAsType().getCanonicalType(); + clang::TypeInfo typeInfo = context.getTypeInfo(templateType); + ioOp = GetYAMLString(templateType.getTypePtr(), typeInfo.Width, "elem", fieldNameBare, p, isDNAType); + } + else if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + if (idx == 1) + { + sizeArg = &arg; + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)expr; + if (uExpr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + llvm::raw_string_ostream strStream(sizeExpr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + } + else if (idx == 2) + { + endianExpr = expr; + if (!expr->isIntegerConstantExpr(endian, context)) + { + bad = true; + break; + } + } + } + ++idx; + } + if (bad) + continue; + + int endianVal = endian.getSExtValue(); + if (endianVal != 0 && endianVal != 1) + continue; + + if (ioOp.empty()) + continue; + + if (sizeExpr.empty()) + continue; + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + { + fileOut << " " << fieldName << ".clear();\n" + " " << fieldName << ".reserve(" << sizeExpr << ");\n" + " " ATHENA_YAML_READER ".enterSubVector(\"" << fieldNameBare << "\");\n"; + if (isDNAType) + fileOut << " for (size_t i=0 ; i<(" << sizeExpr << ") ; ++i)\n" + " {\n" + " " << fieldName << ".emplace_back();\n" + " " ATHENA_YAML_READER ".enterSubRecord(nullptr);\n" + " " << fieldName << ".back()." << ioOp << "\n" + " " ATHENA_YAML_READER ".leaveSubRecord();\n" + " }\n"; + else + fileOut << " for (size_t i=0 ; i<(" << sizeExpr << ") ; ++i)\n" + " " << fieldName << ".push_back(" << ioOp << ");\n"; + fileOut << " " ATHENA_YAML_READER ".leaveSubVector();\n"; + } + else + { + fileOut << " " ATHENA_YAML_WRITER ".enterSubVector(\"" << fieldNameBare << "\");\n"; + if (isDNAType) + { + fileOut << " for (const auto& elem : " << fieldName << ")\n" + " {\n" + " " ATHENA_YAML_WRITER ".enterSubRecord(nullptr);\n" + " elem." << ioOp << "\n" + " " ATHENA_YAML_WRITER ".leaveSubRecord();\n" + " }\n"; + } + else + { + fileOut << " for (const auto& elem : " << fieldName << ")\n" + " " << ioOp << "\n"; + } + fileOut << " " ATHENA_YAML_WRITER ".leaveSubVector();\n"; + } + + } + else if (!tsDecl->getName().compare("Buffer")) + { + const clang::Expr* sizeExpr = nullptr; + std::string sizeExprStr; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::UnaryExprOrTypeTraitExpr* uExpr = (clang::UnaryExprOrTypeTraitExpr*)arg.getAsExpr()->IgnoreImpCasts(); + if (uExpr->getStmtClass() == clang::Stmt::UnaryExprOrTypeTraitExprClass && + uExpr->getKind() == clang::UETT_SizeOf) + { + const clang::Expr* argExpr = uExpr->getArgumentExpr(); + while (argExpr->getStmtClass() == clang::Stmt::ParenExprClass) + argExpr = ((clang::ParenExpr*)argExpr)->getSubExpr(); + sizeExpr = argExpr; + llvm::raw_string_ostream strStream(sizeExprStr); + argExpr->printPretty(strStream, nullptr, context.getPrintingPolicy()); + } + } + } + if (sizeExprStr.empty()) + continue; + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " ATHENA_YAML_READER ".readUBytes(\"" << fieldNameBare << "\");\n"; + else + fileOut << " " ATHENA_YAML_WRITER ".writeUBytes(\"" << fieldNameBare << "\", " << fieldName << ", " << sizeExprStr << ");\n"; + } + else if (!tsDecl->getName().compare("String") || + !tsDecl->getName().compare("WStringAsString")) + { + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " ATHENA_YAML_READER ".readString(\"" << fieldNameBare << "\");\n"; + else + fileOut << " " ATHENA_YAML_WRITER ".writeString(\"" << fieldNameBare << "\", " << fieldName << ");\n"; + } + else if (!tsDecl->getName().compare("WString")) + { + llvm::APSInt endian(64, -1); + const clang::Expr* endianExpr = nullptr; + if (classParms->size() >= 2) + { + const clang::NamedDecl* endianParm = classParms->getParam(1); + if (endianParm->getKind() == clang::Decl::NonTypeTemplateParm) + { + const clang::NonTypeTemplateParmDecl* nttParm = (clang::NonTypeTemplateParmDecl*)endianParm; + const clang::Expr* defArg = nttParm->getDefaultArgument(); + endianExpr = defArg; + if (!defArg->isIntegerConstantExpr(endian, context)) + continue; + } + } + + size_t idx = 0; + bool bad = false; + for (const clang::TemplateArgument& arg : *tsType) + { + if (arg.getKind() == clang::TemplateArgument::Expression) + { + const clang::Expr* expr = arg.getAsExpr()->IgnoreImpCasts(); + if (idx == 1) + { + endianExpr = expr; + if (!expr->isIntegerConstantExpr(endian, context)) + { + bad = true; + break; + } + } + } + ++idx; + } + if (bad) + continue; + + int endianVal = endian.getSExtValue(); + if (endianVal != 0 && endianVal != 1) + continue; + + fileOut << " /* " << fieldName << " */\n"; + if (!p) + fileOut << " " << fieldName << " = " ATHENA_YAML_READER ".readWString(\"" << fieldNameBare << "\");\n"; + else + fileOut << " " ATHENA_YAML_WRITER ".writeWString(\"" << fieldNameBare << "\", " << fieldName << ");\n"; + } + + } + + else if (regType->getTypeClass() == clang::Type::Record) + { + const clang::CXXRecordDecl* cxxRDecl = regType->getAsCXXRecordDecl(); + std::string baseDNA; + bool isYAML = false; + if (cxxRDecl && isDNARecord(cxxRDecl, baseDNA, isYAML)) + { + if (!p) + { + fileOut << " /* " << fieldName << " */\n" + " " ATHENA_YAML_READER ".enterSubRecord(\"" << fieldNameBare << "\");\n" + " " << fieldName << ".fromYAML(" ATHENA_YAML_READER ");\n" + " " ATHENA_YAML_READER ".leaveSubRecord();\n"; + } + else + { + fileOut << " /* " << fieldName << " */\n" + " " ATHENA_YAML_WRITER ".enterSubRecord(\"" << fieldNameBare << "\");\n" + " " << fieldName << ".toYAML(" ATHENA_YAML_WRITER ");\n" + " " ATHENA_YAML_WRITER ".leaveSubRecord();\n"; + } + break; + } + } + + } + + if (isArray) + { + if (!p) + fileOut << " " ATHENA_YAML_READER ".leaveSubVector();\n"; + else + fileOut << " " ATHENA_YAML_WRITER ".leaveSubVector();\n"; + } + + } + + fileOut << "}\n\n"; + } + } + +public: + explicit ATDNAEmitVisitor(clang::ASTContext& ctxin, StreamOut& fo) + : context(ctxin), fileOut(fo) {} + + bool VisitCXXRecordDecl(clang::CXXRecordDecl* decl) + { + if (!EmitIncludes && !context.getSourceManager().isInMainFile(decl->getLocation())) + return true; + + if (decl->isInvalidDecl() || !decl->hasDefinition()) + return true; + + if (!decl->getNumBases()) + return true; + + /* First ensure this inherits from struct Athena::io::DNA */ + std::string baseDNA; + bool isYAML = false; + if (!isDNARecord(decl, baseDNA, isYAML)) + return true; + + /* Make sure there aren't namespace conflicts or Delete meta type */ + for (const clang::FieldDecl* field : decl->fields()) + { + if (!field->getName().compare(ATHENA_DNA_READER) || + !field->getName().compare(ATHENA_DNA_WRITER)) + { + clang::DiagnosticBuilder diag = context.getDiagnostics().Report(field->getLocStart(), AthenaError); + diag.AddString("Field may not be named '" ATHENA_DNA_READER "' or '" ATHENA_DNA_WRITER "'"); + diag.AddSourceRange(clang::CharSourceRange(field->getSourceRange(), true)); + return true; + } + clang::QualType qualType = field->getType().getCanonicalType(); + const clang::Type* regType = qualType.getTypePtrOrNull(); + if (regType) + { + const clang::CXXRecordDecl* rDecl = regType->getAsCXXRecordDecl(); + if (rDecl) + { + if (!rDecl->getName().compare("Delete")) + { + const clang::CXXRecordDecl* rParentDecl = llvm::dyn_cast_or_null(rDecl->getParent()); + if (rParentDecl) + { + std::string parentCheck = rParentDecl->getTypeForDecl()->getCanonicalTypeInternal().getAsString(); + if (!parentCheck.compare(0, sizeof(ATHENA_DNA_BASETYPE)-1, ATHENA_DNA_BASETYPE)) + return true; + } + } + } + } + } + + emitIOFuncs(decl, baseDNA); + if (isYAML) + emitYAMLFuncs(decl, baseDNA); + + return true; + } +}; + +class ATDNAConsumer : public clang::ASTConsumer +{ + ATDNAEmitVisitor emitVisitor; + StreamOut& fileOut; +public: + explicit ATDNAConsumer(clang::ASTContext& context, StreamOut& fo) + : emitVisitor(context, fo), + fileOut(fo) {} + void HandleTranslationUnit(clang::ASTContext& context) + { + /* Write file head */ + fileOut << "/* Auto generated atdna implementation */\n" + "#include \n" + "#include \n" + "#include \n\n"; + for (const std::string& inputf : InputFilenames) + fileOut << "#include \"" << inputf << "\"\n"; + fileOut << "\n"; + + /* Emit file */ + emitVisitor.TraverseDecl(context.getTranslationUnitDecl()); + } +}; + +class ATDNAAction : public clang::ASTFrontendAction +{ +public: + explicit ATDNAAction() {} + std::unique_ptr CreateASTConsumer(clang::CompilerInstance& compiler, + llvm::StringRef /*filename*/) + { + StreamOut* fileout; + if (OutputFilename.size()) + fileout = compiler.createOutputFile(OutputFilename, false, true, "", "", true); + else + fileout = compiler.createDefaultOutputFile(false, "a", "cpp"); + AthenaError = compiler.getASTContext().getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Athena error: %0"); + return std::unique_ptr(new ATDNAConsumer(compiler.getASTContext(), *fileout)); + } +}; + +int main(int argc, const char** argv) +{ + llvm::cl::ParseCommandLineOptions(argc, argv, "Athena DNA Generator"); + if (Help) + llvm::cl::PrintHelpMessage(); + + std::vector args = {"clang-tool", + "-fsyntax-only", + "-std=c++11", + "-D__atdna__=1", + "-I" XSTR(INSTALL_PREFIX) "/lib/clang/" CLANG_VERSION_STRING "/include", + "-I" XSTR(INSTALL_PREFIX) "/include/Athena"}; + for (int a=1 ; a fman(new clang::FileManager(clang::FileSystemOptions())); + clang::tooling::ToolInvocation TI(args, new ATDNAAction, fman.get()); + if (!TI.run()) + return 1; + + return 0; +} + diff --git a/atdna/main.rc.in b/atdna/main.rc.in new file mode 100644 index 0000000..f25a1f2 --- /dev/null +++ b/atdna/main.rc.in @@ -0,0 +1,32 @@ +#include "winver.h" + +IDI_ICON1 ICON DISCARDABLE "@ATHENA_ICO@" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @ATDNA_MAJOR_VERSION@,@ATDNA_MINOR_VERSION@,@ATDNA_PATCH_VERSION@,0 + PRODUCTVERSION @ATDNA_MAJOR_VERSION@,@ATDNA_MINOR_VERSION@,@ATDNA_PATCH_VERSION@,0 + FILEFLAGS 0x0L + FILEFLAGSMASK 0x3fL + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "Jackoalan / Antidote" + VALUE "FileDescription", "ATDNA" + VALUE "FileVersion", "@ATDNA_MAJOR_VERSION@.@ATDNA_MINOR_VERSION@.@ATDNA_PATCH_VERSION@" + VALUE "LegalCopyright", "Copyright (C) 2015 Jackoalan / Antidote" + VALUE "InternalName", "atdna" + VALUE "OriginalFilename", "atdna.exe" + VALUE "ProductName", "ATDNA" + VALUE "ProductVersion", "@ATDNA_MAJOR_VERSION@.@ATDNA_MINOR_VERSION@.@ATDNA_PATCH_VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/atdna/test.hpp b/atdna/test.hpp new file mode 100644 index 0000000..beb202d --- /dev/null +++ b/atdna/test.hpp @@ -0,0 +1,75 @@ +#include + +using namespace Athena; +typedef io::DNAYaml BigDNA; + +struct TESTSubFile : public BigDNA +{ + DECL_DNA + DECL_YAML + Value sub1; + Value sub2; +}; + +struct TESTSubClassFile : public TESTSubFile +{ + DECL_DNA + DECL_YAML + Value sub3; + Value sub4; +}; + +struct TESTSubSubClassFile : public TESTSubClassFile +{ + DECL_DNA + DECL_YAML + Value sub5; + Value sub6; +}; + +struct TESTFile : public BigDNA +{ + DECL_DNA + DECL_YAML + Value varBool; + Value var32; + Value var16; + Value vec3; + Value vec4; + + struct TESTNestedSubFile : public BigDNA + { + DECL_DNA + DECL_YAML + Value nestSub1; + Value nestSub2; + } nestedSubFile; + + TESTSubFile subFile; + + Align<4> align; + + struct TESTExplicitSubFile : public BigDNA + { + DECL_EXPLICIT_DNA + DECL_YAML + Value explSub1; + Value explSub2; + } explSubFile; + + Value arrCount[2]; + Vector array; + + Seek<21, Current> seek; + + Value arrCount2; + Vector array2; + + Value bufSz; + Buffer buf; + + String<32> str; + WString<64> wstr; + WStringAsString<> utf8str[5]; +}; +